Fossil SCM
Build the HTTP reply header in a Blob before sending it on the wire all at once.
Commit
2ac4ab2b2f195f1839c910cf877399839b70202b9620243b08c030107bb941e2
Parent
b62f651cc00280e…
1 file changed
+25
-20
+25
-20
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -281,10 +281,11 @@ | ||
| 281 | 281 | |
| 282 | 282 | /* |
| 283 | 283 | ** Do a normal HTTP reply |
| 284 | 284 | */ |
| 285 | 285 | void cgi_reply(void){ |
| 286 | + Blob hdr = BLOB_INITIALIZER; | |
| 286 | 287 | int total_size; |
| 287 | 288 | if( iReplyStatus<=0 ){ |
| 288 | 289 | iReplyStatus = 200; |
| 289 | 290 | zReplyStatus = "OK"; |
| 290 | 291 | } |
| @@ -295,39 +296,39 @@ | ||
| 295 | 296 | && fossil_strcmp(P("REQUEST_METHOD"),"GET")==0 |
| 296 | 297 | ){ |
| 297 | 298 | iReplyStatus = 206; |
| 298 | 299 | zReplyStatus = "Partial Content"; |
| 299 | 300 | } |
| 300 | - fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); | |
| 301 | - fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); | |
| 302 | - fprintf(g.httpOut, "Connection: close\r\n"); | |
| 303 | - fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); | |
| 301 | + blob_appendf(&hdr, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); | |
| 302 | + blob_appendf(&hdr, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); | |
| 303 | + blob_appendf(&hdr, "Connection: close\r\n"); | |
| 304 | + blob_appendf(&hdr, "X-UA-Compatible: IE=edge\r\n"); | |
| 304 | 305 | }else{ |
| 305 | 306 | assert( rangeEnd==0 ); |
| 306 | - fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); | |
| 307 | + blob_appendf(&hdr, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); | |
| 307 | 308 | } |
| 308 | 309 | if( etag_tag()[0]!=0 ){ |
| 309 | - fprintf(g.httpOut, "ETag: %s\r\n", etag_tag()); | |
| 310 | - fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage()); | |
| 310 | + blob_appendf(&hdr, "ETag: %s\r\n", etag_tag()); | |
| 311 | + blob_appendf(&hdr, "Cache-Control: max-age=%d\r\n", etag_maxage()); | |
| 311 | 312 | if( etag_mtime()>0 ){ |
| 312 | - fprintf(g.httpOut, "Last-Modified: %s\r\n", | |
| 313 | + blob_appendf(&hdr, "Last-Modified: %s\r\n", | |
| 313 | 314 | cgi_rfc822_datestamp(etag_mtime())); |
| 314 | 315 | } |
| 315 | 316 | }else if( g.isConst ){ |
| 316 | 317 | /* isConst means that the reply is guaranteed to be invariant, even |
| 317 | 318 | ** after configuration changes and/or Fossil binary recompiles. */ |
| 318 | - fprintf(g.httpOut, "Cache-Control: max-age=315360000, immutable\r\n"); | |
| 319 | + blob_appendf(&hdr, "Cache-Control: max-age=315360000, immutable\r\n"); | |
| 319 | 320 | }else{ |
| 320 | - fprintf(g.httpOut, "Cache-control: no-cache\r\n"); | |
| 321 | + blob_appendf(&hdr, "Cache-control: no-cache\r\n"); | |
| 321 | 322 | } |
| 322 | 323 | |
| 323 | 324 | if( blob_size(&extraHeader)>0 ){ |
| 324 | - fprintf(g.httpOut, "%s", blob_buffer(&extraHeader)); | |
| 325 | + blob_appendf(&hdr, "%s", blob_buffer(&extraHeader)); | |
| 325 | 326 | } |
| 326 | 327 | |
| 327 | 328 | /* Add headers to turn on useful security options in browsers. */ |
| 328 | - fprintf(g.httpOut, "X-Frame-Options: SAMEORIGIN\r\n"); | |
| 329 | + blob_appendf(&hdr, "X-Frame-Options: SAMEORIGIN\r\n"); | |
| 329 | 330 | /* This stops fossil pages appearing in frames or iframes, preventing |
| 330 | 331 | ** click-jacking attacks on supporting browsers. |
| 331 | 332 | ** |
| 332 | 333 | ** Other good headers would be |
| 333 | 334 | ** Strict-Transport-Security: max-age=62208000 |
| @@ -344,11 +345,11 @@ | ||
| 344 | 345 | |
| 345 | 346 | /* Content intended for logged in users should only be cached in |
| 346 | 347 | ** the browser, not some shared location. |
| 347 | 348 | */ |
| 348 | 349 | if( iReplyStatus!=304 ) { |
| 349 | - fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType); | |
| 350 | + blob_appendf(&hdr, "Content-Type: %s; charset=utf-8\r\n", zContentType); | |
| 350 | 351 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| 351 | 352 | cgi_combine_header_and_body(); |
| 352 | 353 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 353 | 354 | } |
| 354 | 355 | |
| @@ -359,24 +360,26 @@ | ||
| 359 | 360 | int size = blob_size(&cgiContent[i]); |
| 360 | 361 | if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); |
| 361 | 362 | blob_reset(&cgiContent[i]); |
| 362 | 363 | } |
| 363 | 364 | gzip_finish(&cgiContent[0]); |
| 364 | - fprintf(g.httpOut, "Content-Encoding: gzip\r\n"); | |
| 365 | - fprintf(g.httpOut, "Vary: Accept-Encoding\r\n"); | |
| 365 | + blob_appendf(&hdr, "Content-Encoding: gzip\r\n"); | |
| 366 | + blob_appendf(&hdr, "Vary: Accept-Encoding\r\n"); | |
| 366 | 367 | } |
| 367 | 368 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 368 | 369 | if( iReplyStatus==206 ){ |
| 369 | - fprintf(g.httpOut, "Content-Range: bytes %d-%d/%d\r\n", | |
| 370 | + blob_appendf(&hdr, "Content-Range: bytes %d-%d/%d\r\n", | |
| 370 | 371 | rangeStart, rangeEnd-1, total_size); |
| 371 | 372 | total_size = rangeEnd - rangeStart; |
| 372 | 373 | } |
| 373 | - fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); | |
| 374 | + blob_appendf(&hdr, "Content-Length: %d\r\n", total_size); | |
| 374 | 375 | }else{ |
| 375 | 376 | total_size = 0; |
| 376 | 377 | } |
| 377 | - fprintf(g.httpOut, "\r\n"); | |
| 378 | + blob_appendf(&hdr, "\r\n"); | |
| 379 | + fwrite(blob_buffer(&hdr), 1, blob_size(&hdr), g.httpOut); | |
| 380 | + blob_reset(&hdr); | |
| 378 | 381 | if( total_size>0 |
| 379 | 382 | && iReplyStatus!=304 |
| 380 | 383 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 381 | 384 | ){ |
| 382 | 385 | int i, size; |
| @@ -1742,12 +1745,14 @@ | ||
| 1742 | 1745 | cgi_trace(zLine); |
| 1743 | 1746 | zToken = extract_token(zLine, &z); |
| 1744 | 1747 | if( zToken==0 ){ |
| 1745 | 1748 | malformed_request("malformed HTTP header"); |
| 1746 | 1749 | } |
| 1747 | - if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 | |
| 1748 | - && fossil_strcmp(zToken,"HEAD")!=0 ){ | |
| 1750 | + if( fossil_strcmp(zToken,"GET")!=0 | |
| 1751 | + && fossil_strcmp(zToken,"POST")!=0 | |
| 1752 | + && fossil_strcmp(zToken,"HEAD")!=0 | |
| 1753 | + ){ | |
| 1749 | 1754 | malformed_request("unsupported HTTP method"); |
| 1750 | 1755 | } |
| 1751 | 1756 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1752 | 1757 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1753 | 1758 | zToken = extract_token(z, &z); |
| 1754 | 1759 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -281,10 +281,11 @@ | |
| 281 | |
| 282 | /* |
| 283 | ** Do a normal HTTP reply |
| 284 | */ |
| 285 | void cgi_reply(void){ |
| 286 | int total_size; |
| 287 | if( iReplyStatus<=0 ){ |
| 288 | iReplyStatus = 200; |
| 289 | zReplyStatus = "OK"; |
| 290 | } |
| @@ -295,39 +296,39 @@ | |
| 295 | && fossil_strcmp(P("REQUEST_METHOD"),"GET")==0 |
| 296 | ){ |
| 297 | iReplyStatus = 206; |
| 298 | zReplyStatus = "Partial Content"; |
| 299 | } |
| 300 | fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); |
| 301 | fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); |
| 302 | fprintf(g.httpOut, "Connection: close\r\n"); |
| 303 | fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); |
| 304 | }else{ |
| 305 | assert( rangeEnd==0 ); |
| 306 | fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 307 | } |
| 308 | if( etag_tag()[0]!=0 ){ |
| 309 | fprintf(g.httpOut, "ETag: %s\r\n", etag_tag()); |
| 310 | fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage()); |
| 311 | if( etag_mtime()>0 ){ |
| 312 | fprintf(g.httpOut, "Last-Modified: %s\r\n", |
| 313 | cgi_rfc822_datestamp(etag_mtime())); |
| 314 | } |
| 315 | }else if( g.isConst ){ |
| 316 | /* isConst means that the reply is guaranteed to be invariant, even |
| 317 | ** after configuration changes and/or Fossil binary recompiles. */ |
| 318 | fprintf(g.httpOut, "Cache-Control: max-age=315360000, immutable\r\n"); |
| 319 | }else{ |
| 320 | fprintf(g.httpOut, "Cache-control: no-cache\r\n"); |
| 321 | } |
| 322 | |
| 323 | if( blob_size(&extraHeader)>0 ){ |
| 324 | fprintf(g.httpOut, "%s", blob_buffer(&extraHeader)); |
| 325 | } |
| 326 | |
| 327 | /* Add headers to turn on useful security options in browsers. */ |
| 328 | fprintf(g.httpOut, "X-Frame-Options: SAMEORIGIN\r\n"); |
| 329 | /* This stops fossil pages appearing in frames or iframes, preventing |
| 330 | ** click-jacking attacks on supporting browsers. |
| 331 | ** |
| 332 | ** Other good headers would be |
| 333 | ** Strict-Transport-Security: max-age=62208000 |
| @@ -344,11 +345,11 @@ | |
| 344 | |
| 345 | /* Content intended for logged in users should only be cached in |
| 346 | ** the browser, not some shared location. |
| 347 | */ |
| 348 | if( iReplyStatus!=304 ) { |
| 349 | fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType); |
| 350 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| 351 | cgi_combine_header_and_body(); |
| 352 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 353 | } |
| 354 | |
| @@ -359,24 +360,26 @@ | |
| 359 | int size = blob_size(&cgiContent[i]); |
| 360 | if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); |
| 361 | blob_reset(&cgiContent[i]); |
| 362 | } |
| 363 | gzip_finish(&cgiContent[0]); |
| 364 | fprintf(g.httpOut, "Content-Encoding: gzip\r\n"); |
| 365 | fprintf(g.httpOut, "Vary: Accept-Encoding\r\n"); |
| 366 | } |
| 367 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 368 | if( iReplyStatus==206 ){ |
| 369 | fprintf(g.httpOut, "Content-Range: bytes %d-%d/%d\r\n", |
| 370 | rangeStart, rangeEnd-1, total_size); |
| 371 | total_size = rangeEnd - rangeStart; |
| 372 | } |
| 373 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 374 | }else{ |
| 375 | total_size = 0; |
| 376 | } |
| 377 | fprintf(g.httpOut, "\r\n"); |
| 378 | if( total_size>0 |
| 379 | && iReplyStatus!=304 |
| 380 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 381 | ){ |
| 382 | int i, size; |
| @@ -1742,12 +1745,14 @@ | |
| 1742 | cgi_trace(zLine); |
| 1743 | zToken = extract_token(zLine, &z); |
| 1744 | if( zToken==0 ){ |
| 1745 | malformed_request("malformed HTTP header"); |
| 1746 | } |
| 1747 | if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 |
| 1748 | && fossil_strcmp(zToken,"HEAD")!=0 ){ |
| 1749 | malformed_request("unsupported HTTP method"); |
| 1750 | } |
| 1751 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1752 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1753 | zToken = extract_token(z, &z); |
| 1754 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -281,10 +281,11 @@ | |
| 281 | |
| 282 | /* |
| 283 | ** Do a normal HTTP reply |
| 284 | */ |
| 285 | void cgi_reply(void){ |
| 286 | Blob hdr = BLOB_INITIALIZER; |
| 287 | int total_size; |
| 288 | if( iReplyStatus<=0 ){ |
| 289 | iReplyStatus = 200; |
| 290 | zReplyStatus = "OK"; |
| 291 | } |
| @@ -295,39 +296,39 @@ | |
| 296 | && fossil_strcmp(P("REQUEST_METHOD"),"GET")==0 |
| 297 | ){ |
| 298 | iReplyStatus = 206; |
| 299 | zReplyStatus = "Partial Content"; |
| 300 | } |
| 301 | blob_appendf(&hdr, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); |
| 302 | blob_appendf(&hdr, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); |
| 303 | blob_appendf(&hdr, "Connection: close\r\n"); |
| 304 | blob_appendf(&hdr, "X-UA-Compatible: IE=edge\r\n"); |
| 305 | }else{ |
| 306 | assert( rangeEnd==0 ); |
| 307 | blob_appendf(&hdr, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 308 | } |
| 309 | if( etag_tag()[0]!=0 ){ |
| 310 | blob_appendf(&hdr, "ETag: %s\r\n", etag_tag()); |
| 311 | blob_appendf(&hdr, "Cache-Control: max-age=%d\r\n", etag_maxage()); |
| 312 | if( etag_mtime()>0 ){ |
| 313 | blob_appendf(&hdr, "Last-Modified: %s\r\n", |
| 314 | cgi_rfc822_datestamp(etag_mtime())); |
| 315 | } |
| 316 | }else if( g.isConst ){ |
| 317 | /* isConst means that the reply is guaranteed to be invariant, even |
| 318 | ** after configuration changes and/or Fossil binary recompiles. */ |
| 319 | blob_appendf(&hdr, "Cache-Control: max-age=315360000, immutable\r\n"); |
| 320 | }else{ |
| 321 | blob_appendf(&hdr, "Cache-control: no-cache\r\n"); |
| 322 | } |
| 323 | |
| 324 | if( blob_size(&extraHeader)>0 ){ |
| 325 | blob_appendf(&hdr, "%s", blob_buffer(&extraHeader)); |
| 326 | } |
| 327 | |
| 328 | /* Add headers to turn on useful security options in browsers. */ |
| 329 | blob_appendf(&hdr, "X-Frame-Options: SAMEORIGIN\r\n"); |
| 330 | /* This stops fossil pages appearing in frames or iframes, preventing |
| 331 | ** click-jacking attacks on supporting browsers. |
| 332 | ** |
| 333 | ** Other good headers would be |
| 334 | ** Strict-Transport-Security: max-age=62208000 |
| @@ -344,11 +345,11 @@ | |
| 345 | |
| 346 | /* Content intended for logged in users should only be cached in |
| 347 | ** the browser, not some shared location. |
| 348 | */ |
| 349 | if( iReplyStatus!=304 ) { |
| 350 | blob_appendf(&hdr, "Content-Type: %s; charset=utf-8\r\n", zContentType); |
| 351 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| 352 | cgi_combine_header_and_body(); |
| 353 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 354 | } |
| 355 | |
| @@ -359,24 +360,26 @@ | |
| 360 | int size = blob_size(&cgiContent[i]); |
| 361 | if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); |
| 362 | blob_reset(&cgiContent[i]); |
| 363 | } |
| 364 | gzip_finish(&cgiContent[0]); |
| 365 | blob_appendf(&hdr, "Content-Encoding: gzip\r\n"); |
| 366 | blob_appendf(&hdr, "Vary: Accept-Encoding\r\n"); |
| 367 | } |
| 368 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 369 | if( iReplyStatus==206 ){ |
| 370 | blob_appendf(&hdr, "Content-Range: bytes %d-%d/%d\r\n", |
| 371 | rangeStart, rangeEnd-1, total_size); |
| 372 | total_size = rangeEnd - rangeStart; |
| 373 | } |
| 374 | blob_appendf(&hdr, "Content-Length: %d\r\n", total_size); |
| 375 | }else{ |
| 376 | total_size = 0; |
| 377 | } |
| 378 | blob_appendf(&hdr, "\r\n"); |
| 379 | fwrite(blob_buffer(&hdr), 1, blob_size(&hdr), g.httpOut); |
| 380 | blob_reset(&hdr); |
| 381 | if( total_size>0 |
| 382 | && iReplyStatus!=304 |
| 383 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 384 | ){ |
| 385 | int i, size; |
| @@ -1742,12 +1745,14 @@ | |
| 1745 | cgi_trace(zLine); |
| 1746 | zToken = extract_token(zLine, &z); |
| 1747 | if( zToken==0 ){ |
| 1748 | malformed_request("malformed HTTP header"); |
| 1749 | } |
| 1750 | if( fossil_strcmp(zToken,"GET")!=0 |
| 1751 | && fossil_strcmp(zToken,"POST")!=0 |
| 1752 | && fossil_strcmp(zToken,"HEAD")!=0 |
| 1753 | ){ |
| 1754 | malformed_request("unsupported HTTP method"); |
| 1755 | } |
| 1756 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1757 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1758 | zToken = extract_token(z, &z); |
| 1759 |