diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-08-21 10:26:55 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-08-21 10:26:55 +0000 |
commit | e58e8d944417f5255352c6594784017ff7c6fa27 (patch) | |
tree | a348127b1da388cc6665b08a8586c1794b283601 | |
parent | 45946f8b513d9c292613ac08c3ddf4a89b915752 (diff) | |
download | busybox-w32-e58e8d944417f5255352c6594784017ff7c6fa27.tar.gz busybox-w32-e58e8d944417f5255352c6594784017ff7c6fa27.tar.bz2 busybox-w32-e58e8d944417f5255352c6594784017ff7c6fa27.zip |
httpd: add optional support for error pages
(by Pierre Metras <genepi@sympatico.ca>)
-rw-r--r-- | networking/Config.in | 13 | ||||
-rw-r--r-- | networking/httpd.c | 238 |
2 files changed, 166 insertions, 85 deletions
diff --git a/networking/Config.in b/networking/Config.in index 3013be676..5275adc5f 100644 --- a/networking/Config.in +++ b/networking/Config.in | |||
@@ -170,6 +170,19 @@ config FEATURE_HTTPD_ENCODE_URL_STR | |||
170 | For example, httpd -e "<Hello World>" as | 170 | For example, httpd -e "<Hello World>" as |
171 | "<Hello World>". | 171 | "<Hello World>". |
172 | 172 | ||
173 | config FEATURE_HTTPD_ERROR_PAGES | ||
174 | bool "Enable support for custom error pages" | ||
175 | default n | ||
176 | depends on HTTPD | ||
177 | help | ||
178 | This option allows you to define custom error pages in | ||
179 | the configuration file instead of the default HTTP status | ||
180 | error pages. For instance, if you add the line: | ||
181 | E404:/path/e404.html | ||
182 | in the config file, the server will respond the specified | ||
183 | '/path/e404.html' file instead of the terse '404 NOT FOUND' | ||
184 | message. | ||
185 | |||
173 | config IFCONFIG | 186 | config IFCONFIG |
174 | bool "ifconfig" | 187 | bool "ifconfig" |
175 | default n | 188 | default n |
diff --git a/networking/httpd.c b/networking/httpd.c index 070e2a915..7e60fc252 100644 --- a/networking/httpd.c +++ b/networking/httpd.c | |||
@@ -42,6 +42,7 @@ | |||
42 | * A:10.0.0.0/255.255.255.128 # Allow any address that previous set | 42 | * A:10.0.0.0/255.255.255.128 # Allow any address that previous set |
43 | * A:127.0.0.1 # Allow local loopback connections | 43 | * A:127.0.0.1 # Allow local loopback connections |
44 | * D:* # Deny from other IP connections | 44 | * D:* # Deny from other IP connections |
45 | * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page | ||
45 | * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ | 46 | * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ |
46 | * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ | 47 | * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ |
47 | * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ | 48 | * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ |
@@ -84,6 +85,10 @@ | |||
84 | * subdir http request, any merge is discarded when the process exits. As a | 85 | * subdir http request, any merge is discarded when the process exits. As a |
85 | * result, the subdir settings only have a lifetime of a single request. | 86 | * result, the subdir settings only have a lifetime of a single request. |
86 | * | 87 | * |
88 | * Custom error pages can contain an absolute path or be relative to | ||
89 | * 'home_httpd'. Error pages are to be static files (no CGI or script). Error | ||
90 | * page can only be defined in the root configuration file and are not taken | ||
91 | * into account in local (directories) config files. | ||
87 | * | 92 | * |
88 | * If -c is not set, an attempt will be made to open the default | 93 | * If -c is not set, an attempt will be made to open the default |
89 | * root configuration file. If -c is set and the file is not found, the | 94 | * root configuration file. If -c is set and the file is not found, the |
@@ -131,6 +136,84 @@ typedef struct Htaccess_IP { | |||
131 | int allow_deny; | 136 | int allow_deny; |
132 | } Htaccess_IP; | 137 | } Htaccess_IP; |
133 | 138 | ||
139 | enum { | ||
140 | HTTP_OK = 200, | ||
141 | HTTP_MOVED_TEMPORARILY = 302, | ||
142 | HTTP_BAD_REQUEST = 400, /* malformed syntax */ | ||
143 | HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ | ||
144 | HTTP_NOT_FOUND = 404, | ||
145 | HTTP_FORBIDDEN = 403, | ||
146 | HTTP_REQUEST_TIMEOUT = 408, | ||
147 | HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */ | ||
148 | HTTP_INTERNAL_SERVER_ERROR = 500, | ||
149 | HTTP_CONTINUE = 100, | ||
150 | #if 0 /* future use */ | ||
151 | HTTP_SWITCHING_PROTOCOLS = 101, | ||
152 | HTTP_CREATED = 201, | ||
153 | HTTP_ACCEPTED = 202, | ||
154 | HTTP_NON_AUTHORITATIVE_INFO = 203, | ||
155 | HTTP_NO_CONTENT = 204, | ||
156 | HTTP_MULTIPLE_CHOICES = 300, | ||
157 | HTTP_MOVED_PERMANENTLY = 301, | ||
158 | HTTP_NOT_MODIFIED = 304, | ||
159 | HTTP_PAYMENT_REQUIRED = 402, | ||
160 | HTTP_BAD_GATEWAY = 502, | ||
161 | HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ | ||
162 | HTTP_RESPONSE_SETSIZE = 0xffffffff | ||
163 | #endif | ||
164 | }; | ||
165 | |||
166 | static const uint16_t http_response_type[] = { | ||
167 | HTTP_OK, | ||
168 | HTTP_MOVED_TEMPORARILY, | ||
169 | HTTP_REQUEST_TIMEOUT, | ||
170 | HTTP_NOT_IMPLEMENTED, | ||
171 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
172 | HTTP_UNAUTHORIZED, | ||
173 | #endif | ||
174 | HTTP_NOT_FOUND, | ||
175 | HTTP_BAD_REQUEST, | ||
176 | HTTP_FORBIDDEN, | ||
177 | HTTP_INTERNAL_SERVER_ERROR, | ||
178 | #if 0 /* not implemented */ | ||
179 | HTTP_CREATED, | ||
180 | HTTP_ACCEPTED, | ||
181 | HTTP_NO_CONTENT, | ||
182 | HTTP_MULTIPLE_CHOICES, | ||
183 | HTTP_MOVED_PERMANENTLY, | ||
184 | HTTP_NOT_MODIFIED, | ||
185 | HTTP_BAD_GATEWAY, | ||
186 | HTTP_SERVICE_UNAVAILABLE, | ||
187 | #endif | ||
188 | }; | ||
189 | |||
190 | static const struct { | ||
191 | const char *name; | ||
192 | const char *info; | ||
193 | } http_response[ARRAY_SIZE(http_response_type)] = { | ||
194 | { "OK", NULL }, | ||
195 | { "Found", "Directories must end with a slash" }, /* ?? */ | ||
196 | { "Request Timeout", "No request appeared within 60 seconds" }, | ||
197 | { "Not Implemented", "The requested method is not recognized" }, | ||
198 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
199 | { "Unauthorized", "" }, | ||
200 | #endif | ||
201 | { "Not Found", "The requested URL was not found" }, | ||
202 | { "Bad Request", "Unsupported method" }, | ||
203 | { "Forbidden", "" }, | ||
204 | { "Internal Server Error", "Internal Server Error" }, | ||
205 | #if 0 /* not implemented */ | ||
206 | { "Created" }, | ||
207 | { "Accepted" }, | ||
208 | { "No Content" }, | ||
209 | { "Multiple Choices" }, | ||
210 | { "Moved Permanently" }, | ||
211 | { "Not Modified" }, | ||
212 | { "Bad Gateway", "" }, | ||
213 | { "Service Unavailable", "" }, | ||
214 | #endif | ||
215 | }; | ||
216 | |||
134 | struct globals { | 217 | struct globals { |
135 | int verbose; /* must be int (used by getopt32) */ | 218 | int verbose; /* must be int (used by getopt32) */ |
136 | smallint flg_deny_all; | 219 | smallint flg_deny_all; |
@@ -167,6 +250,9 @@ struct globals { | |||
167 | #define hdr_buf bb_common_bufsiz1 | 250 | #define hdr_buf bb_common_bufsiz1 |
168 | char *hdr_ptr; | 251 | char *hdr_ptr; |
169 | int hdr_cnt; | 252 | int hdr_cnt; |
253 | #if ENABLE_FEATURE_HTTPD_ERROR_PAGES | ||
254 | const char *http_error_page[ARRAY_SIZE(http_response_type)]; | ||
255 | #endif | ||
170 | }; | 256 | }; |
171 | #define G (*ptr_to_globals) | 257 | #define G (*ptr_to_globals) |
172 | #define verbose (G.verbose ) | 258 | #define verbose (G.verbose ) |
@@ -192,6 +278,7 @@ struct globals { | |||
192 | #define iobuf (G.iobuf ) | 278 | #define iobuf (G.iobuf ) |
193 | #define hdr_ptr (G.hdr_ptr ) | 279 | #define hdr_ptr (G.hdr_ptr ) |
194 | #define hdr_cnt (G.hdr_cnt ) | 280 | #define hdr_cnt (G.hdr_cnt ) |
281 | #define http_error_page (G.http_error_page ) | ||
195 | #define INIT_G() do { \ | 282 | #define INIT_G() do { \ |
196 | PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ | 283 | PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ |
197 | USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ | 284 | USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \ |
@@ -200,70 +287,13 @@ struct globals { | |||
200 | } while (0) | 287 | } while (0) |
201 | 288 | ||
202 | 289 | ||
203 | typedef enum { | ||
204 | HTTP_OK = 200, | ||
205 | HTTP_MOVED_TEMPORARILY = 302, | ||
206 | HTTP_BAD_REQUEST = 400, /* malformed syntax */ | ||
207 | HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */ | ||
208 | HTTP_NOT_FOUND = 404, | ||
209 | HTTP_FORBIDDEN = 403, | ||
210 | HTTP_REQUEST_TIMEOUT = 408, | ||
211 | HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */ | ||
212 | HTTP_INTERNAL_SERVER_ERROR = 500, | ||
213 | #if 0 /* future use */ | ||
214 | HTTP_CONTINUE = 100, | ||
215 | HTTP_SWITCHING_PROTOCOLS = 101, | ||
216 | HTTP_CREATED = 201, | ||
217 | HTTP_ACCEPTED = 202, | ||
218 | HTTP_NON_AUTHORITATIVE_INFO = 203, | ||
219 | HTTP_NO_CONTENT = 204, | ||
220 | HTTP_MULTIPLE_CHOICES = 300, | ||
221 | HTTP_MOVED_PERMANENTLY = 301, | ||
222 | HTTP_NOT_MODIFIED = 304, | ||
223 | HTTP_PAYMENT_REQUIRED = 402, | ||
224 | HTTP_BAD_GATEWAY = 502, | ||
225 | HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */ | ||
226 | HTTP_RESPONSE_SETSIZE = 0xffffffff | ||
227 | #endif | ||
228 | } HttpResponseNum; | ||
229 | |||
230 | typedef struct { | ||
231 | HttpResponseNum type; | ||
232 | const char *name; | ||
233 | const char *info; | ||
234 | } HttpEnumString; | ||
235 | |||
236 | static const HttpEnumString httpResponseNames[] = { | ||
237 | { HTTP_OK, "OK", NULL }, | ||
238 | { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." }, | ||
239 | { HTTP_REQUEST_TIMEOUT, "Request Timeout", | ||
240 | "No request appeared within a reasonable time period." }, | ||
241 | { HTTP_NOT_IMPLEMENTED, "Not Implemented", | ||
242 | "The requested method is not recognized by this server." }, | ||
243 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
244 | { HTTP_UNAUTHORIZED, "Unauthorized", "" }, | ||
245 | #endif | ||
246 | { HTTP_NOT_FOUND, "Not Found", | ||
247 | "The requested URL was not found on this server." }, | ||
248 | { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." }, | ||
249 | { HTTP_FORBIDDEN, "Forbidden", "" }, | ||
250 | { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error", | ||
251 | "Internal Server Error" }, | ||
252 | #if 0 /* not implemented */ | ||
253 | { HTTP_CREATED, "Created" }, | ||
254 | { HTTP_ACCEPTED, "Accepted" }, | ||
255 | { HTTP_NO_CONTENT, "No Content" }, | ||
256 | { HTTP_MULTIPLE_CHOICES, "Multiple Choices" }, | ||
257 | { HTTP_MOVED_PERMANENTLY, "Moved Permanently" }, | ||
258 | { HTTP_NOT_MODIFIED, "Not Modified" }, | ||
259 | { HTTP_BAD_GATEWAY, "Bad Gateway", "" }, | ||
260 | { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" }, | ||
261 | #endif | ||
262 | }; | ||
263 | 290 | ||
264 | 291 | ||
265 | #define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1) | 292 | #define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1) |
266 | 293 | ||
294 | /* Prototypes */ | ||
295 | static void send_file_and_exit(const char *url, int headers) ATTRIBUTE_NORETURN; | ||
296 | |||
267 | static void free_llist(has_next_ptr **pptr) | 297 | static void free_llist(has_next_ptr **pptr) |
268 | { | 298 | { |
269 | has_next_ptr *cur = *pptr; | 299 | has_next_ptr *cur = *pptr; |
@@ -382,11 +412,13 @@ static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp) | |||
382 | * .ext:mime/type # new mime type not compiled into httpd | 412 | * .ext:mime/type # new mime type not compiled into httpd |
383 | * [adAD]:from # ip address allow/deny, * for wildcard | 413 | * [adAD]:from # ip address allow/deny, * for wildcard |
384 | * /path:user:pass # username/password | 414 | * /path:user:pass # username/password |
415 | * Ennn:error.html # error page for status nnn | ||
385 | * | 416 | * |
386 | * Any previous IP rules are discarded. | 417 | * Any previous IP rules are discarded. |
387 | * If the flag argument is not SUBDIR_PARSE then all /path and mime rules | 418 | * If the flag argument is not SUBDIR_PARSE then all /path and mime rules |
388 | * are also discarded. That is, previous settings are retained if flag is | 419 | * are also discarded. That is, previous settings are retained if flag is |
389 | * SUBDIR_PARSE. | 420 | * SUBDIR_PARSE. |
421 | * Error pages are only parsed on the main config file. | ||
390 | * | 422 | * |
391 | * path Path where to look for httpd.conf (without filename). | 423 | * path Path where to look for httpd.conf (without filename). |
392 | * flag Type of the parse request. | 424 | * flag Type of the parse request. |
@@ -486,18 +518,6 @@ static void parse_conf(const char *path, int flag) | |||
486 | 518 | ||
487 | if (*p0 == 'a') | 519 | if (*p0 == 'a') |
488 | *p0 = 'A'; | 520 | *p0 = 'A'; |
489 | else if (*p0 != 'D' && *p0 != 'A' | ||
490 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | ||
491 | && *p0 != '/' | ||
492 | #endif | ||
493 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES | ||
494 | && *p0 != '.' | ||
495 | #endif | ||
496 | #if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR | ||
497 | && *p0 != '*' | ||
498 | #endif | ||
499 | ) | ||
500 | continue; | ||
501 | if (*p0 == 'A' || *p0 == 'D') { | 521 | if (*p0 == 'A' || *p0 == 'D') { |
502 | /* storing current config IP line */ | 522 | /* storing current config IP line */ |
503 | pip = xzalloc(sizeof(Htaccess_IP)); | 523 | pip = xzalloc(sizeof(Htaccess_IP)); |
@@ -527,6 +547,30 @@ static void parse_conf(const char *path, int flag) | |||
527 | } | 547 | } |
528 | continue; | 548 | continue; |
529 | } | 549 | } |
550 | |||
551 | #if ENABLE_FEATURE_HTTPD_ERROR_PAGES | ||
552 | if (flag == FIRST_PARSE && *p0 == 'E') { | ||
553 | int i; | ||
554 | /* error status code */ | ||
555 | int status = atoi(++p0); | ||
556 | /* c already points at the character following ':' in parse loop */ | ||
557 | // c = strchr(p0, ':'); c++; | ||
558 | if (status < HTTP_CONTINUE) { | ||
559 | bb_error_msg("config error '%s' in '%s'", buf, cf); | ||
560 | continue; | ||
561 | } | ||
562 | |||
563 | /* then error page; find matching status */ | ||
564 | for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { | ||
565 | if (http_response_type[i] == status) { | ||
566 | http_error_page[i] = concat_path_file((*c == '/') ? NULL : home_httpd, c); | ||
567 | break; | ||
568 | } | ||
569 | } | ||
570 | continue; | ||
571 | } | ||
572 | #endif | ||
573 | |||
530 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH | 574 | #if ENABLE_FEATURE_HTTPD_BASIC_AUTH |
531 | if (*p0 == '/') { | 575 | if (*p0 == '/') { |
532 | /* make full path from httpd root / current_path / config_line_path */ | 576 | /* make full path from httpd root / current_path / config_line_path */ |
@@ -813,24 +857,29 @@ static void log_and_exit(void) | |||
813 | * IE will puke big-time if the headers are not sent in one packet and the | 857 | * IE will puke big-time if the headers are not sent in one packet and the |
814 | * second packet is delayed for any reason. | 858 | * second packet is delayed for any reason. |
815 | * responseNum - the result code to send. | 859 | * responseNum - the result code to send. |
816 | * Return result of write(). | ||
817 | */ | 860 | */ |
818 | static void send_headers(HttpResponseNum responseNum) | 861 | static void send_headers(int responseNum) |
819 | { | 862 | { |
820 | static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT"; | 863 | static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT"; |
821 | 864 | ||
822 | const char *responseString = ""; | 865 | const char *responseString = ""; |
823 | const char *infoString = NULL; | 866 | const char *infoString = NULL; |
824 | const char *mime_type; | 867 | const char *mime_type; |
868 | #if ENABLE_FEATURE_HTTPD_ERROR_PAGES | ||
869 | const char *error_page = 0; | ||
870 | #endif | ||
825 | unsigned i; | 871 | unsigned i; |
826 | time_t timer = time(0); | 872 | time_t timer = time(0); |
827 | char tmp_str[80]; | 873 | char tmp_str[80]; |
828 | int len; | 874 | int len; |
829 | 875 | ||
830 | for (i = 0; i < ARRAY_SIZE(httpResponseNames); i++) { | 876 | for (i = 0; i < ARRAY_SIZE(http_response_type); i++) { |
831 | if (httpResponseNames[i].type == responseNum) { | 877 | if (http_response_type[i] == responseNum) { |
832 | responseString = httpResponseNames[i].name; | 878 | responseString = http_response[i].name; |
833 | infoString = httpResponseNames[i].info; | 879 | infoString = http_response[i].info; |
880 | #if ENABLE_FEATURE_HTTPD_ERROR_PAGES | ||
881 | error_page = http_error_page[i]; | ||
882 | #endif | ||
834 | break; | 883 | break; |
835 | } | 884 | } |
836 | } | 885 | } |
@@ -862,6 +911,20 @@ static void send_headers(HttpResponseNum responseNum) | |||
862 | (g_query ? g_query : "")); | 911 | (g_query ? g_query : "")); |
863 | } | 912 | } |
864 | 913 | ||
914 | #if ENABLE_FEATURE_HTTPD_ERROR_PAGES | ||
915 | if (error_page && !access(error_page, R_OK)) { | ||
916 | strcat(iobuf, "\r\n"); | ||
917 | len += 2; | ||
918 | |||
919 | if (DEBUG) | ||
920 | fprintf(stderr, "headers: '%s'\n", iobuf); | ||
921 | full_write(1, iobuf, len); | ||
922 | if (DEBUG) | ||
923 | fprintf(stderr, "writing error page: '%s'\n", error_page); | ||
924 | return send_file_and_exit(error_page, FALSE); | ||
925 | } | ||
926 | #endif | ||
927 | |||
865 | if (ContentLength != -1) { /* file */ | 928 | if (ContentLength != -1) { /* file */ |
866 | strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod)); | 929 | strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod)); |
867 | len += sprintf(iobuf + len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n", | 930 | len += sprintf(iobuf + len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n", |
@@ -885,8 +948,8 @@ static void send_headers(HttpResponseNum responseNum) | |||
885 | } | 948 | } |
886 | } | 949 | } |
887 | 950 | ||
888 | static void send_headers_and_exit(HttpResponseNum responseNum) ATTRIBUTE_NORETURN; | 951 | static void send_headers_and_exit(int responseNum) ATTRIBUTE_NORETURN; |
889 | static void send_headers_and_exit(HttpResponseNum responseNum) | 952 | static void send_headers_and_exit(int responseNum) |
890 | { | 953 | { |
891 | send_headers(responseNum); | 954 | send_headers(responseNum); |
892 | log_and_exit(); | 955 | log_and_exit(); |
@@ -1279,9 +1342,12 @@ static void send_cgi_and_exit( | |||
1279 | 1342 | ||
1280 | /* | 1343 | /* |
1281 | * Send a file response to a HTTP request, and exit | 1344 | * Send a file response to a HTTP request, and exit |
1345 | * | ||
1346 | * Parameters: | ||
1347 | * const char *url The requested URL (with leading /). | ||
1348 | * headers Don't send headers before if FALSE. | ||
1282 | */ | 1349 | */ |
1283 | static void send_file_and_exit(const char *url) ATTRIBUTE_NORETURN; | 1350 | static void send_file_and_exit(const char *url, int headers) |
1284 | static void send_file_and_exit(const char *url) | ||
1285 | { | 1351 | { |
1286 | static const char *const suffixTable[] = { | 1352 | static const char *const suffixTable[] = { |
1287 | /* Warning: shorter equivalent suffix in one line must be first */ | 1353 | /* Warning: shorter equivalent suffix in one line must be first */ |
@@ -1350,10 +1416,12 @@ static void send_file_and_exit(const char *url) | |||
1350 | if (f < 0) { | 1416 | if (f < 0) { |
1351 | if (DEBUG) | 1417 | if (DEBUG) |
1352 | bb_perror_msg("cannot open '%s'", url); | 1418 | bb_perror_msg("cannot open '%s'", url); |
1353 | send_headers_and_exit(HTTP_NOT_FOUND); | 1419 | if (headers) |
1420 | send_headers_and_exit(HTTP_NOT_FOUND); | ||
1354 | } | 1421 | } |
1355 | 1422 | ||
1356 | send_headers(HTTP_OK); | 1423 | if (headers) |
1424 | send_headers(HTTP_OK); | ||
1357 | 1425 | ||
1358 | /* If you want to know about EPIPE below | 1426 | /* If you want to know about EPIPE below |
1359 | * (happens if you abort downloads from local httpd): */ | 1427 | * (happens if you abort downloads from local httpd): */ |
@@ -1789,7 +1857,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) | |||
1789 | * } | 1857 | * } |
1790 | */ | 1858 | */ |
1791 | 1859 | ||
1792 | send_file_and_exit(tptr); | 1860 | send_file_and_exit(tptr, TRUE); |
1793 | } | 1861 | } |
1794 | 1862 | ||
1795 | /* | 1863 | /* |