Fossil SCM
Add support for the Range: attribute in HTTP requests for the "fossil server" and "fossil http" commands.
Commit
b6892ccdd6f8f62522c325decdf2fe0e535c1b9bdf4e0df3d6fb69e4fc79df96
Parent
0ea56bb239024a3…
2 files changed
+33
-4
+33
-4
+33
-4
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -178,10 +178,12 @@ | ||
| 178 | 178 | */ |
| 179 | 179 | static const char *zContentType = "text/html"; /* Content type of the reply */ |
| 180 | 180 | static const char *zReplyStatus = "OK"; /* Reply status description */ |
| 181 | 181 | static int iReplyStatus = 200; /* Reply status code */ |
| 182 | 182 | static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */ |
| 183 | +static int rangeStart = 0; /* Start of Range: */ | |
| 184 | +static int rangeEnd = 0; /* End of Range: plus 1 */ | |
| 183 | 185 | |
| 184 | 186 | /* |
| 185 | 187 | ** Set the reply content type |
| 186 | 188 | */ |
| 187 | 189 | void cgi_set_content_type(const char *zType){ |
| @@ -272,15 +274,22 @@ | ||
| 272 | 274 | iReplyStatus = 200; |
| 273 | 275 | zReplyStatus = "OK"; |
| 274 | 276 | } |
| 275 | 277 | |
| 276 | 278 | if( g.fullHttpReply ){ |
| 279 | + if( rangeEnd>0 | |
| 280 | + && iReplyStatus==200 | |
| 281 | + && fossil_strcmp(P("REQUEST_METHOD"),"GET")==0 | |
| 282 | + ){ | |
| 283 | + iReplyStatus = 206; | |
| 284 | + } | |
| 277 | 285 | fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); |
| 278 | 286 | fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); |
| 279 | 287 | fprintf(g.httpOut, "Connection: close\r\n"); |
| 280 | 288 | fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); |
| 281 | 289 | }else{ |
| 290 | + assert( rangeEnd==0 ); | |
| 282 | 291 | fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 283 | 292 | } |
| 284 | 293 | if( g.isConst ){ |
| 285 | 294 | /* isConst means that the reply is guaranteed to be invariant, even |
| 286 | 295 | ** after configuration changes and/or Fossil binary recompiles. */ |
| @@ -325,12 +334,12 @@ | ||
| 325 | 334 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| 326 | 335 | cgi_combine_header_and_body(); |
| 327 | 336 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 328 | 337 | } |
| 329 | 338 | |
| 330 | - if( iReplyStatus != 304 ) { | |
| 331 | - if( is_gzippable() ){ | |
| 339 | + if( iReplyStatus!=304 ) { | |
| 340 | + if( is_gzippable() && iReplyStatus!=206 ){ | |
| 332 | 341 | int i; |
| 333 | 342 | gzip_begin(0); |
| 334 | 343 | for( i=0; i<2; i++ ){ |
| 335 | 344 | int size = blob_size(&cgiContent[i]); |
| 336 | 345 | if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); |
| @@ -339,10 +348,15 @@ | ||
| 339 | 348 | gzip_finish(&cgiContent[0]); |
| 340 | 349 | fprintf(g.httpOut, "Content-Encoding: gzip\r\n"); |
| 341 | 350 | fprintf(g.httpOut, "Vary: Accept-Encoding\r\n"); |
| 342 | 351 | } |
| 343 | 352 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 353 | + if( iReplyStatus==206 ){ | |
| 354 | + fprintf(g.httpOut, "Content-Range: bytes %d-%d/%d\r\n", | |
| 355 | + rangeStart, rangeEnd-1, total_size); | |
| 356 | + total_size = rangeEnd - rangeStart; | |
| 357 | + } | |
| 344 | 358 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 345 | 359 | }else{ |
| 346 | 360 | total_size = 0; |
| 347 | 361 | } |
| 348 | 362 | fprintf(g.httpOut, "\r\n"); |
| @@ -350,12 +364,20 @@ | ||
| 350 | 364 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 351 | 365 | ){ |
| 352 | 366 | int i, size; |
| 353 | 367 | for(i=0; i<2; i++){ |
| 354 | 368 | size = blob_size(&cgiContent[i]); |
| 355 | - if( size>0 ){ | |
| 356 | - fwrite(blob_buffer(&cgiContent[i]), 1, size, g.httpOut); | |
| 369 | + if( size<=rangeStart ){ | |
| 370 | + rangeStart -= size; | |
| 371 | + }else{ | |
| 372 | + int n = size - rangeStart; | |
| 373 | + if( n>total_size ){ | |
| 374 | + n = total_size; | |
| 375 | + } | |
| 376 | + fwrite(blob_buffer(&cgiContent[i])+rangeStart, 1, n, g.httpOut); | |
| 377 | + rangeStart = 0; | |
| 378 | + total_size -= n; | |
| 357 | 379 | } |
| 358 | 380 | } |
| 359 | 381 | } |
| 360 | 382 | fflush(g.httpOut); |
| 361 | 383 | CGIDEBUG(("-------- END cgi ---------\n")); |
| @@ -1658,10 +1680,17 @@ | ||
| 1658 | 1680 | }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ |
| 1659 | 1681 | const char *zIpAddr = cgi_accept_forwarded_for(zVal); |
| 1660 | 1682 | if( zIpAddr!=0 ){ |
| 1661 | 1683 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1662 | 1684 | cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); |
| 1685 | + } | |
| 1686 | + }else if( fossil_strcmp(zFieldName,"range:")==0 ){ | |
| 1687 | + int x1 = 0; | |
| 1688 | + int x2 = 0; | |
| 1689 | + if( sscanf(zVal,"bytes=%d-%d",&x1,&x2)==2 && x1>=0 && x1<=x2 ){ | |
| 1690 | + rangeStart = x1; | |
| 1691 | + rangeEnd = x2+1; | |
| 1663 | 1692 | } |
| 1664 | 1693 | } |
| 1665 | 1694 | } |
| 1666 | 1695 | cgi_init(); |
| 1667 | 1696 | cgi_trace(0); |
| 1668 | 1697 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -178,10 +178,12 @@ | |
| 178 | */ |
| 179 | static const char *zContentType = "text/html"; /* Content type of the reply */ |
| 180 | static const char *zReplyStatus = "OK"; /* Reply status description */ |
| 181 | static int iReplyStatus = 200; /* Reply status code */ |
| 182 | static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */ |
| 183 | |
| 184 | /* |
| 185 | ** Set the reply content type |
| 186 | */ |
| 187 | void cgi_set_content_type(const char *zType){ |
| @@ -272,15 +274,22 @@ | |
| 272 | iReplyStatus = 200; |
| 273 | zReplyStatus = "OK"; |
| 274 | } |
| 275 | |
| 276 | if( g.fullHttpReply ){ |
| 277 | fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); |
| 278 | fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); |
| 279 | fprintf(g.httpOut, "Connection: close\r\n"); |
| 280 | fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); |
| 281 | }else{ |
| 282 | fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 283 | } |
| 284 | if( g.isConst ){ |
| 285 | /* isConst means that the reply is guaranteed to be invariant, even |
| 286 | ** after configuration changes and/or Fossil binary recompiles. */ |
| @@ -325,12 +334,12 @@ | |
| 325 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| 326 | cgi_combine_header_and_body(); |
| 327 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 328 | } |
| 329 | |
| 330 | if( iReplyStatus != 304 ) { |
| 331 | if( is_gzippable() ){ |
| 332 | int i; |
| 333 | gzip_begin(0); |
| 334 | for( i=0; i<2; i++ ){ |
| 335 | int size = blob_size(&cgiContent[i]); |
| 336 | if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); |
| @@ -339,10 +348,15 @@ | |
| 339 | gzip_finish(&cgiContent[0]); |
| 340 | fprintf(g.httpOut, "Content-Encoding: gzip\r\n"); |
| 341 | fprintf(g.httpOut, "Vary: Accept-Encoding\r\n"); |
| 342 | } |
| 343 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 344 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 345 | }else{ |
| 346 | total_size = 0; |
| 347 | } |
| 348 | fprintf(g.httpOut, "\r\n"); |
| @@ -350,12 +364,20 @@ | |
| 350 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 351 | ){ |
| 352 | int i, size; |
| 353 | for(i=0; i<2; i++){ |
| 354 | size = blob_size(&cgiContent[i]); |
| 355 | if( size>0 ){ |
| 356 | fwrite(blob_buffer(&cgiContent[i]), 1, size, g.httpOut); |
| 357 | } |
| 358 | } |
| 359 | } |
| 360 | fflush(g.httpOut); |
| 361 | CGIDEBUG(("-------- END cgi ---------\n")); |
| @@ -1658,10 +1680,17 @@ | |
| 1658 | }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ |
| 1659 | const char *zIpAddr = cgi_accept_forwarded_for(zVal); |
| 1660 | if( zIpAddr!=0 ){ |
| 1661 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1662 | cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); |
| 1663 | } |
| 1664 | } |
| 1665 | } |
| 1666 | cgi_init(); |
| 1667 | cgi_trace(0); |
| 1668 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -178,10 +178,12 @@ | |
| 178 | */ |
| 179 | static const char *zContentType = "text/html"; /* Content type of the reply */ |
| 180 | static const char *zReplyStatus = "OK"; /* Reply status description */ |
| 181 | static int iReplyStatus = 200; /* Reply status code */ |
| 182 | static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */ |
| 183 | static int rangeStart = 0; /* Start of Range: */ |
| 184 | static int rangeEnd = 0; /* End of Range: plus 1 */ |
| 185 | |
| 186 | /* |
| 187 | ** Set the reply content type |
| 188 | */ |
| 189 | void cgi_set_content_type(const char *zType){ |
| @@ -272,15 +274,22 @@ | |
| 274 | iReplyStatus = 200; |
| 275 | zReplyStatus = "OK"; |
| 276 | } |
| 277 | |
| 278 | if( g.fullHttpReply ){ |
| 279 | if( rangeEnd>0 |
| 280 | && iReplyStatus==200 |
| 281 | && fossil_strcmp(P("REQUEST_METHOD"),"GET")==0 |
| 282 | ){ |
| 283 | iReplyStatus = 206; |
| 284 | } |
| 285 | fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); |
| 286 | fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); |
| 287 | fprintf(g.httpOut, "Connection: close\r\n"); |
| 288 | fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); |
| 289 | }else{ |
| 290 | assert( rangeEnd==0 ); |
| 291 | fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 292 | } |
| 293 | if( g.isConst ){ |
| 294 | /* isConst means that the reply is guaranteed to be invariant, even |
| 295 | ** after configuration changes and/or Fossil binary recompiles. */ |
| @@ -325,12 +334,12 @@ | |
| 334 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| 335 | cgi_combine_header_and_body(); |
| 336 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 337 | } |
| 338 | |
| 339 | if( iReplyStatus!=304 ) { |
| 340 | if( is_gzippable() && iReplyStatus!=206 ){ |
| 341 | int i; |
| 342 | gzip_begin(0); |
| 343 | for( i=0; i<2; i++ ){ |
| 344 | int size = blob_size(&cgiContent[i]); |
| 345 | if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); |
| @@ -339,10 +348,15 @@ | |
| 348 | gzip_finish(&cgiContent[0]); |
| 349 | fprintf(g.httpOut, "Content-Encoding: gzip\r\n"); |
| 350 | fprintf(g.httpOut, "Vary: Accept-Encoding\r\n"); |
| 351 | } |
| 352 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 353 | if( iReplyStatus==206 ){ |
| 354 | fprintf(g.httpOut, "Content-Range: bytes %d-%d/%d\r\n", |
| 355 | rangeStart, rangeEnd-1, total_size); |
| 356 | total_size = rangeEnd - rangeStart; |
| 357 | } |
| 358 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 359 | }else{ |
| 360 | total_size = 0; |
| 361 | } |
| 362 | fprintf(g.httpOut, "\r\n"); |
| @@ -350,12 +364,20 @@ | |
| 364 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 365 | ){ |
| 366 | int i, size; |
| 367 | for(i=0; i<2; i++){ |
| 368 | size = blob_size(&cgiContent[i]); |
| 369 | if( size<=rangeStart ){ |
| 370 | rangeStart -= size; |
| 371 | }else{ |
| 372 | int n = size - rangeStart; |
| 373 | if( n>total_size ){ |
| 374 | n = total_size; |
| 375 | } |
| 376 | fwrite(blob_buffer(&cgiContent[i])+rangeStart, 1, n, g.httpOut); |
| 377 | rangeStart = 0; |
| 378 | total_size -= n; |
| 379 | } |
| 380 | } |
| 381 | } |
| 382 | fflush(g.httpOut); |
| 383 | CGIDEBUG(("-------- END cgi ---------\n")); |
| @@ -1658,10 +1680,17 @@ | |
| 1680 | }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ |
| 1681 | const char *zIpAddr = cgi_accept_forwarded_for(zVal); |
| 1682 | if( zIpAddr!=0 ){ |
| 1683 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1684 | cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); |
| 1685 | } |
| 1686 | }else if( fossil_strcmp(zFieldName,"range:")==0 ){ |
| 1687 | int x1 = 0; |
| 1688 | int x2 = 0; |
| 1689 | if( sscanf(zVal,"bytes=%d-%d",&x1,&x2)==2 && x1>=0 && x1<=x2 ){ |
| 1690 | rangeStart = x1; |
| 1691 | rangeEnd = x2+1; |
| 1692 | } |
| 1693 | } |
| 1694 | } |
| 1695 | cgi_init(); |
| 1696 | cgi_trace(0); |
| 1697 |
+33
-4
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -178,10 +178,12 @@ | ||
| 178 | 178 | */ |
| 179 | 179 | static const char *zContentType = "text/html"; /* Content type of the reply */ |
| 180 | 180 | static const char *zReplyStatus = "OK"; /* Reply status description */ |
| 181 | 181 | static int iReplyStatus = 200; /* Reply status code */ |
| 182 | 182 | static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */ |
| 183 | +static int rangeStart = 0; /* Start of Range: */ | |
| 184 | +static int rangeEnd = 0; /* End of Range: plus 1 */ | |
| 183 | 185 | |
| 184 | 186 | /* |
| 185 | 187 | ** Set the reply content type |
| 186 | 188 | */ |
| 187 | 189 | void cgi_set_content_type(const char *zType){ |
| @@ -272,15 +274,22 @@ | ||
| 272 | 274 | iReplyStatus = 200; |
| 273 | 275 | zReplyStatus = "OK"; |
| 274 | 276 | } |
| 275 | 277 | |
| 276 | 278 | if( g.fullHttpReply ){ |
| 279 | + if( rangeEnd>0 | |
| 280 | + && iReplyStatus==200 | |
| 281 | + && fossil_strcmp(P("REQUEST_METHOD"),"GET")==0 | |
| 282 | + ){ | |
| 283 | + iReplyStatus = 206; | |
| 284 | + } | |
| 277 | 285 | fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); |
| 278 | 286 | fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); |
| 279 | 287 | fprintf(g.httpOut, "Connection: close\r\n"); |
| 280 | 288 | fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); |
| 281 | 289 | }else{ |
| 290 | + assert( rangeEnd==0 ); | |
| 282 | 291 | fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 283 | 292 | } |
| 284 | 293 | if( g.isConst ){ |
| 285 | 294 | /* isConst means that the reply is guaranteed to be invariant, even |
| 286 | 295 | ** after configuration changes and/or Fossil binary recompiles. */ |
| @@ -325,12 +334,12 @@ | ||
| 325 | 334 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| 326 | 335 | cgi_combine_header_and_body(); |
| 327 | 336 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 328 | 337 | } |
| 329 | 338 | |
| 330 | - if( iReplyStatus != 304 ) { | |
| 331 | - if( is_gzippable() ){ | |
| 339 | + if( iReplyStatus!=304 ) { | |
| 340 | + if( is_gzippable() && iReplyStatus!=206 ){ | |
| 332 | 341 | int i; |
| 333 | 342 | gzip_begin(0); |
| 334 | 343 | for( i=0; i<2; i++ ){ |
| 335 | 344 | int size = blob_size(&cgiContent[i]); |
| 336 | 345 | if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); |
| @@ -339,10 +348,15 @@ | ||
| 339 | 348 | gzip_finish(&cgiContent[0]); |
| 340 | 349 | fprintf(g.httpOut, "Content-Encoding: gzip\r\n"); |
| 341 | 350 | fprintf(g.httpOut, "Vary: Accept-Encoding\r\n"); |
| 342 | 351 | } |
| 343 | 352 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 353 | + if( iReplyStatus==206 ){ | |
| 354 | + fprintf(g.httpOut, "Content-Range: bytes %d-%d/%d\r\n", | |
| 355 | + rangeStart, rangeEnd-1, total_size); | |
| 356 | + total_size = rangeEnd - rangeStart; | |
| 357 | + } | |
| 344 | 358 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 345 | 359 | }else{ |
| 346 | 360 | total_size = 0; |
| 347 | 361 | } |
| 348 | 362 | fprintf(g.httpOut, "\r\n"); |
| @@ -350,12 +364,20 @@ | ||
| 350 | 364 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 351 | 365 | ){ |
| 352 | 366 | int i, size; |
| 353 | 367 | for(i=0; i<2; i++){ |
| 354 | 368 | size = blob_size(&cgiContent[i]); |
| 355 | - if( size>0 ){ | |
| 356 | - fwrite(blob_buffer(&cgiContent[i]), 1, size, g.httpOut); | |
| 369 | + if( size<=rangeStart ){ | |
| 370 | + rangeStart -= size; | |
| 371 | + }else{ | |
| 372 | + int n = size - rangeStart; | |
| 373 | + if( n>total_size ){ | |
| 374 | + n = total_size; | |
| 375 | + } | |
| 376 | + fwrite(blob_buffer(&cgiContent[i])+rangeStart, 1, n, g.httpOut); | |
| 377 | + rangeStart = 0; | |
| 378 | + total_size -= n; | |
| 357 | 379 | } |
| 358 | 380 | } |
| 359 | 381 | } |
| 360 | 382 | fflush(g.httpOut); |
| 361 | 383 | CGIDEBUG(("-------- END cgi ---------\n")); |
| @@ -1658,10 +1680,17 @@ | ||
| 1658 | 1680 | }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ |
| 1659 | 1681 | const char *zIpAddr = cgi_accept_forwarded_for(zVal); |
| 1660 | 1682 | if( zIpAddr!=0 ){ |
| 1661 | 1683 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1662 | 1684 | cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); |
| 1685 | + } | |
| 1686 | + }else if( fossil_strcmp(zFieldName,"range:")==0 ){ | |
| 1687 | + int x1 = 0; | |
| 1688 | + int x2 = 0; | |
| 1689 | + if( sscanf(zVal,"bytes=%d-%d",&x1,&x2)==2 && x1>=0 && x1<=x2 ){ | |
| 1690 | + rangeStart = x1; | |
| 1691 | + rangeEnd = x2+1; | |
| 1663 | 1692 | } |
| 1664 | 1693 | } |
| 1665 | 1694 | } |
| 1666 | 1695 | cgi_init(); |
| 1667 | 1696 | cgi_trace(0); |
| 1668 | 1697 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -178,10 +178,12 @@ | |
| 178 | */ |
| 179 | static const char *zContentType = "text/html"; /* Content type of the reply */ |
| 180 | static const char *zReplyStatus = "OK"; /* Reply status description */ |
| 181 | static int iReplyStatus = 200; /* Reply status code */ |
| 182 | static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */ |
| 183 | |
| 184 | /* |
| 185 | ** Set the reply content type |
| 186 | */ |
| 187 | void cgi_set_content_type(const char *zType){ |
| @@ -272,15 +274,22 @@ | |
| 272 | iReplyStatus = 200; |
| 273 | zReplyStatus = "OK"; |
| 274 | } |
| 275 | |
| 276 | if( g.fullHttpReply ){ |
| 277 | fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); |
| 278 | fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); |
| 279 | fprintf(g.httpOut, "Connection: close\r\n"); |
| 280 | fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); |
| 281 | }else{ |
| 282 | fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 283 | } |
| 284 | if( g.isConst ){ |
| 285 | /* isConst means that the reply is guaranteed to be invariant, even |
| 286 | ** after configuration changes and/or Fossil binary recompiles. */ |
| @@ -325,12 +334,12 @@ | |
| 325 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| 326 | cgi_combine_header_and_body(); |
| 327 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 328 | } |
| 329 | |
| 330 | if( iReplyStatus != 304 ) { |
| 331 | if( is_gzippable() ){ |
| 332 | int i; |
| 333 | gzip_begin(0); |
| 334 | for( i=0; i<2; i++ ){ |
| 335 | int size = blob_size(&cgiContent[i]); |
| 336 | if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); |
| @@ -339,10 +348,15 @@ | |
| 339 | gzip_finish(&cgiContent[0]); |
| 340 | fprintf(g.httpOut, "Content-Encoding: gzip\r\n"); |
| 341 | fprintf(g.httpOut, "Vary: Accept-Encoding\r\n"); |
| 342 | } |
| 343 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 344 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 345 | }else{ |
| 346 | total_size = 0; |
| 347 | } |
| 348 | fprintf(g.httpOut, "\r\n"); |
| @@ -350,12 +364,20 @@ | |
| 350 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 351 | ){ |
| 352 | int i, size; |
| 353 | for(i=0; i<2; i++){ |
| 354 | size = blob_size(&cgiContent[i]); |
| 355 | if( size>0 ){ |
| 356 | fwrite(blob_buffer(&cgiContent[i]), 1, size, g.httpOut); |
| 357 | } |
| 358 | } |
| 359 | } |
| 360 | fflush(g.httpOut); |
| 361 | CGIDEBUG(("-------- END cgi ---------\n")); |
| @@ -1658,10 +1680,17 @@ | |
| 1658 | }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ |
| 1659 | const char *zIpAddr = cgi_accept_forwarded_for(zVal); |
| 1660 | if( zIpAddr!=0 ){ |
| 1661 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1662 | cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); |
| 1663 | } |
| 1664 | } |
| 1665 | } |
| 1666 | cgi_init(); |
| 1667 | cgi_trace(0); |
| 1668 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -178,10 +178,12 @@ | |
| 178 | */ |
| 179 | static const char *zContentType = "text/html"; /* Content type of the reply */ |
| 180 | static const char *zReplyStatus = "OK"; /* Reply status description */ |
| 181 | static int iReplyStatus = 200; /* Reply status code */ |
| 182 | static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */ |
| 183 | static int rangeStart = 0; /* Start of Range: */ |
| 184 | static int rangeEnd = 0; /* End of Range: plus 1 */ |
| 185 | |
| 186 | /* |
| 187 | ** Set the reply content type |
| 188 | */ |
| 189 | void cgi_set_content_type(const char *zType){ |
| @@ -272,15 +274,22 @@ | |
| 274 | iReplyStatus = 200; |
| 275 | zReplyStatus = "OK"; |
| 276 | } |
| 277 | |
| 278 | if( g.fullHttpReply ){ |
| 279 | if( rangeEnd>0 |
| 280 | && iReplyStatus==200 |
| 281 | && fossil_strcmp(P("REQUEST_METHOD"),"GET")==0 |
| 282 | ){ |
| 283 | iReplyStatus = 206; |
| 284 | } |
| 285 | fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); |
| 286 | fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); |
| 287 | fprintf(g.httpOut, "Connection: close\r\n"); |
| 288 | fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); |
| 289 | }else{ |
| 290 | assert( rangeEnd==0 ); |
| 291 | fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 292 | } |
| 293 | if( g.isConst ){ |
| 294 | /* isConst means that the reply is guaranteed to be invariant, even |
| 295 | ** after configuration changes and/or Fossil binary recompiles. */ |
| @@ -325,12 +334,12 @@ | |
| 334 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| 335 | cgi_combine_header_and_body(); |
| 336 | blob_compress(&cgiContent[0], &cgiContent[0]); |
| 337 | } |
| 338 | |
| 339 | if( iReplyStatus!=304 ) { |
| 340 | if( is_gzippable() && iReplyStatus!=206 ){ |
| 341 | int i; |
| 342 | gzip_begin(0); |
| 343 | for( i=0; i<2; i++ ){ |
| 344 | int size = blob_size(&cgiContent[i]); |
| 345 | if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); |
| @@ -339,10 +348,15 @@ | |
| 348 | gzip_finish(&cgiContent[0]); |
| 349 | fprintf(g.httpOut, "Content-Encoding: gzip\r\n"); |
| 350 | fprintf(g.httpOut, "Vary: Accept-Encoding\r\n"); |
| 351 | } |
| 352 | total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); |
| 353 | if( iReplyStatus==206 ){ |
| 354 | fprintf(g.httpOut, "Content-Range: bytes %d-%d/%d\r\n", |
| 355 | rangeStart, rangeEnd-1, total_size); |
| 356 | total_size = rangeEnd - rangeStart; |
| 357 | } |
| 358 | fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); |
| 359 | }else{ |
| 360 | total_size = 0; |
| 361 | } |
| 362 | fprintf(g.httpOut, "\r\n"); |
| @@ -350,12 +364,20 @@ | |
| 364 | && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0 |
| 365 | ){ |
| 366 | int i, size; |
| 367 | for(i=0; i<2; i++){ |
| 368 | size = blob_size(&cgiContent[i]); |
| 369 | if( size<=rangeStart ){ |
| 370 | rangeStart -= size; |
| 371 | }else{ |
| 372 | int n = size - rangeStart; |
| 373 | if( n>total_size ){ |
| 374 | n = total_size; |
| 375 | } |
| 376 | fwrite(blob_buffer(&cgiContent[i])+rangeStart, 1, n, g.httpOut); |
| 377 | rangeStart = 0; |
| 378 | total_size -= n; |
| 379 | } |
| 380 | } |
| 381 | } |
| 382 | fflush(g.httpOut); |
| 383 | CGIDEBUG(("-------- END cgi ---------\n")); |
| @@ -1658,10 +1680,17 @@ | |
| 1680 | }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ |
| 1681 | const char *zIpAddr = cgi_accept_forwarded_for(zVal); |
| 1682 | if( zIpAddr!=0 ){ |
| 1683 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1684 | cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); |
| 1685 | } |
| 1686 | }else if( fossil_strcmp(zFieldName,"range:")==0 ){ |
| 1687 | int x1 = 0; |
| 1688 | int x2 = 0; |
| 1689 | if( sscanf(zVal,"bytes=%d-%d",&x1,&x2)==2 && x1>=0 && x1<=x2 ){ |
| 1690 | rangeStart = x1; |
| 1691 | rangeEnd = x2+1; |
| 1692 | } |
| 1693 | } |
| 1694 | } |
| 1695 | cgi_init(); |
| 1696 | cgi_trace(0); |
| 1697 |