]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_app_session.c
60aef9693fb9bd46822efd0c978d097d49fb91d5
[OpenSRF.git] / src / libopensrf / osrf_app_session.c
1 /**
2         @file osrf_app_session.c
3         @brief Implementation of osrfAppSession.
4 */
5
6 #include <time.h>
7 #include "opensrf/osrf_app_session.h"
8 #include "opensrf/osrf_stack.h"
9
10 struct osrf_app_request_struct {
11         /** The controlling session. */
12         struct osrf_app_session_struct* session;
13
14         /** Request id.  It is the same as the thread_trace of the REQUEST message
15                 for which it was created.
16         */
17         int request_id;
18         /** True if we have received a 'request complete' message from our request. */
19         int complete;
20         /** The original REQUEST message payload. */
21         osrfMessage* payload;
22         /** Linked list of responses to the request. */
23         osrfMessage* result;
24
25         /** Boolean; if true, then a call that is waiting on a response will reset the
26         timeout and set this variable back to false. */
27         int reset_timeout;
28         /** Linkage pointers for a linked list.  We maintain a hash table of pending requests,
29             and each slot of the hash table is a doubly linked list. */
30         osrfAppRequest* next;
31         osrfAppRequest* prev;
32 };
33
34 static inline unsigned int request_id_hash( int req_id );
35 static osrfAppRequest* find_app_request( const osrfAppSession* session, int req_id );
36 static void add_app_request( osrfAppSession* session, osrfAppRequest* req );
37
38 /* Send the given message */
39 static int _osrf_app_session_send( osrfAppSession*, osrfMessage* msg );
40
41 static int osrfAppSessionMakeLocaleRequest(
42                 osrfAppSession* session, const jsonObject* params, const char* method_name,
43                 int protocol, osrfStringArray* param_strings, char* locale );
44
45 /** @brief The global session cache.
46
47         Key: session_id.  Data: osrfAppSession.
48 */
49 static osrfHash* osrfAppSessionCache = NULL;
50
51 // --------------------------------------------------------------------------
52 // Request API
53 // --------------------------------------------------------------------------
54
55 /**
56         @brief Create a new osrfAppRequest.
57         @param session Pointer to the osrfAppSession that will own the new osrfAppRequest.
58         @param msg Pointer to the osrfMessage representing the request.
59         @return Pointer to the new osrfAppRequest.
60
61         The calling code is responsible for freeing the osrfAppRequest by calling
62         _osrf_app_request_free().
63 */
64 static osrfAppRequest* _osrf_app_request_init(
65                 osrfAppSession* session, osrfMessage* msg ) {
66
67         osrfAppRequest* req = safe_malloc(sizeof(osrfAppRequest));
68
69         req->session        = session;
70         req->request_id     = msg->thread_trace;
71         req->complete       = 0;
72         req->payload        = msg;
73         req->result         = NULL;
74         req->reset_timeout  = 0;
75         req->next           = NULL;
76         req->prev           = NULL;
77
78         return req;
79 }
80
81
82 /**
83         @brief Free an osrfAppRequest and everything it owns.
84         @param req Pointer to an osrfAppRequest.
85 */
86 static void _osrf_app_request_free( osrfAppRequest * req ) {
87         if( req ) {
88                 if( req->payload )
89                         osrfMessageFree( req->payload );
90
91                 /* Free the messages in the result queue */
92                 osrfMessage* next_msg;
93                 while( req->result ) {
94                         next_msg = req->result->next;
95                         osrfMessageFree( req->result );
96                         req->result = next_msg;
97                 }
98
99                 free( req );
100         }
101 }
102
103 /**
104         @brief Append a new message to the list of responses to a request.
105         @param req Pointer to the osrfAppRequest for the original REQUEST message.
106         @param result Pointer to an osrfMessage received in response to the request.
107
108         We maintain a linked list of response messages, and traverse it to find the end.
109 */
110 static void _osrf_app_request_push_queue( osrfAppRequest* req, osrfMessage* result ){
111         if(req == NULL || result == NULL)
112                 return;
113
114         osrfLogDebug( OSRF_LOG_MARK, "App Session pushing request [%d] onto request queue",
115                         result->thread_trace );
116         if(req->result == NULL) {
117                 req->result = result;   // Add the first node
118
119         } else {
120
121                 // Find the last node in the list, and append the new node to it
122                 osrfMessage* ptr = req->result;
123                 osrfMessage* ptr2 = req->result->next;
124                 while( ptr2 ) {
125                         ptr = ptr2;
126                         ptr2 = ptr2->next;
127                 }
128                 ptr->next = result;
129         }
130 }
131
132 /**
133         @brief Remove an osrfAppRequest (identified by request_id) from an osrfAppSession.
134         @param session Pointer to the osrfAppSession that owns the osrfAppRequest.
135         @param req_id request_id of the osrfAppRequest to be removed.
136 */
137 void osrf_app_session_request_finish( osrfAppSession* session, int req_id ) {
138
139         if( session ) {
140                 // Search the hash table for the request in question
141                 unsigned int index = request_id_hash( req_id );
142                 osrfAppRequest* old_req = session->request_hash[ index ];
143                 while( old_req ) {
144                         if( old_req->request_id == req_id )
145                                 break;
146                         else
147                                 old_req = old_req->next;
148                 }
149
150                 if( old_req ) {
151                         // Remove the request from the doubly linked list
152                         if( old_req->prev )
153                                 old_req->prev->next = old_req->next;
154                         else
155                                 session->request_hash[ index ] = old_req->next;
156
157                         if( old_req->next )
158                                 old_req->next->prev = old_req->prev;
159
160                         _osrf_app_request_free( old_req );
161                 }
162         }
163 }
164
165 /**
166         @brief Derive a hash key from a request id.
167         @param req_id The request id.
168         @return The corresponding hash key; an index into request_hash[].
169
170         If OSRF_REQUEST_HASH_SIZE is a power of two, then this calculation should
171         reduce to a binary AND.
172 */
173 static inline unsigned int request_id_hash( int req_id ) {
174         return ((unsigned int) req_id ) % OSRF_REQUEST_HASH_SIZE;
175 }
176
177 /**
178         @brief Search for an osrfAppRequest in the hash table, given a request id.
179         @param session Pointer to the relevant osrfAppSession.
180         @param req_id The request_id of the osrfAppRequest being sought.
181         @return A pointer to the osrfAppRequest if found, or NULL if not.
182 */
183 static osrfAppRequest* find_app_request( const osrfAppSession* session, int req_id ) {
184
185         osrfAppRequest* req = session->request_hash[ request_id_hash( req_id) ];
186         while( req ) {
187                 if( req->request_id == req_id )
188                         break;
189                 else
190                         req = req->next;
191         }
192
193         return req;
194 }
195
196 /**
197         @brief Add an osrfAppRequest to the hash table of a given osrfAppSession.
198         @param session Pointer to the session to which the request belongs.
199         @param req Pointer to the osrfAppRequest to be stored.
200
201         Find the right spot in the hash table; then add the request to the linked list at that
202         spot.  We just add it to the head of the list, without trying to maintain any particular
203         ordering.
204 */
205 static void add_app_request( osrfAppSession* session, osrfAppRequest* req ) {
206         if( session && req ) {
207                 unsigned int index = request_id_hash( req->request_id );
208                 req->next = session->request_hash[ index ];
209                 req->prev = NULL;
210                 session->request_hash[ index ] = req;
211         }
212 }
213
214 /**
215         @brief Set the timeout for a request to one second.
216         @param session Pointer to the relevant osrfAppSession.
217         @param req_id Request ID of the request whose timeout is to be reset.
218
219         The request to be reset is identified by the combination of session and request id.
220 */
221 void osrf_app_session_request_reset_timeout( osrfAppSession* session, int req_id ) {
222         if(session == NULL)
223                 return;
224         osrfLogDebug( OSRF_LOG_MARK, "Resetting request timeout %d", req_id );
225         osrfAppRequest* req = find_app_request( session, req_id );
226         if( req )
227                 req->reset_timeout = 1;
228 }
229
230 /**
231         Checks the receive queue for messages.  If any are found, the first
232         is popped off and returned.  Otherwise, this method will wait at most timeout
233         seconds for a message to appear in the receive queue.  Once it arrives it is returned.
234         If no messages arrive in the timeout provided, null is returned.
235 */
236 /**
237         @brief Return the next response message for a given request, subject to a timeout.
238         @param req Pointer to the osrfAppRequest representing the request.
239         @param timeout Maxmimum time to wait, in seconds.
240
241         @return Pointer to the next osrfMessage for this request, if one is available, or if it
242         becomes available before the end of the timeout; otherwise NULL;
243
244         If there is already a request available in the input queue, dequeuand return it
245         immediately.
246 */
247 static osrfMessage* _osrf_app_request_recv( osrfAppRequest* req, int timeout ) {
248
249         if(req == NULL) return NULL;
250
251         if( req->result != NULL ) {
252                 /* Dequeue the next message in the list */
253                 osrfMessage* tmp_msg = req->result;
254                 req->result = req->result->next;
255                 return tmp_msg;
256         }
257
258         time_t start = time(NULL);
259         time_t remaining = (time_t) timeout;
260
261         while( remaining >= 0 ) {
262                 /* tell the session to wait for stuff */
263                 osrfLogDebug( OSRF_LOG_MARK,  "In app_request receive with remaining time [%d]",
264                                 (int) remaining );
265
266                 osrf_app_session_queue_wait( req->session, 0, NULL );
267                 if(req->session->transport_error) {
268                         osrfLogError(OSRF_LOG_MARK, "Transport error in recv()");
269                         return NULL;
270                 }
271
272                 if( req->result != NULL ) { /* if we received anything */
273                         /* pop off the first message in the list */
274                         osrfLogDebug( OSRF_LOG_MARK, "app_request_recv received a message, returning it" );
275                         osrfMessage* ret_msg = req->result;
276                         req->result = ret_msg->next;
277                         if (ret_msg->sender_locale)
278                                 osrf_app_session_set_locale(req->session, ret_msg->sender_locale);
279
280                         return ret_msg;
281                 }
282
283                 if( req->complete )
284                         return NULL;
285
286                 osrf_app_session_queue_wait( req->session, (int) remaining, NULL );
287
288                 if(req->session->transport_error) {
289                         osrfLogError(OSRF_LOG_MARK, "Transport error in recv()");
290                         return NULL;
291                 }
292
293                 if( req->result != NULL ) { /* if we received anything */
294                         /* pop off the first message in the list */
295                         osrfLogDebug( OSRF_LOG_MARK,  "app_request_recv received a message, returning it");
296                         osrfMessage* ret_msg = req->result;
297                         req->result = ret_msg->next;
298                         if (ret_msg->sender_locale)
299                                 osrf_app_session_set_locale(req->session, ret_msg->sender_locale);
300
301                         return ret_msg;
302                 }
303                 if( req->complete )
304                         return NULL;
305
306                 if(req->reset_timeout) {
307                         remaining = (time_t) timeout;
308                         req->reset_timeout = 0;
309                         osrfLogDebug( OSRF_LOG_MARK, "Received a timeout reset");
310                 } else {
311                         remaining -= (int) (time(NULL) - start);
312                 }
313         }
314
315         char* paramString = jsonObjectToJSON(req->payload->_params);
316         osrfLogInfo( OSRF_LOG_MARK, "Returning NULL from app_request_recv after timeout: %s %s",
317                 req->payload->method_name, paramString);
318         free(paramString);
319
320         return NULL;
321 }
322
323 // --------------------------------------------------------------------------
324 // Session API
325 // --------------------------------------------------------------------------
326
327 /**
328         @brief Install a copy of a locale string in a specified session.
329         @param session Pointer to the osrfAppSession in which the locale is to be installed.
330         @param locale The locale string to be copied and installed.
331         @return A pointer to the installed copy of the locale string.
332 */
333 char* osrf_app_session_set_locale( osrfAppSession* session, const char* locale ) {
334         if (!session || !locale)
335                 return NULL;
336
337         if(session->session_locale) {
338                 if( strlen(session->session_locale) >= strlen(locale) ) {
339                         /* There's room available; just copy */
340                         strcpy(session->session_locale, locale);
341                 } else {
342                         free(session->session_locale);
343                         session->session_locale = strdup( locale );
344                 }
345         } else {
346                 session->session_locale = strdup( locale );
347         }
348
349         return session->session_locale;
350 }
351
352 /**
353         @brief Find the osrfAppSession for a given session id.
354         @param session_id The session id to look for.
355         @return Pointer to the corresponding osrfAppSession if found, or NULL if not.
356
357         Search the global session cache for the specified session id.
358 */
359 osrfAppSession* osrf_app_session_find_session( const char* session_id ) {
360         if(session_id)
361                 return osrfHashGet( osrfAppSessionCache, session_id );
362         return NULL;
363 }
364
365
366 /**
367         @brief Add a session to the global session cache, keyed by session id.
368         @param session Pointer to the osrfAppSession to be added.
369
370         If a cache doesn't exist yet, create one.  It's an osrfHash using session ids for the
371         key and osrfAppSessions for the data.
372 */
373 static void _osrf_app_session_push_session( osrfAppSession* session ) {
374         if( session ) {
375                 if( osrfAppSessionCache == NULL )
376                         osrfAppSessionCache = osrfNewHash();
377                 if( osrfHashGet( osrfAppSessionCache, session->session_id ) )
378                         return;   // A session with this id is already in the cache.  Shouldn't happen.
379                 osrfHashSet( osrfAppSessionCache, session, session->session_id );
380         }
381 }
382
383 /**
384         @brief Create an osrfAppSession for a client.
385         @param remote_service Name of the service to which to connect
386         @return Pointer to the new osrfAppSession if successful, or NULL upon error.
387
388         Allocate memory for an osrfAppSession, and initialize it as follows:
389
390         - For talking with Jabber, grab an existing transport_client that must have been
391         already set up by a prior call to osrfSystemBootstrapClientResc().
392         - Build a Jabber ID for addressing the service.
393         - Build a session ID based on a fine-grained timestamp and a process ID.  This ID is
394         expected to be unique across the system, but uniqueness is not strictly guaranteed.
395         - Initialize various other bits and scraps.
396         - Add the session to the global session cache.
397
398         Do @em not connect to the service at this point.
399 */
400 osrfAppSession* osrfAppSessionClientInit( const char* remote_service ) {
401
402         if (!remote_service) {
403                 osrfLogWarning( OSRF_LOG_MARK, "No remote service specified in osrfAppSessionClientInit");
404                 return NULL;
405         }
406
407         osrfAppSession* session = safe_malloc(sizeof(osrfAppSession));
408
409         // Grab an existing transport_client for talking with Jabber
410         session->transport_handle = osrfSystemGetTransportClient();
411         if( session->transport_handle == NULL ) {
412                 osrfLogWarning( OSRF_LOG_MARK, "No transport client for service 'client'");
413                 free( session );
414                 return NULL;
415         }
416
417         // Get a list of domain names from the config settings;
418         // ignore all but the first one in the list.
419         osrfStringArray* arr = osrfNewStringArray(8);
420         osrfConfigGetValueList(NULL, arr, "/domain");
421         const char* domain = osrfStringArrayGetString(arr, 0);
422         if (!domain) {
423                 osrfLogWarning( OSRF_LOG_MARK, "No domains specified in the OpenSRF config file");
424                 free( session );
425                 osrfStringArrayFree(arr);
426                 return NULL;
427         }
428
429         // Get a router name from the config settings.
430         char* router_name = osrfConfigGetValue(NULL, "/router_name");
431         if (!router_name) {
432                 osrfLogWarning( OSRF_LOG_MARK, "No router name specified in the OpenSRF config file");
433                 free( session );
434                 osrfStringArrayFree(arr);
435                 return NULL;
436         }
437
438         char target_buf[512];
439         target_buf[ 0 ] = '\0';
440
441         // Using the router name, domain, and service name,
442         // build a Jabber ID for addressing the service.
443         int len = snprintf( target_buf, sizeof(target_buf), "%s@%s/%s",
444                         router_name ? router_name : "(null)",
445                         domain ? domain : "(null)",
446                         remote_service ? remote_service : "(null)" );
447         osrfStringArrayFree(arr);
448         free(router_name);
449
450         if( len >= sizeof( target_buf ) ) {
451                 osrfLogWarning( OSRF_LOG_MARK, "Buffer overflow for remote_id");
452                 free( session );
453                 return NULL;
454         }
455
456         session->remote_id = strdup(target_buf);
457         session->orig_remote_id = strdup(session->remote_id);
458         session->remote_service = strdup(remote_service);
459         session->session_locale = NULL;
460         session->transport_error = 0;
461
462         #ifdef ASSUME_STATELESS
463         session->stateless = 1;
464         osrfLogDebug( OSRF_LOG_MARK, "%s session is stateless", remote_service );
465         #else
466         session->stateless = 0;
467         osrfLogDebug( OSRF_LOG_MARK, "%s session is NOT stateless", remote_service );
468         #endif
469
470         /* build a chunky, random session id */
471         char id[256];
472
473         snprintf(id, sizeof(id), "%f.%d%ld", get_timestamp_millis(), (int)time(NULL), (long) getpid());
474         session->session_id = strdup(id);
475         osrfLogDebug( OSRF_LOG_MARK,  "Building a new client session with id [%s] [%s]",
476                         session->remote_service, session->session_id );
477
478         session->thread_trace = 0;
479         session->state = OSRF_SESSION_DISCONNECTED;
480         session->type = OSRF_SESSION_CLIENT;
481
482         session->userData = NULL;
483         session->userDataFree = NULL;
484
485         // Initialize the hash table
486         int i;
487         for( i = 0; i < OSRF_REQUEST_HASH_SIZE; ++i )
488                 session->request_hash[ i ] = NULL;
489
490         _osrf_app_session_push_session( session );
491         return session;
492 }
493
494 /**
495         @brief Find or create an osrfAppSession for a server.
496         @param session_id The session ID.  In practice this comes from the thread member of
497         the transport message from the client.
498         @param our_app The name of the service being provided.
499         @param remote_id Jabber ID of the client.
500         @return Pointer to the newly created osrfAppSession if successful, or NULL upon failure.
501
502         First, look in the global session cache for session with the specified session ID.  IF
503         you find one, return it immediately, ignoring the values of the @a our_app and @a
504         remote_id parameters.  Otherwise:
505
506         - Allocate memory for an osrfAppSession.
507         - For talking with Jabber, grab an existing transport_client that must have been
508         already set up by a prior call to osrfSystemBootstrapClientResc().
509         - Install a copy of the @a our_app string as remote_service.
510         - Install copies of the @a remote_id string as remote_id and orig_remote_id.
511         - Initialize various other bits and scraps.
512         - Add the session to the global session cache.
513
514         Do @em not respond to the client at this point.
515 */
516 osrfAppSession* osrf_app_server_session_init(
517                 const char* session_id, const char* our_app, const char* remote_id ) {
518
519         osrfLogDebug( OSRF_LOG_MARK, "Initing server session with session id %s, service %s,"
520                         " and remote_id %s", session_id, our_app, remote_id );
521
522         // If such a session already exists, return it without further ado.
523         // In practice this should never happen, and should probably be treated as an
524         // error if it ever does.
525         osrfAppSession* session = osrf_app_session_find_session( session_id );
526         if(session)
527                 return session;
528
529         session = safe_malloc(sizeof(osrfAppSession));
530
531         // Grab an existing transport_client for talking with Jabber
532         session->transport_handle = osrfSystemGetTransportClient();
533         if( session->transport_handle == NULL ) {
534                 osrfLogWarning( OSRF_LOG_MARK, "No transport client for service '%s'", our_app );
535                 free(session);
536                 return NULL;
537         }
538
539         // Decide from a config setting whether the session is stateless or not.  However
540         // this determination is pointless because it will immediately be overruled according
541         // to the compile-time macro ASSUME_STATELESS.
542         int stateless = 0;
543         char* statel = osrf_settings_host_value("/apps/%s/stateless", our_app );
544         if(statel) stateless = atoi(statel);
545         free(statel);
546
547         session->remote_id = strdup(remote_id);
548         session->orig_remote_id = strdup(remote_id);
549         session->session_id = strdup(session_id);
550         session->remote_service = strdup(our_app);
551         session->stateless = stateless;
552
553         #ifdef ASSUME_STATELESS
554         session->stateless = 1;
555         #else
556         session->stateless = 0;
557         #endif
558
559         session->thread_trace = 0;
560         session->state = OSRF_SESSION_DISCONNECTED;
561         session->type = OSRF_SESSION_SERVER;
562         session->session_locale = NULL;
563
564         session->userData = NULL;
565         session->userDataFree = NULL;
566
567         // Initialize the hash table
568         int i;
569         for( i = 0; i < OSRF_REQUEST_HASH_SIZE; ++i )
570                 session->request_hash[ i ] = NULL;
571
572         _osrf_app_session_push_session( session );
573         return session;
574 }
575
576 /**
577         @brief Create a REQUEST message, send it, and save it for future reference.
578         @param session Pointer to the current session, which has the addressing information.
579         @param params One way of specifying the parameters for the method.
580         @param method_name The name of the method to be called.
581         @param protocol Protocol.
582         @param param_strings Another way of specifying the parameters for the method.
583         @return The request ID of the resulting REQUEST message, or -1 upon error.
584
585         DEPRECATED.  Use osrfAppSessionSendRequest() instead.  It is identical except that it
586         doesn't use the param_strings argument, which is redundant, confusing, and unused.
587
588         If @a params is non-NULL, use it to specify the parameters to the method.  Otherwise
589         use @a param_strings.
590
591         If @a params points to a JSON_ARRAY, then pass each element of the array as a separate
592         parameter.  If @a params points to any other kind of jsonObject, pass it as a single
593         parameter.
594
595         If @a params is NULL, and @a param_strings is not NULL, then each pointer in the
596         osrfStringArray must point to a JSON string encoding a parameter.  Pass them.
597
598         At this writing, all calls to this function use @a params to pass parameters, rather than
599         @a param_strings.
600
601         This function is a thin wrapper for osrfAppSessionMakeLocaleRequest().
602 */
603 int osrfAppSessionMakeRequest(
604                 osrfAppSession* session, const jsonObject* params,
605                 const char* method_name, int protocol, osrfStringArray* param_strings ) {
606
607         osrfLogWarning( OSRF_LOG_MARK, "Function osrfAppSessionMakeRequest() is deprecasted; "
608                         "call osrfAppSessionSendRequest() instead" );
609         return osrfAppSessionMakeLocaleRequest( session, params,
610                         method_name, protocol, param_strings, NULL );
611 }
612
613 /**
614         @brief Create a REQUEST message, send it, and save it for future reference.
615         @param session Pointer to the current session, which has the addressing information.
616         @param params One way of specifying the parameters for the method.
617         @param method_name The name of the method to be called.
618         @param protocol Protocol.
619         @return The request ID of the resulting REQUEST message, or -1 upon error.
620
621         If @a params points to a JSON_ARRAY, then pass each element of the array as a separate
622         parameter.  If @a params points to any other kind of jsonObject, pass it as a single
623         parameter.
624
625         This function is a thin wrapper for osrfAppSessionMakeLocaleRequest().
626 */
627 int osrfAppSessionSendRequest( osrfAppSession* session, const jsonObject* params,
628                 const char* method_name, int protocol ) {
629
630         return osrfAppSessionMakeLocaleRequest( session, params,
631                  method_name, protocol, NULL, NULL );
632 }
633
634 /**
635         @brief Create a REQUEST message, send it, and save it for future reference.
636         @param session Pointer to the current session, which has the addressing information.
637         @param params One way of specifying the parameters for the method.
638         @param method_name The name of the method to be called.
639         @param protocol Protocol.
640         @param param_strings Another way of specifying the parameters for the method.
641         @param locale Pointer to a locale string.
642         @return The request ID of the resulting REQUEST message, or -1 upon error.
643
644         See the discussion of osrfAppSessionSendRequest(), which at this writing is the only
645         place that calls this function, except for the similar but deprecated function
646         osrfAppSessionMakeRequest().
647
648         At this writing, the @a param_strings and @a locale parameters are always NULL.
649 */
650 static int osrfAppSessionMakeLocaleRequest(
651                 osrfAppSession* session, const jsonObject* params, const char* method_name,
652                 int protocol, osrfStringArray* param_strings, char* locale ) {
653
654         if(session == NULL) return -1;
655
656         osrfLogMkXid();
657
658         osrfMessage* req_msg = osrf_message_init( REQUEST, ++(session->thread_trace), protocol );
659         osrf_message_set_method(req_msg, method_name);
660
661         if (locale) {
662                 osrf_message_set_locale(req_msg, locale);
663         } else if (session->session_locale) {
664                 osrf_message_set_locale(req_msg, session->session_locale);
665         }
666
667         if(params) {
668                 osrf_message_set_params(req_msg, params);
669
670         } else {
671
672                 if(param_strings) {
673                         int i;
674                         for(i = 0; i!= param_strings->size ; i++ ) {
675                                 osrf_message_add_param(req_msg,
676                                         osrfStringArrayGetString(param_strings,i));
677                         }
678                 }
679         }
680
681         osrfAppRequest* req = _osrf_app_request_init( session, req_msg );
682         if(_osrf_app_session_send( session, req_msg ) ) {
683                 osrfLogWarning( OSRF_LOG_MARK,  "Error sending request message [%d]",
684                                 session->thread_trace );
685                 _osrf_app_request_free(req);
686                 return -1;
687         }
688
689         osrfLogDebug( OSRF_LOG_MARK,  "Pushing [%d] onto request queue for session [%s] [%s]",
690                         req->request_id, session->remote_service, session->session_id );
691         add_app_request( session, req );
692         return req->request_id;
693 }
694
695 void osrf_app_session_set_complete( osrfAppSession* session, int request_id ) {
696         if(session == NULL)
697                 return;
698
699         osrfAppRequest* req = find_app_request( session, request_id );
700         if(req) req->complete = 1;
701 }
702
703 int osrf_app_session_request_complete( const osrfAppSession* session, int request_id ) {
704         if(session == NULL)
705                 return 0;
706         osrfAppRequest* req = find_app_request( session, request_id );
707         if(req)
708                 return req->complete;
709         return 0;
710 }
711
712
713 /** Resets the remote connection id to that of the original*/
714 void osrf_app_session_reset_remote( osrfAppSession* session ){
715         if( session==NULL )
716                 return;
717
718         osrfLogDebug( OSRF_LOG_MARK,  "App Session [%s] [%s] resetting remote id to %s",
719                         session->remote_service, session->session_id, session->orig_remote_id );
720
721         osrf_app_session_set_remote( session, session->orig_remote_id );
722 }
723
724 void osrf_app_session_set_remote( osrfAppSession* session, const char* remote_id ) {
725         if(session == NULL)
726                 return;
727
728         if( session->remote_id ) {
729                 if( strlen(session->remote_id) >= strlen(remote_id) ) {
730                         // There's enough room; just copy it
731                         strcpy(session->remote_id, remote_id);
732                 } else {
733                         free(session->remote_id );
734                         session->remote_id = strdup( remote_id );
735                 }
736         } else
737                 session->remote_id = strdup( remote_id );
738 }
739
740 /**
741         pushes the given message into the result list of the app_request
742         with the given request_id
743 */
744 int osrf_app_session_push_queue(
745                 osrfAppSession* session, osrfMessage* msg ){
746         if(session == NULL || msg == NULL) return 0;
747
748         osrfAppRequest* req = find_app_request( session, msg->thread_trace );
749         if(req == NULL) return 0;
750         _osrf_app_request_push_queue( req, msg );
751
752         return 0;
753 }
754
755 /** Attempts to connect to the remote service */
756 int osrfAppSessionConnect( osrfAppSession* session ) {
757
758         if(session == NULL)
759                 return 0;
760
761         if(session->state == OSRF_SESSION_CONNECTED) {
762                 return 1;
763         }
764
765         int timeout = 5; /* XXX CONFIG VALUE */
766
767         osrfLogDebug( OSRF_LOG_MARK,  "AppSession connecting to %s", session->remote_id );
768
769         /* defaulting to protocol 1 for now */
770         osrfMessage* con_msg = osrf_message_init( CONNECT, session->thread_trace, 1 );
771         osrf_app_session_reset_remote( session );
772         session->state = OSRF_SESSION_CONNECTING;
773         int ret = _osrf_app_session_send( session, con_msg );
774         osrfMessageFree(con_msg);
775         if(ret)
776                 return 0;
777
778         time_t start = time(NULL);
779         time_t remaining = (time_t) timeout;
780
781         while( session->state != OSRF_SESSION_CONNECTED && remaining >= 0 ) {
782                 osrf_app_session_queue_wait( session, remaining, NULL );
783                 if(session->transport_error) {
784                         osrfLogError(OSRF_LOG_MARK, "cannot communicate with %s", session->remote_service);
785                         return 0;
786                 }
787                 remaining -= (int) (time(NULL) - start);
788         }
789
790         if(session->state == OSRF_SESSION_CONNECTED)
791                 osrfLogDebug( OSRF_LOG_MARK, " * Connected Successfully to %s", session->remote_service );
792
793         if(session->state != OSRF_SESSION_CONNECTED)
794                 return 0;
795
796         return 1;
797 }
798
799
800
801 /** Disconnects from the remote service */
802 int osrf_app_session_disconnect( osrfAppSession* session){
803         if(session == NULL)
804                 return 1;
805
806         if(session->state == OSRF_SESSION_DISCONNECTED)
807                 return 1;
808
809         if(session->stateless && session->state != OSRF_SESSION_CONNECTED) {
810                 osrfLogDebug( OSRF_LOG_MARK,
811                                 "Exiting disconnect on stateless session %s",
812                                 session->session_id);
813                 return 1;
814         }
815
816         osrfLogDebug(OSRF_LOG_MARK,  "AppSession disconnecting from %s", session->remote_id );
817
818         osrfMessage* dis_msg = osrf_message_init( DISCONNECT, session->thread_trace, 1 );
819         _osrf_app_session_send( session, dis_msg );
820         session->state = OSRF_SESSION_DISCONNECTED;
821
822         osrfMessageFree( dis_msg );
823         osrf_app_session_reset_remote( session );
824         return 1;
825 }
826
827 /** Resend a request message, as specified by session and request id. */
828 int osrf_app_session_request_resend( osrfAppSession* session, int req_id ) {
829         osrfAppRequest* req = find_app_request( session, req_id );
830
831         int rc;
832         if(req == NULL) {
833                 rc = 0;
834         } else if(!req->complete) {
835                 osrfLogDebug( OSRF_LOG_MARK,  "Resending request [%d]", req->request_id );
836                 rc = _osrf_app_session_send( req->session, req->payload );
837         } else {
838                 rc = 1;
839         }
840
841         return rc;
842 }
843
844 static int osrfAppSessionSendBatch( osrfAppSession* session, osrfMessage* msgs[], int size ) {
845
846         if( !(session && msgs && size > 0) ) return 0;
847         int retval = 0;
848
849         osrfMessage* msg = msgs[0];
850
851         if(msg) {
852
853                 osrf_app_session_queue_wait( session, 0, NULL );
854
855                 if(session->state != OSRF_SESSION_CONNECTED)  {
856
857                         if(session->stateless) { /* stateless session always send to the root listener */
858                                 osrf_app_session_reset_remote(session);
859
860                         } else {
861
862                                 /* do an auto-connect if necessary */
863                                 if( ! session->stateless &&
864                                         (msg->m_type != CONNECT) &&
865                                         (msg->m_type != DISCONNECT) &&
866                                         (session->state != OSRF_SESSION_CONNECTED) ) {
867
868                                         if(!osrfAppSessionConnect( session ))
869                                                 return 0;
870                                 }
871                         }
872                 }
873         }
874
875         char* string = osrfMessageSerializeBatch(msgs, size);
876
877         if( string ) {
878
879                 transport_message* t_msg = message_init(
880                                 string, "", session->session_id, session->remote_id, NULL );
881                 message_set_osrf_xid( t_msg, osrfLogGetXid() );
882
883                 retval = client_send_message( session->transport_handle, t_msg );
884
885                 if( retval ) osrfLogError(OSRF_LOG_MARK, "client_send_message failed");
886
887                 osrfLogInfo(OSRF_LOG_MARK, "[%s] sent %d bytes of data to %s",
888                         session->remote_service, strlen(string), t_msg->recipient );
889
890                 osrfLogDebug(OSRF_LOG_MARK, "Sent: %s", string );
891
892                 free(string);
893                 message_free( t_msg );
894         }
895
896         return retval;
897 }
898
899
900
901 static int _osrf_app_session_send( osrfAppSession* session, osrfMessage* msg ){
902         if( !(session && msg) ) return 0;
903         osrfMessage* a[1];
904         a[0] = msg;
905         return osrfAppSessionSendBatch( session, a, 1 );
906 }
907
908
909 /**
910         Waits up to 'timeout' seconds for some data to arrive.
911         Any data that arrives will be processed according to its
912         payload and message type.  This method will return after
913         any data has arrived.
914 */
915 int osrf_app_session_queue_wait( osrfAppSession* session, int timeout, int* recvd ){
916         if(session == NULL) return 0;
917         osrfLogDebug(OSRF_LOG_MARK, "AppSession in queue_wait with timeout %d", timeout );
918         return osrf_stack_process(session->transport_handle, timeout, recvd);
919 }
920
921 /**
922         @brief Shut down and destroy an osrfAppSession.
923         @param session Pointer to the osrfAppSession to be destroyed.
924
925         If this is a client session, send a DISCONNECT message.
926
927         Remove the session from the global session cache.
928
929         Free all associated resources, including any pending osrfAppRequests.
930 */
931 void osrfAppSessionFree( osrfAppSession* session ){
932         if(session == NULL) return;
933
934         /* Disconnect */
935
936         osrfLogDebug(OSRF_LOG_MARK,  "AppSession [%s] [%s] destroying self and deleting requests",
937                         session->remote_service, session->session_id );
938         if(session->type == OSRF_SESSION_CLIENT
939                         && session->state != OSRF_SESSION_DISCONNECTED ) { /* disconnect if we're a client */
940                 osrfMessage* dis_msg = osrf_message_init( DISCONNECT, session->thread_trace, 1 );
941                 _osrf_app_session_send( session, dis_msg );
942                 osrfMessageFree(dis_msg);
943         }
944
945         /* Remove self from the global session cache */
946
947         osrfHashRemove( osrfAppSessionCache, session->session_id );
948
949         /* Free the memory */
950
951         if( session->userDataFree && session->userData )
952                 session->userDataFree(session->userData);
953
954         if(session->session_locale)
955                 free(session->session_locale);
956
957         free(session->remote_id);
958         free(session->orig_remote_id);
959         free(session->session_id);
960         free(session->remote_service);
961         
962         // Free the request hash
963         int i;
964         for( i = 0; i < OSRF_REQUEST_HASH_SIZE; ++i ) {
965                 osrfAppRequest* app = session->request_hash[ i ];
966                 while( app ) {
967                         osrfAppRequest* next = app->next;
968                         _osrf_app_request_free( app );
969                         app = next;
970                 }
971         }
972         free(session);
973 }
974
975 osrfMessage* osrfAppSessionRequestRecv(
976                 osrfAppSession* session, int req_id, int timeout ) {
977         if(req_id < 0 || session == NULL)
978                 return NULL;
979         osrfAppRequest* req = find_app_request( session, req_id );
980         return _osrf_app_request_recv( req, timeout );
981 }
982
983 int osrfAppRequestRespond( osrfAppSession* ses, int requestId, const jsonObject* data ) {
984         if(!ses || ! data ) return -1;
985
986         osrfMessage* msg = osrf_message_init( RESULT, requestId, 1 );
987         osrf_message_set_status_info( msg, NULL, "OK", OSRF_STATUS_OK );
988         char* json = jsonObjectToJSON( data );
989
990         osrf_message_set_result_content( msg, json );
991         _osrf_app_session_send( ses, msg );
992
993         free(json);
994         osrfMessageFree( msg );
995
996         return 0;
997 }
998
999
1000 int osrfAppRequestRespondComplete(
1001                 osrfAppSession* ses, int requestId, const jsonObject* data ) {
1002
1003         osrfMessage* status = osrf_message_init( STATUS, requestId, 1);
1004         osrf_message_set_status_info( status, "osrfConnectStatus", "Request Complete",
1005                         OSRF_STATUS_COMPLETE );
1006
1007         if (data) {
1008                 osrfMessage* payload = osrf_message_init( RESULT, requestId, 1 );
1009                 osrf_message_set_status_info( payload, NULL, "OK", OSRF_STATUS_OK );
1010
1011                 char* json = jsonObjectToJSON( data );
1012                 osrf_message_set_result_content( payload, json );
1013                 free(json);
1014
1015                 osrfMessage* ms[2];
1016                 ms[0] = payload;
1017                 ms[1] = status;
1018
1019                 osrfAppSessionSendBatch( ses, ms, 2 );
1020
1021                 osrfMessageFree( payload );
1022         } else {
1023                 osrfAppSessionSendBatch( ses, &status, 1 );
1024         }
1025
1026         osrfMessageFree( status );
1027
1028         return 0;
1029 }
1030
1031 int osrfAppSessionStatus( osrfAppSession* ses, int type,
1032                 const char* name, int reqId, const char* message ) {
1033
1034         if(ses) {
1035                 osrfMessage* msg = osrf_message_init( STATUS, reqId, 1);
1036                 osrf_message_set_status_info( msg, name, message, type );
1037                 _osrf_app_session_send( ses, msg );
1038                 osrfMessageFree( msg );
1039                 return 0;
1040         }
1041         return -1;
1042 }
1043
1044 /**
1045         @brief Free the global session cache.
1046         
1047         Note that the osrfHash that implements the global session cache does @em not have a
1048         callback function installed for freeing its cargo.  As a result, any outstanding
1049         osrfAppSessions are leaked, along with all the osrfAppRequests and osrfMessages they
1050         own.
1051 */
1052 void osrfAppSessionCleanup( void ) {
1053         osrfHashFree(osrfAppSessionCache);
1054         osrfAppSessionCache = NULL;
1055 }
1056
1057
1058
1059
1060