2 @file osrf_app_session.c
3 @brief Implementation of osrfAppSession.
7 #include "opensrf/osrf_app_session.h"
8 #include "opensrf/osrf_stack.h"
10 struct osrf_app_request_struct {
11 /** The controlling session. */
12 struct osrf_app_session_struct* session;
14 /** Request id. It is the same as the thread_trace of the REQUEST message
15 for which it was created.
18 /** True if we have received a 'request complete' message from our request. */
20 /** The original REQUEST message payload. */
22 /** Linked list of responses to the request. */
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. */
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. */
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 );
38 /* Send the given message */
39 static int _osrf_app_session_send( osrfAppSession*, osrfMessage* msg );
41 static int osrfAppSessionMakeLocaleRequest(
42 osrfAppSession* session, const jsonObject* params, const char* method_name,
43 int protocol, osrfStringArray* param_strings, char* locale );
45 /** @brief The global session cache.
47 Key: session_id. Data: osrfAppSession.
49 static osrfHash* osrfAppSessionCache = NULL;
51 // --------------------------------------------------------------------------
53 // --------------------------------------------------------------------------
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.
61 The calling code is responsible for freeing the osrfAppRequest by calling
62 _osrf_app_request_free().
64 static osrfAppRequest* _osrf_app_request_init(
65 osrfAppSession* session, osrfMessage* msg ) {
67 osrfAppRequest* req = safe_malloc(sizeof(osrfAppRequest));
69 req->session = session;
70 req->request_id = msg->thread_trace;
74 req->reset_timeout = 0;
83 @brief Free an osrfAppRequest and everything it owns.
84 @param req Pointer to an osrfAppRequest.
86 static void _osrf_app_request_free( osrfAppRequest * req ) {
89 osrfMessageFree( req->payload );
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;
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.
108 We maintain a linked list of response messages, and traverse it to find the end.
110 static void _osrf_app_request_push_queue( osrfAppRequest* req, osrfMessage* result ){
111 if(req == NULL || result == NULL)
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
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;
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.
137 void osrf_app_session_request_finish( osrfAppSession* session, int req_id ) {
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 ];
144 if( old_req->request_id == req_id )
147 old_req = old_req->next;
151 // Remove the request from the doubly linked list
153 old_req->prev->next = old_req->next;
155 session->request_hash[ index ] = old_req->next;
158 old_req->next->prev = old_req->prev;
160 _osrf_app_request_free( old_req );
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[].
170 If OSRF_REQUEST_HASH_SIZE is a power of two, then this calculation should
171 reduce to a binary AND.
173 static inline unsigned int request_id_hash( int req_id ) {
174 return ((unsigned int) req_id ) % OSRF_REQUEST_HASH_SIZE;
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.
183 static osrfAppRequest* find_app_request( const osrfAppSession* session, int req_id ) {
185 osrfAppRequest* req = session->request_hash[ request_id_hash( req_id) ];
187 if( req->request_id == req_id )
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.
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
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 ];
210 session->request_hash[ index ] = req;
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.
219 The request to be reset is identified by the combination of session and request id.
221 void osrf_app_session_request_reset_timeout( osrfAppSession* session, int req_id ) {
224 osrfLogDebug( OSRF_LOG_MARK, "Resetting request timeout %d", req_id );
225 osrfAppRequest* req = find_app_request( session, req_id );
227 req->reset_timeout = 1;
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.
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.
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;
244 If there is already a request available in the input queue, dequeuand return it
247 static osrfMessage* _osrf_app_request_recv( osrfAppRequest* req, int timeout ) {
249 if(req == NULL) return NULL;
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;
258 time_t start = time(NULL);
259 time_t remaining = (time_t) timeout;
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]",
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()");
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);
286 osrf_app_session_queue_wait( req->session, (int) remaining, NULL );
288 if(req->session->transport_error) {
289 osrfLogError(OSRF_LOG_MARK, "Transport error in recv()");
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);
306 if(req->reset_timeout) {
307 remaining = (time_t) timeout;
308 req->reset_timeout = 0;
309 osrfLogDebug( OSRF_LOG_MARK, "Received a timeout reset");
311 remaining -= (int) (time(NULL) - start);
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);
323 // --------------------------------------------------------------------------
325 // --------------------------------------------------------------------------
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.
333 char* osrf_app_session_set_locale( osrfAppSession* session, const char* locale ) {
334 if (!session || !locale)
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);
342 free(session->session_locale);
343 session->session_locale = strdup( locale );
346 session->session_locale = strdup( locale );
349 return session->session_locale;
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.
357 Search the global session cache for the specified session id.
359 osrfAppSession* osrf_app_session_find_session( const char* session_id ) {
361 return osrfHashGet( osrfAppSessionCache, session_id );
367 @brief Add a session to the global session cache, keyed by session id.
368 @param session Pointer to the osrfAppSession to be added.
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.
373 static void _osrf_app_session_push_session( osrfAppSession* 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 );
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.
388 Allocate memory for an osrfAppSession, and initialize it as follows:
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.
398 Do @em not connect to the service at this point.
400 osrfAppSession* osrfAppSessionClientInit( const char* remote_service ) {
402 if (!remote_service) {
403 osrfLogWarning( OSRF_LOG_MARK, "No remote service specified in osrfAppSessionClientInit");
407 osrfAppSession* session = safe_malloc(sizeof(osrfAppSession));
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'");
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);
423 osrfLogWarning( OSRF_LOG_MARK, "No domains specified in the OpenSRF config file");
425 osrfStringArrayFree(arr);
429 // Get a router name from the config settings.
430 char* router_name = osrfConfigGetValue(NULL, "/router_name");
432 osrfLogWarning( OSRF_LOG_MARK, "No router name specified in the OpenSRF config file");
434 osrfStringArrayFree(arr);
438 char target_buf[512];
439 target_buf[ 0 ] = '\0';
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);
450 if( len >= sizeof( target_buf ) ) {
451 osrfLogWarning( OSRF_LOG_MARK, "Buffer overflow for remote_id");
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;
462 #ifdef ASSUME_STATELESS
463 session->stateless = 1;
464 osrfLogDebug( OSRF_LOG_MARK, "%s session is stateless", remote_service );
466 session->stateless = 0;
467 osrfLogDebug( OSRF_LOG_MARK, "%s session is NOT stateless", remote_service );
470 /* build a chunky, random session id */
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 );
478 session->thread_trace = 0;
479 session->state = OSRF_SESSION_DISCONNECTED;
480 session->type = OSRF_SESSION_CLIENT;
482 session->userData = NULL;
483 session->userDataFree = NULL;
485 // Initialize the hash table
487 for( i = 0; i < OSRF_REQUEST_HASH_SIZE; ++i )
488 session->request_hash[ i ] = NULL;
490 _osrf_app_session_push_session( session );
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.
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:
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.
514 Do @em not respond to the client at this point.
516 osrfAppSession* osrf_app_server_session_init(
517 const char* session_id, const char* our_app, const char* remote_id ) {
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 );
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 );
529 session = safe_malloc(sizeof(osrfAppSession));
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 );
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.
543 char* statel = osrf_settings_host_value("/apps/%s/stateless", our_app );
544 if(statel) stateless = atoi(statel);
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;
553 #ifdef ASSUME_STATELESS
554 session->stateless = 1;
556 session->stateless = 0;
559 session->thread_trace = 0;
560 session->state = OSRF_SESSION_DISCONNECTED;
561 session->type = OSRF_SESSION_SERVER;
562 session->session_locale = NULL;
564 session->userData = NULL;
565 session->userDataFree = NULL;
567 // Initialize the hash table
569 for( i = 0; i < OSRF_REQUEST_HASH_SIZE; ++i )
570 session->request_hash[ i ] = NULL;
572 _osrf_app_session_push_session( session );
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.
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.
588 If @a params is non-NULL, use it to specify the parameters to the method. Otherwise
589 use @a param_strings.
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
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.
598 At this writing, all calls to this function use @a params to pass parameters, rather than
601 This function is a thin wrapper for osrfAppSessionMakeLocaleRequest().
603 int osrfAppSessionMakeRequest(
604 osrfAppSession* session, const jsonObject* params,
605 const char* method_name, int protocol, osrfStringArray* param_strings ) {
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 );
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.
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
625 This function is a thin wrapper for osrfAppSessionMakeLocaleRequest().
627 int osrfAppSessionSendRequest( osrfAppSession* session, const jsonObject* params,
628 const char* method_name, int protocol ) {
630 return osrfAppSessionMakeLocaleRequest( session, params,
631 method_name, protocol, NULL, NULL );
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.
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().
648 At this writing, the @a param_strings and @a locale parameters are always NULL.
650 static int osrfAppSessionMakeLocaleRequest(
651 osrfAppSession* session, const jsonObject* params, const char* method_name,
652 int protocol, osrfStringArray* param_strings, char* locale ) {
654 if(session == NULL) return -1;
658 osrfMessage* req_msg = osrf_message_init( REQUEST, ++(session->thread_trace), protocol );
659 osrf_message_set_method(req_msg, method_name);
662 osrf_message_set_locale(req_msg, locale);
663 } else if (session->session_locale) {
664 osrf_message_set_locale(req_msg, session->session_locale);
668 osrf_message_set_params(req_msg, params);
674 for(i = 0; i!= param_strings->size ; i++ ) {
675 osrf_message_add_param(req_msg,
676 osrfStringArrayGetString(param_strings,i));
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);
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;
695 void osrf_app_session_set_complete( osrfAppSession* session, int request_id ) {
699 osrfAppRequest* req = find_app_request( session, request_id );
700 if(req) req->complete = 1;
703 int osrf_app_session_request_complete( const osrfAppSession* session, int request_id ) {
706 osrfAppRequest* req = find_app_request( session, request_id );
708 return req->complete;
713 /** Resets the remote connection id to that of the original*/
714 void osrf_app_session_reset_remote( osrfAppSession* session ){
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 );
721 osrf_app_session_set_remote( session, session->orig_remote_id );
724 void osrf_app_session_set_remote( osrfAppSession* session, const char* remote_id ) {
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);
733 free(session->remote_id );
734 session->remote_id = strdup( remote_id );
737 session->remote_id = strdup( remote_id );
741 pushes the given message into the result list of the app_request
742 with the given request_id
744 int osrf_app_session_push_queue(
745 osrfAppSession* session, osrfMessage* msg ){
746 if(session == NULL || msg == NULL) return 0;
748 osrfAppRequest* req = find_app_request( session, msg->thread_trace );
749 if(req == NULL) return 0;
750 _osrf_app_request_push_queue( req, msg );
755 /** Attempts to connect to the remote service */
756 int osrfAppSessionConnect( osrfAppSession* session ) {
761 if(session->state == OSRF_SESSION_CONNECTED) {
765 int timeout = 5; /* XXX CONFIG VALUE */
767 osrfLogDebug( OSRF_LOG_MARK, "AppSession connecting to %s", session->remote_id );
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);
778 time_t start = time(NULL);
779 time_t remaining = (time_t) timeout;
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);
787 remaining -= (int) (time(NULL) - start);
790 if(session->state == OSRF_SESSION_CONNECTED)
791 osrfLogDebug( OSRF_LOG_MARK, " * Connected Successfully to %s", session->remote_service );
793 if(session->state != OSRF_SESSION_CONNECTED)
801 /** Disconnects from the remote service */
802 int osrf_app_session_disconnect( osrfAppSession* session){
806 if(session->state == OSRF_SESSION_DISCONNECTED)
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);
816 osrfLogDebug(OSRF_LOG_MARK, "AppSession disconnecting from %s", session->remote_id );
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;
822 osrfMessageFree( dis_msg );
823 osrf_app_session_reset_remote( session );
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 );
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 );
844 static int osrfAppSessionSendBatch( osrfAppSession* session, osrfMessage* msgs[], int size ) {
846 if( !(session && msgs && size > 0) ) return 0;
849 osrfMessage* msg = msgs[0];
853 osrf_app_session_queue_wait( session, 0, NULL );
855 if(session->state != OSRF_SESSION_CONNECTED) {
857 if(session->stateless) { /* stateless session always send to the root listener */
858 osrf_app_session_reset_remote(session);
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) ) {
868 if(!osrfAppSessionConnect( session ))
875 char* string = osrfMessageSerializeBatch(msgs, size);
879 transport_message* t_msg = message_init(
880 string, "", session->session_id, session->remote_id, NULL );
881 message_set_osrf_xid( t_msg, osrfLogGetXid() );
883 retval = client_send_message( session->transport_handle, t_msg );
885 if( retval ) osrfLogError(OSRF_LOG_MARK, "client_send_message failed");
887 osrfLogInfo(OSRF_LOG_MARK, "[%s] sent %d bytes of data to %s",
888 session->remote_service, strlen(string), t_msg->recipient );
890 osrfLogDebug(OSRF_LOG_MARK, "Sent: %s", string );
893 message_free( t_msg );
901 static int _osrf_app_session_send( osrfAppSession* session, osrfMessage* msg ){
902 if( !(session && msg) ) return 0;
905 return osrfAppSessionSendBatch( session, a, 1 );
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.
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);
922 @brief Shut down and destroy an osrfAppSession.
923 @param session Pointer to the osrfAppSession to be destroyed.
925 If this is a client session, send a DISCONNECT message.
927 Remove the session from the global session cache.
929 Free all associated resources, including any pending osrfAppRequests.
931 void osrfAppSessionFree( osrfAppSession* session ){
932 if(session == NULL) return;
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);
945 /* Remove self from the global session cache */
947 osrfHashRemove( osrfAppSessionCache, session->session_id );
949 /* Free the memory */
951 if( session->userDataFree && session->userData )
952 session->userDataFree(session->userData);
954 if(session->session_locale)
955 free(session->session_locale);
957 free(session->remote_id);
958 free(session->orig_remote_id);
959 free(session->session_id);
960 free(session->remote_service);
962 // Free the request hash
964 for( i = 0; i < OSRF_REQUEST_HASH_SIZE; ++i ) {
965 osrfAppRequest* app = session->request_hash[ i ];
967 osrfAppRequest* next = app->next;
968 _osrf_app_request_free( app );
975 osrfMessage* osrfAppSessionRequestRecv(
976 osrfAppSession* session, int req_id, int timeout ) {
977 if(req_id < 0 || session == NULL)
979 osrfAppRequest* req = find_app_request( session, req_id );
980 return _osrf_app_request_recv( req, timeout );
983 int osrfAppRequestRespond( osrfAppSession* ses, int requestId, const jsonObject* data ) {
984 if(!ses || ! data ) return -1;
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 );
990 osrf_message_set_result_content( msg, json );
991 _osrf_app_session_send( ses, msg );
994 osrfMessageFree( msg );
1000 int osrfAppRequestRespondComplete(
1001 osrfAppSession* ses, int requestId, const jsonObject* data ) {
1003 osrfMessage* status = osrf_message_init( STATUS, requestId, 1);
1004 osrf_message_set_status_info( status, "osrfConnectStatus", "Request Complete",
1005 OSRF_STATUS_COMPLETE );
1008 osrfMessage* payload = osrf_message_init( RESULT, requestId, 1 );
1009 osrf_message_set_status_info( payload, NULL, "OK", OSRF_STATUS_OK );
1011 char* json = jsonObjectToJSON( data );
1012 osrf_message_set_result_content( payload, json );
1019 osrfAppSessionSendBatch( ses, ms, 2 );
1021 osrfMessageFree( payload );
1023 osrfAppSessionSendBatch( ses, &status, 1 );
1026 osrfMessageFree( status );
1031 int osrfAppSessionStatus( osrfAppSession* ses, int type,
1032 const char* name, int reqId, const char* message ) {
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 );
1045 @brief Free the global session cache.
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
1052 void osrfAppSessionCleanup( void ) {
1053 osrfHashFree(osrfAppSessionCache);
1054 osrfAppSessionCache = NULL;