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>
9 #include <sys/resource.h>
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
19 #define GATEWAY_DEFAULT_CONFIG "/openils/conf/opensrf_core.xml"
22 /* our config structure */
24 char* configfile; /* our bootstrap config file */
25 } osrf_json_gateway_config;
29 } osrf_json_gateway_dir_config;
32 module AP_MODULE_DECLARE_DATA osrf_json_gateway_module;
34 char* osrf_json_gateway_config_file = NULL;
37 osrfStringArray* allowedServices = NULL;
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;
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;
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"),
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;
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;
78 static void osrf_json_gateway_child_init(apr_pool_t *p, server_rec *s) {
80 char* cfg = osrf_json_gateway_config_file;
84 snprintf(buf, 32, "%d", t);
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);
93 allowedServices = osrfNewStringArray(8);
94 osrfLogInfo(OSRF_LOG_MARK, "Bootstrapping gateway child for requests");
95 osrfConfigGetValueList( NULL, allowedServices, "/services/service" );
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));
104 static int osrf_json_gateway_method_handler (request_rec *r) {
106 /* make sure we're needed first thing*/
107 if (strcmp(r->handler, MODULE_NAME )) return DECLINED;
110 osrf_json_gateway_dir_config* dir_conf =
111 ap_get_module_config(r->per_dir_config, &osrf_json_gateway_module);
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;
118 if(dir_conf->legacyJSON) {
119 ap_log_rerror( APLOG_MARK, APLOG_INFO, 0, r, "Using legacy JSON");
122 ap_log_rerror( APLOG_MARK, APLOG_INFO, 0, r, "Not using legacy JSON");
123 parseJSONFunc = jsonParseString;
124 jsonToStringFunc = jsonObjectToJSON;
128 osrfLogDebug(OSRF_LOG_MARK, "osrf gateway: entered request handler");
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 */
138 osrfLogSetAppname("osrf_json_gw");
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' */
148 r->allowed |= (AP_METHOD_BIT << M_GET);
149 r->allowed |= (AP_METHOD_BIT << M_POST);
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 */
163 if(input_format == NULL)
164 input_format = format;
166 /* set the user defined timeout value */
168 char* tout = apacheGetFirstParamValue( params, "timeout" ); /* request timeout in seconds */
170 timeout = atoi(tout);
171 osrfLogDebug(OSRF_LOG_MARK, "Client supplied timeout of %d", timeout);
176 api_level = atoi(a_l);
178 if (!strcasecmp(format, "xml")) {
180 ap_set_content_type(r, "application/xml");
182 ap_set_content_type(r, "text/plain");
187 if(!(service && method) ||
188 !osrfStringArrayContains(allowedServices, service)) {
190 osrfLogError(OSRF_LOG_MARK,
191 "Service [%s] not found or not allowed", service);
192 ret = HTTP_NOT_FOUND;
196 /* This will log all heaers to the apache error log
197 const apr_array_header_t* arr = apr_table_elts(r->headers_in);
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 );
207 osrfAppSession* session = osrf_app_client_session_init(service);
209 double starttime = get_timestamp_millis();
212 if(!strcasecmp(input_format, "json")) {
213 jsonObject * arr = jsonNewObject(NULL);
216 while( (str = osrfStringArrayGetString(mparams, i++)) )
217 jsonObjectPush(arr, parseJSONFunc(str));
219 req_id = osrf_app_session_make_req( session, arr, method, api_level, NULL );
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);
231 while( (str = osrfStringArrayGetString(mparams, i++)) ) {
232 jsonObjectPush(jsonParams, jsonXMLToJSONObject(str));
235 req_id = osrf_app_session_make_req( session, jsonParams, method, api_level, NULL );
236 jsonObjectFree(jsonParams);
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 */
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++)) ) {
259 OSRF_BUFFER_ADD(act, " ");
260 OSRF_BUFFER_ADD(act, str);
262 OSRF_BUFFER_ADD(act, ", ");
263 OSRF_BUFFER_ADD(act, str);
267 osrfLogActivity( OSRF_LOG_MARK, act->buf );
269 /* ----------------------------------------------------------------- */
272 osrf_message* omsg = NULL;
274 int statuscode = 200;
276 /* kick off the object */
278 ap_rputs("<response xmlns=\"http://opensrf.org/-/namespaces/gateway/v1\"><payload>", r);
280 ap_rputs("{\"payload\":[", r);
283 char* statusname = NULL;
284 char* statustext = NULL;
287 while((omsg = osrfAppSessionRequestRecv( session, req_id, timeout ))) {
289 statuscode = omsg->status_code;
292 if( ( res = osrfMessageGetResult(omsg)) ) {
295 output = jsonObjectToXML( res );
297 //output = jsonObjectToJSON( res );
298 output = jsonToStringFunc( res );
299 if( morethan1 ) ap_rputs(",", r); /* comma between JSON array items */
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 );
314 osrf_message_free(omsg);
315 if(statusname) break;
318 double duration = get_timestamp_millis() - starttime;
319 osrfLogDebug(OSRF_LOG_MARK, "gateway request took %f seconds", duration);
323 ap_rputs("</payload>", r);
325 ap_rputs("]",r); /* finish off the payload array */
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;
337 snprintf( buf, l, "<debug>\"%s : %s\"</debug>", statusname, statustext );
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);
357 /* insert the status code */
362 snprintf(buf, 32, "<status>%d</status>", statuscode );
364 snprintf(buf, 32, ",\"status\":%d", statuscode );
369 ap_rputs("</response>", r);
371 ap_rputs( "}", r ); /* finish off the object */
373 osrf_app_session_destroy(session);
376 osrfLogInfo(OSRF_LOG_MARK, "Completed processing service=%s, method=%s", service, method);
377 string_array_destroy(params);
378 string_array_destroy(mparams);
380 osrfLogDebug(OSRF_LOG_MARK, "Gateway served %d requests", ++numserved);
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);
394 module AP_MODULE_DECLARE_DATA osrf_json_gateway_module = {
395 STANDARD20_MODULE_STUFF,
396 osrf_json_gateway_create_dir_config,
398 osrf_json_gateway_create_config,
400 osrf_json_gateway_cmds,
401 osrf_json_gateway_register_hooks,