1 #include <sys/select.h>
3 #include "opensrf/utils.h"
4 #include "opensrf/log.h"
5 #include "opensrf/osrf_list.h"
6 #include "opensrf/string_array.h"
7 #include "opensrf/osrf_hash.h"
8 #include "osrf_router.h"
9 #include "opensrf/transport_client.h"
10 #include "opensrf/transport_message.h"
11 #include "opensrf/osrf_message.h"
15 @brief Implementation of osrfRouter.
17 The router opens multiple Jabber sessions for the same username and domain, one for
18 each server class. The Jabber IDs for these sessions are distinguished by the use of
19 the class names as Jabber resource names.
21 For each server class there may be multiple server nodes. Each node corresponds to a
22 listener process for a service.
26 @brief Collection of server classes, with connection parameters for Jabber.
28 struct osrfRouterStruct {
31 @brief Hash store of server classes.
33 For each entry, the key is the class name, and the corresponding datum is an
37 osrfHashIterator* class_itr; /**< For traversing the list of classes. */
38 char* domain; /**< Domain name of Jabber server. */
39 char* name; /**< Router's username for the Jabber logon. */
40 char* resource; /**< Router's resource name for the Jabber logon. */
41 char* password; /**< Router's password for the Jabber logon. */
42 int port; /**< Jabber's port number. */
43 volatile sig_atomic_t stop; /**< To be set by signal handler to interrupt main loop. */
45 /** Array of client domains that we allow to send requests through us. */
46 osrfStringArray* trustedClients;
47 /** Array of server domains that we allow to register, etc. with us. */
48 osrfStringArray* trustedServers;
49 /** List of osrfMessages to be returned from osrfMessageDeserialize() */
50 osrfList* message_list;
52 transport_client* connection;
56 @brief Maintains a set of server nodes belonging to the same class.
58 struct _osrfRouterClassStruct {
59 osrfRouter* router; /**< The osrfRouter that owns this osrfRouterClass. */
60 osrfHashIterator* itr; /**< Iterator for set of osrfRouterNodes. */
62 @brief Hash store of server nodes.
64 The key of each entry is a node name, and the associated data is an osrfRouterNode.
65 We install a callback for freeing the entries.
68 /** The transport_client used for communicating with this server. */
69 transport_client* connection;
71 typedef struct _osrfRouterClassStruct osrfRouterClass;
74 @brief Represents a link to a single server's inbound connection.
76 struct _osrfRouterNodeStruct {
77 char* remoteId; /**< Send message to me via this login. */
78 int count; /**< How many message have been sent to this node. */
79 transport_message* lastMessage;
81 typedef struct _osrfRouterNodeStruct osrfRouterNode;
83 static osrfRouterClass* osrfRouterAddClass( osrfRouter* router, const char* classname );
84 static void osrfRouterClassAddNode( osrfRouterClass* rclass, const char* remoteId );
85 static void osrfRouterHandleCommand( osrfRouter* router, const transport_message* msg );
86 static void osrfRouterClassHandleMessage( osrfRouter* router,
87 osrfRouterClass* rclass, const transport_message* msg );
88 static void osrfRouterRemoveClass( osrfRouter* router, const char* classname );
89 static void osrfRouterClassRemoveNode( osrfRouter* router, const char* classname,
90 const char* remoteId );
91 static void osrfRouterClassFree( char* classname, void* rclass );
92 static void osrfRouterNodeFree( char* remoteId, void* node );
93 static osrfRouterClass* osrfRouterFindClass( osrfRouter* router, const char* classname );
94 static osrfRouterNode* osrfRouterClassFindNode( osrfRouterClass* rclass,
95 const char* remoteId );
96 static int _osrfRouterFillFDSet( osrfRouter* router, fd_set* set );
97 static void osrfRouterHandleIncoming( osrfRouter* router );
98 static void osrfRouterClassHandleIncoming( osrfRouter* router,
99 const char* classname, osrfRouterClass* class );
100 static transport_message* osrfRouterClassHandleBounce( osrfRouter* router,
101 const char* classname, osrfRouterClass* rclass, const transport_message* msg );
102 static void osrfRouterHandleAppRequest( osrfRouter* router, const transport_message* msg );
103 static void osrfRouterRespondConnect( osrfRouter* router, const transport_message* msg,
104 const osrfMessage* omsg );
105 static void osrfRouterProcessAppRequest( osrfRouter* router, const transport_message* msg,
106 const osrfMessage* omsg );
107 static void osrfRouterSendAppResponse( osrfRouter* router, const transport_message* msg,
108 const osrfMessage* omsg, const jsonObject* response );
109 static void osrfRouterHandleMethodNFound( osrfRouter* router,
110 const transport_message* msg, const osrfMessage* omsg );
112 #define ROUTER_REGISTER "register"
113 #define ROUTER_UNREGISTER "unregister"
115 #define ROUTER_REQUEST_CLASS_LIST "opensrf.router.info.class.list"
116 #define ROUTER_REQUEST_STATS_NODE_FULL "opensrf.router.info.stats.class.node.all"
117 #define ROUTER_REQUEST_STATS_CLASS_FULL "opensrf.router.info.stats.class.all"
118 #define ROUTER_REQUEST_STATS_CLASS "opensrf.router.info.stats.class"
119 #define ROUTER_REQUEST_STATS_CLASS_SUMMARY "opensrf.router.info.stats.class.summary"
122 @brief Stop the otherwise endless main loop of the router.
123 @param router Pointer to the osrfRouter to be stopped.
125 To be called by a signal handler. We don't stop the loop immediately; we just set
126 a switch that the main loop checks on each iteration.
128 void router_stop( osrfRouter* router )
135 @brief Allocate and initialize a new osrfRouter.
136 @param domain Domain name of Jabber server.
137 @param name Router's username for the Jabber logon.
138 @param resource Router's resource name for the Jabber logon.
139 @param password Router's password for the Jabber logon.
140 @param port Jabber's port number.
141 @param trustedClients Array of client domains that we allow to send requests through us.
142 @param trustedServers Array of server domains that we allow to register, etc. with us.
143 @return Pointer to the newly allocated osrfRouter, or NULL upon error.
145 Don't connect to Jabber yet. We'll do that later, upon a call to osrfRouterConnect().
147 The calling code is responsible for freeing the osrfRouter by calling osrfRouterFree().
149 osrfRouter* osrfNewRouter(
150 const char* domain, const char* name,
151 const char* resource, const char* password, int port,
152 osrfStringArray* trustedClients, osrfStringArray* trustedServers ) {
154 if(!( domain && name && resource && password && port && trustedClients && trustedServers ))
157 osrfRouter* router = safe_malloc(sizeof(osrfRouter));
158 router->domain = strdup(domain);
159 router->name = strdup(name);
160 router->password = strdup(password);
161 router->resource = strdup(resource);
165 router->trustedClients = trustedClients;
166 router->trustedServers = trustedServers;
169 router->classes = osrfNewHash();
170 osrfHashSetCallback(router->classes, &osrfRouterClassFree);
171 router->class_itr = osrfNewHashIterator( router->classes );
172 router->message_list = NULL; // We'll allocate one later
174 // Prepare to connect to Jabber, as a non-component, over TCP (not UNIX domain).
175 router->connection = client_init( domain, port, NULL, 0 );
181 @brief Connect to Jabber.
182 @param router Pointer to the osrfRouter to connect to Jabber.
183 @return 0 if successful, or -1 on error.
185 Allow up to 10 seconds for the logon to succeed.
187 We connect over TCP (not over a UNIX domain), as a non-component.
189 int osrfRouterConnect( osrfRouter* router ) {
190 if(!router) return -1;
191 int ret = client_connect( router->connection, router->name,
192 router->password, router->resource, 10, AUTH_DIGEST );
193 if( ret == 0 ) return -1;
198 @brief Enter endless loop to receive and respond to input.
199 @param router Pointer to the osrfRouter that's looping.
201 On each iteration: wait for incoming messages to arrive on any of our sockets -- i.e.
202 either the top level socket belonging to the router or any of the lower level sockets
203 belonging to the classes. React to the incoming activity as needed.
205 We don't exit the loop until we receive a signal to stop, or until we encounter an error.
207 void osrfRouterRun( osrfRouter* router ) {
208 if(!(router && router->classes)) return;
210 int routerfd = client_sock_fd( router->connection );
213 // Loop until a signal handler sets router->stop
214 while( ! router->stop ) {
217 int maxfd = _osrfRouterFillFDSet( router, &set );
219 // Wait indefinitely for an incoming message
220 if( (selectret = select(maxfd + 1, &set, NULL, NULL, NULL)) < 0 ) {
221 if( EINTR == errno ) {
223 osrfLogInfo(OSRF_LOG_MARK, "Router shutting down");
227 continue; // Irrelevant signal; ignore it
229 osrfLogWarning( OSRF_LOG_MARK, "Top level select call failed with errno %d: %s",
230 errno, strerror( errno ) );
235 /* see if there is a top level router message */
236 if( FD_ISSET(routerfd, &set) ) {
237 osrfLogDebug( OSRF_LOG_MARK, "Top router socket is active: %d", routerfd );
238 osrfRouterHandleIncoming( router );
241 /* Check each of the connected classes and see if they have data to route */
242 osrfRouterClass* class;
243 osrfHashIterator* itr = router->class_itr; // remove a layer of indirection
244 osrfHashIteratorReset( itr );
246 while( (class = osrfHashIteratorNext(itr)) ) { // for each class
248 const char* classname = osrfHashIteratorKey(itr);
252 osrfLogDebug( OSRF_LOG_MARK, "Checking %s for activity...", classname );
254 int sockfd = client_sock_fd( class->connection );
255 if(FD_ISSET( sockfd, &set )) {
256 osrfLogDebug( OSRF_LOG_MARK, "Socket is active: %d", sockfd );
257 osrfRouterClassHandleIncoming( router, classname, class );
266 @brief Handle incoming requests to the router.
267 @param router Pointer to the osrfRouter.
269 Read all available input messages from the top-level transport_client. For each one:
270 if the domain of the sender's Jabber id is on the list of approved domains, pass the
271 message to osrfRouterHandleCommand() (if there's a message) or osrfRouterHandleAppRequest()
272 (if there isn't). If the domain is @em not on the approved list, log a warning and
275 static void osrfRouterHandleIncoming( osrfRouter* router ) {
278 transport_message* msg = NULL;
280 while( (msg = client_recv( router->connection, 0 )) ) { // for each message
284 osrfLogDebug(OSRF_LOG_MARK,
285 "osrfRouterHandleIncoming(): investigating message from %s", msg->sender);
287 /* if the server is not on a trusted domain, drop the message */
288 int len = strlen(msg->sender) + 1;
290 jid_get_domain( msg->sender, domain, len - 1 );
292 if(osrfStringArrayContains( router->trustedServers, domain)) {
294 // If there's a command, obey it. Otherwise, treat
295 // the message as an app session level request.
296 if( msg->router_command && *msg->router_command )
297 osrfRouterHandleCommand( router, msg );
299 osrfRouterHandleAppRequest( router, msg );
302 osrfLogWarning( OSRF_LOG_MARK,
303 "Received message from un-trusted server domain %s", msg->sender);
311 @brief Handle all available incoming messages for a router class.
312 @param router Pointer to the osrfRouter.
313 @param classname Class name.
314 @param class Pointer to the osrfRouterClass.
316 For each message: if the sender is on a trusted domain, process the message.
318 static void osrfRouterClassHandleIncoming( osrfRouter* router, const char* classname,
319 osrfRouterClass* class ) {
320 if(!(router && class)) return;
322 transport_message* msg;
323 osrfLogDebug( OSRF_LOG_MARK, "osrfRouterClassHandleIncoming()");
325 // For each incoming message for this class:
326 while( (msg = client_recv( class->connection, 0 )) ) {
328 // Save the transaction id so that we can incorporate it
329 // into any relevant messages
330 osrfLogSetXid(msg->osrf_xid);
334 osrfLogDebug(OSRF_LOG_MARK,
335 "osrfRouterClassHandleIncoming(): investigating message from %s", msg->sender);
337 /* if the client is not from a trusted domain, drop the message */
338 int len = strlen(msg->sender) + 1;
340 jid_get_domain( msg->sender, domain, len - 1 );
342 if(osrfStringArrayContains( router->trustedClients, domain )) {
344 if( msg->is_error ) {
346 // A previous message bounced. Try to send a clone of it to a
347 // different node of the same class.
349 // First make a local copy of the class name. If the class gets
350 // deleted, the classname parameter becomes invalid.
351 char classname_copy[ strlen( classname ) + 1 ];
352 strcpy( classname_copy, classname );
354 transport_message* bouncedMessage = osrfRouterClassHandleBounce(
355 router, classname, class, msg );
356 /* handle bounced message */
357 if( !bouncedMessage ) {
358 /* we have no one to send the requested message to on our domain */
361 /* XXX Here's where we plug in peer domains, inserting this brick into a wall */
367 // See if the class still exists
368 if( osrfHashGet( router->classes, classname_copy ) )
369 continue; // It does; keep going
371 break; // It doesn't; don't try to read from it any more
373 osrfRouterClassHandleMessage( router, class, bouncedMessage );
374 message_free( bouncedMessage );
376 osrfRouterClassHandleMessage( router, class, msg );
379 osrfLogWarning( OSRF_LOG_MARK,
380 "Received client message from untrusted client domain %s", domain );
385 osrfLogClearXid(); // We're done with this transaction id
390 @brief Handle a top level router command.
391 @param router Pointer to the osrfRouter.
392 @param msg Pointer to the transport_message to be handled.
394 Currently supported commands:
395 - "register" -- Add a server class and/or a server node to our lists.
396 - "unregister" -- Remove a node from a class, and the class as well if no nodes are
399 static void osrfRouterHandleCommand( osrfRouter* router, const transport_message* msg ) {
400 if(!(router && msg && msg->router_class)) return;
402 if( !strcmp( msg->router_command, ROUTER_REGISTER ) ) {
404 osrfLogInfo( OSRF_LOG_MARK, "Registering class %s", msg->router_class );
406 // Add the server class to the list, if it isn't already there
407 osrfRouterClass* class = osrfRouterFindClass( router, msg->router_class );
409 class = osrfRouterAddClass( router, msg->router_class );
411 // Add the node to the osrfRouterClass's list, if it isn't already there
412 if(class && ! osrfRouterClassFindNode( class, msg->sender ) )
413 osrfRouterClassAddNode( class, msg->sender );
415 } else if( !strcmp( msg->router_command, ROUTER_UNREGISTER ) ) {
417 if( msg->router_class && *msg->router_class ) {
418 osrfLogInfo( OSRF_LOG_MARK, "Unregistering router class %s", msg->router_class );
419 osrfRouterClassRemoveNode( router, msg->router_class, msg->sender );
426 @brief Add an osrfRouterClass to a router, and open a connection for it.
427 @param router Pointer to the osrfRouter.
428 @param classname The name of the class this node handles.
429 @return A pointer to the new osrfRouterClass, or NULL upon error.
431 Open a Jabber session to be used for this server class. The Jabber ID incorporates the
432 class name as the resource name.
434 static osrfRouterClass* osrfRouterAddClass( osrfRouter* router, const char* classname ) {
435 if(!(router && router->classes && classname)) return NULL;
437 osrfRouterClass* class = safe_malloc(sizeof(osrfRouterClass));
438 class->nodes = osrfNewHash();
439 class->itr = osrfNewHashIterator(class->nodes);
440 osrfHashSetCallback(class->nodes, &osrfRouterNodeFree);
441 class->router = router;
443 class->connection = client_init( router->domain, router->port, NULL, 0 );
445 if(!client_connect( class->connection, router->name,
446 router->password, classname, 10, AUTH_DIGEST ) ) {
447 // Cast away the constness of classname. Though ugly, this
448 // cast is benign because osrfRouterClassFree doesn't actually
449 // write through the pointer. We can't readily change its
450 // signature because it is used for a function pointer, and
451 // we would have to change other signatures the same way.
452 osrfRouterClassFree( (char *) classname, class );
456 osrfHashSet( router->classes, class, classname );
462 @brief Add a new server node to an osrfRouterClass.
463 @param rclass Pointer to the osrfRouterClass to which we are to add the node.
464 @param remoteId The remote login of the osrfRouterNode.
466 static void osrfRouterClassAddNode( osrfRouterClass* rclass, const char* remoteId ) {
467 if(!(rclass && rclass->nodes && remoteId)) return;
469 osrfLogInfo( OSRF_LOG_MARK, "Adding router node for remote id %s", remoteId );
471 osrfRouterNode* node = safe_malloc(sizeof(osrfRouterNode));
473 node->lastMessage = NULL;
474 node->remoteId = strdup(remoteId);
476 osrfHashSet( rclass->nodes, node, remoteId );
480 @brief Handle an input message representing a Jabber error stanza.
481 @param router Pointer to the current osrfRouter.
482 @param classname Name of the class to which the error stanza was sent.
483 @param rclass Pointer to the osrfRouterClass to which the error stanza was sent.
484 @param msg Pointer to the transport_message representing the error stanza.
485 @return Pointer to a newly allocated transport_message; or NULL (see remarks).
487 The presumption is that the relevant node is dead. If another node is available for
488 the same class, then remove the dead one, create a clone of the message to be sent
489 elsewhere, and return a pointer to it. If there is no other node for the same class,
490 send a cancel message back to the sender, remove both the node and the class it belongs
491 to, and return NULL. If we can't even do that because the entire class is dead, log
492 a message to that effect and return NULL.
494 static transport_message* osrfRouterClassHandleBounce( osrfRouter* router,
495 const char* classname, osrfRouterClass* rclass, const transport_message* msg ) {
497 osrfLogDebug( OSRF_LOG_MARK, "osrfRouterClassHandleBounce()");
499 osrfLogInfo( OSRF_LOG_MARK, "Received network layer error message from %s", msg->sender );
500 osrfRouterNode* node = osrfRouterClassFindNode( rclass, msg->sender );
502 osrfLogInfo( OSRF_LOG_MARK,
503 "network error occurred after we removed the class.. ignoring");
507 if( osrfHashGetCount(rclass->nodes) == 1 ) { /* the last node is dead */
509 if( node->lastMessage ) {
510 osrfLogWarning( OSRF_LOG_MARK,
511 "We lost the last node in the class, responding with error and removing...");
513 transport_message* error = message_init(
514 node->lastMessage->body, node->lastMessage->subject,
515 node->lastMessage->thread, node->lastMessage->router_from,
516 node->lastMessage->recipient );
517 message_set_osrf_xid(error, node->lastMessage->osrf_xid);
518 set_msg_error( error, "cancel", 501 );
520 /* send the error message back to the original sender */
521 client_send_message( rclass->connection, error );
522 message_free( error );
525 /* remove the dead node */
526 osrfRouterClassRemoveNode( router, classname, msg->sender);
531 transport_message* lastSent = NULL;
533 if( node->lastMessage ) {
534 osrfLogDebug( OSRF_LOG_MARK, "Cloning lastMessage so next node can send it");
535 lastSent = message_init( node->lastMessage->body,
536 node->lastMessage->subject, node->lastMessage->thread, "",
537 node->lastMessage->router_from );
538 message_set_router_info( lastSent, node->lastMessage->router_from,
539 NULL, NULL, NULL, 0 );
540 message_set_osrf_xid( lastSent, node->lastMessage->osrf_xid );
543 /* remove the dead node */
544 osrfRouterClassRemoveNode( router, classname, msg->sender);
551 @brief Forward a class-level message to a listener for the corresponding service.
552 @param router Pointer to the current osrfRouter.
553 @param rclass Pointer to the class to which the message is directed.
554 @param msg Pointer to the message to be forwarded.
556 Pick a node for the specified class, and forward the message to it.
558 We use an iterator, stored with the class, to maintain a position in the class's list
559 of nodes. Advance the iterator to pick the next node, and if we reach the end, go
560 back to the beginning of the list.
562 static void osrfRouterClassHandleMessage(
563 osrfRouter* router, osrfRouterClass* rclass, const transport_message* msg ) {
564 if(!(router && rclass && msg)) return;
566 osrfLogDebug( OSRF_LOG_MARK, "osrfRouterClassHandleMessage()");
568 // Pick a node, in a round-robin.
569 osrfRouterNode* node = osrfHashIteratorNext( rclass->itr );
570 if(!node) { // wrap around to the beginning of the list
571 osrfHashIteratorReset(rclass->itr);
572 node = osrfHashIteratorNext( rclass->itr );
575 if(node) { // should always be true -- no class without a node
577 // Build a transport message
578 transport_message* new_msg = message_init( msg->body,
579 msg->subject, msg->thread, node->remoteId, msg->sender );
580 message_set_router_info( new_msg, msg->sender, NULL, NULL, NULL, 0 );
581 message_set_osrf_xid( new_msg, msg->osrf_xid );
583 osrfLogInfo( OSRF_LOG_MARK, "Routing message:\nfrom: [%s]\nto: [%s]",
584 new_msg->router_from, new_msg->recipient );
586 // Save it for possible future reference
587 message_free( node->lastMessage );
588 node->lastMessage = new_msg;
591 if ( client_send_message( rclass->connection, new_msg ) == 0 )
595 message_prepare_xml(new_msg);
596 osrfLogWarning( OSRF_LOG_MARK, "Error sending message from %s to %s\n%s",
597 new_msg->sender, new_msg->recipient, new_msg->msg_xml );
599 // We don't free new_msg here because we saved it as node->lastMessage.
605 @brief Remove a given osrfRouterClass from an osrfRouter
606 @param router Pointer to the osrfRouter.
607 @param classname The name of the class to be removed.
609 Delete an osrfRouterClass from the router's list of classes. Indirectly (via a callback
610 function installed in the osrfHash), free the osrfRouterClass and any associated nodes.
612 static void osrfRouterRemoveClass( osrfRouter* router, const char* classname ) {
613 if( router && router->classes && classname ) {
614 osrfLogInfo( OSRF_LOG_MARK, "Removing router class %s", classname );
615 osrfHashRemove( router->classes, classname );
621 @brief Remove a node from a class. If the class thereby becomes empty, remove it as well.
622 @param router Pointer to the current osrfRouter.
623 @param classname Class name.
624 @param remoteId Identifier for the node to be removed.
626 static void osrfRouterClassRemoveNode(
627 osrfRouter* router, const char* classname, const char* remoteId ) {
629 if(!(router && router->classes && classname && remoteId)) // sanity check
632 osrfLogInfo( OSRF_LOG_MARK, "Removing router node %s", remoteId );
634 osrfRouterClass* class = osrfRouterFindClass( router, classname );
636 osrfHashRemove( class->nodes, remoteId );
637 if( osrfHashGetCount(class->nodes) == 0 ) {
638 osrfRouterRemoveClass( router, classname );
645 @brief Free a router class object.
646 @param classname Class name (not used).
647 @param c Pointer to the osrfRouterClass, cast to a void pointer.
649 This function is invoked as a callback when we remove an osrfRouterClass from the
650 router's list of classes.
652 static void osrfRouterClassFree( char* classname, void* c ) {
655 osrfRouterClass* rclass = (osrfRouterClass*) c;
656 client_disconnect( rclass->connection );
657 client_free( rclass->connection );
659 osrfHashIteratorReset( rclass->itr );
660 osrfRouterNode* node;
662 while( (node = osrfHashIteratorNext(rclass->itr)) )
663 osrfHashRemove( rclass->nodes, node->remoteId );
665 osrfHashIteratorFree(rclass->itr);
666 osrfHashFree(rclass->nodes);
672 @brief Free an osrfRouterNode.
673 @param remoteId Jabber ID of node (not used).
674 @param n Pointer to the osrfRouterNode to be freed, cast to a void pointer.
676 This is a callback installed in an osrfHash (the nodes member of an osrfRouterClass).
678 static void osrfRouterNodeFree( char* remoteId, void* n ) {
680 osrfRouterNode* node = (osrfRouterNode*) n;
681 free(node->remoteId);
682 message_free(node->lastMessage);
688 @brief Free an osrfRouter and everything it owns.
689 @param router Pointer to the osrfRouter to be freed.
691 The osrfRouterClasses and osrfRouterNodes are freed by callback functions installed in
694 void osrfRouterFree( osrfRouter* router ) {
697 osrfHashIteratorFree( router->class_itr);
698 osrfHashFree(router->classes);
699 free(router->domain);
701 free(router->resource);
702 free(router->password);
704 osrfStringArrayFree( router->trustedClients );
705 osrfStringArrayFree( router->trustedServers );
706 osrfListFree( router->message_list );
708 client_free( router->connection );
714 @brief Given a class name, find the corresponding osrfRouterClass.
715 @param router Pointer to the osrfRouter that owns the osrfRouterClass.
716 @param classname Name of the class.
717 @return Pointer to a matching osrfRouterClass if found, or NULL if not.
719 static osrfRouterClass* osrfRouterFindClass( osrfRouter* router, const char* classname ) {
720 if(!( router && router->classes && classname )) return NULL;
721 return (osrfRouterClass*) osrfHashGet( router->classes, classname );
726 @brief Find a given node for a given class.
727 @param rclass Pointer to the osrfRouterClass in which to search.
728 @param remoteId Jabber ID of the node for which to search.
729 @return Pointer to the matching osrfRouterNode, if found; otherwise NULL.
731 static osrfRouterNode* osrfRouterClassFindNode( osrfRouterClass* rclass,
732 const char* remoteId ) {
733 if(!(rclass && remoteId)) return NULL;
734 return (osrfRouterNode*) osrfHashGet( rclass->nodes, remoteId );
739 @brief Fill an fd_set with all the sockets owned by the osrfRouter.
740 @param router Pointer to the osrfRouter whose sockets are to be used.
741 @param set Pointer to the fd_set that is to be filled.
742 @return The largest file descriptor loaded into the fd_set; or -1 upon error.
744 There's one socket for the osrfRouter as a whole, and one for each osrfRouterClass
745 that belongs to it. We load them all.
747 static int _osrfRouterFillFDSet( osrfRouter* router, fd_set* set ) {
748 if(!(router && router->classes && set)) return -1;
751 int maxfd = client_sock_fd( router->connection );
756 osrfRouterClass* class = NULL;
757 osrfHashIterator* itr = router->class_itr;
758 osrfHashIteratorReset( itr );
760 while( (class = osrfHashIteratorNext(itr)) ) {
761 const char* classname = osrfHashIteratorKey(itr);
763 if( classname && (class = osrfRouterFindClass( router, classname )) ) {
764 sockid = client_sock_fd( class->connection );
766 if( osrfUtilsCheckFileDescriptor( sockid ) ) {
768 osrfLogWarning(OSRF_LOG_MARK,
769 "Removing router class '%s' because of a bad top-level file descriptor [%d]",
771 osrfRouterRemoveClass( router, classname );
774 if( sockid > maxfd ) maxfd = sockid;
784 @brief Handler a router-level message that isn't a command; presumed to be an app request.
785 @param router Pointer to the current osrfRouter.
786 @param msg Pointer to the incoming message.
788 The body of the transport_message is a JSON string, specifically an JSON array.
789 Translate the JSON into a series of osrfMessages, one for each element of the JSON
790 array. Process each osrfMessage in turn. Each message is either a CONNECT or a
793 static void osrfRouterHandleAppRequest( osrfRouter* router, const transport_message* msg ) {
795 // Translate the JSON into a list of osrfMessages
796 router->message_list = osrfMessageDeserialize( msg->body, router->message_list );
797 const osrfMessage* omsg = NULL;
799 // Process each osrfMessage
801 for( i = 0; i < router->message_list->size; ++i ) {
803 omsg = osrfListGetIndex( router->message_list, i );
806 switch( omsg->m_type ) {
809 osrfRouterRespondConnect( router, msg, omsg );
813 osrfRouterProcessAppRequest( router, msg, omsg );
821 osrfListClear( router->message_list );
827 @brief Respond to a CONNECT message.
828 @param router Pointer to the current osrfRouter.
829 @param msg Pointer to the transport_message that the osrfMessage came from.
830 @param omsg Pointer to the osrfMessage to be processed.
832 An application is trying to connect to the router. Reply with a STATUS message
835 "CONNECT" is a bit of a misnomer. We don't establish a stateful session; i.e. we
836 don't retain any memory of this message. We just confirm that the router is alive,
837 and that the client has a good address for it.
839 static void osrfRouterRespondConnect( osrfRouter* router, const transport_message* msg,
840 const osrfMessage* omsg ) {
841 if(!(router && msg && omsg))
844 osrfLogDebug( OSRF_LOG_MARK, "router received a CONNECT message from %s", msg->sender );
846 // Build a success message
847 osrfMessage* success = osrf_message_init( STATUS, omsg->thread_trace, omsg->protocol );
848 osrf_message_set_status_info(
849 success, "osrfConnectStatus", "Connection Successful", OSRF_STATUS_OK );
851 // Translate the success message into JSON,
852 // and then package the JSON in a transport message
853 char* data = osrf_message_serialize(success);
854 osrfMessageFree( success );
855 transport_message* return_msg = message_init(
856 data, // message payload, translated from osrfMessage
858 msg->thread, // same thread
859 msg->sender, // destination (client's Jabber ID)
860 "" // don't send our address; client already has it
864 client_send_message( router->connection, return_msg );
865 message_free( return_msg );
870 @brief Respond to a REQUEST message.
871 @param router Pointer to the current osrfRouter.
872 @param msg Pointer to the transport_message that the osrfMessage came from.
873 @param omsg Pointer to the osrfMessage to be processed.
875 Respond to an information request from an application. Most types of request involve
876 one or more counts of messages successfully routed.
878 Request types currently supported:
879 - "opensrf.router.info.class.list" -- list of class names.
880 - "opensrf.router.info.stats.class.summary" -- total count for a specified class.
881 - "opensrf.router.info.stats.class" -- count for every node of a specified class.
882 - "opensrf.router.info.stats.class.all" -- count for every node of every class.
883 - "opensrf.router.info.stats.class.node.all" -- total count for every class.
885 static void osrfRouterProcessAppRequest( osrfRouter* router, const transport_message* msg,
886 const osrfMessage* omsg ) {
888 if(!(router && msg && omsg && omsg->method_name))
891 osrfLogInfo( OSRF_LOG_MARK, "Router received app request: %s", omsg->method_name );
893 // Branch on the request type. Build a jsonObject as an answer to the request.
894 jsonObject* jresponse = NULL;
895 if(!strcmp( omsg->method_name, ROUTER_REQUEST_CLASS_LIST )) {
897 // Prepare an array of class names.
899 jresponse = jsonNewObjectType(JSON_ARRAY);
901 osrfStringArray* keys = osrfHashKeys( router->classes );
902 for( i = 0; i != keys->size; i++ )
903 jsonObjectPush( jresponse, jsonNewObject(osrfStringArrayGetString( keys, i )) );
904 osrfStringArrayFree(keys);
906 } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_CLASS_SUMMARY )) {
908 // Prepare a count of all the messages successfully routed for a given class.
911 // class name is the first parameter
912 const char* classname = jsonObjectGetString( jsonObjectGetIndex( omsg->_params, 0 ) );
916 osrfRouterClass* class = osrfHashGet(router->classes, classname);
918 // For each node: add the count to the total.
919 osrfRouterNode* node;
920 osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
921 while( (node = osrfHashIteratorNext(node_itr)) ) {
922 count += node->count;
923 // jsonObjectSetKey( class_res, node->remoteId,
924 // jsonNewNumberObject( (double) node->count ) );
926 osrfHashIteratorFree(node_itr);
928 jresponse = jsonNewNumberObject( (double) count );
930 } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_CLASS )) {
932 // Prepare a hash for a given class. Key: the remoteId of a node. Datum: the
933 // number of messages successfully routed for that node.
935 // class name is the first parameter
936 const char* classname = jsonObjectGetString( jsonObjectGetIndex( omsg->_params, 0 ) );
940 jresponse = jsonNewObjectType(JSON_HASH);
941 osrfRouterClass* class = osrfHashGet(router->classes, classname);
943 // For each node: get the count and store it in the hash.
944 osrfRouterNode* node;
945 osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
946 while( (node = osrfHashIteratorNext(node_itr)) ) {
947 jsonObjectSetKey( jresponse, node->remoteId,
948 jsonNewNumberObject( (double) node->count ) );
950 osrfHashIteratorFree(node_itr);
952 } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_CLASS_FULL )) {
954 // Prepare a hash of hashes, giving the message counts for each node for each class.
956 osrfRouterClass* class;
957 osrfRouterNode* node;
958 jresponse = jsonNewObjectType(JSON_HASH); // Key: class name.
960 // Traverse the list of classes.
961 osrfHashIterator* class_itr = osrfNewHashIterator(router->classes);
962 while( (class = osrfHashIteratorNext(class_itr)) ) {
964 jsonObject* class_res = jsonNewObjectType(JSON_HASH); // Key: remoteId of node.
965 const char* classname = osrfHashIteratorKey(class_itr);
967 // Traverse the list of nodes for the current class.
968 osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
969 while( (node = osrfHashIteratorNext(node_itr)) ) {
970 jsonObjectSetKey( class_res, node->remoteId,
971 jsonNewNumberObject( (double) node->count ) );
973 osrfHashIteratorFree(node_itr);
975 jsonObjectSetKey( jresponse, classname, class_res );
978 osrfHashIteratorFree(class_itr);
980 } else if(!strcmp( omsg->method_name, ROUTER_REQUEST_STATS_NODE_FULL )) {
982 // Prepare a hash. Key: class name. Datum: total number of successfully routed
983 // messages routed for nodes of that class.
985 osrfRouterClass* class;
986 osrfRouterNode* node;
987 jresponse = jsonNewObjectType(JSON_HASH);
989 osrfHashIterator* class_itr = osrfNewHashIterator(router->classes);
990 while( (class = osrfHashIteratorNext(class_itr)) ) { // For each class
993 const char* classname = osrfHashIteratorKey(class_itr);
995 osrfHashIterator* node_itr = osrfNewHashIterator(class->nodes);
996 while( (node = osrfHashIteratorNext(node_itr)) ) { // For each node
997 count += node->count;
999 osrfHashIteratorFree(node_itr);
1001 jsonObjectSetKey( jresponse, classname, jsonNewNumberObject( (double) count ) );
1004 osrfHashIteratorFree(class_itr);
1006 } else { // None of the above
1008 osrfRouterHandleMethodNFound( router, msg, omsg );
1012 // Send the result back to the requester.
1013 osrfRouterSendAppResponse( router, msg, omsg, jresponse );
1014 jsonObjectFree(jresponse);
1019 @brief Respond to an invalid REQUEST message.
1020 @param router Pointer to the current osrfRouter.
1021 @param msg Pointer to the transport_message that contained the REQUEST message.
1022 @param omsg Pointer to the osrfMessage that contained the REQUEST.
1024 static void osrfRouterHandleMethodNFound( osrfRouter* router,
1025 const transport_message* msg, const osrfMessage* omsg ) {
1027 // Create an exception message
1028 osrfMessage* err = osrf_message_init( STATUS, omsg->thread_trace, 1 );
1029 osrf_message_set_status_info( err,
1030 "osrfMethodException", "Router method not found", OSRF_STATUS_NOTFOUND );
1032 // Translate it into JSON
1033 char* data = osrf_message_serialize(err);
1034 osrfMessageFree( err );
1036 // Wrap the JSON up in a transport_message
1037 transport_message* tresponse = message_init(
1038 data, "", msg->thread, msg->sender, msg->recipient );
1042 client_send_message( router->connection, tresponse );
1043 message_free( tresponse );
1048 @brief Send a response to a router REQUEST message.
1049 @param router Pointer to the current osrfRouter.
1050 @param msg Pointer to the transport_message that contained the REQUEST message.
1051 @param omsg Pointer to the osrfMessage that contained the REQUEST.
1052 @param response Pointer to the jsonObject that constitutes the response.
1054 static void osrfRouterSendAppResponse( osrfRouter* router, const transport_message* msg,
1055 const osrfMessage* omsg, const jsonObject* response ) {
1057 if( response ) { /* send the response message */
1059 // Turn the jsonObject into JSON, and load it into an osrfMessage
1060 osrfMessage* oresponse = osrf_message_init(
1061 RESULT, omsg->thread_trace, omsg->protocol );
1063 char* json = jsonObjectToJSON(response);
1064 osrf_message_set_result_content( oresponse, json );
1067 // Package the osrfMessage into a transport_message, and send it
1068 char* data = osrf_message_serialize(oresponse);
1069 osrfMessageFree(oresponse);
1070 osrfLogDebug( OSRF_LOG_MARK, "Responding to client app request with data: \n%s\n", data );
1072 transport_message* tresponse = message_init(
1073 data, "", msg->thread, msg->sender, msg->recipient );
1076 client_send_message(router->connection, tresponse );
1077 message_free(tresponse);
1080 /* now send the 'request complete' message */
1081 osrfMessage* status = osrf_message_init( STATUS, omsg->thread_trace, 1);
1082 osrf_message_set_status_info( status, "osrfConnectStatus", "Request Complete",
1083 OSRF_STATUS_COMPLETE );
1084 char* statusdata = osrf_message_serialize(status);
1085 osrfMessageFree(status);
1087 transport_message* sresponse = message_init(
1088 statusdata, "", msg->thread, msg->sender, msg->recipient );
1091 client_send_message(router->connection, sresponse );
1092 message_free(sresponse);