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