aboutsummaryrefslogtreecommitdiff
path: root/networking/httpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'networking/httpd.c')
-rw-r--r--networking/httpd.c57
1 files changed, 54 insertions, 3 deletions
diff --git a/networking/httpd.c b/networking/httpd.c
index 94f7297ad..0297cf9ec 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -215,6 +215,19 @@
215//config: Makes httpd send files using GZIP content encoding if the 215//config: Makes httpd send files using GZIP content encoding if the
216//config: client supports it and a pre-compressed <file>.gz exists. 216//config: client supports it and a pre-compressed <file>.gz exists.
217//config: 217//config:
218//config:config FEATURE_HTTPD_ETAG
219//config: bool "Support caching via ETag header"
220//config: default y
221//config: depends on HTTPD
222//config: help
223//config: If server responds with ETag then next time client (browser)
224//config: resend it via If-None-Match header.
225//config: Then httpd will check if file wasn't modified and if not,
226//config: return 304 Not Modified status code.
227//config: The ETag value is constructed from last modification date
228//config: in unix epoch, and size: "hex(last_mod)-hex(file_size)".
229//config: It's not completely reliable as hash functions but fair enough.
230//config:
218//config:config FEATURE_HTTPD_LAST_MODIFIED 231//config:config FEATURE_HTTPD_LAST_MODIFIED
219//config: bool "Add Last-Modified header to response" 232//config: bool "Add Last-Modified header to response"
220//config: default y 233//config: default y
@@ -328,6 +341,7 @@ enum {
328 HTTP_OK = 200, 341 HTTP_OK = 200,
329 HTTP_PARTIAL_CONTENT = 206, 342 HTTP_PARTIAL_CONTENT = 206,
330 HTTP_MOVED_TEMPORARILY = 302, 343 HTTP_MOVED_TEMPORARILY = 302,
344 HTTP_NOT_MODIFIED = 304,
331 HTTP_BAD_REQUEST = 400, /* malformed syntax */ 345 HTTP_BAD_REQUEST = 400, /* malformed syntax */
332 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ 346 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
333 HTTP_NOT_FOUND = 404, 347 HTTP_NOT_FOUND = 404,
@@ -345,7 +359,6 @@ enum {
345 HTTP_NO_CONTENT = 204, 359 HTTP_NO_CONTENT = 204,
346 HTTP_MULTIPLE_CHOICES = 300, 360 HTTP_MULTIPLE_CHOICES = 300,
347 HTTP_MOVED_PERMANENTLY = 301, 361 HTTP_MOVED_PERMANENTLY = 301,
348 HTTP_NOT_MODIFIED = 304,
349 HTTP_PAYMENT_REQUIRED = 402, 362 HTTP_PAYMENT_REQUIRED = 402,
350 HTTP_BAD_GATEWAY = 502, 363 HTTP_BAD_GATEWAY = 502,
351 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ 364 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
@@ -358,6 +371,9 @@ static const uint16_t http_response_type[] ALIGN2 = {
358 HTTP_PARTIAL_CONTENT, 371 HTTP_PARTIAL_CONTENT,
359#endif 372#endif
360 HTTP_MOVED_TEMPORARILY, 373 HTTP_MOVED_TEMPORARILY,
374#if ENABLE_FEATURE_HTTPD_ETAG
375 HTTP_NOT_MODIFIED,
376#endif
361 HTTP_REQUEST_TIMEOUT, 377 HTTP_REQUEST_TIMEOUT,
362 HTTP_NOT_IMPLEMENTED, 378 HTTP_NOT_IMPLEMENTED,
363#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 379#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
@@ -374,7 +390,6 @@ static const uint16_t http_response_type[] ALIGN2 = {
374 HTTP_NO_CONTENT, 390 HTTP_NO_CONTENT,
375 HTTP_MULTIPLE_CHOICES, 391 HTTP_MULTIPLE_CHOICES,
376 HTTP_MOVED_PERMANENTLY, 392 HTTP_MOVED_PERMANENTLY,
377 HTTP_NOT_MODIFIED,
378 HTTP_BAD_GATEWAY, 393 HTTP_BAD_GATEWAY,
379 HTTP_SERVICE_UNAVAILABLE, 394 HTTP_SERVICE_UNAVAILABLE,
380#endif 395#endif
@@ -389,6 +404,9 @@ static const struct {
389 { "Partial Content", NULL }, 404 { "Partial Content", NULL },
390#endif 405#endif
391 { "Found", NULL }, 406 { "Found", NULL },
407#if ENABLE_FEATURE_HTTPD_ETAG
408 { "Not Modified" },
409#endif
392 { "Request Timeout", "No request appeared within 60 seconds" }, 410 { "Request Timeout", "No request appeared within 60 seconds" },
393 { "Not Implemented", "The requested method is not recognized" }, 411 { "Not Implemented", "The requested method is not recognized" },
394#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 412#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
@@ -405,7 +423,6 @@ static const struct {
405 { "No Content" }, 423 { "No Content" },
406 { "Multiple Choices" }, 424 { "Multiple Choices" },
407 { "Moved Permanently" }, 425 { "Moved Permanently" },
408 { "Not Modified" },
409 { "Bad Gateway", "" }, 426 { "Bad Gateway", "" },
410 { "Service Unavailable", "" }, 427 { "Service Unavailable", "" },
411#endif 428#endif
@@ -419,6 +436,9 @@ struct globals {
419 smallint content_gzip; 436 smallint content_gzip;
420#endif 437#endif
421 time_t last_mod; 438 time_t last_mod;
439#if ENABLE_FEATURE_HTTPD_ETAG
440 char *if_none_match;
441#endif
422 char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */ 442 char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */
423 const char *bind_addr_or_port; 443 const char *bind_addr_or_port;
424 444
@@ -453,6 +473,9 @@ struct globals {
453#define sizeof_hdr_buf COMMON_BUFSIZE 473#define sizeof_hdr_buf COMMON_BUFSIZE
454 char *hdr_ptr; 474 char *hdr_ptr;
455 int hdr_cnt; 475 int hdr_cnt;
476#if ENABLE_FEATURE_HTTPD_ETAG
477 char etag[sizeof("'%llx-%llx'") + 2 * sizeof(long long)*3];
478#endif
456#if ENABLE_FEATURE_HTTPD_ERROR_PAGES 479#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
457 const char *http_error_page[ARRAY_SIZE(http_response_type)]; 480 const char *http_error_page[ARRAY_SIZE(http_response_type)];
458#endif 481#endif
@@ -1208,6 +1231,10 @@ static void send_headers(unsigned responseNum)
1208#if ENABLE_FEATURE_HTTPD_LAST_MODIFIED 1231#if ENABLE_FEATURE_HTTPD_LAST_MODIFIED
1209 "Last-Modified: %s\r\n" 1232 "Last-Modified: %s\r\n"
1210#endif 1233#endif
1234#if ENABLE_FEATURE_HTTPD_ETAG
1235 "ETag: %s\r\n"
1236#endif
1237
1211 /* Because of 4.4 (5), we can forgo sending of "Content-Length" 1238 /* Because of 4.4 (5), we can forgo sending of "Content-Length"
1212 * since we close connection afterwards, but it helps clients 1239 * since we close connection afterwards, but it helps clients
1213 * to e.g. estimate download times, show progress bars etc. 1240 * to e.g. estimate download times, show progress bars etc.
@@ -1218,6 +1245,9 @@ static void send_headers(unsigned responseNum)
1218#if ENABLE_FEATURE_HTTPD_LAST_MODIFIED 1245#if ENABLE_FEATURE_HTTPD_LAST_MODIFIED
1219 date_str, 1246 date_str,
1220#endif 1247#endif
1248#if ENABLE_FEATURE_HTTPD_ETAG
1249 G.etag,
1250#endif
1221 file_size 1251 file_size
1222 ); 1252 );
1223 } 1253 }
@@ -1730,6 +1760,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1730 } 1760 }
1731 } else { 1761 } else {
1732 fd = open(url, O_RDONLY); 1762 fd = open(url, O_RDONLY);
1763 /* file_size and last_mod are already populated */
1733 } 1764 }
1734 if (fd < 0) { 1765 if (fd < 0) {
1735 if (DEBUG) 1766 if (DEBUG)
@@ -1741,6 +1772,19 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1741 send_headers_and_exit(HTTP_NOT_FOUND); 1772 send_headers_and_exit(HTTP_NOT_FOUND);
1742 log_and_exit(); 1773 log_and_exit();
1743 } 1774 }
1775#if ENABLE_FEATURE_HTTPD_ETAG
1776 /* ETag is "hex(last_mod)-hex(file_size)" e.g. "5e132e20-417" */
1777 sprintf(G.etag, "\"%llx-%llx\"", (unsigned long long)last_mod, (unsigned long long)file_size);
1778
1779 if (G.if_none_match) {
1780 if (DEBUG)
1781 bb_perror_msg("If-None-Match and file's ETag are: '%s' '%s'\n", G.if_none_match, G.etag);
1782 /* Weak ETag comparision.
1783 * If-None-Match may have many ETags but they are quoted so we can use simple substring search */
1784 if (strstr(G.if_none_match, G.etag))
1785 send_headers_and_exit(HTTP_NOT_MODIFIED);
1786 }
1787#endif
1744 /* If you want to know about EPIPE below 1788 /* If you want to know about EPIPE below
1745 * (happens if you abort downloads from local httpd): */ 1789 * (happens if you abort downloads from local httpd): */
1746 signal(SIGPIPE, SIG_IGN); 1790 signal(SIGPIPE, SIG_IGN);
@@ -2480,6 +2524,13 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2480 continue; 2524 continue;
2481 } 2525 }
2482#endif 2526#endif
2527#if ENABLE_FEATURE_HTTPD_ETAG
2528 if (STRNCASECMP(iobuf, "If-None-Match:") == 0) {
2529 free(G.if_none_match);
2530 G.if_none_match = xstrdup(skip_whitespace(iobuf + sizeof("If-None-Match:") - 1));
2531 continue;
2532 }
2533#endif
2483#if ENABLE_FEATURE_HTTPD_CGI 2534#if ENABLE_FEATURE_HTTPD_CGI
2484 if (cgi_type != CGI_NONE) { 2535 if (cgi_type != CGI_NONE) {
2485 bool ct = (STRNCASECMP(iobuf, "Content-Type:") == 0); 2536 bool ct = (STRNCASECMP(iobuf, "Content-Type:") == 0);