};
typedef struct osrf_app_session_struct osrfAppSession;
-
-
// --------------------------------------------------------------------------
// PUBLIC API ***
// --------------------------------------------------------------------------
-/** Allocates a initializes a new app_session */
osrfAppSession* osrfAppSessionClientInit( const char* remote_service );
-/** Allocates and initializes a new server session. The global session cache
- is checked to see if this session already exists, if so, it's returned
-*/
osrfAppSession* osrf_app_server_session_init(
const char* session_id, const char* our_app, const char* remote_id );
-/** sets the default locale for a session **/
char* osrf_app_session_set_locale( osrfAppSession*, const char* );
-/** returns a session from the global session hash */
osrfAppSession* osrf_app_session_find_session( const char* session_id );
-/** Builds a new app_request object with the given payload and returns
- the id of the request. This id is then used to perform work on the
- request. DEPRECATED; use osrfAppSessionSendRequest() instead.
-*/
+/* DEPRECATED; use osrfAppSessionSendRequest() instead. */
int osrfAppSessionMakeRequest(
osrfAppSession* session, const jsonObject* params,
const char* method_name, int protocol, osrfStringArray* param_strings);
-/** Builds a new app_request object with the given payload and returns
- the id of the request. This id is then used to perform work on the
- request.
-*/
int osrfAppSessionSendRequest(
osrfAppSession* session, const jsonObject* params,
const char* method_name, int protocol );
-/** Sets the given request to complete state */
void osrf_app_session_set_complete( osrfAppSession* session, int request_id );
-/** Returns true if the given request is complete */
int osrf_app_session_request_complete( const osrfAppSession* session, int request_id );
-/** Does a recv call on the given request */
osrfMessage* osrfAppSessionRequestRecv(
osrfAppSession* session, int request_id, int timeout );
-/** Removes the request from the request set and frees the reqest */
void osrf_app_session_request_finish( osrfAppSession* session, int request_id );
-/** Resends the orginal request with the given request id */
int osrf_app_session_request_resend( osrfAppSession*, int request_id );
-/** Resets the remote connection target to that of the original*/
void osrf_app_session_reset_remote( osrfAppSession* );
-/** Sets the remote target to 'remote_id' */
void osrf_app_session_set_remote( osrfAppSession* session, const char* remote_id );
-/** pushes the given message into the result list of the app_request
- whose request_id matches the messages thread_trace
-*/
-int osrf_app_session_push_queue( osrfAppSession*, osrfMessage* msg );
+void osrf_app_session_push_queue( osrfAppSession*, osrfMessage* msg );
-/** Attempts to connect to the remote service. Returns 1 on successful
- connection, 0 otherwise.
-*/
int osrfAppSessionConnect( osrfAppSession* );
-/** Sends a disconnect message to the remote service. No response is expected */
int osrf_app_session_disconnect( osrfAppSession* );
-/** Waits up to 'timeout' seconds for some data to arrive.
- Any data that arrives will be processed according to its
- payload and message type. This method will return after
- any data has arrived.
-*/
int osrf_app_session_queue_wait( osrfAppSession*, int timeout, int* recvd );
-/** Disconnects (if client), frees any attached app_reuqests, removes the session from the
- global session cache and frees the session. Needless to say, only call this when the
- session is completely done.
-*/
void osrfAppSessionFree( osrfAppSession* );
-/** Tells the request to reset its wait timeout */
void osrf_app_session_request_reset_timeout( osrfAppSession* session, int req_id );
int osrfAppRequestRespond( osrfAppSession* ses, int requestId, const jsonObject* data );
+
int osrfAppRequestRespondComplete(
osrfAppSession* ses, int requestId, const jsonObject* data );
@param req Pointer to the osrfAppRequest for the original REQUEST message.
@param result Pointer to an osrfMessage received in response to the request.
- We maintain a linked list of response messages, and traverse it to find the end.
+ For each osrfAppRequest we maintain a linked list of response messages, and traverse
+ it to find the end.
*/
static void _osrf_app_request_push_queue( osrfAppRequest* req, osrfMessage* result ){
if(req == NULL || result == NULL)
}
/**
- @brief Set the timeout for a request to one second.
+ @brief Request a reset of the timeout period for a request.
@param session Pointer to the relevant osrfAppSession.
@param req_id Request ID of the request whose timeout is to be reset.
+ This happens when a client receives a STATUS message with a status code
+ OSRF_STATUS_CONTINUE; in effect the server is asking for more time.
+
The request to be reset is identified by the combination of session and request id.
*/
void osrf_app_session_request_reset_timeout( osrfAppSession* session, int req_id ) {
}
/**
- Checks the receive queue for messages. If any are found, the first
- is popped off and returned. Otherwise, this method will wait at most timeout
- seconds for a message to appear in the receive queue. Once it arrives it is returned.
- If no messages arrive in the timeout provided, null is returned.
-*/
-/**
- @brief Return the next response message for a given request, subject to a timeout.
+ @brief Fetch the next response message to a given previous request, subject to a timeout.
@param req Pointer to the osrfAppRequest representing the request.
@param timeout Maxmimum time to wait, in seconds.
@return Pointer to the next osrfMessage for this request, if one is available, or if it
becomes available before the end of the timeout; otherwise NULL;
- If there is already a request available in the input queue, dequeuand return it
- immediately.
+ If there is already a message available in the input queue for this request, dequeue and
+ return it immediately. Otherwise wait up to timeout seconds until you either get an
+ input message for the specified request, run out of time, or encounter an error.
+
+ You may also receive other messages for other requests, and other sessions. These other
+ messages will be wholly or partially processed behind the scenes while you wait for the
+ one you want.
*/
static osrfMessage* _osrf_app_request_recv( osrfAppRequest* req, int timeout ) {
time_t start = time(NULL);
time_t remaining = (time_t) timeout;
+ // Wait repeatedly for input messages until you either receive one for the request
+ // you're interested in, run out of time, or encounter an error.
+ // Wait repeatedly because you may also receive messages for other requests, or for
+ // other sessions, and process them behind the scenes. These are not the messages
+ // you're looking for.
while( remaining >= 0 ) {
/* tell the session to wait for stuff */
osrfLogDebug( OSRF_LOG_MARK, "In app_request receive with remaining time [%d]",
(int) remaining );
+
osrf_app_session_queue_wait( req->session, 0, NULL );
if(req->session->transport_error) {
osrfLogError(OSRF_LOG_MARK, "Transport error in recv()");
return NULL;
}
- if( req->result != NULL ) { /* if we received anything */
- /* pop off the first message in the list */
+ if( req->result != NULL ) { /* if we received any results for this request */
+ /* dequeue the first message in the list */
osrfLogDebug( OSRF_LOG_MARK, "app_request_recv received a message, returning it" );
osrfMessage* ret_msg = req->result;
req->result = ret_msg->next;
return NULL;
}
- if( req->result != NULL ) { /* if we received anything */
- /* pop off the first message in the list */
+ if( req->result != NULL ) { /* if we received any results for this request */
+ /* dequeue the first message in the list */
osrfLogDebug( OSRF_LOG_MARK, "app_request_recv received a message, returning it");
osrfMessage* ret_msg = req->result;
req->result = ret_msg->next;
return ret_msg;
}
+
if( req->complete )
return NULL;
+ // Determine how much time is left
if(req->reset_timeout) {
+ // We got a reprieve. This happens when a client receives a STATUS message
+ // with a status code OSRF_STATUS_CONTINUE. We restart the timer from the
+ // beginning -- but only once. We reset reset_timeout to zero. so that a
+ // second attempted reprieve will allow, at most, only one more second.
remaining = (time_t) timeout;
req->reset_timeout = 0;
osrfLogDebug( OSRF_LOG_MARK, "Received a timeout reset");
}
}
+ // Timeout exhausted; no messages for the request in question
char* paramString = jsonObjectToJSON(req->payload->_params);
osrfLogInfo( OSRF_LOG_MARK, "Returning NULL from app_request_recv after timeout: %s %s",
req->payload->method_name, paramString);
Allocate memory for an osrfAppSession, and initialize it as follows:
- - For talking with Jabber, grab an existing transport_client that must have been
+ - For talking with Jabber, grab an existing transport_client. It must have been
already set up by a prior call to osrfSystemBootstrapClientResc().
- Build a Jabber ID for addressing the service.
- Build a session ID based on a fine-grained timestamp and a process ID. This ID is
- expected to be unique across the system, but uniqueness is not strictly guaranteed.
+ intended to be unique across the system, but uniqueness is not strictly guaranteed.
- Initialize various other bits and scraps.
- Add the session to the global session cache.
@param remote_id Jabber ID of the client.
@return Pointer to the newly created osrfAppSession if successful, or NULL upon failure.
- First, look in the global session cache for session with the specified session ID. IF
- you find one, return it immediately, ignoring the values of the @a our_app and @a
+ First, look in the global session cache for a session with the specified session ID.
+ If you find one, return it immediately, ignoring the values of the @a our_app and @a
remote_id parameters. Otherwise:
- Allocate memory for an osrfAppSession.
- - For talking with Jabber, grab an existing transport_client that must have been
+ - For talking with Jabber, grab an existing transport_client. It should have been
already set up by a prior call to osrfSystemBootstrapClientResc().
- Install a copy of the @a our_app string as remote_service.
- Install copies of the @a remote_id string as remote_id and orig_remote_id.
return req->request_id;
}
+/**
+ @brief Mark an osrfAppRequest (identified by session and ID) as complete.
+ @param session Pointer to the osrfAppSession that owns the request.
+ @param request_id Request ID of the osrfAppRequest.
+*/
void osrf_app_session_set_complete( osrfAppSession* session, int request_id ) {
if(session == NULL)
return;
osrfAppRequest* req = find_app_request( session, request_id );
- if(req) req->complete = 1;
+ if(req)
+ req->complete = 1;
}
+/**
+ @brief Determine whether a osrfAppRequest, identified by session and ID, is complete.
+ @param session Pointer to the osrfAppSession that owns the request.
+ @param request_id Request ID of the osrfAppRequest.
+ @return Non-zero if the request is complete; zero if it isn't, or if it can't be found.
+*/
int osrf_app_session_request_complete( const osrfAppSession* session, int request_id ) {
if(session == NULL)
return 0;
+
osrfAppRequest* req = find_app_request( session, request_id );
if(req)
return req->complete;
+
return 0;
}
-
-/** Resets the remote connection id to that of the original*/
+/**
+ @brief Reset the remote ID of a session to its original remote ID.
+ @param session Pointer to the osrfAppSession to be reset.
+*/
void osrf_app_session_reset_remote( osrfAppSession* session ){
if( session==NULL )
return;
osrf_app_session_set_remote( session, session->orig_remote_id );
}
+/**
+ @brief Set a session's remote ID to a specified value.
+ @param session Pointer to the osrfAppSession whose remote ID is to be set.
+ @param remote_id Pointer to the new remote id.
+*/
void osrf_app_session_set_remote( osrfAppSession* session, const char* remote_id ) {
- if(session == NULL)
+ if( session == NULL || remote_id == NULL )
return;
if( session->remote_id ) {
}
/**
- pushes the given message into the result list of the app_request
- with the given request_id
+ @brief Append an osrfMessage to the list of responses to an osrfAppRequest.
+ @param session Pointer to the osrfAppSession that owns the request.
+ @param msg Pointer to the osrfMessage to be added.
+
+ The thread_trace member of the osrfMessage is the request_id of the osrfAppRequest.
+ Find the corresponding request in the session and append the osrfMessage to its list.
*/
-int osrf_app_session_push_queue(
- osrfAppSession* session, osrfMessage* msg ){
- if(session == NULL || msg == NULL) return 0;
+void osrf_app_session_push_queue( osrfAppSession* session, osrfMessage* msg ) {
+ if( session && msg ) {
+ osrfAppRequest* req = find_app_request( session, msg->thread_trace );
+ if( req )
+ _osrf_app_request_push_queue( req, msg );
+ }
+}
- osrfAppRequest* req = find_app_request( session, msg->thread_trace );
- if(req == NULL) return 0;
- _osrf_app_request_push_queue( req, msg );
+/**
+ @brief Connect to the remote service.
+ @param session Pointer to the osrfAppSession for the service.
+ @return 1 if successful, or 0 if not.
- return 0;
-}
+ If already connected, exit immediately, reporting success. Otherwise, build a CONNECT
+ message and send it to the service. Wait for up to five seconds for an acknowledgement.
-/** Attempts to connect to the remote service */
+ The timeout value is currently hard-coded. Perhaps it should be configurable.
+*/
int osrfAppSessionConnect( osrfAppSession* session ) {
if(session == NULL)
time_t start = time(NULL);
time_t remaining = (time_t) timeout;
+ // Wait for the acknowledgement. We look for it repeatedly because, under the covers,
+ // we may receive and process messages other than the one we're looking for.
while( session->state != OSRF_SESSION_CONNECTED && remaining >= 0 ) {
osrf_app_session_queue_wait( session, remaining, NULL );
if(session->transport_error) {
return 1;
}
+/**
+ @brief Disconnect from the remote service. No response is expected.
+ @param session Pointer to the osrfAppSession to be disconnected.
+ @return 1 in all cases.
-
-/** Disconnects from the remote service */
+ If we're already disconnected, return immediately without doing anything. Likewise if
+ we have a stateless session and we're in the process of connecting. Otherwise, send a
+ DISCONNECT message to the service.
+*/
int osrf_app_session_disconnect( osrfAppSession* session){
if(session == NULL)
return 1;
return 1;
}
-/** Resend a request message, as specified by session and request id. */
+/**
+ @brief Resend a request message, as specified by session and request id.
+ @param session Pointer to the osrfAppSession.
+ @param req_id Request ID for the request to be resent.
+ @return Zero if successful, or if the specified request cannot be found; 1 if the
+ request is already complete, or if the attempt to resend the message fails.
+
+ The choice of return codes may seem seem capricious, but at this writing nothing
+ pays any attention to the return code anyway.
+*/
int osrf_app_session_request_resend( osrfAppSession* session, int req_id ) {
osrfAppRequest* req = find_app_request( session, req_id );
if(req == NULL) {
rc = 0;
} else if(!req->complete) {
- osrfLogDebug( OSRF_LOG_MARK, "Resending request [%d]", req->request_id );
+ osrfLogDebug( OSRF_LOG_MARK, "Resending request [%d]", req->request_id );
rc = _osrf_app_session_send( req->session, req->payload );
} else {
rc = 1;
return rc;
}
+/**
+ @brief Send one or more osrfMessages to the remote service or client.
+ @param session Pointer to the osrfAppSession responsible for sending the message(s).
+ @param msgs Pointer to an array of pointers to osrfMessages.
+ @param size How many messages to send.
+ @return 0 upon success, or -1 upon failure.
+*/
static int osrfAppSessionSendBatch( osrfAppSession* session, osrfMessage* msgs[], int size ) {
- if( !(session && msgs && size > 0) ) return 0;
+ if( !(session && msgs && size > 0) ) return -1;
int retval = 0;
osrfMessage* msg = msgs[0];
if(msg) {
+ // First grab and process any input messages, for any app session. This gives us
+ // a chance to see any CONNECT or DISCONNECT messages that may have arrived. We
+ // may also see some unrelated messages, but we have to process those sooner or
+ // later anyway, so we might as well do it now.
osrf_app_session_queue_wait( session, 0, NULL );
if(session->state != OSRF_SESSION_CONNECTED) {
(session->state != OSRF_SESSION_CONNECTED) ) {
if(!osrfAppSessionConnect( session ))
- return 0;
+ return -1;
}
}
}
}
+ // Bundle all the osrfMessages into a single transport_message, then send it.
char* string = osrfMessageSerializeBatch(msgs, size);
if( string ) {
message_set_osrf_xid( t_msg, osrfLogGetXid() );
retval = client_send_message( session->transport_handle, t_msg );
-
- if( retval ) osrfLogError(OSRF_LOG_MARK, "client_send_message failed");
+ if( retval )
+ osrfLogError(OSRF_LOG_MARK, "client_send_message failed");
osrfLogInfo(OSRF_LOG_MARK, "[%s] sent %d bytes of data to %s",
session->remote_service, strlen(string), t_msg->recipient );
return retval;
}
+/**
+ @brief Send a single osrfMessage to the remote service or client.
+ @param session Pointer to the osrfAppSession.
+ @param msg Pointer to the osrfMessage to be sent.
+ @return zero upon success, or 1 upon failure.
-
+ A thin wrapper. Create an array of one element, and pass it to osrfAppSessionSendBatch().
+*/
static int _osrf_app_session_send( osrfAppSession* session, osrfMessage* msg ){
- if( !(session && msg) ) return 0;
+ if( !(session && msg) )
+ return 1;
osrfMessage* a[1];
a[0] = msg;
- return osrfAppSessionSendBatch( session, a, 1 );
+ return - osrfAppSessionSendBatch( session, a, 1 );
}
/**
- Waits up to 'timeout' seconds for some data to arrive.
- Any data that arrives will be processed according to its
- payload and message type. This method will return after
- any data has arrived.
+ @brief Wait for any input messages to arrive, and process them as needed.
+ @param session Pointer to the osrfAppSession whose transport_session we will use.
+ @param timeout How many seconds to wait for the first input message.
+ @param recvd Pointer to an boolean int. If you receive at least one message, set the boolean
+ to true; otherwise set it to false.
+ @return 0 upon success (even if a timeout occurs), or -1 upon failure.
+
+ A thin wrapper for osrf_stack_process(). The timeout applies only to the first
+ message; process subsequent messages if they are available, but don't wait for them.
+
+ The first parameter identifies an osrfApp session, but all we really use it for is to
+ get a pointer to the transport_session. Typically, if not always, a given process
+ opens only a single transport_session (to talk to the Jabber server), and all app
+ sessions in that process use the same transport_session.
+
+ Hence this function indiscriminately waits for input messages for all osrfAppSessions,
+ not just the one specified.
+
+ Dispatch each message to the appropriate processing routine, depending on its type
+ and contents, and on whether we're acting as a client or as a server for that message.
+ For example, a response to a request may be appended to the input queue of the
+ relevant request. A server session receiving a REQUEST message may execute the
+ requested method. And so forth.
*/
int osrf_app_session_queue_wait( osrfAppSession* session, int timeout, int* recvd ){
if(session == NULL) return 0;
osrfLogDebug(OSRF_LOG_MARK, "AppSession [%s] [%s] destroying self and deleting requests",
session->remote_service, session->session_id );
+ /* disconnect if we're a client */
if(session->type == OSRF_SESSION_CLIENT
- && session->state != OSRF_SESSION_DISCONNECTED ) { /* disconnect if we're a client */
+ && session->state != OSRF_SESSION_DISCONNECTED ) {
osrfMessage* dis_msg = osrf_message_init( DISCONNECT, session->thread_trace, 1 );
_osrf_app_session_send( session, dis_msg );
osrfMessageFree(dis_msg);
free(session);
}
+/**
+ @brief Wait for a response to a given request, subject to a timeout.
+ @param session Pointer to the osrfAppSession that owns the request.
+ @param req_id Request ID for the request.
+ @param timeout How many seconds to wait.
+ @return A pointer to the received osrfMessage if one arrives; otherwise NULL.
+
+ A thin wrapper. Given a session and a request ID, look up the corresponding request
+ and pass it to _osrf_app_request_recv().
+*/
osrfMessage* osrfAppSessionRequestRecv(
osrfAppSession* session, int req_id, int timeout ) {
if(req_id < 0 || session == NULL)
return _osrf_app_request_recv( req, timeout );
}
+/**
+ @brief In response to a specified request, send a payload of data to a client.
+ @param ses Pointer to the osrfAppSession that owns the request.
+ @param requestId Request ID of the osrfAppRequest.
+ @param data Pointer to a jsonObject containing the data payload.
+ @return 0 upon success, or -1 upon failure.
+
+ Translate the jsonObject to a JSON string, and send it wrapped in a RESULT message.
+
+ The only failure detected is if either of the two pointer parameters is NULL.
+*/
int osrfAppRequestRespond( osrfAppSession* ses, int requestId, const jsonObject* data ) {
- if(!ses || ! data ) return -1;
+ if( !ses || ! data )
+ return -1;
osrfMessage* msg = osrf_message_init( RESULT, requestId, 1 );
osrf_message_set_status_info( msg, NULL, "OK", OSRF_STATUS_OK );
}
+/**
+ @brief Send one or two messages to a client in response to a specified request.
+ @param ses Pointer to the osrfAppSession that owns the request.
+ @param requestId Request ID of the osrfAppRequest.
+ @param data Pointer to a jsonObject containing the data payload.
+ @return Zero in all cases.
+
+ If the @a data parameter is not NULL, translate the jsonObject into a JSON string, and
+ incorporate that string into a RESULT message as as the payload . Also build a STATUS
+ message indicating that the response is complete. Send both messages bundled together
+ in the same transport_message.
+
+ If the @a data parameter is NULL, send only a STATUS message indicating that the response
+ is complete.
+*/
int osrfAppRequestRespondComplete(
osrfAppSession* ses, int requestId, const jsonObject* data ) {
return 0;
}
+/**
+ @brief Send a STATUS message, for a specified request, back to the client.
+ @param ses Pointer to the osrfAppSession connected to the client.
+ @param type A numeric code denoting the status.
+ @param name A string naming the status.
+ @param reqId The request ID of the request.
+ @param message A brief message describing the status.
+ @return 0 upon success, or -1 upon failure.
+
+ The only detected failure is when the @a ses parameter is NULL.
+*/
int osrfAppSessionStatus( osrfAppSession* ses, int type,
const char* name, int reqId, const char* message ) {
_osrf_app_session_send( ses, msg );
osrfMessageFree( msg );
return 0;
- }
- return -1;
+ } else
+ return -1;
}
/**
@brief Free the global session cache.
Note that the osrfHash that implements the global session cache does @em not have a
- callback function installed for freeing its cargo. As a result, any outstanding
+ callback function installed for freeing its cargo. As a result, any remaining
osrfAppSessions are leaked, along with all the osrfAppRequests and osrfMessages they
own.
*/