Fossil SCM
Add support for SCGI via the --scgi command-line option to the "server" and "ui" and "http" commands.
Commit
a2e7472d0fa04132edf6b425252cb76366334a7a
Parent
0b75e2e61529784…
6 files changed
+68
-7
+15
-2
+77
-3
+2
-2
+3
+22
+68
-7
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -812,10 +812,12 @@ | ||
| 812 | 812 | } |
| 813 | 813 | } |
| 814 | 814 | fputs(z, pLog); |
| 815 | 815 | } |
| 816 | 816 | |
| 817 | +/* Forward declaration */ | |
| 818 | +static NORETURN void malformed_request(const char *zMsg); | |
| 817 | 819 | |
| 818 | 820 | /* |
| 819 | 821 | ** Initialize the query parameter database. Information is pulled from |
| 820 | 822 | ** the QUERY_STRING environment variable (if it exists), from standard |
| 821 | 823 | ** input if there is POST data, and from HTTP_COOKIE. |
| @@ -822,15 +824,31 @@ | ||
| 822 | 824 | */ |
| 823 | 825 | void cgi_init(void){ |
| 824 | 826 | char *z; |
| 825 | 827 | const char *zType; |
| 826 | 828 | int len; |
| 829 | + const char *zRequestUri = cgi_parameter("REQUEST_URI",0); | |
| 830 | + const char *zScriptName = cgi_parameter("SCRIPT_NAME",0); | |
| 831 | + | |
| 827 | 832 | #ifdef FOSSIL_ENABLE_JSON |
| 828 | 833 | json_main_bootstrap(); |
| 829 | 834 | #endif |
| 830 | 835 | g.isHTTP = 1; |
| 831 | 836 | cgi_destination(CGI_BODY); |
| 837 | + if( zRequestUri==0 ) malformed_request("missing REQUEST_URI"); | |
| 838 | + if( zScriptName==0 ) malformed_request("missing SCRIPT_NAME"); | |
| 839 | + if( cgi_parameter("PATH_INFO",0)==0 ){ | |
| 840 | + int i, j; | |
| 841 | + for(i=0; zRequestUri[i]==zScriptName[i] && zRequestUri[i]; i++){} | |
| 842 | + if( zRequestUri[i]=='/' ){ | |
| 843 | + for(j=i; zRequestUri[j] && zRequestUri[j]!='?'; j++){} | |
| 844 | + cgi_set_parameter("PATH_INFO", mprintf("%.*s", j-i, zRequestUri+i)); | |
| 845 | + }else{ | |
| 846 | + malformed_request("cannot compute PATH_INFO from REQUEST_URI" | |
| 847 | + " and SCRIPT_NAME"); | |
| 848 | + } | |
| 849 | + } | |
| 832 | 850 | |
| 833 | 851 | z = (char*)P("HTTP_COOKIE"); |
| 834 | 852 | if( z ){ |
| 835 | 853 | z = mprintf("%s",z); |
| 836 | 854 | add_param_list(z, ';'); |
| @@ -1092,14 +1110,14 @@ | ||
| 1092 | 1110 | |
| 1093 | 1111 | |
| 1094 | 1112 | /* |
| 1095 | 1113 | ** Send a reply indicating that the HTTP request was malformed |
| 1096 | 1114 | */ |
| 1097 | -static NORETURN void malformed_request(void){ | |
| 1115 | +static NORETURN void malformed_request(const char *zMsg){ | |
| 1098 | 1116 | cgi_set_status(501, "Not Implemented"); |
| 1099 | 1117 | cgi_printf( |
| 1100 | - "<html><body>Unrecognized HTTP Request</body></html>\n" | |
| 1118 | + "<html><body><p>Bad Request: %s</p></body></html>\n", zMsg | |
| 1101 | 1119 | ); |
| 1102 | 1120 | cgi_reply(); |
| 1103 | 1121 | fossil_exit(0); |
| 1104 | 1122 | } |
| 1105 | 1123 | |
| @@ -1187,29 +1205,30 @@ | ||
| 1187 | 1205 | struct sockaddr_in remoteName; |
| 1188 | 1206 | socklen_t size = sizeof(struct sockaddr_in); |
| 1189 | 1207 | char zLine[2000]; /* A single line of input. */ |
| 1190 | 1208 | g.fullHttpReply = 1; |
| 1191 | 1209 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1192 | - malformed_request(); | |
| 1210 | + malformed_request("missing HTTP header"); | |
| 1193 | 1211 | } |
| 1194 | 1212 | blob_append(&g.httpHeader, zLine, -1); |
| 1195 | 1213 | cgi_trace(zLine); |
| 1196 | 1214 | zToken = extract_token(zLine, &z); |
| 1197 | 1215 | if( zToken==0 ){ |
| 1198 | - malformed_request(); | |
| 1216 | + malformed_request("malformed HTTP header"); | |
| 1199 | 1217 | } |
| 1200 | 1218 | if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 |
| 1201 | 1219 | && fossil_strcmp(zToken,"HEAD")!=0 ){ |
| 1202 | - malformed_request(); | |
| 1220 | + malformed_request("unsupported HTTP method"); | |
| 1203 | 1221 | } |
| 1204 | 1222 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1205 | 1223 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1206 | 1224 | zToken = extract_token(z, &z); |
| 1207 | 1225 | if( zToken==0 ){ |
| 1208 | - malformed_request(); | |
| 1226 | + malformed_request("malformed URL in HTTP header"); | |
| 1209 | 1227 | } |
| 1210 | 1228 | cgi_setenv("REQUEST_URI", zToken); |
| 1229 | + cgi_setenv("SCRIPT_NAME", ""); | |
| 1211 | 1230 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1212 | 1231 | if( zToken[i] ) zToken[i++] = 0; |
| 1213 | 1232 | cgi_setenv("PATH_INFO", zToken); |
| 1214 | 1233 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1215 | 1234 | if( zIpAddr==0 && |
| @@ -1269,16 +1288,57 @@ | ||
| 1269 | 1288 | } |
| 1270 | 1289 | } |
| 1271 | 1290 | cgi_init(); |
| 1272 | 1291 | cgi_trace(0); |
| 1273 | 1292 | } |
| 1293 | + | |
| 1294 | +/* | |
| 1295 | +** This routine handles a single SCGI request which is coming in on | |
| 1296 | +** g.httpIn and which replies on g.httpOut | |
| 1297 | +** | |
| 1298 | +** The SCGI request is read from g.httpIn and is used to initialize | |
| 1299 | +** entries in the cgi_parameter() hash, as if those entries were | |
| 1300 | +** environment variables. A call to cgi_init() completes | |
| 1301 | +** the setup. Once all the setup is finished, this procedure returns | |
| 1302 | +** and subsequent code handles the actual generation of the webpage. | |
| 1303 | +*/ | |
| 1304 | +void cgi_handle_scgi_request(void){ | |
| 1305 | + char *zHdr; | |
| 1306 | + char *zToFree; | |
| 1307 | + int nHdr = 0; | |
| 1308 | + int nRead; | |
| 1309 | + int n, m; | |
| 1310 | + char c; | |
| 1311 | + | |
| 1312 | + while( (c = fgetc(g.httpIn))!=EOF && fossil_isdigit(c) ){ | |
| 1313 | + nHdr = nHdr*10 + c - '0'; | |
| 1314 | + } | |
| 1315 | + if( nHdr<16 ) malformed_request("SCGI header too short"); | |
| 1316 | + zToFree = zHdr = fossil_malloc(nHdr); | |
| 1317 | + nRead = (int)fread(zHdr, 1, nHdr, g.httpIn); | |
| 1318 | + if( nRead<nHdr ) malformed_request("cannot read entire SCGI header"); | |
| 1319 | + nHdr = nRead; | |
| 1320 | + while( nHdr ){ | |
| 1321 | + for(n=0; n<nHdr && zHdr[n]; n++){} | |
| 1322 | + for(m=n+1; m<nHdr && zHdr[m]; m++){} | |
| 1323 | + if( m>=nHdr ) malformed_request("SCGI header formatting error"); | |
| 1324 | + cgi_set_parameter(zHdr, zHdr+n+1); | |
| 1325 | + zHdr += m+1; | |
| 1326 | + nHdr -= m+1; | |
| 1327 | + } | |
| 1328 | + fossil_free(zToFree); | |
| 1329 | + fgetc(g.httpIn); /* Read past the "," separating header from content */ | |
| 1330 | + cgi_init(); | |
| 1331 | +} | |
| 1332 | + | |
| 1274 | 1333 | |
| 1275 | 1334 | #if INTERFACE |
| 1276 | 1335 | /* |
| 1277 | 1336 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1278 | 1337 | */ |
| 1279 | 1338 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1339 | +#define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */ | |
| 1280 | 1340 | |
| 1281 | 1341 | #endif /* INTERFACE */ |
| 1282 | 1342 | |
| 1283 | 1343 | /* |
| 1284 | 1344 | ** Maximum number of child processes that we can have running |
| @@ -1356,11 +1416,12 @@ | ||
| 1356 | 1416 | } |
| 1357 | 1417 | } |
| 1358 | 1418 | if( iPort>mxPort ) return 1; |
| 1359 | 1419 | listen(listener,10); |
| 1360 | 1420 | if( iPort>mnPort ){ |
| 1361 | - fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); | |
| 1421 | + fossil_print("Listening for %s requests on TCP port %d\n", | |
| 1422 | + (flags & HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); | |
| 1362 | 1423 | fflush(stdout); |
| 1363 | 1424 | } |
| 1364 | 1425 | if( zBrowser ){ |
| 1365 | 1426 | zBrowser = mprintf(zBrowser, iPort); |
| 1366 | 1427 | if( system(zBrowser)<0 ){ |
| 1367 | 1428 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -812,10 +812,12 @@ | |
| 812 | } |
| 813 | } |
| 814 | fputs(z, pLog); |
| 815 | } |
| 816 | |
| 817 | |
| 818 | /* |
| 819 | ** Initialize the query parameter database. Information is pulled from |
| 820 | ** the QUERY_STRING environment variable (if it exists), from standard |
| 821 | ** input if there is POST data, and from HTTP_COOKIE. |
| @@ -822,15 +824,31 @@ | |
| 822 | */ |
| 823 | void cgi_init(void){ |
| 824 | char *z; |
| 825 | const char *zType; |
| 826 | int len; |
| 827 | #ifdef FOSSIL_ENABLE_JSON |
| 828 | json_main_bootstrap(); |
| 829 | #endif |
| 830 | g.isHTTP = 1; |
| 831 | cgi_destination(CGI_BODY); |
| 832 | |
| 833 | z = (char*)P("HTTP_COOKIE"); |
| 834 | if( z ){ |
| 835 | z = mprintf("%s",z); |
| 836 | add_param_list(z, ';'); |
| @@ -1092,14 +1110,14 @@ | |
| 1092 | |
| 1093 | |
| 1094 | /* |
| 1095 | ** Send a reply indicating that the HTTP request was malformed |
| 1096 | */ |
| 1097 | static NORETURN void malformed_request(void){ |
| 1098 | cgi_set_status(501, "Not Implemented"); |
| 1099 | cgi_printf( |
| 1100 | "<html><body>Unrecognized HTTP Request</body></html>\n" |
| 1101 | ); |
| 1102 | cgi_reply(); |
| 1103 | fossil_exit(0); |
| 1104 | } |
| 1105 | |
| @@ -1187,29 +1205,30 @@ | |
| 1187 | struct sockaddr_in remoteName; |
| 1188 | socklen_t size = sizeof(struct sockaddr_in); |
| 1189 | char zLine[2000]; /* A single line of input. */ |
| 1190 | g.fullHttpReply = 1; |
| 1191 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1192 | malformed_request(); |
| 1193 | } |
| 1194 | blob_append(&g.httpHeader, zLine, -1); |
| 1195 | cgi_trace(zLine); |
| 1196 | zToken = extract_token(zLine, &z); |
| 1197 | if( zToken==0 ){ |
| 1198 | malformed_request(); |
| 1199 | } |
| 1200 | if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 |
| 1201 | && fossil_strcmp(zToken,"HEAD")!=0 ){ |
| 1202 | malformed_request(); |
| 1203 | } |
| 1204 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1205 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1206 | zToken = extract_token(z, &z); |
| 1207 | if( zToken==0 ){ |
| 1208 | malformed_request(); |
| 1209 | } |
| 1210 | cgi_setenv("REQUEST_URI", zToken); |
| 1211 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1212 | if( zToken[i] ) zToken[i++] = 0; |
| 1213 | cgi_setenv("PATH_INFO", zToken); |
| 1214 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1215 | if( zIpAddr==0 && |
| @@ -1269,16 +1288,57 @@ | |
| 1269 | } |
| 1270 | } |
| 1271 | cgi_init(); |
| 1272 | cgi_trace(0); |
| 1273 | } |
| 1274 | |
| 1275 | #if INTERFACE |
| 1276 | /* |
| 1277 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1278 | */ |
| 1279 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1280 | |
| 1281 | #endif /* INTERFACE */ |
| 1282 | |
| 1283 | /* |
| 1284 | ** Maximum number of child processes that we can have running |
| @@ -1356,11 +1416,12 @@ | |
| 1356 | } |
| 1357 | } |
| 1358 | if( iPort>mxPort ) return 1; |
| 1359 | listen(listener,10); |
| 1360 | if( iPort>mnPort ){ |
| 1361 | fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); |
| 1362 | fflush(stdout); |
| 1363 | } |
| 1364 | if( zBrowser ){ |
| 1365 | zBrowser = mprintf(zBrowser, iPort); |
| 1366 | if( system(zBrowser)<0 ){ |
| 1367 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -812,10 +812,12 @@ | |
| 812 | } |
| 813 | } |
| 814 | fputs(z, pLog); |
| 815 | } |
| 816 | |
| 817 | /* Forward declaration */ |
| 818 | static NORETURN void malformed_request(const char *zMsg); |
| 819 | |
| 820 | /* |
| 821 | ** Initialize the query parameter database. Information is pulled from |
| 822 | ** the QUERY_STRING environment variable (if it exists), from standard |
| 823 | ** input if there is POST data, and from HTTP_COOKIE. |
| @@ -822,15 +824,31 @@ | |
| 824 | */ |
| 825 | void cgi_init(void){ |
| 826 | char *z; |
| 827 | const char *zType; |
| 828 | int len; |
| 829 | const char *zRequestUri = cgi_parameter("REQUEST_URI",0); |
| 830 | const char *zScriptName = cgi_parameter("SCRIPT_NAME",0); |
| 831 | |
| 832 | #ifdef FOSSIL_ENABLE_JSON |
| 833 | json_main_bootstrap(); |
| 834 | #endif |
| 835 | g.isHTTP = 1; |
| 836 | cgi_destination(CGI_BODY); |
| 837 | if( zRequestUri==0 ) malformed_request("missing REQUEST_URI"); |
| 838 | if( zScriptName==0 ) malformed_request("missing SCRIPT_NAME"); |
| 839 | if( cgi_parameter("PATH_INFO",0)==0 ){ |
| 840 | int i, j; |
| 841 | for(i=0; zRequestUri[i]==zScriptName[i] && zRequestUri[i]; i++){} |
| 842 | if( zRequestUri[i]=='/' ){ |
| 843 | for(j=i; zRequestUri[j] && zRequestUri[j]!='?'; j++){} |
| 844 | cgi_set_parameter("PATH_INFO", mprintf("%.*s", j-i, zRequestUri+i)); |
| 845 | }else{ |
| 846 | malformed_request("cannot compute PATH_INFO from REQUEST_URI" |
| 847 | " and SCRIPT_NAME"); |
| 848 | } |
| 849 | } |
| 850 | |
| 851 | z = (char*)P("HTTP_COOKIE"); |
| 852 | if( z ){ |
| 853 | z = mprintf("%s",z); |
| 854 | add_param_list(z, ';'); |
| @@ -1092,14 +1110,14 @@ | |
| 1110 | |
| 1111 | |
| 1112 | /* |
| 1113 | ** Send a reply indicating that the HTTP request was malformed |
| 1114 | */ |
| 1115 | static NORETURN void malformed_request(const char *zMsg){ |
| 1116 | cgi_set_status(501, "Not Implemented"); |
| 1117 | cgi_printf( |
| 1118 | "<html><body><p>Bad Request: %s</p></body></html>\n", zMsg |
| 1119 | ); |
| 1120 | cgi_reply(); |
| 1121 | fossil_exit(0); |
| 1122 | } |
| 1123 | |
| @@ -1187,29 +1205,30 @@ | |
| 1205 | struct sockaddr_in remoteName; |
| 1206 | socklen_t size = sizeof(struct sockaddr_in); |
| 1207 | char zLine[2000]; /* A single line of input. */ |
| 1208 | g.fullHttpReply = 1; |
| 1209 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1210 | malformed_request("missing HTTP header"); |
| 1211 | } |
| 1212 | blob_append(&g.httpHeader, zLine, -1); |
| 1213 | cgi_trace(zLine); |
| 1214 | zToken = extract_token(zLine, &z); |
| 1215 | if( zToken==0 ){ |
| 1216 | malformed_request("malformed HTTP header"); |
| 1217 | } |
| 1218 | if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 |
| 1219 | && fossil_strcmp(zToken,"HEAD")!=0 ){ |
| 1220 | malformed_request("unsupported HTTP method"); |
| 1221 | } |
| 1222 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1223 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1224 | zToken = extract_token(z, &z); |
| 1225 | if( zToken==0 ){ |
| 1226 | malformed_request("malformed URL in HTTP header"); |
| 1227 | } |
| 1228 | cgi_setenv("REQUEST_URI", zToken); |
| 1229 | cgi_setenv("SCRIPT_NAME", ""); |
| 1230 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1231 | if( zToken[i] ) zToken[i++] = 0; |
| 1232 | cgi_setenv("PATH_INFO", zToken); |
| 1233 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1234 | if( zIpAddr==0 && |
| @@ -1269,16 +1288,57 @@ | |
| 1288 | } |
| 1289 | } |
| 1290 | cgi_init(); |
| 1291 | cgi_trace(0); |
| 1292 | } |
| 1293 | |
| 1294 | /* |
| 1295 | ** This routine handles a single SCGI request which is coming in on |
| 1296 | ** g.httpIn and which replies on g.httpOut |
| 1297 | ** |
| 1298 | ** The SCGI request is read from g.httpIn and is used to initialize |
| 1299 | ** entries in the cgi_parameter() hash, as if those entries were |
| 1300 | ** environment variables. A call to cgi_init() completes |
| 1301 | ** the setup. Once all the setup is finished, this procedure returns |
| 1302 | ** and subsequent code handles the actual generation of the webpage. |
| 1303 | */ |
| 1304 | void cgi_handle_scgi_request(void){ |
| 1305 | char *zHdr; |
| 1306 | char *zToFree; |
| 1307 | int nHdr = 0; |
| 1308 | int nRead; |
| 1309 | int n, m; |
| 1310 | char c; |
| 1311 | |
| 1312 | while( (c = fgetc(g.httpIn))!=EOF && fossil_isdigit(c) ){ |
| 1313 | nHdr = nHdr*10 + c - '0'; |
| 1314 | } |
| 1315 | if( nHdr<16 ) malformed_request("SCGI header too short"); |
| 1316 | zToFree = zHdr = fossil_malloc(nHdr); |
| 1317 | nRead = (int)fread(zHdr, 1, nHdr, g.httpIn); |
| 1318 | if( nRead<nHdr ) malformed_request("cannot read entire SCGI header"); |
| 1319 | nHdr = nRead; |
| 1320 | while( nHdr ){ |
| 1321 | for(n=0; n<nHdr && zHdr[n]; n++){} |
| 1322 | for(m=n+1; m<nHdr && zHdr[m]; m++){} |
| 1323 | if( m>=nHdr ) malformed_request("SCGI header formatting error"); |
| 1324 | cgi_set_parameter(zHdr, zHdr+n+1); |
| 1325 | zHdr += m+1; |
| 1326 | nHdr -= m+1; |
| 1327 | } |
| 1328 | fossil_free(zToFree); |
| 1329 | fgetc(g.httpIn); /* Read past the "," separating header from content */ |
| 1330 | cgi_init(); |
| 1331 | } |
| 1332 | |
| 1333 | |
| 1334 | #if INTERFACE |
| 1335 | /* |
| 1336 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1337 | */ |
| 1338 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1339 | #define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */ |
| 1340 | |
| 1341 | #endif /* INTERFACE */ |
| 1342 | |
| 1343 | /* |
| 1344 | ** Maximum number of child processes that we can have running |
| @@ -1356,11 +1416,12 @@ | |
| 1416 | } |
| 1417 | } |
| 1418 | if( iPort>mxPort ) return 1; |
| 1419 | listen(listener,10); |
| 1420 | if( iPort>mnPort ){ |
| 1421 | fossil_print("Listening for %s requests on TCP port %d\n", |
| 1422 | (flags & HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); |
| 1423 | fflush(stdout); |
| 1424 | } |
| 1425 | if( zBrowser ){ |
| 1426 | zBrowser = mprintf(zBrowser, iPort); |
| 1427 | if( system(zBrowser)<0 ){ |
| 1428 |
+15
-2
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -1636,19 +1636,21 @@ | ||
| 1636 | 1636 | ** --https signal a request coming in via https |
| 1637 | 1637 | ** --nossl signal that no SSL connections are available |
| 1638 | 1638 | ** --notfound URL use URL as "HTTP 404, object not found" page. |
| 1639 | 1639 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 1640 | 1640 | ** --baseurl URL base URL (useful with reverse proxies) |
| 1641 | +** --scgi Interpret input as SCGI rather than HTTP | |
| 1641 | 1642 | ** |
| 1642 | 1643 | ** See also: cgi, server, winsrv |
| 1643 | 1644 | */ |
| 1644 | 1645 | void cmd_http(void){ |
| 1645 | 1646 | const char *zIpAddr; |
| 1646 | 1647 | const char *zNotFound; |
| 1647 | 1648 | const char *zHost; |
| 1648 | 1649 | const char *zAltBase; |
| 1649 | 1650 | const char *zFileGlob; |
| 1651 | + int useSCGI; | |
| 1650 | 1652 | |
| 1651 | 1653 | /* The winhttp module passes the --files option as --files-urlenc with |
| 1652 | 1654 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 1653 | 1655 | ** shell. This option is for internal use and is undocumented. |
| 1654 | 1656 | */ |
| @@ -1661,10 +1663,11 @@ | ||
| 1661 | 1663 | zFileGlob = find_option("files",0,1); |
| 1662 | 1664 | } |
| 1663 | 1665 | zNotFound = find_option("notfound", 0, 1); |
| 1664 | 1666 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1665 | 1667 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1668 | + useSCGI = find_option("scgi", 0, 0)!=0; | |
| 1666 | 1669 | zAltBase = find_option("baseurl", 0, 1); |
| 1667 | 1670 | if( zAltBase ) set_base_url(zAltBase); |
| 1668 | 1671 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1669 | 1672 | zHost = find_option("host", 0, 1); |
| 1670 | 1673 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| @@ -1682,11 +1685,15 @@ | ||
| 1682 | 1685 | g.httpOut = stdout; |
| 1683 | 1686 | zIpAddr = 0; |
| 1684 | 1687 | } |
| 1685 | 1688 | find_server_repository(0); |
| 1686 | 1689 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1687 | - cgi_handle_http_request(zIpAddr); | |
| 1690 | + if( useSCGI ){ | |
| 1691 | + cgi_handle_scgi_request(); | |
| 1692 | + }else{ | |
| 1693 | + cgi_handle_http_request(zIpAddr); | |
| 1694 | + } | |
| 1688 | 1695 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1689 | 1696 | } |
| 1690 | 1697 | |
| 1691 | 1698 | /* |
| 1692 | 1699 | ** Note that the following command is used by ssh:// processing. |
| @@ -1778,10 +1785,11 @@ | ||
| 1778 | 1785 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1779 | 1786 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1780 | 1787 | ** --baseurl URL Use URL as the base (useful for reverse proxies) |
| 1781 | 1788 | ** --notfound URL Redirect |
| 1782 | 1789 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 1790 | +** --scgi Accept SCGI rather than HTTP | |
| 1783 | 1791 | ** |
| 1784 | 1792 | ** See also: cgi, http, winsrv |
| 1785 | 1793 | */ |
| 1786 | 1794 | void cmd_webserver(void){ |
| 1787 | 1795 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1804,10 +1812,11 @@ | ||
| 1804 | 1812 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1805 | 1813 | Th_InitTraceLog(); |
| 1806 | 1814 | zPort = find_option("port", "P", 1); |
| 1807 | 1815 | zNotFound = find_option("notfound", 0, 1); |
| 1808 | 1816 | zAltBase = find_option("baseurl", 0, 1); |
| 1817 | + if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI; | |
| 1809 | 1818 | if( zAltBase ){ |
| 1810 | 1819 | set_base_url(zAltBase); |
| 1811 | 1820 | } |
| 1812 | 1821 | if ( find_option("localhost", 0, 0)!=0 ){ |
| 1813 | 1822 | flags |= HTTP_SERVER_LOCALHOST; |
| @@ -1868,11 +1877,15 @@ | ||
| 1868 | 1877 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 1869 | 1878 | } |
| 1870 | 1879 | g.cgiOutput = 1; |
| 1871 | 1880 | find_server_repository(isUiCmd && zNotFound==0); |
| 1872 | 1881 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1873 | - cgi_handle_http_request(0); | |
| 1882 | + if( flags & HTTP_SERVER_SCGI ){ | |
| 1883 | + cgi_handle_scgi_request(); | |
| 1884 | + }else{ | |
| 1885 | + cgi_handle_http_request(0); | |
| 1886 | + } | |
| 1874 | 1887 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1875 | 1888 | #else |
| 1876 | 1889 | /* Win32 implementation */ |
| 1877 | 1890 | if( isUiCmd ){ |
| 1878 | 1891 | zBrowser = db_get("web-browser", "start"); |
| 1879 | 1892 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1636,19 +1636,21 @@ | |
| 1636 | ** --https signal a request coming in via https |
| 1637 | ** --nossl signal that no SSL connections are available |
| 1638 | ** --notfound URL use URL as "HTTP 404, object not found" page. |
| 1639 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 1640 | ** --baseurl URL base URL (useful with reverse proxies) |
| 1641 | ** |
| 1642 | ** See also: cgi, server, winsrv |
| 1643 | */ |
| 1644 | void cmd_http(void){ |
| 1645 | const char *zIpAddr; |
| 1646 | const char *zNotFound; |
| 1647 | const char *zHost; |
| 1648 | const char *zAltBase; |
| 1649 | const char *zFileGlob; |
| 1650 | |
| 1651 | /* The winhttp module passes the --files option as --files-urlenc with |
| 1652 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 1653 | ** shell. This option is for internal use and is undocumented. |
| 1654 | */ |
| @@ -1661,10 +1663,11 @@ | |
| 1661 | zFileGlob = find_option("files",0,1); |
| 1662 | } |
| 1663 | zNotFound = find_option("notfound", 0, 1); |
| 1664 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1665 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1666 | zAltBase = find_option("baseurl", 0, 1); |
| 1667 | if( zAltBase ) set_base_url(zAltBase); |
| 1668 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1669 | zHost = find_option("host", 0, 1); |
| 1670 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| @@ -1682,11 +1685,15 @@ | |
| 1682 | g.httpOut = stdout; |
| 1683 | zIpAddr = 0; |
| 1684 | } |
| 1685 | find_server_repository(0); |
| 1686 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1687 | cgi_handle_http_request(zIpAddr); |
| 1688 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1689 | } |
| 1690 | |
| 1691 | /* |
| 1692 | ** Note that the following command is used by ssh:// processing. |
| @@ -1778,10 +1785,11 @@ | |
| 1778 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1779 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1780 | ** --baseurl URL Use URL as the base (useful for reverse proxies) |
| 1781 | ** --notfound URL Redirect |
| 1782 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 1783 | ** |
| 1784 | ** See also: cgi, http, winsrv |
| 1785 | */ |
| 1786 | void cmd_webserver(void){ |
| 1787 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1804,10 +1812,11 @@ | |
| 1804 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1805 | Th_InitTraceLog(); |
| 1806 | zPort = find_option("port", "P", 1); |
| 1807 | zNotFound = find_option("notfound", 0, 1); |
| 1808 | zAltBase = find_option("baseurl", 0, 1); |
| 1809 | if( zAltBase ){ |
| 1810 | set_base_url(zAltBase); |
| 1811 | } |
| 1812 | if ( find_option("localhost", 0, 0)!=0 ){ |
| 1813 | flags |= HTTP_SERVER_LOCALHOST; |
| @@ -1868,11 +1877,15 @@ | |
| 1868 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 1869 | } |
| 1870 | g.cgiOutput = 1; |
| 1871 | find_server_repository(isUiCmd && zNotFound==0); |
| 1872 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1873 | cgi_handle_http_request(0); |
| 1874 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1875 | #else |
| 1876 | /* Win32 implementation */ |
| 1877 | if( isUiCmd ){ |
| 1878 | zBrowser = db_get("web-browser", "start"); |
| 1879 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1636,19 +1636,21 @@ | |
| 1636 | ** --https signal a request coming in via https |
| 1637 | ** --nossl signal that no SSL connections are available |
| 1638 | ** --notfound URL use URL as "HTTP 404, object not found" page. |
| 1639 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 1640 | ** --baseurl URL base URL (useful with reverse proxies) |
| 1641 | ** --scgi Interpret input as SCGI rather than HTTP |
| 1642 | ** |
| 1643 | ** See also: cgi, server, winsrv |
| 1644 | */ |
| 1645 | void cmd_http(void){ |
| 1646 | const char *zIpAddr; |
| 1647 | const char *zNotFound; |
| 1648 | const char *zHost; |
| 1649 | const char *zAltBase; |
| 1650 | const char *zFileGlob; |
| 1651 | int useSCGI; |
| 1652 | |
| 1653 | /* The winhttp module passes the --files option as --files-urlenc with |
| 1654 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 1655 | ** shell. This option is for internal use and is undocumented. |
| 1656 | */ |
| @@ -1661,10 +1663,11 @@ | |
| 1663 | zFileGlob = find_option("files",0,1); |
| 1664 | } |
| 1665 | zNotFound = find_option("notfound", 0, 1); |
| 1666 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1667 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1668 | useSCGI = find_option("scgi", 0, 0)!=0; |
| 1669 | zAltBase = find_option("baseurl", 0, 1); |
| 1670 | if( zAltBase ) set_base_url(zAltBase); |
| 1671 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1672 | zHost = find_option("host", 0, 1); |
| 1673 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| @@ -1682,11 +1685,15 @@ | |
| 1685 | g.httpOut = stdout; |
| 1686 | zIpAddr = 0; |
| 1687 | } |
| 1688 | find_server_repository(0); |
| 1689 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1690 | if( useSCGI ){ |
| 1691 | cgi_handle_scgi_request(); |
| 1692 | }else{ |
| 1693 | cgi_handle_http_request(zIpAddr); |
| 1694 | } |
| 1695 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1696 | } |
| 1697 | |
| 1698 | /* |
| 1699 | ** Note that the following command is used by ssh:// processing. |
| @@ -1778,10 +1785,11 @@ | |
| 1785 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1786 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1787 | ** --baseurl URL Use URL as the base (useful for reverse proxies) |
| 1788 | ** --notfound URL Redirect |
| 1789 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 1790 | ** --scgi Accept SCGI rather than HTTP |
| 1791 | ** |
| 1792 | ** See also: cgi, http, winsrv |
| 1793 | */ |
| 1794 | void cmd_webserver(void){ |
| 1795 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1804,10 +1812,11 @@ | |
| 1812 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1813 | Th_InitTraceLog(); |
| 1814 | zPort = find_option("port", "P", 1); |
| 1815 | zNotFound = find_option("notfound", 0, 1); |
| 1816 | zAltBase = find_option("baseurl", 0, 1); |
| 1817 | if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI; |
| 1818 | if( zAltBase ){ |
| 1819 | set_base_url(zAltBase); |
| 1820 | } |
| 1821 | if ( find_option("localhost", 0, 0)!=0 ){ |
| 1822 | flags |= HTTP_SERVER_LOCALHOST; |
| @@ -1868,11 +1877,15 @@ | |
| 1877 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 1878 | } |
| 1879 | g.cgiOutput = 1; |
| 1880 | find_server_repository(isUiCmd && zNotFound==0); |
| 1881 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1882 | if( flags & HTTP_SERVER_SCGI ){ |
| 1883 | cgi_handle_scgi_request(); |
| 1884 | }else{ |
| 1885 | cgi_handle_http_request(0); |
| 1886 | } |
| 1887 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1888 | #else |
| 1889 | /* Win32 implementation */ |
| 1890 | if( isUiCmd ){ |
| 1891 | zBrowser = db_get("web-browser", "start"); |
| 1892 |
+77
-3
| --- src/winhttp.c | ||
| +++ src/winhttp.c | ||
| @@ -61,11 +61,11 @@ | ||
| 61 | 61 | } |
| 62 | 62 | |
| 63 | 63 | /* |
| 64 | 64 | ** Process a single incoming HTTP request. |
| 65 | 65 | */ |
| 66 | -static void win32_process_one_http_request(void *pAppData){ | |
| 66 | +static void win32_http_request(void *pAppData){ | |
| 67 | 67 | HttpRequest *p = (HttpRequest*)pAppData; |
| 68 | 68 | FILE *in = 0, *out = 0; |
| 69 | 69 | int amt, got; |
| 70 | 70 | int wanted = 0; |
| 71 | 71 | char *z; |
| @@ -128,10 +128,73 @@ | ||
| 128 | 128 | closesocket(p->s); |
| 129 | 129 | file_delete(zRequestFName); |
| 130 | 130 | file_delete(zReplyFName); |
| 131 | 131 | free(p); |
| 132 | 132 | } |
| 133 | + | |
| 134 | +/* | |
| 135 | +** Process a single incoming SCGI request. | |
| 136 | +*/ | |
| 137 | +static void win32_scgi_request(void *pAppData){ | |
| 138 | + HttpRequest *p = (HttpRequest*)pAppData; | |
| 139 | + FILE *in = 0, *out = 0; | |
| 140 | + int amt, got, nHdr, i; | |
| 141 | + int wanted = 0; | |
| 142 | + char *z; | |
| 143 | + char zRequestFName[MAX_PATH]; | |
| 144 | + char zReplyFName[MAX_PATH]; | |
| 145 | + char zCmd[2000]; /* Command-line to process the request */ | |
| 146 | + char zHdr[2000]; /* The SCGI request header */ | |
| 147 | + | |
| 148 | + sqlite3_snprintf(MAX_PATH, zRequestFName, | |
| 149 | + "%s_in%d.txt", zTempPrefix, p->id); | |
| 150 | + sqlite3_snprintf(MAX_PATH, zReplyFName, | |
| 151 | + "%s_out%d.txt", zTempPrefix, p->id); | |
| 152 | + out = fossil_fopen(zRequestFName, "wb"); | |
| 153 | + if( out==0 ) goto end_request; | |
| 154 | + amt = 0; | |
| 155 | + got = recv(p->s, zHdr, sizeof(zHdr), 0); | |
| 156 | + if( got==SOCKET_ERROR ) goto end_request; | |
| 157 | + amt = fwrite(zHdr, 1, got, out); | |
| 158 | + nHdr = 0; | |
| 159 | + for(i=0; zHdr[i]>='0' && zHdr[i]<='9'; i++){ | |
| 160 | + nHdr = 10*nHdr + zHdr[i] - '0'; | |
| 161 | + } | |
| 162 | + wanted = nHdr + i + 1; | |
| 163 | + if( strcmp(zHdr+i+1, "CONTENT_LENGTH")==0 ){ | |
| 164 | + wanted += atoi(zHdr+i+15); | |
| 165 | + } | |
| 166 | + while( wanted>amt ){ | |
| 167 | + got = recv(p->s, zHdr, wanted<sizeof(zHdr) ? wanted : sizeof(zHdr), 0); | |
| 168 | + if( got<=0 ) break; | |
| 169 | + fwrite(zHdr, 1, got, out); | |
| 170 | + wanted += got; | |
| 171 | + } | |
| 172 | + fclose(out); | |
| 173 | + out = 0; | |
| 174 | + sqlite3_snprintf(sizeof(zCmd), zCmd, | |
| 175 | + "\"%s\" http \"%s\" %s %s %s --scgi --nossl%s", | |
| 176 | + g.nameOfExe, g.zRepositoryName, zRequestFName, zReplyFName, | |
| 177 | + inet_ntoa(p->addr.sin_addr), p->zOptions | |
| 178 | + ); | |
| 179 | + fossil_system(zCmd); | |
| 180 | + in = fossil_fopen(zReplyFName, "rb"); | |
| 181 | + if( in ){ | |
| 182 | + while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ | |
| 183 | + send(p->s, zHdr, got, 0); | |
| 184 | + } | |
| 185 | + } | |
| 186 | + | |
| 187 | +end_request: | |
| 188 | + if( out ) fclose(out); | |
| 189 | + if( in ) fclose(in); | |
| 190 | + closesocket(p->s); | |
| 191 | + file_delete(zRequestFName); | |
| 192 | + file_delete(zReplyFName); | |
| 193 | + free(p); | |
| 194 | +} | |
| 195 | + | |
| 133 | 196 | |
| 134 | 197 | /* |
| 135 | 198 | ** Start a listening socket and process incoming HTTP requests on |
| 136 | 199 | ** that socket. |
| 137 | 200 | */ |
| @@ -206,11 +269,12 @@ | ||
| 206 | 269 | if( !GetTempPathW(MAX_PATH, zTmpPath) ){ |
| 207 | 270 | fossil_fatal("unable to get path to the temporary directory."); |
| 208 | 271 | } |
| 209 | 272 | zTempPrefix = mprintf("%sfossil_server_P%d_", |
| 210 | 273 | fossil_unicode_to_utf8(zTmpPath), iPort); |
| 211 | - fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); | |
| 274 | + fossil_print("Listening for %s requests on TCP port %d\n", | |
| 275 | + (flags&HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); | |
| 212 | 276 | if( zBrowser ){ |
| 213 | 277 | zBrowser = mprintf(zBrowser, iPort); |
| 214 | 278 | fossil_print("Launch webbrowser: %s\n", zBrowser); |
| 215 | 279 | fossil_system(zBrowser); |
| 216 | 280 | } |
| @@ -244,11 +308,15 @@ | ||
| 244 | 308 | p = fossil_malloc( sizeof(*p) ); |
| 245 | 309 | p->id = ++idCnt; |
| 246 | 310 | p->s = client; |
| 247 | 311 | p->addr = client_addr; |
| 248 | 312 | p->zOptions = blob_str(&options); |
| 249 | - _beginthread(win32_process_one_http_request, 0, (void*)p); | |
| 313 | + if( flags & HTTP_SERVER_SCGI ){ | |
| 314 | + _beginthread(win32_scgi_request, 0, (void*)p); | |
| 315 | + }else{ | |
| 316 | + _beginthread(win32_http_request, 0, (void*)p); | |
| 317 | + } | |
| 250 | 318 | } |
| 251 | 319 | closesocket(s); |
| 252 | 320 | WSACleanup(); |
| 253 | 321 | } |
| 254 | 322 | |
| @@ -539,10 +607,14 @@ | ||
| 539 | 607 | ** |
| 540 | 608 | ** Enables automatic login if the --localauth option is present |
| 541 | 609 | ** and the "localauth" setting is off and the connection is from |
| 542 | 610 | ** localhost. |
| 543 | 611 | ** |
| 612 | +** --scgi | |
| 613 | +** | |
| 614 | +** Create an SCGI server instead of an HTTP server | |
| 615 | +** | |
| 544 | 616 | ** |
| 545 | 617 | ** fossil winsrv delete ?SERVICE-NAME? |
| 546 | 618 | ** |
| 547 | 619 | ** Deletes a service. If the service is currently running, it will be |
| 548 | 620 | ** stopped first and then deleted. |
| @@ -592,10 +664,11 @@ | ||
| 592 | 664 | const char *zPort = find_option("port", "P", 1); |
| 593 | 665 | const char *zNotFound = find_option("notfound", 0, 1); |
| 594 | 666 | const char *zFileGlob = find_option("files", 0, 1); |
| 595 | 667 | const char *zLocalAuth = find_option("localauth", 0, 0); |
| 596 | 668 | const char *zRepository = find_option("repository", "R", 1); |
| 669 | + int useSCGI = find_option("scgi", 0, 0)!=0; | |
| 597 | 670 | Blob binPath; |
| 598 | 671 | |
| 599 | 672 | verify_all_options(); |
| 600 | 673 | if( g.argc==4 ){ |
| 601 | 674 | zSvcName = g.argv[3]; |
| @@ -632,10 +705,11 @@ | ||
| 632 | 705 | db_close(0); |
| 633 | 706 | /* Build the fully-qualified path to the service binary file. */ |
| 634 | 707 | blob_zero(&binPath); |
| 635 | 708 | blob_appendf(&binPath, "\"%s\" server", g.nameOfExe); |
| 636 | 709 | if( zPort ) blob_appendf(&binPath, " --port %s", zPort); |
| 710 | + if( useSCGI ) blob_appendf(&binPath, " --scgi"); | |
| 637 | 711 | if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound); |
| 638 | 712 | if( zFileGlob ) blob_appendf(&binPath, " --files-urlenc %T", zFileGlob); |
| 639 | 713 | if( zLocalAuth ) blob_append(&binPath, " --localauth", -1); |
| 640 | 714 | blob_appendf(&binPath, " \"%s\"", g.zRepositoryName); |
| 641 | 715 | /* Create the service. */ |
| 642 | 716 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -61,11 +61,11 @@ | |
| 61 | } |
| 62 | |
| 63 | /* |
| 64 | ** Process a single incoming HTTP request. |
| 65 | */ |
| 66 | static void win32_process_one_http_request(void *pAppData){ |
| 67 | HttpRequest *p = (HttpRequest*)pAppData; |
| 68 | FILE *in = 0, *out = 0; |
| 69 | int amt, got; |
| 70 | int wanted = 0; |
| 71 | char *z; |
| @@ -128,10 +128,73 @@ | |
| 128 | closesocket(p->s); |
| 129 | file_delete(zRequestFName); |
| 130 | file_delete(zReplyFName); |
| 131 | free(p); |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | ** Start a listening socket and process incoming HTTP requests on |
| 136 | ** that socket. |
| 137 | */ |
| @@ -206,11 +269,12 @@ | |
| 206 | if( !GetTempPathW(MAX_PATH, zTmpPath) ){ |
| 207 | fossil_fatal("unable to get path to the temporary directory."); |
| 208 | } |
| 209 | zTempPrefix = mprintf("%sfossil_server_P%d_", |
| 210 | fossil_unicode_to_utf8(zTmpPath), iPort); |
| 211 | fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); |
| 212 | if( zBrowser ){ |
| 213 | zBrowser = mprintf(zBrowser, iPort); |
| 214 | fossil_print("Launch webbrowser: %s\n", zBrowser); |
| 215 | fossil_system(zBrowser); |
| 216 | } |
| @@ -244,11 +308,15 @@ | |
| 244 | p = fossil_malloc( sizeof(*p) ); |
| 245 | p->id = ++idCnt; |
| 246 | p->s = client; |
| 247 | p->addr = client_addr; |
| 248 | p->zOptions = blob_str(&options); |
| 249 | _beginthread(win32_process_one_http_request, 0, (void*)p); |
| 250 | } |
| 251 | closesocket(s); |
| 252 | WSACleanup(); |
| 253 | } |
| 254 | |
| @@ -539,10 +607,14 @@ | |
| 539 | ** |
| 540 | ** Enables automatic login if the --localauth option is present |
| 541 | ** and the "localauth" setting is off and the connection is from |
| 542 | ** localhost. |
| 543 | ** |
| 544 | ** |
| 545 | ** fossil winsrv delete ?SERVICE-NAME? |
| 546 | ** |
| 547 | ** Deletes a service. If the service is currently running, it will be |
| 548 | ** stopped first and then deleted. |
| @@ -592,10 +664,11 @@ | |
| 592 | const char *zPort = find_option("port", "P", 1); |
| 593 | const char *zNotFound = find_option("notfound", 0, 1); |
| 594 | const char *zFileGlob = find_option("files", 0, 1); |
| 595 | const char *zLocalAuth = find_option("localauth", 0, 0); |
| 596 | const char *zRepository = find_option("repository", "R", 1); |
| 597 | Blob binPath; |
| 598 | |
| 599 | verify_all_options(); |
| 600 | if( g.argc==4 ){ |
| 601 | zSvcName = g.argv[3]; |
| @@ -632,10 +705,11 @@ | |
| 632 | db_close(0); |
| 633 | /* Build the fully-qualified path to the service binary file. */ |
| 634 | blob_zero(&binPath); |
| 635 | blob_appendf(&binPath, "\"%s\" server", g.nameOfExe); |
| 636 | if( zPort ) blob_appendf(&binPath, " --port %s", zPort); |
| 637 | if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound); |
| 638 | if( zFileGlob ) blob_appendf(&binPath, " --files-urlenc %T", zFileGlob); |
| 639 | if( zLocalAuth ) blob_append(&binPath, " --localauth", -1); |
| 640 | blob_appendf(&binPath, " \"%s\"", g.zRepositoryName); |
| 641 | /* Create the service. */ |
| 642 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -61,11 +61,11 @@ | |
| 61 | } |
| 62 | |
| 63 | /* |
| 64 | ** Process a single incoming HTTP request. |
| 65 | */ |
| 66 | static void win32_http_request(void *pAppData){ |
| 67 | HttpRequest *p = (HttpRequest*)pAppData; |
| 68 | FILE *in = 0, *out = 0; |
| 69 | int amt, got; |
| 70 | int wanted = 0; |
| 71 | char *z; |
| @@ -128,10 +128,73 @@ | |
| 128 | closesocket(p->s); |
| 129 | file_delete(zRequestFName); |
| 130 | file_delete(zReplyFName); |
| 131 | free(p); |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | ** Process a single incoming SCGI request. |
| 136 | */ |
| 137 | static void win32_scgi_request(void *pAppData){ |
| 138 | HttpRequest *p = (HttpRequest*)pAppData; |
| 139 | FILE *in = 0, *out = 0; |
| 140 | int amt, got, nHdr, i; |
| 141 | int wanted = 0; |
| 142 | char *z; |
| 143 | char zRequestFName[MAX_PATH]; |
| 144 | char zReplyFName[MAX_PATH]; |
| 145 | char zCmd[2000]; /* Command-line to process the request */ |
| 146 | char zHdr[2000]; /* The SCGI request header */ |
| 147 | |
| 148 | sqlite3_snprintf(MAX_PATH, zRequestFName, |
| 149 | "%s_in%d.txt", zTempPrefix, p->id); |
| 150 | sqlite3_snprintf(MAX_PATH, zReplyFName, |
| 151 | "%s_out%d.txt", zTempPrefix, p->id); |
| 152 | out = fossil_fopen(zRequestFName, "wb"); |
| 153 | if( out==0 ) goto end_request; |
| 154 | amt = 0; |
| 155 | got = recv(p->s, zHdr, sizeof(zHdr), 0); |
| 156 | if( got==SOCKET_ERROR ) goto end_request; |
| 157 | amt = fwrite(zHdr, 1, got, out); |
| 158 | nHdr = 0; |
| 159 | for(i=0; zHdr[i]>='0' && zHdr[i]<='9'; i++){ |
| 160 | nHdr = 10*nHdr + zHdr[i] - '0'; |
| 161 | } |
| 162 | wanted = nHdr + i + 1; |
| 163 | if( strcmp(zHdr+i+1, "CONTENT_LENGTH")==0 ){ |
| 164 | wanted += atoi(zHdr+i+15); |
| 165 | } |
| 166 | while( wanted>amt ){ |
| 167 | got = recv(p->s, zHdr, wanted<sizeof(zHdr) ? wanted : sizeof(zHdr), 0); |
| 168 | if( got<=0 ) break; |
| 169 | fwrite(zHdr, 1, got, out); |
| 170 | wanted += got; |
| 171 | } |
| 172 | fclose(out); |
| 173 | out = 0; |
| 174 | sqlite3_snprintf(sizeof(zCmd), zCmd, |
| 175 | "\"%s\" http \"%s\" %s %s %s --scgi --nossl%s", |
| 176 | g.nameOfExe, g.zRepositoryName, zRequestFName, zReplyFName, |
| 177 | inet_ntoa(p->addr.sin_addr), p->zOptions |
| 178 | ); |
| 179 | fossil_system(zCmd); |
| 180 | in = fossil_fopen(zReplyFName, "rb"); |
| 181 | if( in ){ |
| 182 | while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ |
| 183 | send(p->s, zHdr, got, 0); |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | end_request: |
| 188 | if( out ) fclose(out); |
| 189 | if( in ) fclose(in); |
| 190 | closesocket(p->s); |
| 191 | file_delete(zRequestFName); |
| 192 | file_delete(zReplyFName); |
| 193 | free(p); |
| 194 | } |
| 195 | |
| 196 | |
| 197 | /* |
| 198 | ** Start a listening socket and process incoming HTTP requests on |
| 199 | ** that socket. |
| 200 | */ |
| @@ -206,11 +269,12 @@ | |
| 269 | if( !GetTempPathW(MAX_PATH, zTmpPath) ){ |
| 270 | fossil_fatal("unable to get path to the temporary directory."); |
| 271 | } |
| 272 | zTempPrefix = mprintf("%sfossil_server_P%d_", |
| 273 | fossil_unicode_to_utf8(zTmpPath), iPort); |
| 274 | fossil_print("Listening for %s requests on TCP port %d\n", |
| 275 | (flags&HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); |
| 276 | if( zBrowser ){ |
| 277 | zBrowser = mprintf(zBrowser, iPort); |
| 278 | fossil_print("Launch webbrowser: %s\n", zBrowser); |
| 279 | fossil_system(zBrowser); |
| 280 | } |
| @@ -244,11 +308,15 @@ | |
| 308 | p = fossil_malloc( sizeof(*p) ); |
| 309 | p->id = ++idCnt; |
| 310 | p->s = client; |
| 311 | p->addr = client_addr; |
| 312 | p->zOptions = blob_str(&options); |
| 313 | if( flags & HTTP_SERVER_SCGI ){ |
| 314 | _beginthread(win32_scgi_request, 0, (void*)p); |
| 315 | }else{ |
| 316 | _beginthread(win32_http_request, 0, (void*)p); |
| 317 | } |
| 318 | } |
| 319 | closesocket(s); |
| 320 | WSACleanup(); |
| 321 | } |
| 322 | |
| @@ -539,10 +607,14 @@ | |
| 607 | ** |
| 608 | ** Enables automatic login if the --localauth option is present |
| 609 | ** and the "localauth" setting is off and the connection is from |
| 610 | ** localhost. |
| 611 | ** |
| 612 | ** --scgi |
| 613 | ** |
| 614 | ** Create an SCGI server instead of an HTTP server |
| 615 | ** |
| 616 | ** |
| 617 | ** fossil winsrv delete ?SERVICE-NAME? |
| 618 | ** |
| 619 | ** Deletes a service. If the service is currently running, it will be |
| 620 | ** stopped first and then deleted. |
| @@ -592,10 +664,11 @@ | |
| 664 | const char *zPort = find_option("port", "P", 1); |
| 665 | const char *zNotFound = find_option("notfound", 0, 1); |
| 666 | const char *zFileGlob = find_option("files", 0, 1); |
| 667 | const char *zLocalAuth = find_option("localauth", 0, 0); |
| 668 | const char *zRepository = find_option("repository", "R", 1); |
| 669 | int useSCGI = find_option("scgi", 0, 0)!=0; |
| 670 | Blob binPath; |
| 671 | |
| 672 | verify_all_options(); |
| 673 | if( g.argc==4 ){ |
| 674 | zSvcName = g.argv[3]; |
| @@ -632,10 +705,11 @@ | |
| 705 | db_close(0); |
| 706 | /* Build the fully-qualified path to the service binary file. */ |
| 707 | blob_zero(&binPath); |
| 708 | blob_appendf(&binPath, "\"%s\" server", g.nameOfExe); |
| 709 | if( zPort ) blob_appendf(&binPath, " --port %s", zPort); |
| 710 | if( useSCGI ) blob_appendf(&binPath, " --scgi"); |
| 711 | if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound); |
| 712 | if( zFileGlob ) blob_appendf(&binPath, " --files-urlenc %T", zFileGlob); |
| 713 | if( zLocalAuth ) blob_append(&binPath, " --localauth", -1); |
| 714 | blob_appendf(&binPath, " \"%s\"", g.zRepositoryName); |
| 715 | /* Create the service. */ |
| 716 |
+2
-2
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -91,13 +91,13 @@ | ||
| 91 | 91 | for all network communications, meaning that it works fine from behind |
| 92 | 92 | restrictive firewalls. The protocol is |
| 93 | 93 | [./stats.wiki | bandwidth efficient] to the point that Fossil can be |
| 94 | 94 | used comfortably over a dial-up internet connection. |
| 95 | 95 | |
| 96 | - 6. <b>CGI Enabled</b> - | |
| 96 | + 6. <b>CGI and SCGI Enabled</b> - | |
| 97 | 97 | No server is required to use fossil. But a |
| 98 | - server does make collaboration easier. Fossil supports three different | |
| 98 | + server does make collaboration easier. Fossil supports four different | |
| 99 | 99 | yet simple [./quickstart.wiki#serversetup | server configurations]. |
| 100 | 100 | The most popular is a 2-line CGI script. This is the approach |
| 101 | 101 | used by the [./selfhost.wiki | self-hosting fossil repositories]. |
| 102 | 102 | |
| 103 | 103 | 7. <b>Robust & Reliable</b> - |
| 104 | 104 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -91,13 +91,13 @@ | |
| 91 | for all network communications, meaning that it works fine from behind |
| 92 | restrictive firewalls. The protocol is |
| 93 | [./stats.wiki | bandwidth efficient] to the point that Fossil can be |
| 94 | used comfortably over a dial-up internet connection. |
| 95 | |
| 96 | 6. <b>CGI Enabled</b> - |
| 97 | No server is required to use fossil. But a |
| 98 | server does make collaboration easier. Fossil supports three different |
| 99 | yet simple [./quickstart.wiki#serversetup | server configurations]. |
| 100 | The most popular is a 2-line CGI script. This is the approach |
| 101 | used by the [./selfhost.wiki | self-hosting fossil repositories]. |
| 102 | |
| 103 | 7. <b>Robust & Reliable</b> - |
| 104 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -91,13 +91,13 @@ | |
| 91 | for all network communications, meaning that it works fine from behind |
| 92 | restrictive firewalls. The protocol is |
| 93 | [./stats.wiki | bandwidth efficient] to the point that Fossil can be |
| 94 | used comfortably over a dial-up internet connection. |
| 95 | |
| 96 | 6. <b>CGI and SCGI Enabled</b> - |
| 97 | No server is required to use fossil. But a |
| 98 | server does make collaboration easier. Fossil supports four different |
| 99 | yet simple [./quickstart.wiki#serversetup | server configurations]. |
| 100 | The most popular is a 2-line CGI script. This is the approach |
| 101 | used by the [./selfhost.wiki | self-hosting fossil repositories]. |
| 102 | |
| 103 | 7. <b>Robust & Reliable</b> - |
| 104 |
+3
| --- www/quickstart.wiki | ||
| +++ www/quickstart.wiki | ||
| @@ -309,10 +309,13 @@ | ||
| 309 | 309 | server. For cross-machine collaboration, use the <b>server</b> command, |
| 310 | 310 | which binds on all IP addresses and does not try to start a web browser. |
| 311 | 311 | You can omit the <i>repository-filename</i> if you are within |
| 312 | 312 | a checked-out local tree. The <b>server</b> uses port 8080 by default |
| 313 | 313 | but you can specify a different port using the <b>-port</b> option.</p> |
| 314 | + | |
| 315 | + <p>The same commands can be used with the --scgi option to run | |
| 316 | + [./scgi.wiki | SCGI] from Nginx, if desired.</p> | |
| 314 | 317 | |
| 315 | 318 | <p>Command-line servers like this are useful when two people want |
| 316 | 319 | to share a repository on temporary or ad-hoc basis. For a more |
| 317 | 320 | permanent installation, you should use either the CGI server or the |
| 318 | 321 | inetd server. |
| 319 | 322 | |
| 320 | 323 | ADDED www/scgi.wiki |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -309,10 +309,13 @@ | |
| 309 | server. For cross-machine collaboration, use the <b>server</b> command, |
| 310 | which binds on all IP addresses and does not try to start a web browser. |
| 311 | You can omit the <i>repository-filename</i> if you are within |
| 312 | a checked-out local tree. The <b>server</b> uses port 8080 by default |
| 313 | but you can specify a different port using the <b>-port</b> option.</p> |
| 314 | |
| 315 | <p>Command-line servers like this are useful when two people want |
| 316 | to share a repository on temporary or ad-hoc basis. For a more |
| 317 | permanent installation, you should use either the CGI server or the |
| 318 | inetd server. |
| 319 | |
| 320 | DDED www/scgi.wiki |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -309,10 +309,13 @@ | |
| 309 | server. For cross-machine collaboration, use the <b>server</b> command, |
| 310 | which binds on all IP addresses and does not try to start a web browser. |
| 311 | You can omit the <i>repository-filename</i> if you are within |
| 312 | a checked-out local tree. The <b>server</b> uses port 8080 by default |
| 313 | but you can specify a different port using the <b>-port</b> option.</p> |
| 314 | |
| 315 | <p>The same commands can be used with the --scgi option to run |
| 316 | [./scgi.wiki | SCGI] from Nginx, if desired.</p> |
| 317 | |
| 318 | <p>Command-line servers like this are useful when two people want |
| 319 | to share a repository on temporary or ad-hoc basis. For a more |
| 320 | permanent installation, you should use either the CGI server or the |
| 321 | inetd server. |
| 322 | |
| 323 | DDED www/scgi.wiki |
+22
| --- a/www/scgi.wiki | ||
| +++ b/www/scgi.wiki | ||
| @@ -0,0 +1,22 @@ | ||
| 1 | +<title>Fossil SCGI</title> | |
| 2 | + | |
| 3 | +To run Fossil using SCGI, start the [/help/server|fossil server] command | |
| 4 | +with the --scgi command-line option. You will probably also want to | |
| 5 | +specific an alternative TCP/IP port usiblockquote>ing --port. For example: | |
| 6 | + | |
| 7 | +<pr</blockquote> | |
| 8 | + | |
| 9 | +Then configure your SCGI-aware web-server to send SCGI requests to port | |
| 10 | +9000 on the machine where Fossil is running. A typical configuratiblockquote><pre> | |
| 11 | +location ~ ^/demo_project/ { | |
| 12 | + include scgi_params; | |
| 13 | + scgi_pass localhost:9000; | |
| 14 | + scgi_param SCRIPT_NAME "/demo_project"; | |
| 15 | + scgi</blockquotgi_param HTTPS "on"; | |
| 16 | +} | |
| 17 | +</pre> | |
| 18 | + | |
| 19 | +NoteO or SCRIPT_NAME | |
| 20 | +variables via SCGI, but Fossil needs one or the other. So the configuration | |
| 21 | +above needs to add SCRIPT_N an | |
| 22 | +error. |
| --- a/www/scgi.wiki | |
| +++ b/www/scgi.wiki | |
| @@ -0,0 +1,22 @@ | |
| --- a/www/scgi.wiki | |
| +++ b/www/scgi.wiki | |
| @@ -0,0 +1,22 @@ | |
| 1 | <title>Fossil SCGI</title> |
| 2 | |
| 3 | To run Fossil using SCGI, start the [/help/server|fossil server] command |
| 4 | with the --scgi command-line option. You will probably also want to |
| 5 | specific an alternative TCP/IP port usiblockquote>ing --port. For example: |
| 6 | |
| 7 | <pr</blockquote> |
| 8 | |
| 9 | Then configure your SCGI-aware web-server to send SCGI requests to port |
| 10 | 9000 on the machine where Fossil is running. A typical configuratiblockquote><pre> |
| 11 | location ~ ^/demo_project/ { |
| 12 | include scgi_params; |
| 13 | scgi_pass localhost:9000; |
| 14 | scgi_param SCRIPT_NAME "/demo_project"; |
| 15 | scgi</blockquotgi_param HTTPS "on"; |
| 16 | } |
| 17 | </pre> |
| 18 | |
| 19 | NoteO or SCRIPT_NAME |
| 20 | variables via SCGI, but Fossil needs one or the other. So the configuration |
| 21 | above needs to add SCRIPT_N an |
| 22 | error. |