diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-09-21 22:35:18 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-09-21 22:35:18 +0000 |
commit | f43101753558a772b8317409cf542b3e3631cd13 (patch) | |
tree | 365763acc3ee1b21fcc13656fce6f069a7623e61 | |
parent | bcceb0c5a4a6b69225ae9b748df96e013996d9d3 (diff) | |
download | busybox-w32-f43101753558a772b8317409cf542b3e3631cd13.tar.gz busybox-w32-f43101753558a772b8317409cf542b3e3631cd13.tar.bz2 busybox-w32-f43101753558a772b8317409cf542b3e3631cd13.zip |
httpd: optional support for partial downloads
-rw-r--r-- | networking/Config.in | 9 | ||||
-rw-r--r-- | networking/httpd.c | 119 | ||||
-rw-r--r-- | shell/ash.c | 17 |
3 files changed, 128 insertions, 17 deletions
diff --git a/networking/Config.in b/networking/Config.in index d687dbed9..030b1c0de 100644 --- a/networking/Config.in +++ b/networking/Config.in | |||
@@ -83,6 +83,15 @@ config HTTPD | |||
83 | help | 83 | help |
84 | Serve web pages via an HTTP server. | 84 | Serve web pages via an HTTP server. |
85 | 85 | ||
86 | config FEATURE_HTTPD_RANGES | ||
87 | bool "Support 'Ranges:' header" | ||
88 | default n | ||
89 | depends on HTTPD | ||
90 | help | ||
91 | Makes httpd emit "Accept-Ranges: bytes" header and understand | ||
92 | "Range: bytes=NNN-[MMM]" header. Allows for resuming interrupted | ||
93 | downloads, seeking in multimedia players etc. | ||
94 | |||
86 | config FEATURE_HTTPD_USE_SENDFILE | 95 | config FEATURE_HTTPD_USE_SENDFILE |
87 | bool "Use sendfile system call" | 96 | bool "Use sendfile system call" |
88 | default n | 97 | default n |
diff --git a/networking/httpd.c b/networking/httpd.c index a8e5df98c..057416040 100644 --- a/networking/httpd.c +++ b/networking/httpd.c | |||
@@ -138,6 +138,7 @@ typedef struct Htaccess_IP { | |||
138 | 138 | ||
139 | enum { | 139 | enum { |
140 | HTTP_OK = 200, | 140 | HTTP_OK = 200, |
141 | HTTP_PARTIAL_CONTENT = 206, | ||
141 | HTTP_MOVED_TEMPORARILY = 302, | 142 | HTTP_MOVED_TEMPORARILY = 302, |
142 | HTTP_BAD_REQUEST = 400, /* malformed syntax */ | 143 | HTTP_BAD_REQUEST = 400, /* malformed syntax */ |
143 | HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ | 144 | HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ |
@@ -165,6 +166,9 @@ enum { | |||
165 | 166 | ||
166 | static const uint16_t http_response_type[] ALIGN2 = { | 167 | static const uint16_t http_response_type[] ALIGN2 = { |
167 | HTTP_OK, | 168 | HTTP_OK, |
169 | #if ENABLE_FEATURE_HTTPD_RANGES | ||
170 | HTTP_PARTIAL_CONTENT, | ||
171 | #endif | ||
168 | HTTP_MOVED_TEMPORARILY, | 172 | HTTP_MOVED_TEMPORARILY, |
169 | HTTP_REQUEST_TIMEOUT, | 173 | HTTP_REQUEST_TIMEOUT, |
170 | HTTP_NOT_IMPLEMENTED, | 174 | HTTP_NOT_IMPLEMENTED, |
@@ -192,7 +196,10 @@ static const struct { | |||
192 | const char *info; | 196 | const char *info; |
193 | } http_response[ARRAY_SIZE(http_response_type)] = { | 197 | } http_response[ARRAY_SIZE(http_response_type)] = { |
194 | { "OK", NULL }, | 198 | { "OK", NULL }, |
195 | { "Found", "Directories must end with a slash" }, /* ?? */ | 199 | #if ENABLE_FEATURE_HTTPD_RANGES |
200 | { "Partial Content", NULL }, | ||
201 | #endif | ||
202 | { "Found", NULL }, | ||
196 | { "Request Timeout", "No request appeared within 60 seconds" }, | 203 | { "Request Timeout", "No request appeared within 60 seconds" }, |
197 | { "Not Implemented", "The requested method is not recognized" }, | 204 | { "Not Implemented", "The requested method is not recognized" }, |
198 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | 205 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
@@ -214,13 +221,13 @@ static const struct { | |||
214 | #endif | 221 | #endif |
215 | }; | 222 | }; |
216 | 223 | ||
224 | |||
217 | struct globals { | 225 | struct globals { |
218 | int verbose; /* must be int (used by getopt32) */ | 226 | int verbose; /* must be int (used by getopt32) */ |
219 | smallint flg_deny_all; | 227 | smallint flg_deny_all; |
220 | 228 | ||
221 | unsigned rmt_ip; /* used for IP-based allow/deny rules */ | 229 | unsigned rmt_ip; /* used for IP-based allow/deny rules */ |
222 | time_t last_mod; | 230 | time_t last_mod; |
223 | off_t ContentLength; /* -1 - unknown */ | ||
224 | char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */ | 231 | char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */ |
225 | const char *bind_addr_or_port; | 232 | const char *bind_addr_or_port; |
226 | 233 | ||
@@ -237,6 +244,13 @@ struct globals { | |||
237 | USE_FEATURE_HTTPD_CGI(char *referer;) | 244 | USE_FEATURE_HTTPD_CGI(char *referer;) |
238 | USE_FEATURE_HTTPD_CGI(char *user_agent;) | 245 | USE_FEATURE_HTTPD_CGI(char *user_agent;) |
239 | 246 | ||
247 | off_t file_size; /* -1 - unknown */ | ||
248 | #if ENABLE_FEATURE_HTTPD_RANGES | ||
249 | off_t range_start; | ||
250 | off_t range_end; | ||
251 | off_t range_len; | ||
252 | #endif | ||
253 | |||
240 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | 254 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
241 | Htaccess *g_auth; /* config user:password lines */ | 255 | Htaccess *g_auth; /* config user:password lines */ |
242 | #endif | 256 | #endif |
@@ -264,13 +278,18 @@ struct globals { | |||
264 | #define home_httpd (G.home_httpd ) | 278 | #define home_httpd (G.home_httpd ) |
265 | #define found_mime_type (G.found_mime_type ) | 279 | #define found_mime_type (G.found_mime_type ) |
266 | #define found_moved_temporarily (G.found_moved_temporarily) | 280 | #define found_moved_temporarily (G.found_moved_temporarily) |
267 | #define ContentLength (G.ContentLength ) | ||
268 | #define last_mod (G.last_mod ) | 281 | #define last_mod (G.last_mod ) |
269 | #define ip_a_d (G.ip_a_d ) | 282 | #define ip_a_d (G.ip_a_d ) |
270 | #define g_realm (G.g_realm ) | 283 | #define g_realm (G.g_realm ) |
271 | #define remoteuser (G.remoteuser ) | 284 | #define remoteuser (G.remoteuser ) |
272 | #define referer (G.referer ) | 285 | #define referer (G.referer ) |
273 | #define user_agent (G.user_agent ) | 286 | #define user_agent (G.user_agent ) |
287 | #define file_size (G.file_size ) | ||
288 | #if ENABLE_FEATURE_HTTPD_RANGES | ||
289 | #define range_start (G.range_start ) | ||
290 | #define range_end (G.range_end ) | ||
291 | #define range_len (G.range_len ) | ||
292 | #endif | ||
274 | #define rmt_ip_str (G.rmt_ip_str ) | 293 | #define rmt_ip_str (G.rmt_ip_str ) |
275 | #define g_auth (G.g_auth ) | 294 | #define g_auth (G.g_auth ) |
276 | #define mime_a (G.mime_a ) | 295 | #define mime_a (G.mime_a ) |
@@ -283,9 +302,17 @@ struct globals { | |||
283 | PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ | 302 | PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ |
284 | USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ | 303 | USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ |
285 | bind_addr_or_port = "80"; \ | 304 | bind_addr_or_port = "80"; \ |
286 | ContentLength = -1; \ | 305 | file_size = -1; \ |
287 | } while (0) | 306 | } while (0) |
288 | 307 | ||
308 | #if !ENABLE_FEATURE_HTTPD_RANGES | ||
309 | enum { | ||
310 | range_start = 0, | ||
311 | range_end = MAXINT(off_t) - 1, | ||
312 | range_len = MAXINT(off_t), | ||
313 | }; | ||
314 | #endif | ||
315 | |||
289 | 316 | ||
290 | #define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1) | 317 | #define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1) |
291 | 318 | ||
@@ -921,10 +948,26 @@ static void send_headers(int responseNum) | |||
921 | } | 948 | } |
922 | #endif | 949 | #endif |
923 | 950 | ||
924 | if (ContentLength != -1) { /* file */ | 951 | if (file_size != -1) { /* file */ |
925 | strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod)); | 952 | strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod)); |
926 | len += sprintf(iobuf + len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n", | 953 | #if ENABLE_FEATURE_HTTPD_RANGES |
927 | tmp_str, "Content-length:", ContentLength); | 954 | if (responseNum == HTTP_PARTIAL_CONTENT) { |
955 | len += sprintf(iobuf + len, "Content-Range: bytes %"OFF_FMT"d-%"OFF_FMT"d/%"OFF_FMT"d\r\n", | ||
956 | range_start, | ||
957 | range_end, | ||
958 | file_size); | ||
959 | file_size = range_end - range_start + 1; | ||
960 | } | ||
961 | #endif | ||
962 | len += sprintf(iobuf + len, | ||
963 | #if ENABLE_FEATURE_HTTPD_RANGES | ||
964 | "Accept-Ranges: bytes\r\n" | ||
965 | #endif | ||
966 | "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n", | ||
967 | tmp_str, | ||
968 | "Content-length:", | ||
969 | file_size | ||
970 | ); | ||
928 | } | 971 | } |
929 | iobuf[len++] = '\r'; | 972 | iobuf[len++] = '\r'; |
930 | iobuf[len++] = '\n'; | 973 | iobuf[len++] = '\n'; |
@@ -1373,7 +1416,7 @@ static void send_file_and_exit(const char *url, int headers) | |||
1373 | const char *try_suffix; | 1416 | const char *try_suffix; |
1374 | ssize_t count; | 1417 | ssize_t count; |
1375 | #if ENABLE_FEATURE_HTTPD_USE_SENDFILE | 1418 | #if ENABLE_FEATURE_HTTPD_USE_SENDFILE |
1376 | off_t offset = 0; | 1419 | off_t offset; |
1377 | #endif | 1420 | #endif |
1378 | 1421 | ||
1379 | suffix = strrchr(url, '.'); | 1422 | suffix = strrchr(url, '.'); |
@@ -1415,6 +1458,26 @@ static void send_file_and_exit(const char *url, int headers) | |||
1415 | if (headers) | 1458 | if (headers) |
1416 | send_headers_and_exit(HTTP_NOT_FOUND); | 1459 | send_headers_and_exit(HTTP_NOT_FOUND); |
1417 | } | 1460 | } |
1461 | #if ENABLE_FEATURE_HTTPD_RANGES | ||
1462 | if (!headers) | ||
1463 | range_start = 0; /* err pages and ranges don't mix */ | ||
1464 | range_len = MAXINT(off_t); | ||
1465 | if (range_start) { | ||
1466 | if (!range_end) { | ||
1467 | range_end = file_size - 1; | ||
1468 | } | ||
1469 | if (range_end < range_start | ||
1470 | || lseek(f, range_start, SEEK_SET) != range_start | ||
1471 | ) { | ||
1472 | lseek(f, 0, SEEK_SET); | ||
1473 | range_start = 0; | ||
1474 | } else { | ||
1475 | range_len = range_end - range_start + 1; | ||
1476 | send_headers(HTTP_PARTIAL_CONTENT); | ||
1477 | headers = 0; | ||
1478 | } | ||
1479 | } | ||
1480 | #endif | ||
1418 | 1481 | ||
1419 | if (headers) | 1482 | if (headers) |
1420 | send_headers(HTTP_OK); | 1483 | send_headers(HTTP_OK); |
@@ -1424,24 +1487,32 @@ static void send_file_and_exit(const char *url, int headers) | |||
1424 | signal(SIGPIPE, SIG_IGN); | 1487 | signal(SIGPIPE, SIG_IGN); |
1425 | 1488 | ||
1426 | #if ENABLE_FEATURE_HTTPD_USE_SENDFILE | 1489 | #if ENABLE_FEATURE_HTTPD_USE_SENDFILE |
1490 | offset = range_start; | ||
1427 | do { | 1491 | do { |
1428 | /* byte count (3rd arg) is rounded down to 64k */ | 1492 | /* sz is rounded down to 64k */ |
1429 | count = sendfile(1, f, &offset, MAXINT(ssize_t) - 0xffff); | 1493 | ssize_t sz = MAXINT(ssize_t) - 0xffff; |
1494 | USE_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;) | ||
1495 | count = sendfile(1, f, &offset, sz); | ||
1430 | if (count < 0) { | 1496 | if (count < 0) { |
1431 | if (offset == 0) | 1497 | if (offset == range_start) |
1432 | goto fallback; | 1498 | goto fallback; |
1433 | goto fin; | 1499 | goto fin; |
1434 | } | 1500 | } |
1435 | } while (count > 0); | 1501 | USE_FEATURE_HTTPD_RANGES(range_len -= sz;) |
1502 | } while (count > 0 && range_len); | ||
1436 | log_and_exit(); | 1503 | log_and_exit(); |
1437 | 1504 | ||
1438 | fallback: | 1505 | fallback: |
1439 | #endif | 1506 | #endif |
1440 | while ((count = safe_read(f, iobuf, IOBUF_SIZE)) > 0) { | 1507 | while ((count = safe_read(f, iobuf, IOBUF_SIZE)) > 0) { |
1441 | ssize_t n = count; | 1508 | ssize_t n; |
1442 | count = full_write(1, iobuf, count); | 1509 | USE_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;) |
1510 | n = full_write(1, iobuf, count); | ||
1443 | if (count != n) | 1511 | if (count != n) |
1444 | break; | 1512 | break; |
1513 | USE_FEATURE_HTTPD_RANGES(range_len -= count;) | ||
1514 | if (!range_len) | ||
1515 | break; | ||
1445 | } | 1516 | } |
1446 | #if ENABLE_FEATURE_HTTPD_USE_SENDFILE | 1517 | #if ENABLE_FEATURE_HTTPD_USE_SENDFILE |
1447 | fin: | 1518 | fin: |
@@ -1781,6 +1852,23 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) | |||
1781 | credentials = checkPerm(urlcopy, tptr); | 1852 | credentials = checkPerm(urlcopy, tptr); |
1782 | } | 1853 | } |
1783 | #endif /* FEATURE_HTTPD_BASIC_AUTH */ | 1854 | #endif /* FEATURE_HTTPD_BASIC_AUTH */ |
1855 | #if ENABLE_FEATURE_HTTPD_RANGES | ||
1856 | if (STRNCASECMP(iobuf, "Range:") == 0) { | ||
1857 | // We know only bytes=NNN-[MMM] | ||
1858 | char *s = skip_whitespace(iobuf + sizeof("Range:")-1); | ||
1859 | if (strncmp(s, "bytes=", 6) == 0) { | ||
1860 | s += sizeof("bytes=")-1; | ||
1861 | range_start = BB_STRTOOFF(s, &s, 10); | ||
1862 | if (s[0] != '-' || range_start < 0) { | ||
1863 | range_start = 0; | ||
1864 | } else if (s[1]) { | ||
1865 | range_end = BB_STRTOOFF(s+1, NULL, 10); | ||
1866 | if (errno || range_end < range_start) | ||
1867 | range_start = 0; | ||
1868 | } | ||
1869 | } | ||
1870 | } | ||
1871 | #endif | ||
1784 | } /* while extra header reading */ | 1872 | } /* while extra header reading */ |
1785 | } | 1873 | } |
1786 | 1874 | ||
@@ -1833,8 +1921,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) | |||
1833 | if (urlp[-1] == '/') | 1921 | if (urlp[-1] == '/') |
1834 | strcpy(urlp, "index.html"); | 1922 | strcpy(urlp, "index.html"); |
1835 | if (stat(tptr, &sb) == 0) { | 1923 | if (stat(tptr, &sb) == 0) { |
1836 | /* It's a dir URL and there is index.html */ | 1924 | file_size = sb.st_size; |
1837 | ContentLength = sb.st_size; | ||
1838 | last_mod = sb.st_mtime; | 1925 | last_mod = sb.st_mtime; |
1839 | } | 1926 | } |
1840 | #if ENABLE_FEATURE_HTTPD_CGI | 1927 | #if ENABLE_FEATURE_HTTPD_CGI |
diff --git a/shell/ash.c b/shell/ash.c index 167232c33..492474110 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -2494,6 +2494,7 @@ pwdcmd(int argc, char **argv) | |||
2494 | #define DQSYNTAX 1 /* in double quotes */ | 2494 | #define DQSYNTAX 1 /* in double quotes */ |
2495 | #define SQSYNTAX 2 /* in single quotes */ | 2495 | #define SQSYNTAX 2 /* in single quotes */ |
2496 | #define ARISYNTAX 3 /* in arithmetic */ | 2496 | #define ARISYNTAX 3 /* in arithmetic */ |
2497 | #define PSSYNTAX 4 /* prompt */ | ||
2497 | 2498 | ||
2498 | #if ENABLE_ASH_OPTIMIZE_FOR_SIZE | 2499 | #if ENABLE_ASH_OPTIMIZE_FOR_SIZE |
2499 | #define USE_SIT_FUNCTION | 2500 | #define USE_SIT_FUNCTION |
@@ -9886,6 +9887,9 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) | |||
9886 | smallint dblquote; | 9887 | smallint dblquote; |
9887 | smallint oldstyle; | 9888 | smallint oldstyle; |
9888 | smallint prevsyntax; /* syntax before arithmetic */ | 9889 | smallint prevsyntax; /* syntax before arithmetic */ |
9890 | #if ENABLE_ASH_EXPAND_PRMT | ||
9891 | smallint pssyntax; /* we are expanding a prompt string */ | ||
9892 | #endif | ||
9889 | int varnest; /* levels of variables expansion */ | 9893 | int varnest; /* levels of variables expansion */ |
9890 | int arinest; /* levels of arithmetic expansion */ | 9894 | int arinest; /* levels of arithmetic expansion */ |
9891 | int parenlevel; /* levels of parens in arithmetic */ | 9895 | int parenlevel; /* levels of parens in arithmetic */ |
@@ -9910,6 +9914,11 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) | |||
9910 | dblquote = (syntax == DQSYNTAX); | 9914 | dblquote = (syntax == DQSYNTAX); |
9911 | oldstyle = 0; | 9915 | oldstyle = 0; |
9912 | prevsyntax = 0; | 9916 | prevsyntax = 0; |
9917 | #if ENABLE_ASH_EXPAND_PRMT | ||
9918 | pssyntax = (syntax == PSSYNTAX); | ||
9919 | if (pssyntax) | ||
9920 | syntax = DQSYNTAX; | ||
9921 | #endif | ||
9913 | varnest = 0; | 9922 | varnest = 0; |
9914 | arinest = 0; | 9923 | arinest = 0; |
9915 | parenlevel = 0; | 9924 | parenlevel = 0; |
@@ -9948,6 +9957,12 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) | |||
9948 | if (doprompt) | 9957 | if (doprompt) |
9949 | setprompt(2); | 9958 | setprompt(2); |
9950 | } else { | 9959 | } else { |
9960 | #if ENABLE_ASH_EXPAND_PRMT | ||
9961 | if (c == '$' && pssyntax) { | ||
9962 | USTPUTC(CTLESC, out); | ||
9963 | USTPUTC('\\', out); | ||
9964 | } | ||
9965 | #endif | ||
9951 | if (dblquote && | 9966 | if (dblquote && |
9952 | c != '\\' && c != '`' && | 9967 | c != '\\' && c != '`' && |
9953 | c != '$' && ( | 9968 | c != '$' && ( |
@@ -10780,7 +10795,7 @@ expandstr(const char *ps) | |||
10780 | 10795 | ||
10781 | /* XXX Fix (char *) cast. */ | 10796 | /* XXX Fix (char *) cast. */ |
10782 | setinputstring((char *)ps); | 10797 | setinputstring((char *)ps); |
10783 | readtoken1(pgetc(), DQSYNTAX, nullstr, 0); | 10798 | readtoken1(pgetc(), PSSYNTAX, nullstr, 0); |
10784 | popfile(); | 10799 | popfile(); |
10785 | 10800 | ||
10786 | n.narg.type = NARG; | 10801 | n.narg.type = NARG; |