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
 	  "&#60Hello&#32World&#62".
 
+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

Reply via email to