Added a new XML-2-JSON utility for converting XMLized OpenSRF objects to their intern...
[OpenSRF.git] / src / gateway / osrf_json_gateway.c
1 #include "apachetools.h"
2 #include "opensrf/osrf_app_session.h"
3 #include "opensrf/osrf_system.h"
4 #include "opensrf/osrfConfig.h"
5 #include "objson/object.h"
6 #include "objson/json2xml.h"
7 #include "objson/xml2json.h"
8 #include <sys/time.h>
9 #include <sys/resource.h>
10 #include <unistd.h>
11
12
13 #define MODULE_NAME "osrf_json_gateway_module"
14 #define GATEWAY_CONFIG "OSRFGatewayConfig"
15 #define CONFIG_CONTEXT "gateway"
16
17 #define GATEWAY_DEFAULT_CONFIG "/openils/conf/opensrf_core.xml"
18
19
20 /* our config structure */
21 typedef struct { 
22         char* configfile;  /* our bootstrap config file */
23 } osrf_json_gateway_config;
24
25 module AP_MODULE_DECLARE_DATA osrf_json_gateway_module;
26
27 char* osrf_json_gateway_config_file = NULL;
28 int bootstrapped = 0;
29 int numserved = 0;
30 osrfStringArray* allowedServices = NULL;
31
32 static const char* osrf_json_gateway_set_config(cmd_parms *parms, void *config, const char *arg) {
33         osrf_json_gateway_config  *cfg;
34         cfg = ap_get_module_config(parms->server->module_config, &osrf_json_gateway_module);
35         cfg->configfile = (char*) arg;
36         osrf_json_gateway_config_file = (char*) arg;
37         return NULL;
38 }
39
40 /* tell apache about our commands */
41 static const command_rec osrf_json_gateway_cmds[] = {
42         AP_INIT_TAKE1( GATEWAY_CONFIG, osrf_json_gateway_set_config, 
43                         NULL, RSRC_CONF, "osrf json gateway config file"),
44         {NULL}
45 };
46
47 /* build the config object */
48 static void* osrf_json_gateway_create_config( apr_pool_t* p, server_rec* s) {
49         osrf_json_gateway_config* cfg = (osrf_json_gateway_config*) 
50                         apr_palloc(p, sizeof(osrf_json_gateway_config));
51         cfg->configfile = GATEWAY_DEFAULT_CONFIG;
52         return (void*) cfg;
53 }
54
55
56 static void osrf_json_gateway_child_init(apr_pool_t *p, server_rec *s) {
57
58         char* cfg = osrf_json_gateway_config_file;
59         char buf[32];
60         memset(buf, 0x0, 32);
61         int t = time(NULL);
62         snprintf(buf, 32, "%d", t);
63
64         if( ! osrfSystemBootstrapClientResc( cfg, CONFIG_CONTEXT, buf ) ) {
65                 ap_log_error( APLOG_MARK, APLOG_ERR, 0, s, 
66                         "Unable to Bootstrap OpenSRF Client with config %s..", cfg);
67                 return;
68         }
69
70         bootstrapped = 1;
71         allowedServices = osrfNewStringArray(8);
72         osrfLogInfo(OSRF_LOG_MARK, "Bootstrapping gateway child for requests");
73         osrfConfigGetValueList( NULL, allowedServices, "/services/service" );
74
75         int i;
76         for( i = 0; i < allowedServices->size; i++ ) {
77                 ap_log_error( APLOG_MARK, APLOG_DEBUG, 0, s, 
78                         "allowed service: %s\n", osrfStringArrayGetString(allowedServices, i));
79         }
80 }
81
82 static int osrf_json_gateway_method_handler (request_rec *r) {
83
84         /* make sure we're needed first thing*/
85         if (strcmp(r->handler, MODULE_NAME )) return DECLINED;
86
87         osrfLogDebug(OSRF_LOG_MARK, "osrf gateway: entered request handler");
88
89         /* verify we are connected */
90         if( !bootstrapped || !osrf_system_get_transport_client()) {
91                 ap_log_rerror( APLOG_MARK, APLOG_ERR, 0, r, "Cannot process request "
92                                 "because the OpenSRF JSON gateway has not been bootstrapped...");
93                 usleep( 100000 ); /* 100 milliseconds */
94                 exit(1);
95         }
96
97         osrfLogSetAppname("osrf_json_gw");
98
99         char* service           = NULL; /* service to connect to */
100         char* method            = NULL; /* method to perform */
101         char* format            = NULL; /* method to perform */
102         char* a_l                       = NULL; /* request api level */
103     char* input_format  = NULL; /* POST data format, defaults to 'format' */
104         int   isXML                     = 0;
105         int   api_level = 1;
106
107         r->allowed |= (AP_METHOD_BIT << M_GET);
108         r->allowed |= (AP_METHOD_BIT << M_POST);
109
110         osrfLogDebug(OSRF_LOG_MARK, "osrf gateway: parsing URL params");
111         string_array* mparams   = NULL;
112         string_array* params            = apacheParseParms(r); /* free me */
113         service         = apacheGetFirstParamValue( params, "service" );
114         method          = apacheGetFirstParamValue( params, "method" ); 
115         format          = apacheGetFirstParamValue( params, "format" ); 
116         input_format = apacheGetFirstParamValue( params, "input_format" ); 
117         a_l                     = apacheGetFirstParamValue( params, "api_level" ); 
118         mparams         = apacheGetParamValues( params, "param" ); /* free me */
119
120     if(format == NULL)
121         format = "json";
122     if(input_format == NULL)
123         input_format = format;
124
125    /* set the user defined timeout value */
126    int timeout = 60;
127    char* tout = apacheGetFirstParamValue( params, "timeout" ); /* request timeout in seconds */
128    if( tout ) {
129       timeout = atoi(tout);
130       osrfLogDebug(OSRF_LOG_MARK, "Client supplied timeout of %d", timeout);
131    }
132
133
134         if (a_l)
135                 api_level = atoi(a_l);
136
137         if (!strcasecmp(format, "xml")) {
138                 isXML = 1;
139                 ap_set_content_type(r, "application/xml");
140         } else {
141                 ap_set_content_type(r, "text/plain");
142         }
143
144         int ret = OK;
145
146         if(!(service && method) || 
147                 !osrfStringArrayContains(allowedServices, service)) {
148
149                 osrfLogError(OSRF_LOG_MARK, 
150                         "Service [%s] not found or not allowed", service);
151                 ret = HTTP_NOT_FOUND;
152
153         } else {
154
155                 /* This will log all heaers to the apache error log 
156                 const apr_array_header_t* arr = apr_table_elts(r->headers_in);
157                 const void* ptr;
158
159                 while( (ptr = apr_array_pop(arr)) ) {
160                         apr_table_entry_t* e = (apr_table_entry_t*) ptr;
161                         fprintf(stderr, "Table entry: %s : %s\n", e->key, e->val );
162                 }
163                 fflush(stderr);
164                 */
165
166                 osrfAppSession* session = osrf_app_client_session_init(service);
167
168                 double starttime = get_timestamp_millis();
169                 int req_id = -1;
170
171         if(!strcasecmp(input_format, "json")) {
172                     req_id = osrf_app_session_make_req( session, NULL, method, api_level, mparams );
173
174         } else {
175
176             /**
177              * If we receive XML method params, convert each param to a JSON object
178              * and pass the array of JSON object params to the method */
179             if(!strcasecmp(input_format, "xml")) {
180                 jsonObject* jsonParams = jsonNewObject(NULL);
181
182                 char* str;
183                 int i = 0;
184                 while( (str = osrfStringArrayGetString(mparams, i++)) ) {
185                     jsonObjectPush(jsonParams, jsonXMLToJSONObject(str));
186                 }
187
188                         req_id = osrf_app_session_make_req( session, jsonParams, method, api_level, NULL );
189                 jsonObjectFree(jsonParams);
190             }
191         }
192
193
194                 if( req_id == -1 ) {
195                         osrfLogError(OSRF_LOG_MARK, "I am unable to communcate with opensrf..going away...");
196                         /* we don't want to spawn an intense re-forking storm 
197                          * if there is no jabber server.. so give it some time before we die */
198                         usleep( 100000 ); /* 100 milliseconds */
199                         exit(1);
200                 }
201
202
203                 /* ----------------------------------------------------------------- */
204                 /* log all requests to the activity log */
205                 const char* authtoken = apr_table_get(r->headers_in, "X-OILS-Authtoken");
206                 if(!authtoken) authtoken = "";
207                 growing_buffer* act = buffer_init(128); 
208                 buffer_fadd(act, "[%s] [%s] %s %s", r->connection->remote_ip, authtoken, service, method );
209                 char* str; int i = 0;
210                 while( (str = osrfStringArrayGetString(mparams, i++)) ) {
211                         if( i == 1 ) {
212             OSRF_BUFFER_ADD(act, " ");
213                                 OSRF_BUFFER_ADD(act, str);
214                         } else {
215                                 OSRF_BUFFER_ADD(act, ", ");
216                                 OSRF_BUFFER_ADD(act, str);
217                         }
218                 }
219
220                 osrfLogActivity( OSRF_LOG_MARK, act->buf );
221                 buffer_free(act);
222                 /* ----------------------------------------------------------------- */
223
224
225                 osrf_message* omsg = NULL;
226
227                 int statuscode = 200;
228
229                 /* kick off the object */
230                 if (isXML)
231                         ap_rputs("<response xmlns=\"http://opensrf.org/-/namespaces/gateway/v1\"><payload>", r);
232                 else
233                         ap_rputs("{\"payload\":[", r);
234
235                 int morethan1           = 0;
236                 char* statusname        = NULL;
237                 char* statustext        = NULL;
238                 char* output            = NULL;
239
240                 while((omsg = osrfAppSessionRequestRecv( session, req_id, timeout ))) {
241         
242                         statuscode = omsg->status_code;
243                         jsonObject* res;        
244
245                         if( ( res = osrfMessageGetResult(omsg)) ) {
246
247                                 if (isXML) {
248                                         output = jsonObjectToXML( res );
249                                 } else {
250                                         output = jsonObjectToJSON( res );
251                                         if( morethan1 ) ap_rputs(",", r); /* comma between JSON array items */
252                                 }
253                                 ap_rputs(output, r);
254                                 free(output);
255                                 morethan1 = 1;
256                 
257                         } else {
258         
259                                 if( statuscode > 299 ) { /* the request returned a low level error */
260                                         statusname = omsg->status_name ? strdup(omsg->status_name) : strdup("Unknown Error");
261                                         statustext = omsg->status_text ? strdup(omsg->status_text) : strdup("No Error Message");
262                                         osrfLogError( OSRF_LOG_MARK,  "Gateway received error: %s", statustext );
263                                 }
264                         }
265         
266                         osrf_message_free(omsg);
267                         if(statusname) break;
268                 }
269
270                 double duration = get_timestamp_millis() - starttime;
271                 osrfLogDebug(OSRF_LOG_MARK, "gateway request took %f seconds", duration);
272
273
274                 if (isXML)
275                         ap_rputs("</payload>", r);
276                 else
277                         ap_rputs("]",r); /* finish off the payload array */
278
279                 if(statusname) {
280
281                         /* add a debug field if the request died */
282                         ap_log_rerror( APLOG_MARK, APLOG_INFO, 0, r, 
283                                         "OpenSRF JSON Request returned error: %s -> %s", statusname, statustext );
284                         int l = strlen(statusname) + strlen(statustext) + 32;
285                         char buf[l];
286                         bzero(buf,l);
287
288                         if (isXML)
289                                 snprintf( buf, l, "<debug>\"%s : %s\"</debug>", statusname, statustext );
290
291                         else {
292                                 char bb[l];
293                                 bzero(bb, l);
294                                 snprintf(bb, l,  "%s : %s", statusname, statustext);
295                                 jsonObject* tmp = jsonNewObject(bb);
296                                 char* j = jsonObjectToJSON(tmp);
297                                 snprintf( buf, l, ",\"debug\": %s", j);
298                                 free(j);
299                                 jsonObjectFree(tmp);
300                         }
301
302                         ap_rputs(buf, r);
303
304                         free(statusname);
305                         free(statustext);
306                 }
307
308                 /* insert the status code */
309                 char buf[32];
310                 bzero(buf,32);
311
312                 if (isXML)
313                         snprintf(buf, 32, "<status>%d</status>", statuscode );
314                 else
315                         snprintf(buf, 32, ",\"status\":%d", statuscode );
316
317                 ap_rputs( buf, r );
318
319                 if (isXML)
320                         ap_rputs("</response>", r);
321                 else
322                         ap_rputs( "}", r ); /* finish off the object */
323
324                 osrf_app_session_destroy(session);
325         }
326
327         osrfLogInfo(OSRF_LOG_MARK, "Completed processing service=%s, method=%s", service, method);
328         string_array_destroy(params);
329         string_array_destroy(mparams);
330
331         osrfLogDebug(OSRF_LOG_MARK, "Gateway served %d requests", ++numserved);
332    osrfLogClearXid();
333
334         return ret;
335 }
336
337
338
339 static void osrf_json_gateway_register_hooks (apr_pool_t *p) {
340         ap_hook_handler(osrf_json_gateway_method_handler, NULL, NULL, APR_HOOK_MIDDLE);
341         ap_hook_child_init(osrf_json_gateway_child_init,NULL,NULL,APR_HOOK_MIDDLE);
342 }
343
344
345 module AP_MODULE_DECLARE_DATA osrf_json_gateway_module = {
346         STANDARD20_MODULE_STUFF,
347         NULL,
348         NULL,
349         osrf_json_gateway_create_config,
350         NULL,
351         osrf_json_gateway_cmds,
352         osrf_json_gateway_register_hooks,
353 };
354
355
356
357