Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Attempt to provide more useful error messages when an intermediate server (such as Apache) does something wrong and messes up an HTTP request. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | content-length-errors |
Files: | files | file ages | folders |
SHA3-256: |
131ba0fa1bf1e200b3b211c5761041b7 |
User & Date: | drh 2024-04-16 22:55:16 |
Context
2024-04-17
| ||
12:18 | Arrange for the HTTP reply parser to be able to deal with a missing Content-Length header. Add the --xverbose option to the "fossil clone" command. ... (check-in: dfefd069 user: drh tags: content-length-errors) | |
2024-04-16
| ||
22:55 | Attempt to provide more useful error messages when an intermediate server (such as Apache) does something wrong and messes up an HTTP request. ... (check-in: 131ba0fa user: drh tags: content-length-errors) | |
13:50 | Improvements to the /test_env page that can be used to help diagnose problems such as missing CONTENT_LENGTH CGI parameters. ... (check-in: 9c40ddbc user: drh tags: trunk) | |
Changes
Changes to src/cgi.c.
︙ | ︙ | |||
1236 1237 1238 1239 1240 1241 1242 | return; } } fputs(z, pLog); } /* Forward declaration */ | | | 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 | return; } } fputs(z, pLog); } /* Forward declaration */ static NORETURN void malformed_request(const char *zMsg, ...); /* ** Checks the QUERY_STRING environment variable, sets it up ** via add_param_list() and, if found, applies its "skin" ** setting. Returns 0 if no QUERY_STRING is set, 1 if it is, ** and 2 if it sets the skin (in which case the cookie may ** still need flushing by the page, via cookie_render()). |
︙ | ︙ | |||
1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 | char *z; const char *zType; char *zSemi; int len; const char *zRequestUri = cgi_parameter("REQUEST_URI",0); const char *zScriptName = cgi_parameter("SCRIPT_NAME",0); const char *zPathInfo = cgi_parameter("PATH_INFO",0); #ifdef _WIN32 const char *zServerSoftware = cgi_parameter("SERVER_SOFTWARE",0); #endif #ifdef FOSSIL_ENABLE_JSON const int noJson = P("no_json")!=0; #endif | > | 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 | char *z; const char *zType; char *zSemi; int len; const char *zRequestUri = cgi_parameter("REQUEST_URI",0); const char *zScriptName = cgi_parameter("SCRIPT_NAME",0); const char *zPathInfo = cgi_parameter("PATH_INFO",0); const char *zContentLength = 0; #ifdef _WIN32 const char *zServerSoftware = cgi_parameter("SERVER_SOFTWARE",0); #endif #ifdef FOSSIL_ENABLE_JSON const int noJson = P("no_json")!=0; #endif |
︙ | ︙ | |||
1416 1417 1418 1419 1420 1421 1422 | cgi_setup_query_string(); z = (char*)P("REMOTE_ADDR"); if( z ){ g.zIpAddr = fossil_strdup(z); } | | > > > > > > > > | 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 | cgi_setup_query_string(); z = (char*)P("REMOTE_ADDR"); if( z ){ g.zIpAddr = fossil_strdup(z); } zContentLength = P("CONTENT_LENGTH"); if( zContentLength==0 ){ len = 0; if( sqlite3_stricmp(PD("REQUEST_METHOD",""),"POST")==0 ){ malformed_request("missing CONTENT_LENGTH on a POST method"); } }else{ len = atoi(zContentLength); } zType = P("CONTENT_TYPE"); zSemi = zType ? strchr(zType, ';') : 0; if( zSemi ){ g.zContentType = fossil_strndup(zType, (int)(zSemi-zType)); zType = g.zContentType; }else{ g.zContentType = zType; |
︙ | ︙ | |||
1910 1911 1912 1913 1914 1915 1916 | vxprintf(pContent,zFormat,ap); } /* ** Send a reply indicating that the HTTP request was malformed */ | | > > > > > | > > > > | > | > | | 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 | vxprintf(pContent,zFormat,ap); } /* ** Send a reply indicating that the HTTP request was malformed */ static NORETURN void malformed_request(const char *zMsg, ...){ va_list ap; char *z; va_start(ap, zMsg); z = vmprintf(zMsg, ap); va_end(ap); cgi_set_status(400, "Bad Request"); zContentType = "text/plain"; if( g.zReqType==0 ) g.zReqType = "WWW"; if( g.zReqType[0]=='C' && PD("SERVER_SOFTWARE",0)!=0 ){ const char *zServer = PD("SERVER_SOFTWARE",""); cgi_printf("Bad CGI Request from \"%s\": %s\n",zServer,z); }else{ cgi_printf("Bad %s Request: %s\n", g.zReqType, z); } fossil_free(z); cgi_reply(); fossil_exit(0); } /* ** Panic and die while processing a webpage. */ |
︙ | ︙ | |||
2031 2032 2033 2034 2035 2036 2037 2038 | */ void cgi_handle_http_request(const char *zIpAddr){ char *z, *zToken; int i; const char *zScheme = "http"; char zLine[2000]; /* A single line of input. */ g.fullHttpReply = 1; if( cgi_fgets(zLine, sizeof(zLine))==0 ){ | > | | > | | 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 | */ void cgi_handle_http_request(const char *zIpAddr){ char *z, *zToken; int i; const char *zScheme = "http"; char zLine[2000]; /* A single line of input. */ g.fullHttpReply = 1; g.zReqType = "HTTP"; if( cgi_fgets(zLine, sizeof(zLine))==0 ){ malformed_request("missing header"); } blob_append(&g.httpHeader, zLine, -1); cgi_trace(zLine); zToken = extract_token(zLine, &z); if( zToken==0 ){ malformed_request("malformed HTTP header"); } if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 && fossil_strcmp(zToken,"HEAD")!=0 ){ malformed_request("unsupported HTTP method: \"%s\" - Fossil only supports" "GET, POST, and HEAD", zToken); } cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); cgi_setenv("REQUEST_METHOD",zToken); zToken = extract_token(z, &z); if( zToken==0 ){ malformed_request("malformed URI in the HTTP header"); } cgi_setenv("REQUEST_URI", zToken); cgi_setenv("SCRIPT_NAME", ""); for(i=0; zToken[i] && zToken[i]!='?'; i++){} if( zToken[i] ) zToken[i++] = 0; cgi_setenv("PATH_INFO", zToken); cgi_setenv("QUERY_STRING", &zToken[i]); |
︙ | ︙ | |||
2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 | if( nCycles==0 ){ cgi_setenv("REMOTE_ADDR", zIpAddr); g.zIpAddr = fossil_strdup(zIpAddr); } }else{ fossil_fatal("missing SSH IP address"); } if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ malformed_request("missing HTTP header"); } cgi_trace(zLine); zToken = extract_token(zLine, &z); if( zToken==0 ){ malformed_request("malformed HTTP header"); | > | 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 | if( nCycles==0 ){ cgi_setenv("REMOTE_ADDR", zIpAddr); g.zIpAddr = fossil_strdup(zIpAddr); } }else{ fossil_fatal("missing SSH IP address"); } g.zReqType = "HTTP"; if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ malformed_request("missing HTTP header"); } cgi_trace(zLine); zToken = extract_token(zLine, &z); if( zToken==0 ){ malformed_request("malformed HTTP header"); |
︙ | ︙ |
Changes to src/clone.c.
︙ | ︙ | |||
265 266 267 268 269 270 271 | nErr = client_sync(syncFlags,CONFIGSET_ALL,0,0,0); g.xlinkClusterOnly = 0; verify_cancel(); db_end_transaction(0); db_close(1); if( nErr ){ file_delete(zRepo); | > > > > > > > | > > > | 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | nErr = client_sync(syncFlags,CONFIGSET_ALL,0,0,0); g.xlinkClusterOnly = 0; verify_cancel(); db_end_transaction(0); db_close(1); if( nErr ){ file_delete(zRepo); if( g.fHttpTrace ){ fossil_fatal( "server returned an error - clone aborted\n\n%s", http_last_trace_reply() ); }else{ fossil_fatal( "server returned an error - clone aborted\n" "Rerun using --httptrace for more detail" ); } } db_open_repository(zRepo); } db_begin_transaction(); if( db_exists("SELECT 1 FROM delta WHERE srcId IN phantom") ){ fossil_fatal("there are unresolved deltas -" " the clone is probably incomplete and unusable."); |
︙ | ︙ |
Changes to src/http.c.
︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 56 57 58 | /* Maximum number of HTTP Authorization attempts */ #define MAX_HTTP_AUTH 2 /* Keep track of HTTP Basic Authorization failures */ static int fSeenHttpAuth = 0; /* ** Construct the "login" card with the client credentials. ** ** login LOGIN NONCE SIGNATURE ** ** The LOGIN is the user id of the client. NONCE is the sha1 checksum ** of all payload that follows the login card. SIGNATURE is the sha1 | > > > > > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | /* Maximum number of HTTP Authorization attempts */ #define MAX_HTTP_AUTH 2 /* Keep track of HTTP Basic Authorization failures */ static int fSeenHttpAuth = 0; /* The N value for most recent http-request-N.txt and http-reply-N.txt ** trace files. */ static int traceCnt = 0; /* ** Construct the "login" card with the client credentials. ** ** login LOGIN NONCE SIGNATURE ** ** The LOGIN is the user id of the client. NONCE is the sha1 checksum ** of all payload that follows the login card. SIGNATURE is the sha1 |
︙ | ︙ | |||
387 388 389 390 391 392 393 394 395 396 397 398 399 400 | ** that cache whether or not a PATH= is needed for a particular ** HOSTNAME. */ void ssh_add_path_argument(Blob *pCmd){ blob_append_escaped_arg(pCmd, "PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1); } /* ** Sign the content in pSend, compress it, and send it to the server ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply ** in pRecv. pRecv is assumed to be uninitialized when ** this routine is called - this routine will initialize it. ** | > > > > > > > > > > > > > > > > > > > | 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | ** that cache whether or not a PATH= is needed for a particular ** HOSTNAME. */ void ssh_add_path_argument(Blob *pCmd){ blob_append_escaped_arg(pCmd, "PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1); } /* ** Return the complete text of the last HTTP reply as saved in the ** http-reply-N.txt file. This only works if run using --httptrace. ** Without the --httptrace option, this routine returns a NULL pointer. ** It still might return a NULL pointer if for some reason it cannot ** find and open the last http-reply-N.txt file. */ char *http_last_trace_reply(void){ Blob x; int n; char *zFilename; if( g.fHttpTrace==0 ) return 0; zFilename = mprintf("http-reply-%d.txt", traceCnt); n = blob_read_from_file(&x, zFilename, ExtFILE); fossil_free(zFilename); if( n<=0 ) return 0; return blob_str(&x); } /* ** Sign the content in pSend, compress it, and send it to the server ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply ** in pRecv. pRecv is assumed to be uninitialized when ** this routine is called - this routine will initialize it. ** |
︙ | ︙ | |||
463 464 465 466 467 468 469 | /* When tracing, write the transmitted HTTP message both to standard ** output and into a file. The file can then be used to drive the ** server-side like this: ** ** ./fossil test-http <http-request-1.txt */ if( g.fHttpTrace ){ | < | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 | /* When tracing, write the transmitted HTTP message both to standard ** output and into a file. The file can then be used to drive the ** server-side like this: ** ** ./fossil test-http <http-request-1.txt */ if( g.fHttpTrace ){ char *zOutFile; FILE *out; traceCnt++; zOutFile = mprintf("http-request-%d.txt", traceCnt); out = fopen(zOutFile, "wb"); if( out ){ fwrite(blob_buffer(&hdr), 1, blob_size(&hdr), out); |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
225 226 227 228 229 230 231 232 233 234 235 236 237 238 | UrlData url; /* Information about current URL */ const char *zLogin; /* Login name. NULL or "" if not logged in. */ const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */ const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */ const char *zSSLIdentity; /* Value of --ssl-identity option, filename of ** SSL client identity */ const char *zCgiFile; /* Name of the CGI file */ #if USE_SEE const char *zPidKey; /* Saved value of the --usepidkey option. Only * applicable when using SEE on Windows or Linux. */ #endif int useLocalauth; /* No login required if from 127.0.0.1 */ int noPswd; /* Logged in without password (on 127.0.0.1) */ int userUid; /* Integer user id */ | > | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | UrlData url; /* Information about current URL */ const char *zLogin; /* Login name. NULL or "" if not logged in. */ const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */ const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */ const char *zSSLIdentity; /* Value of --ssl-identity option, filename of ** SSL client identity */ const char *zCgiFile; /* Name of the CGI file */ const char *zReqType; /* Type of request: "HTTP", "CGI", "SCGI" */ #if USE_SEE const char *zPidKey; /* Saved value of the --usepidkey option. Only * applicable when using SEE on Windows or Linux. */ #endif int useLocalauth; /* No login required if from 127.0.0.1 */ int noPswd; /* Logged in without password (on 127.0.0.1) */ int userUid; /* Integer user id */ |
︙ | ︙ | |||
2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 | Blob config, line, key, value, value2; /* Initialize the CGI environment. */ g.httpOut = stdout; g.httpIn = stdin; fossil_binary_mode(g.httpOut); fossil_binary_mode(g.httpIn); g.cgiOutput = 1; fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT); /* Find the name of the CGI control file */ if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){ g.zCgiFile = g.argv[2]; }else if( g.argc>=2 ){ g.zCgiFile = g.argv[1]; }else{ | > | 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 | Blob config, line, key, value, value2; /* Initialize the CGI environment. */ g.httpOut = stdout; g.httpIn = stdin; fossil_binary_mode(g.httpOut); fossil_binary_mode(g.httpIn); g.cgiOutput = 1; g.zReqType = "CGI"; fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT); /* Find the name of the CGI control file */ if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){ g.zCgiFile = g.argv[2]; }else if( g.argc>=2 ){ g.zCgiFile = g.argv[1]; }else{ |
︙ | ︙ | |||
2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 | noJail = find_option("nojail",0,0)!=0; allowRepoList = find_option("repolist",0,0)!=0; g.useLocalauth = find_option("localauth", 0, 0)!=0; g.sslNotAvailable = find_option("nossl", 0, 0)!=0; g.fNoHttpCompress = find_option("nocompress",0,0)!=0; g.zExtRoot = find_option("extroot",0,1); g.zCkoutAlias = find_option("ckout-alias",0,1); zInFile = find_option("in",0,1); if( zInFile ){ backoffice_disable(); g.httpIn = fossil_fopen(zInFile, "rb"); if( g.httpIn==0 ) fossil_fatal("cannot open \"%s\" for reading", zInFile); }else{ g.httpIn = stdin; | > | 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 | noJail = find_option("nojail",0,0)!=0; allowRepoList = find_option("repolist",0,0)!=0; g.useLocalauth = find_option("localauth", 0, 0)!=0; g.sslNotAvailable = find_option("nossl", 0, 0)!=0; g.fNoHttpCompress = find_option("nocompress",0,0)!=0; g.zExtRoot = find_option("extroot",0,1); g.zCkoutAlias = find_option("ckout-alias",0,1); g.zReqType = "HTTP"; zInFile = find_option("in",0,1); if( zInFile ){ backoffice_disable(); g.httpIn = fossil_fopen(zInFile, "rb"); if( g.httpIn==0 ) fossil_fatal("cannot open \"%s\" for reading", zInFile); }else{ g.httpIn = stdin; |
︙ | ︙ | |||
2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 | g.httpOut = stdout; #if defined(_WIN32) _setmode(_fileno(stdout), _O_BINARY); #endif } zIpAddr = find_option("ipaddr",0,1); useSCGI = find_option("scgi", 0, 0)!=0; zAltBase = find_option("baseurl", 0, 1); if( find_option("nodelay",0,0)!=0 ) backoffice_no_delay(); if( zAltBase ) set_base_url(zAltBase); if( find_option("https",0,0)!=0 ){ zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */ cgi_replace_parameter("HTTPS","on"); } | > | 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 | g.httpOut = stdout; #if defined(_WIN32) _setmode(_fileno(stdout), _O_BINARY); #endif } zIpAddr = find_option("ipaddr",0,1); useSCGI = find_option("scgi", 0, 0)!=0; if( useSCGI ) g.zReqType = "SCGI"; zAltBase = find_option("baseurl", 0, 1); if( find_option("nodelay",0,0)!=0 ) backoffice_no_delay(); if( zAltBase ) set_base_url(zAltBase); if( find_option("https",0,0)!=0 ){ zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */ cgi_replace_parameter("HTTPS","on"); } |
︙ | ︙ | |||
2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 | login_set_capabilities(zUserCap, 0); g.httpIn = stdin; g.httpOut = stdout; fossil_binary_mode(g.httpOut); fossil_binary_mode(g.httpIn); g.zExtRoot = find_option("extroot",0,1); find_server_repository(2, 0); g.cgiOutput = 1; g.fNoHttpCompress = 1; g.fullHttpReply = 1; g.sslNotAvailable = 1; /* Avoid attempts to redirect */ zIpAddr = bTest ? 0 : cgi_ssh_remote_addr(0); if( zIpAddr && zIpAddr[0] ){ g.fSshClient |= CGI_SSH_CLIENT; | > | 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 | login_set_capabilities(zUserCap, 0); g.httpIn = stdin; g.httpOut = stdout; fossil_binary_mode(g.httpOut); fossil_binary_mode(g.httpIn); g.zExtRoot = find_option("extroot",0,1); find_server_repository(2, 0); g.zReqType = "HTTP"; g.cgiOutput = 1; g.fNoHttpCompress = 1; g.fullHttpReply = 1; g.sslNotAvailable = 1; /* Avoid attempts to redirect */ zIpAddr = bTest ? 0 : cgi_ssh_remote_addr(0); if( zIpAddr && zIpAddr[0] ){ g.fSshClient |= CGI_SSH_CLIENT; |
︙ | ︙ | |||
3229 3230 3231 3232 3233 3234 3235 | zFossilCmd = find_option("fossilcmd", 0, 1); } zNotFound = find_option("notfound", 0, 1); allowRepoList = find_option("repolist",0,0)!=0; if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1; zAltBase = find_option("baseurl", 0, 1); fCreate = find_option("create",0,0)!=0; | > | > > > | 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 | zFossilCmd = find_option("fossilcmd", 0, 1); } zNotFound = find_option("notfound", 0, 1); allowRepoList = find_option("repolist",0,0)!=0; if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1; zAltBase = find_option("baseurl", 0, 1); fCreate = find_option("create",0,0)!=0; g.zReqType = "HTTP"; if( find_option("scgi", 0, 0)!=0 ){ g.zReqType = "SCGI"; flags |= HTTP_SERVER_SCGI; } if( zAltBase ){ set_base_url(zAltBase); } g.sslNotAvailable = find_option("nossl", 0, 0)!=0 || isUiCmd; fNoBrowser = find_option("nobrowser", "B", 0)!=0; decode_ssl_options(); if( find_option("https",0,0)!=0 || g.httpUseSSL ){ |
︙ | ︙ |