Fossil SCM

Added initial JSONP support, but lacks significant testing.

stephan 2011-09-23 00:39 UTC json
Commit e72ae99dc2aa29b57f2c29014225c2c73178eee7
1 file changed +82 -24
+82 -24
--- src/json.c
+++ src/json.c
@@ -217,10 +217,18 @@
217217
cson_value * root = NULL;
218218
blob_rewind( pSrc );
219219
cson_parse( &root, cson_data_src_Blob, pSrc, NULL, pInfo );
220220
return root;
221221
}
222
+/*
223
+** Implements the cson_data_dest_f() interface and outputs the data to
224
+** cgi_append_content(). pState is ignored.
225
+**/
226
+int cson_data_dest_cgi(void * pState, void const * src, unsigned int n){
227
+ cgi_append_content( (char const *)src, (int)n );
228
+ return 0;
229
+}
222230
223231
/*
224232
** Returns a string in the form FOSSIL-XXXX, where XXXX is a
225233
** left-zero-padded value of code. The returned buffer is static, and
226234
** must be copied if needed for later. The returned value will always
@@ -424,10 +432,46 @@
424432
return "text/plain";
425433
}
426434
}
427435
}
428436
}
437
+
438
+/*
439
+** Sends pResponse to the output stream as the response object. This
440
+** function does no validation of pResponse except to assert() that it
441
+** is not NULL. The caller is responsible for ensuring that it meets
442
+** API response envelope conventions.
443
+**
444
+** In CLI mode pResponse is sent to stdout immediately. In HTTP
445
+** mode pResponse replaces any current CGI content but cgi_reply()
446
+** is not called to flush the output.
447
+**
448
+** If g.json.jsonp is not NULL then the content type is set to
449
+** application/javascript and the output is wrapped in a jsonp
450
+** wrapper.
451
+*/
452
+void json_send_response( cson_value const * pResponse ){
453
+ assert( NULL != pResponse );
454
+ if( g.isHTTP ){
455
+ cgi_reset_content();
456
+ if( g.json.jsonp ){
457
+ cgi_printf("%s(",g.json.jsonp);
458
+ }
459
+ cson_output( pResponse, cson_data_dest_cgi, NULL, &g.json.outOpt );
460
+ if( g.json.jsonp ){
461
+ cgi_append_content(")",1);
462
+ }
463
+ }else{/*CLI mode*/
464
+ if( g.json.jsonp ){
465
+ fprintf(stdout,"%s(",g.json.jsonp);
466
+ }
467
+ cson_output_FILE( pResponse, stdout, &g.json.outOpt );
468
+ if( g.json.jsonp ){
469
+ fwrite(")\n", 2, 1, stdout);
470
+ }
471
+ }
472
+}
429473
430474
/*
431475
** Returns the current request's JSON authentication token, or NULL if
432476
** none is found. The token's memory is owned by (or shared with)
433477
** g.json.
@@ -714,27 +758,35 @@
714758
if( once ){
715759
return;
716760
}else{
717761
once = 1;
718762
}
763
+ g.json.jsonp = PD("jsonp",NULL);
719764
g.json.isJsonMode = 1;
720765
g.json.resultCode = 0;
721766
g.json.cmd.offset = -1;
722767
if( !g.isHTTP && g.fullHttpReply ){
723768
/* workaround for server mode, so we see it as CGI mode. */
724769
g.isHTTP = 1;
725770
}
771
+
772
+ if(!g.json.jsonp && g.json.post.o){
773
+ g.json.jsonp = cson_string_cstr(cson_value_get_string(cson_object_get(g.json.post.o,"jsonp")));
774
+ }
726775
if( !g.isHTTP ){
727776
g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;
777
+ if(!g.json.jsonp){
778
+ g.json.jsonp = find_option("jsonp",NULL,1);
779
+ }
728780
}
729781
730
- if(! g.json.post.v ){
731
- /* If cgi_init() reads POSTed JSON then it sets the content type.
732
- If it did not then we need to set it.
733
- */
734
- cgi_set_content_type(json_guess_content_type());
735
- }
782
+ /* FIXME: do some sanity checking on g.json.jsonp and ignore it
783
+ if it is not halfway reasonable.
784
+ */
785
+ cgi_set_content_type(json_guess_content_type())
786
+ /* reminder: must be done after g.json.jsonp is initialized */
787
+ ;
736788
737789
#if defined(NDEBUG)
738790
/* avoids debug messages on stderr in JSON mode */
739791
sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0);
740792
#endif
@@ -793,11 +845,13 @@
793845
cson_int_t const n = cson_value_get_integer(indentV);
794846
indent = (n>0) ? (unsigned char)n : 0;
795847
}
796848
}
797849
g.json.outOpt.indentation = indent;
798
- g.json.outOpt.addNewline = g.isHTTP ? 0 : 1;
850
+ g.json.outOpt.addNewline = g.isHTTP
851
+ ? 0
852
+ : (g.json.jsonp ? 0 : 1);
799853
}
800854
801855
if( g.isHTTP ){
802856
json_auth_token()/* will copy our auth token, if any, to fossil's
803857
core, which we need before we call
@@ -994,12 +1048,12 @@
9941048
** provide his own or may use an empty string to suppress the
9951049
** resultText property.
9961050
**
9971051
*/
9981052
cson_value * json_create_response( int resultCode,
999
- cson_value * payload,
1000
- char const * pMsg ){
1053
+ char const * pMsg,
1054
+ cson_value * payload){
10011055
cson_value * v = NULL;
10021056
cson_value * tmp = NULL;
10031057
cson_object * o = NULL;
10041058
int rc;
10051059
resultCode = json_dumbdown_rc(resultCode);
@@ -1100,29 +1154,36 @@
11001154
cson_value * resp = NULL;
11011155
rc = json_dumbdown_rc(rc);
11021156
if( rc && !msg ){
11031157
msg = json_err_str(rc);
11041158
}
1105
- resp = json_create_response(rc, NULL, msg);
1159
+ resp = json_create_response(rc, msg, NULL);
11061160
if(!resp){
11071161
/* about the only error case here is out-of-memory. DO NOT
11081162
call fossil_panic() here because that calls this function.
11091163
*/
11101164
fprintf(stderr, "%s: Fatal error: could not allocate "
11111165
"response object.\n", fossil_nameofexe());
11121166
fossil_exit(1);
11131167
}
11141168
if( g.isHTTP ){
1115
- Blob buf = empty_blob;
1116
- cgi_reset_content();
1117
- cson_output_Blob( resp, &buf, &g.json.outOpt );
1118
- cgi_set_content(&buf);
1119
- if( alsoOutput ){
1120
- cgi_reply();
1169
+ if(alsoOutput){
1170
+ json_send_response(resp);
1171
+ }else{
1172
+ /* almost a duplicate of json_send_response() :( */
1173
+ cgi_set_content_type("application/javascript");
1174
+ cgi_reset_content();
1175
+ if( g.json.jsonp ){
1176
+ cgi_printf("%s(",g.json.jsonp);
1177
+ }
1178
+ cson_output( resp, cson_data_dest_cgi, NULL, &g.json.outOpt );
1179
+ if( g.json.jsonp ){
1180
+ cgi_append_content(")",1);
1181
+ }
11211182
}
11221183
}else{
1123
- cson_output_FILE( resp, stdout, &g.json.outOpt );
1184
+ json_send_response(resp);
11241185
}
11251186
cson_value_free(resp);
11261187
}
11271188
11281189
/*
@@ -2348,11 +2409,10 @@
23482409
** This function dispatches them, and is the HTTP equivalent of
23492410
** json_cmd_top().
23502411
*/
23512412
void json_page_top(void){
23522413
int rc = FSL_JSON_E_UNKNOWN_COMMAND;
2353
- Blob buf = empty_blob;
23542414
char const * cmd;
23552415
cson_value * payload = NULL;
23562416
cson_value * root = NULL;
23572417
JsonPageDef const * pageDef = NULL;
23582418
json_mode_bootstrap();
@@ -2369,15 +2429,13 @@
23692429
payload = (*pageDef->func)(1);
23702430
}
23712431
if( g.json.resultCode ){
23722432
json_err(g.json.resultCode, NULL, 0);
23732433
}else{
2374
- blob_zero(&buf);
2375
- root = json_create_response(rc, payload, NULL);
2376
- cson_output_Blob( root, &buf, NULL );
2434
+ root = json_create_response(rc, NULL, payload);
2435
+ json_send_response(payload);
23772436
cson_value_free(root);
2378
- cgi_set_content(&buf)/*takes ownership of the buf memory*/;
23792437
}
23802438
}
23812439
23822440
/*
23832441
** This function dispatches json commands and is the CLI equivalent of
@@ -2443,12 +2501,12 @@
24432501
payload = (*pageDef->func)(1);
24442502
}
24452503
if( g.json.resultCode ){
24462504
json_err(g.json.resultCode, NULL, 1);
24472505
}else{
2448
- payload = json_create_response(rc, payload, NULL);
2449
- cson_output_FILE( payload, stdout, &g.json.outOpt );
2506
+ payload = json_create_response(rc, NULL, payload);
2507
+ json_send_response(payload);
24502508
cson_value_free( payload );
24512509
if((0 != rc) && !g.isHTTP){
24522510
/* FIXME: we need a way of passing this error back
24532511
up to the routine which called this callback.
24542512
e.g. add g.errCode.
24552513
--- src/json.c
+++ src/json.c
@@ -217,10 +217,18 @@
217 cson_value * root = NULL;
218 blob_rewind( pSrc );
219 cson_parse( &root, cson_data_src_Blob, pSrc, NULL, pInfo );
220 return root;
221 }
 
 
 
 
 
 
 
 
222
223 /*
224 ** Returns a string in the form FOSSIL-XXXX, where XXXX is a
225 ** left-zero-padded value of code. The returned buffer is static, and
226 ** must be copied if needed for later. The returned value will always
@@ -424,10 +432,46 @@
424 return "text/plain";
425 }
426 }
427 }
428 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
430 /*
431 ** Returns the current request's JSON authentication token, or NULL if
432 ** none is found. The token's memory is owned by (or shared with)
433 ** g.json.
@@ -714,27 +758,35 @@
714 if( once ){
715 return;
716 }else{
717 once = 1;
718 }
 
719 g.json.isJsonMode = 1;
720 g.json.resultCode = 0;
721 g.json.cmd.offset = -1;
722 if( !g.isHTTP && g.fullHttpReply ){
723 /* workaround for server mode, so we see it as CGI mode. */
724 g.isHTTP = 1;
725 }
 
 
 
 
726 if( !g.isHTTP ){
727 g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;
 
 
 
728 }
729
730 if(! g.json.post.v ){
731 /* If cgi_init() reads POSTed JSON then it sets the content type.
732 If it did not then we need to set it.
733 */
734 cgi_set_content_type(json_guess_content_type());
735 }
736
737 #if defined(NDEBUG)
738 /* avoids debug messages on stderr in JSON mode */
739 sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0);
740 #endif
@@ -793,11 +845,13 @@
793 cson_int_t const n = cson_value_get_integer(indentV);
794 indent = (n>0) ? (unsigned char)n : 0;
795 }
796 }
797 g.json.outOpt.indentation = indent;
798 g.json.outOpt.addNewline = g.isHTTP ? 0 : 1;
 
 
799 }
800
801 if( g.isHTTP ){
802 json_auth_token()/* will copy our auth token, if any, to fossil's
803 core, which we need before we call
@@ -994,12 +1048,12 @@
994 ** provide his own or may use an empty string to suppress the
995 ** resultText property.
996 **
997 */
998 cson_value * json_create_response( int resultCode,
999 cson_value * payload,
1000 char const * pMsg ){
1001 cson_value * v = NULL;
1002 cson_value * tmp = NULL;
1003 cson_object * o = NULL;
1004 int rc;
1005 resultCode = json_dumbdown_rc(resultCode);
@@ -1100,29 +1154,36 @@
1100 cson_value * resp = NULL;
1101 rc = json_dumbdown_rc(rc);
1102 if( rc && !msg ){
1103 msg = json_err_str(rc);
1104 }
1105 resp = json_create_response(rc, NULL, msg);
1106 if(!resp){
1107 /* about the only error case here is out-of-memory. DO NOT
1108 call fossil_panic() here because that calls this function.
1109 */
1110 fprintf(stderr, "%s: Fatal error: could not allocate "
1111 "response object.\n", fossil_nameofexe());
1112 fossil_exit(1);
1113 }
1114 if( g.isHTTP ){
1115 Blob buf = empty_blob;
1116 cgi_reset_content();
1117 cson_output_Blob( resp, &buf, &g.json.outOpt );
1118 cgi_set_content(&buf);
1119 if( alsoOutput ){
1120 cgi_reply();
 
 
 
 
 
 
 
1121 }
1122 }else{
1123 cson_output_FILE( resp, stdout, &g.json.outOpt );
1124 }
1125 cson_value_free(resp);
1126 }
1127
1128 /*
@@ -2348,11 +2409,10 @@
2348 ** This function dispatches them, and is the HTTP equivalent of
2349 ** json_cmd_top().
2350 */
2351 void json_page_top(void){
2352 int rc = FSL_JSON_E_UNKNOWN_COMMAND;
2353 Blob buf = empty_blob;
2354 char const * cmd;
2355 cson_value * payload = NULL;
2356 cson_value * root = NULL;
2357 JsonPageDef const * pageDef = NULL;
2358 json_mode_bootstrap();
@@ -2369,15 +2429,13 @@
2369 payload = (*pageDef->func)(1);
2370 }
2371 if( g.json.resultCode ){
2372 json_err(g.json.resultCode, NULL, 0);
2373 }else{
2374 blob_zero(&buf);
2375 root = json_create_response(rc, payload, NULL);
2376 cson_output_Blob( root, &buf, NULL );
2377 cson_value_free(root);
2378 cgi_set_content(&buf)/*takes ownership of the buf memory*/;
2379 }
2380 }
2381
2382 /*
2383 ** This function dispatches json commands and is the CLI equivalent of
@@ -2443,12 +2501,12 @@
2443 payload = (*pageDef->func)(1);
2444 }
2445 if( g.json.resultCode ){
2446 json_err(g.json.resultCode, NULL, 1);
2447 }else{
2448 payload = json_create_response(rc, payload, NULL);
2449 cson_output_FILE( payload, stdout, &g.json.outOpt );
2450 cson_value_free( payload );
2451 if((0 != rc) && !g.isHTTP){
2452 /* FIXME: we need a way of passing this error back
2453 up to the routine which called this callback.
2454 e.g. add g.errCode.
2455
--- src/json.c
+++ src/json.c
@@ -217,10 +217,18 @@
217 cson_value * root = NULL;
218 blob_rewind( pSrc );
219 cson_parse( &root, cson_data_src_Blob, pSrc, NULL, pInfo );
220 return root;
221 }
222 /*
223 ** Implements the cson_data_dest_f() interface and outputs the data to
224 ** cgi_append_content(). pState is ignored.
225 **/
226 int cson_data_dest_cgi(void * pState, void const * src, unsigned int n){
227 cgi_append_content( (char const *)src, (int)n );
228 return 0;
229 }
230
231 /*
232 ** Returns a string in the form FOSSIL-XXXX, where XXXX is a
233 ** left-zero-padded value of code. The returned buffer is static, and
234 ** must be copied if needed for later. The returned value will always
@@ -424,10 +432,46 @@
432 return "text/plain";
433 }
434 }
435 }
436 }
437
438 /*
439 ** Sends pResponse to the output stream as the response object. This
440 ** function does no validation of pResponse except to assert() that it
441 ** is not NULL. The caller is responsible for ensuring that it meets
442 ** API response envelope conventions.
443 **
444 ** In CLI mode pResponse is sent to stdout immediately. In HTTP
445 ** mode pResponse replaces any current CGI content but cgi_reply()
446 ** is not called to flush the output.
447 **
448 ** If g.json.jsonp is not NULL then the content type is set to
449 ** application/javascript and the output is wrapped in a jsonp
450 ** wrapper.
451 */
452 void json_send_response( cson_value const * pResponse ){
453 assert( NULL != pResponse );
454 if( g.isHTTP ){
455 cgi_reset_content();
456 if( g.json.jsonp ){
457 cgi_printf("%s(",g.json.jsonp);
458 }
459 cson_output( pResponse, cson_data_dest_cgi, NULL, &g.json.outOpt );
460 if( g.json.jsonp ){
461 cgi_append_content(")",1);
462 }
463 }else{/*CLI mode*/
464 if( g.json.jsonp ){
465 fprintf(stdout,"%s(",g.json.jsonp);
466 }
467 cson_output_FILE( pResponse, stdout, &g.json.outOpt );
468 if( g.json.jsonp ){
469 fwrite(")\n", 2, 1, stdout);
470 }
471 }
472 }
473
474 /*
475 ** Returns the current request's JSON authentication token, or NULL if
476 ** none is found. The token's memory is owned by (or shared with)
477 ** g.json.
@@ -714,27 +758,35 @@
758 if( once ){
759 return;
760 }else{
761 once = 1;
762 }
763 g.json.jsonp = PD("jsonp",NULL);
764 g.json.isJsonMode = 1;
765 g.json.resultCode = 0;
766 g.json.cmd.offset = -1;
767 if( !g.isHTTP && g.fullHttpReply ){
768 /* workaround for server mode, so we see it as CGI mode. */
769 g.isHTTP = 1;
770 }
771
772 if(!g.json.jsonp && g.json.post.o){
773 g.json.jsonp = cson_string_cstr(cson_value_get_string(cson_object_get(g.json.post.o,"jsonp")));
774 }
775 if( !g.isHTTP ){
776 g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;
777 if(!g.json.jsonp){
778 g.json.jsonp = find_option("jsonp",NULL,1);
779 }
780 }
781
782 /* FIXME: do some sanity checking on g.json.jsonp and ignore it
783 if it is not halfway reasonable.
784 */
785 cgi_set_content_type(json_guess_content_type())
786 /* reminder: must be done after g.json.jsonp is initialized */
787 ;
788
789 #if defined(NDEBUG)
790 /* avoids debug messages on stderr in JSON mode */
791 sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0);
792 #endif
@@ -793,11 +845,13 @@
845 cson_int_t const n = cson_value_get_integer(indentV);
846 indent = (n>0) ? (unsigned char)n : 0;
847 }
848 }
849 g.json.outOpt.indentation = indent;
850 g.json.outOpt.addNewline = g.isHTTP
851 ? 0
852 : (g.json.jsonp ? 0 : 1);
853 }
854
855 if( g.isHTTP ){
856 json_auth_token()/* will copy our auth token, if any, to fossil's
857 core, which we need before we call
@@ -994,12 +1048,12 @@
1048 ** provide his own or may use an empty string to suppress the
1049 ** resultText property.
1050 **
1051 */
1052 cson_value * json_create_response( int resultCode,
1053 char const * pMsg,
1054 cson_value * payload){
1055 cson_value * v = NULL;
1056 cson_value * tmp = NULL;
1057 cson_object * o = NULL;
1058 int rc;
1059 resultCode = json_dumbdown_rc(resultCode);
@@ -1100,29 +1154,36 @@
1154 cson_value * resp = NULL;
1155 rc = json_dumbdown_rc(rc);
1156 if( rc && !msg ){
1157 msg = json_err_str(rc);
1158 }
1159 resp = json_create_response(rc, msg, NULL);
1160 if(!resp){
1161 /* about the only error case here is out-of-memory. DO NOT
1162 call fossil_panic() here because that calls this function.
1163 */
1164 fprintf(stderr, "%s: Fatal error: could not allocate "
1165 "response object.\n", fossil_nameofexe());
1166 fossil_exit(1);
1167 }
1168 if( g.isHTTP ){
1169 if(alsoOutput){
1170 json_send_response(resp);
1171 }else{
1172 /* almost a duplicate of json_send_response() :( */
1173 cgi_set_content_type("application/javascript");
1174 cgi_reset_content();
1175 if( g.json.jsonp ){
1176 cgi_printf("%s(",g.json.jsonp);
1177 }
1178 cson_output( resp, cson_data_dest_cgi, NULL, &g.json.outOpt );
1179 if( g.json.jsonp ){
1180 cgi_append_content(")",1);
1181 }
1182 }
1183 }else{
1184 json_send_response(resp);
1185 }
1186 cson_value_free(resp);
1187 }
1188
1189 /*
@@ -2348,11 +2409,10 @@
2409 ** This function dispatches them, and is the HTTP equivalent of
2410 ** json_cmd_top().
2411 */
2412 void json_page_top(void){
2413 int rc = FSL_JSON_E_UNKNOWN_COMMAND;
 
2414 char const * cmd;
2415 cson_value * payload = NULL;
2416 cson_value * root = NULL;
2417 JsonPageDef const * pageDef = NULL;
2418 json_mode_bootstrap();
@@ -2369,15 +2429,13 @@
2429 payload = (*pageDef->func)(1);
2430 }
2431 if( g.json.resultCode ){
2432 json_err(g.json.resultCode, NULL, 0);
2433 }else{
2434 root = json_create_response(rc, NULL, payload);
2435 json_send_response(payload);
 
2436 cson_value_free(root);
 
2437 }
2438 }
2439
2440 /*
2441 ** This function dispatches json commands and is the CLI equivalent of
@@ -2443,12 +2501,12 @@
2501 payload = (*pageDef->func)(1);
2502 }
2503 if( g.json.resultCode ){
2504 json_err(g.json.resultCode, NULL, 1);
2505 }else{
2506 payload = json_create_response(rc, NULL, payload);
2507 json_send_response(payload);
2508 cson_value_free( payload );
2509 if((0 != rc) && !g.isHTTP){
2510 /* FIXME: we need a way of passing this error back
2511 up to the routine which called this callback.
2512 e.g. add g.errCode.
2513

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button