diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2026-01-23 10:14:04 +0100 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2026-01-23 10:14:04 +0100 |
| commit | a5f120b6205c4e22792c818350b33bf57644ed1c (patch) | |
| tree | ed04cab0817886ddb89afa8aebd7a3a3282d0341 | |
| parent | 01ea35e81d7f3dcc9d5032ac53b794a5e9d7cedd (diff) | |
| download | busybox-w32-a5f120b6205c4e22792c818350b33bf57644ed1c.tar.gz busybox-w32-a5f120b6205c4e22792c818350b33bf57644ed1c.tar.bz2 busybox-w32-a5f120b6205c4e22792c818350b33bf57644ed1c.zip | |
httpd: simplify CGI headers handling, check "HTTP/1.1" prefix, not just "HTTP"
function old new delta
cgi_io_loop_and_exit 477 498 +21
.rodata 106830 106821 -9
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 21/-9) Total: 12 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
| -rw-r--r-- | include/platform.h | 2 | ||||
| -rw-r--r-- | networking/httpd.c | 58 |
2 files changed, 32 insertions, 28 deletions
diff --git a/include/platform.h b/include/platform.h index a5b61757f..c449075b9 100644 --- a/include/platform.h +++ b/include/platform.h | |||
| @@ -211,6 +211,7 @@ | |||
| 211 | # define IF_LITTLE_ENDIAN(...) | 211 | # define IF_LITTLE_ENDIAN(...) |
| 212 | /* How do bytes a,b,c,d (sequential in memory) look if fetched into uint32_t? */ | 212 | /* How do bytes a,b,c,d (sequential in memory) look if fetched into uint32_t? */ |
| 213 | # define PACK32_BYTES(a,b,c,d) (uint32_t)((d)+((c)<<8)+((b)<<16)+((a)<<24)) | 213 | # define PACK32_BYTES(a,b,c,d) (uint32_t)((d)+((c)<<8)+((b)<<16)+((a)<<24)) |
| 214 | # define PACK64_LITERAL_STR(s) (((uint64_t)PACK32_BYTES((s)[0],(s)[1],(s)[2],(s)[3])<<32) + PACK32_BYTES((s)[4],(s)[5],(s)[6],(s)[7])) | ||
| 214 | #else | 215 | #else |
| 215 | # define SWAP_BE16(x) bswap_16(x) | 216 | # define SWAP_BE16(x) bswap_16(x) |
| 216 | # define SWAP_BE32(x) bswap_32(x) | 217 | # define SWAP_BE32(x) bswap_32(x) |
| @@ -221,6 +222,7 @@ | |||
| 221 | # define IF_BIG_ENDIAN(...) | 222 | # define IF_BIG_ENDIAN(...) |
| 222 | # define IF_LITTLE_ENDIAN(...) __VA_ARGS__ | 223 | # define IF_LITTLE_ENDIAN(...) __VA_ARGS__ |
| 223 | # define PACK32_BYTES(a,b,c,d) (uint32_t)((a)+((b)<<8)+((c)<<16)+((d)<<24)) | 224 | # define PACK32_BYTES(a,b,c,d) (uint32_t)((a)+((b)<<8)+((c)<<16)+((d)<<24)) |
| 225 | # define PACK64_LITERAL_STR(s) (((uint64_t)PACK32_BYTES((s)[4],(s)[5],(s)[6],(s)[7])<<32) + PACK32_BYTES((s)[0],(s)[1],(s)[2],(s)[3])) | ||
| 224 | #endif | 226 | #endif |
| 225 | 227 | ||
| 226 | 228 | ||
diff --git a/networking/httpd.c b/networking/httpd.c index 5101f86f7..e98da9c57 100644 --- a/networking/httpd.c +++ b/networking/httpd.c | |||
| @@ -537,7 +537,7 @@ struct globals { | |||
| 537 | #if ENABLE_FEATURE_HTTPD_PROXY | 537 | #if ENABLE_FEATURE_HTTPD_PROXY |
| 538 | Htaccess_Proxy *proxy; | 538 | Htaccess_Proxy *proxy; |
| 539 | #endif | 539 | #endif |
| 540 | char iobuf[IOBUF_SIZE]; | 540 | char iobuf[IOBUF_SIZE] ALIGN8; |
| 541 | }; | 541 | }; |
| 542 | #define G (*ptr_to_globals) | 542 | #define G (*ptr_to_globals) |
| 543 | #define verbose (G.verbose ) | 543 | #define verbose (G.verbose ) |
| @@ -1425,7 +1425,7 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post | |||
| 1425 | /*pfd[FROM_CGI].events = POLLIN; - moved out of loop */ | 1425 | /*pfd[FROM_CGI].events = POLLIN; - moved out of loop */ |
| 1426 | /*pfd[FROM_CGI].revents = 0; - not needed */ | 1426 | /*pfd[FROM_CGI].revents = 0; - not needed */ |
| 1427 | 1427 | ||
| 1428 | /* gcc-4.8.0 still doesnt fill two shorts with one insn :( */ | 1428 | /* gcc-4.8.0 still doesn't fill two shorts with one insn :( */ |
| 1429 | /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47059 */ | 1429 | /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47059 */ |
| 1430 | /* hopefully one day it will... */ | 1430 | /* hopefully one day it will... */ |
| 1431 | pfd[TO_CGI].events = POLLOUT; | 1431 | pfd[TO_CGI].events = POLLOUT; |
| @@ -1507,8 +1507,6 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post | |||
| 1507 | 1507 | ||
| 1508 | if (pfd[FROM_CGI].revents) { | 1508 | if (pfd[FROM_CGI].revents) { |
| 1509 | /* There is something to read from CGI */ | 1509 | /* There is something to read from CGI */ |
| 1510 | char *rbuf = iobuf; | ||
| 1511 | |||
| 1512 | /* Still buffering CGI output? */ | 1510 | /* Still buffering CGI output? */ |
| 1513 | if (out_cnt >= 0) { | 1511 | if (out_cnt >= 0) { |
| 1514 | /* HTTP_200[] has single "\r\n" at the end. | 1512 | /* HTTP_200[] has single "\r\n" at the end. |
| @@ -1523,34 +1521,34 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post | |||
| 1523 | * CGI may output a few first bytes and then wait | 1521 | * CGI may output a few first bytes and then wait |
| 1524 | * for POSTDATA without closing stdout. | 1522 | * for POSTDATA without closing stdout. |
| 1525 | * With full_read we may wait here forever. */ | 1523 | * With full_read we may wait here forever. */ |
| 1526 | count = safe_read(fromCgi_rd, rbuf + out_cnt, IOBUF_SIZE); | 1524 | count = safe_read(fromCgi_rd, iobuf + out_cnt, IOBUF_SIZE); |
| 1527 | if (count <= 0) { | 1525 | if (count <= 0) { |
| 1528 | /* eof (or error) and there was no "HTTP" (tiny 0..3 bytes output), | 1526 | /* EOF (or error, and out_cnt=0..7 |
| 1529 | * send "HTTP/1.1 200 OK\r\n", then send received 0..3 bytes */ | 1527 | * send "HTTP/1.1 200 OK\r\n", then send received 0..7 bytes */ |
| 1530 | goto write_HTTP_200_OK; | 1528 | goto write_HTTP_200_OK; |
| 1531 | /* we'll read once more later, in "no longer buffering" | 1529 | /* we'll read once more later, in "no longer buffering" |
| 1532 | * code path, get another EOF there and exit */ | 1530 | * code path, get another EOF there and exit */ |
| 1533 | } | 1531 | } |
| 1534 | out_cnt += count; | 1532 | out_cnt += count; |
| 1535 | count = 0; | 1533 | count = 0; |
| 1536 | /* "Status" header format is: "Status: 302 Redirected\r\n" */ | 1534 | if (out_cnt >= 8) { |
| 1537 | if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) { | ||
| 1538 | //FIXME: "Status: " is not required to be the first header! It can be anywhere! | 1535 | //FIXME: "Status: " is not required to be the first header! It can be anywhere! |
| 1539 | //FIXME: many servers also check "Location: ". If it exists but "Status: " does _not_, "302 Found" is assumed instead of "200 OK". | 1536 | //FIXME: many servers also check "Location: ". If it exists but "Status: " does _not_, "302 Found" is assumed instead of "200 OK". |
| 1540 | /* send "HTTP/1.1 " */ | 1537 | /* "Status" header format is: "Status: 302 Redirected\r\n" */ |
| 1541 | if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9) | 1538 | //if (memcmp(iobuf, "Status: ", 8) == 0) |
| 1542 | break; | 1539 | if (*(uint64_t*)iobuf == PACK64_LITERAL_STR("Status: ")) { |
| 1543 | /* skip "Status: " (including space, sending "HTTP/1.1 NNN" is wrong) */ | 1540 | /* send "HTTP/1.1 " */ |
| 1544 | rbuf += 8; | 1541 | if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9) |
| 1545 | count = out_cnt - 8; | 1542 | break; |
| 1546 | out_cnt = -1; /* buffering off */ | 1543 | /* skip "Status: " (including space, sending "HTTP/1.1 NNN" is wrong) */ |
| 1547 | } else if (out_cnt >= 4) { | 1544 | out_cnt -= 8; |
| 1548 | //NB: Apache has no such autodetection. It always adds its own HTTP/ header, | 1545 | memmove(iobuf, iobuf + 8, out_cnt); |
| 1546 | } | ||
| 1547 | //NB: Apache has no such autodetection. It always adds its own HTTP/1.x header, | ||
| 1549 | //unless the CGI name starts with "nph-", in which case it passes its output verbatim to network. | 1548 | //unless the CGI name starts with "nph-", in which case it passes its output verbatim to network. |
| 1550 | /* Did CGI add "HTTP"? */ | 1549 | else if (memcmp(iobuf, HTTP_200, 8) != 0) { /* Did CGI send "HTTP/1.1"? */ |
| 1551 | if (memcmp(rbuf, HTTP_200, 4) != 0) { | ||
| 1552 | write_HTTP_200_OK: | 1550 | write_HTTP_200_OK: |
| 1553 | /* there is no "HTTP", send "HTTP/1.1 200 OK\r\n" ourself */ | 1551 | /* no, send "HTTP/1.1 200 OK\r\n" ourself */ |
| 1554 | if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1) | 1552 | if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1) |
| 1555 | break; | 1553 | break; |
| 1556 | } | 1554 | } |
| @@ -1562,17 +1560,16 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post | |||
| 1562 | */ | 1560 | */ |
| 1563 | count = out_cnt; | 1561 | count = out_cnt; |
| 1564 | out_cnt = -1; /* buffering off */ | 1562 | out_cnt = -1; /* buffering off */ |
| 1565 | /* NB: we mishandle CGI writing "Stat","us..." in two separate writes */ | ||
| 1566 | } | 1563 | } |
| 1567 | } else { | 1564 | } else { |
| 1568 | count = safe_read(fromCgi_rd, rbuf, IOBUF_SIZE); | 1565 | count = safe_read(fromCgi_rd, iobuf, IOBUF_SIZE); |
| 1569 | if (count <= 0) | 1566 | if (count <= 0) |
| 1570 | send_EOF_and_exit(); /* eof (or error) */ | 1567 | send_EOF_and_exit(); /* EOF (or error) */ |
| 1571 | } | 1568 | } |
| 1572 | //FIXME: many (most?) servers translate bare "\n" to "\r\n", often only in the headers, not body (the part after empty line) | 1569 | //FIXME: many (most?) servers translate bare "\n" to "\r\n", only in the headers, not body (the part after empty line) |
| 1573 | if (full_write(STDOUT_FILENO, rbuf, count) != count) | 1570 | if (full_write(STDOUT_FILENO, iobuf, count) != count) |
| 1574 | break; | 1571 | break; |
| 1575 | dbg("cgi read %d bytes: '%.*s'\n", count, count, rbuf); | 1572 | dbg("cgi read %d bytes: '%.*s'\n", count, count, iobuf); |
| 1576 | } /* if (pfd[FROM_CGI].revents) */ | 1573 | } /* if (pfd[FROM_CGI].revents) */ |
| 1577 | } /* while (1) */ | 1574 | } /* while (1) */ |
| 1578 | log_and_exit(); | 1575 | log_and_exit(); |
| @@ -2948,7 +2945,12 @@ int httpd_main(int argc UNUSED_PARAM, char **argv) | |||
| 2948 | IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;) | 2945 | IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;) |
| 2949 | IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;) | 2946 | IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;) |
| 2950 | IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;) | 2947 | IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;) |
| 2951 | 2948 | #if 0 // PACK64_LITERAL_STR test | |
| 2949 | char testing[16] = "Status: "; | ||
| 2950 | printf("0x%08llx\n", PACK64_LITERAL_STR("Status: ")); | ||
| 2951 | printf("0x%08llx\n", *(uint64_t*)testing); | ||
| 2952 | exit(1); | ||
| 2953 | #endif | ||
| 2952 | INIT_G(); | 2954 | INIT_G(); |
| 2953 | 2955 | ||
| 2954 | #if ENABLE_LOCALE_SUPPORT | 2956 | #if ENABLE_LOCALE_SUPPORT |
