Hi,
As recommended by Walter Harms, this version of the patch uses xasprintf()
instead of malloc()/strcpy()/strcat(). The final size of the binary is even
smaller: 324 bytes instead of 388 bytes with the previous patch.
make bloatcheck
function old new delta
parse_conf 1201 1372 +171
sendHeaders 461 571 +110
sendFile 252 317 +65
httpResponseNames 108 144 +36
handleIncoming 2032 2038 +6
.rodata 138727 138663 -64
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/1 up/down: 388/-64) Total: 324 bytes
text data bss dec hex filename
636180 3124 12800 652104 9f348 busybox_old
636525 3288 12800 652613 9f545 busybox_unstripped
-- Pierre Métras
Index: networking/httpd.c
===================================================================
--- networking/httpd.c (revision 18994)
+++ networking/httpd.c (working copy)
@@ -42,6 +42,7 @@
* A:10.0.0.0/255.255.255.128 # Allow any address that previous set
* A:127.0.0.1 # Allow local loopback connections
* D:* # Deny from other IP connections
+ * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page
* /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
* /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
* /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
@@ -85,6 +86,11 @@
* result, the subdir settings only have a lifetime of a single request.
*
*
+ * Custom error pages can contain an absolute path or be relative to
+ * 'home_httpd'. Error pages are to be static files (no CGI or script). Error
+ * page can only be defined in the root configuration file and are not taken
+ * into account in local (directories) config files.
+ *
* If -c is not set, an attempt will be made to open the default
* root configuration file. If -c is set and the file is not found, the
* server exits with an error.
@@ -234,8 +240,8 @@
HTTP_REQUEST_TIMEOUT = 408,
HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
HTTP_INTERNAL_SERVER_ERROR = 500,
+ HTTP_CONTINUE = 100,
#if 0 /* future use */
- HTTP_CONTINUE = 100,
HTTP_SWITCHING_PROTOCOLS = 101,
HTTP_CREATED = 201,
HTTP_ACCEPTED = 202,
@@ -255,24 +261,28 @@
HttpResponseNum type;
const char *name;
const char *info;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+ const char *error_page;
+#endif
} HttpEnumString;
-static const HttpEnumString httpResponseNames[] = {
- { HTTP_OK, "OK", NULL },
- { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
+
+static HttpEnumString httpResponseNames[] = {
+ { HTTP_OK, "OK", NULL USE_FEATURE_HTTPD_ERROR_PAGES(, NULL) },
+ { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." USE_FEATURE_HTTPD_ERROR_PAGES(, NULL) },
{ HTTP_REQUEST_TIMEOUT, "Request Timeout",
- "No request appeared within a reasonable time period." },
+ "No request appeared within a reasonable time period." USE_FEATURE_HTTPD_ERROR_PAGES(, NULL) },
{ HTTP_NOT_IMPLEMENTED, "Not Implemented",
- "The requested method is not recognized by this server." },
+ "The requested method is not recognized by this server." USE_FEATURE_HTTPD_ERROR_PAGES(, NULL) },
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
- { HTTP_UNAUTHORIZED, "Unauthorized", "" },
+ { HTTP_UNAUTHORIZED, "Unauthorized", "" USE_FEATURE_HTTPD_ERROR_PAGES(, NULL) },
#endif
{ HTTP_NOT_FOUND, "Not Found",
- "The requested URL was not found on this server." },
- { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
- { HTTP_FORBIDDEN, "Forbidden", "" },
+ "The requested URL was not found on this server." USE_FEATURE_HTTPD_ERROR_PAGES(, NULL) },
+ { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." USE_FEATURE_HTTPD_ERROR_PAGES(, NULL) },
+ { HTTP_FORBIDDEN, "Forbidden", "" USE_FEATURE_HTTPD_ERROR_PAGES(, NULL) },
{ HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
- "Internal Server Error" },
+ "Internal Server Error" USE_FEATURE_HTTPD_ERROR_PAGES(, NULL) },
#if 0 /* not implemented */
{ HTTP_CREATED, "Created" },
{ HTTP_ACCEPTED, "Accepted" },
@@ -291,6 +301,8 @@
#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
+/* prototypes */
+static int sendFile(const char *url, int headers);
static int scan_ip(const char **ep, unsigned *ip, unsigned char endc)
{
@@ -399,6 +411,7 @@
* .ext:mime/type # new mime type not compiled into httpd
* [adAD]:from # ip address allow/deny, * for wildcard
* /path:user:pass # username/password
+ * Ennn:error.html # error page for status nnn
*
* Any previous IP rules are discarded.
* If the flag argument is not SUBDIR_PARSE then all /path and mime rules
@@ -523,6 +536,9 @@
#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
&& *p0 != '*'
#endif
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+ && *p0 != 'E'
+#endif
)
continue;
if (*p0 == 'A' || *p0 == 'D') {
@@ -554,6 +570,35 @@
}
continue;
}
+
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+ if (flag == FIRST_PARSE && *p0 == 'E') {
+ /* error status code */
+ c = strchr(++p0, ':');
+ *c = 0;
+ int status = xatoi(p0);
+ p0 = ++c;
+
+ /* then error page */
+ if (status >= HTTP_CONTINUE) {
+ if (*p0 == '/') {
+ c = xasprintf("%s", p0);
+ } else {
+ c = xasprintf("%s/%s", home_httpd, p0);
+ }
+
+ /* find matching status */
+ for (int i = 0; i < ARRAY_SIZE(httpResponseNames); i++) {
+ if (httpResponseNames[i].type == status) {
+ httpResponseNames[i].error_page = c;
+ break;
+ }
+ }
+ continue;
+ }
+ }
+#endif
+
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
if (*p0 == '/') {
/* make full path from httpd root / curent_path / config_line_path */
@@ -865,7 +910,7 @@
* $Parameter:
* (HttpResponseNum) responseNum . . . The result code to send.
*
- * $Return: (int) . . . . writing errors
+ * $Return: (int) . . . . !0 in case of write errors or error file not found.
*
****************************************************************************/
static int sendHeaders(HttpResponseNum responseNum)
@@ -873,6 +918,9 @@
char *buf = iobuf;
const char *responseString = "";
const char *infoString = 0;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+ const char *error_page = 0;
+#endif
const char *mime_type;
unsigned i;
time_t timer = time(0);
@@ -883,6 +931,9 @@
if (httpResponseNames[i].type == responseNum) {
responseString = httpResponseNames[i].name;
infoString = httpResponseNames[i].info;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+ error_page = httpResponseNames[i].error_page;
+#endif
break;
}
}
@@ -911,6 +962,23 @@
(g_query ? g_query : ""));
}
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+ if (error_page && !access(error_page, R_OK)) {
+ strcat(buf, "\r\n");
+ len += 2;
+
+ if (DEBUG)
+ fprintf(stderr, "headers: '%s'\n", buf);
+ i = accepted_socket;
+ if (i == 0) i++; /* write to fd #1 in inetd mode */
+ int c = full_write(i, buf, len);
+
+ if (DEBUG)
+ fprintf(stderr, "writing error page: '%s'\n", error_page);
+ return c != len || sendFile(error_page, FALSE);
+ }
+#endif
+
if (ContentLength != -1) { /* file */
strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&last_mod));
len += sprintf(buf+len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n",
@@ -918,18 +986,20 @@
}
strcat(buf, "\r\n");
len += 2;
+
if (infoString) {
len += sprintf(buf+len,
- "<HEAD><TITLE>%d %s</TITLE></HEAD>\n"
- "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n",
+ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
+ "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n"
+ "<BODY><H1>%d %s</H1>\n%s\n</BODY></HTML>\n",
responseNum, responseString,
responseNum, responseString, infoString);
}
if (DEBUG)
fprintf(stderr, "headers: '%s'\n", buf);
i = accepted_socket;
- if (i == 0) i++; /* write to fd# 1 in inetd mode */
- return full_write(i, buf, len);
+ if (i == 0) i++; /* write to fd #1 in inetd mode */
+ return full_write(i, buf, len) != len;
}
/****************************************************************************
@@ -1317,11 +1387,12 @@
*
* $Parameter:
* (const char *) url . . The URL requested.
+ * (int) headers . . . . If false, don't send headers before.
*
- * $Return: (int) . . . . . . Always 0.
+ * $Return: (int) . . . . . . !0 if the file was not found.
*
****************************************************************************/
-static int sendFile(const char *url)
+static int sendFile(const char *url, int headers)
{
char * suffix;
int f;
@@ -1357,25 +1428,29 @@
f = open(url, O_RDONLY);
if (f >= 0) {
- int count;
- char *buf = iobuf;
-
- sendHeaders(HTTP_OK);
- /* TODO: sendfile() */
- while ((count = full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
- int fd = accepted_socket;
- if (fd == 0) fd++; /* write to fd# 1 in inetd mode */
- if (full_write(fd, buf, count) != count)
- break;
+ if (headers) {
+ sendHeaders(HTTP_OK);
}
+
+ int fd = accepted_socket;
+ if (fd == 0) fd++; /* write to fd #1 in inetd mode */
+
+ struct stat f_stat;
+ if (fstat(f, &f_stat)) {
+ bb_perror_msg("cannot stat file '%s'", url);
+ }
+ off_t offset = 0;
+ sendfile(fd, f, &offset, f_stat.st_size);
close(f);
} else {
if (DEBUG)
bb_perror_msg("cannot open '%s'", url);
- sendHeaders(HTTP_NOT_FOUND);
+ if (headers) {
+ sendHeaders(HTTP_NOT_FOUND);
+ }
}
- return 0;
+ return (f < 0);
}
static int checkPermIP(void)
@@ -1670,11 +1745,11 @@
if ((STRNCASECMP(buf, "Content-length:") == 0)) {
/* extra read only for POST */
if (prequest != request_GET) {
- test = buf + sizeof("Content-length:")-1;
+ test = buf + sizeof("Content-length:") - 1;
if (!test[0])
goto bail_out;
errno = 0;
- /* not using strtoul: it ignores leading munis! */
+ /* not using strtoul: it ignores leading minus! */
length = strtol(test, &test, 10);
/* length is "ulong", but we need to pass it to int later */
/* so we check for negative or too large values in one go: */
@@ -1783,7 +1858,7 @@
}
}
#endif /* FEATURE_HTTPD_CGI */
- sendFile(test);
+ sendFile(test, TRUE);
ContentLength = -1;
} while (0);
Index: networking/Config.in
===================================================================
--- networking/Config.in (revision 18994)
+++ networking/Config.in (working copy)
@@ -162,6 +162,19 @@
For example, httpd -e "<Hello World>" as
"<Hello World>".
+config FEATURE_HTTPD_ERROR_PAGES
+ bool "Enable support for custom error pages"
+ default n
+ depends on HTTPD
+ help
+ This option allows you to define custom error pages in
+ the configuration file instead of the default HTTP status
+ error pages. For instance, if you add the line:
+ E404:/path/e404.html
+ in the config file, the server will respond the specified
+ '/path/e404.html' file instead of the terse '404 NOT FOUND'
+ message.
+
config IFCONFIG
bool "ifconfig"
default n
Index: include/libbb.h
===================================================================
--- include/libbb.h (revision 18994)
+++ include/libbb.h (working copy)
@@ -35,6 +35,9 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
+#if ENABLE_HTTPD
+#include <sys/sendfile.h>
+#endif
#include <termios.h>
#include <time.h>
#include <unistd.h>
_______________________________________________
busybox mailing list
[email protected]
http://busybox.net/cgi-bin/mailman/listinfo/busybox