svn commit: trunk/busybox/networking

vda at busybox.net vda at busybox.net
Tue Aug 21 10:26:57 UTC 2007


Author: vda
Date: 2007-08-21 03:26:55 -0700 (Tue, 21 Aug 2007)
New Revision: 19601

Log:
httpd: add optional support for error pages  
(by Pierre Metras <genepi at sympatico.ca>)



Modified:
   trunk/busybox/networking/Config.in
   trunk/busybox/networking/httpd.c


Changeset:
Modified: trunk/busybox/networking/Config.in
===================================================================
--- trunk/busybox/networking/Config.in	2007-08-21 08:31:19 UTC (rev 19600)
+++ trunk/busybox/networking/Config.in	2007-08-21 10:26:55 UTC (rev 19601)
@@ -170,6 +170,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

Modified: trunk/busybox/networking/httpd.c
===================================================================
--- trunk/busybox/networking/httpd.c	2007-08-21 08:31:19 UTC (rev 19600)
+++ trunk/busybox/networking/httpd.c	2007-08-21 10:26:55 UTC (rev 19601)
@@ -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/
@@ -84,6 +85,10 @@
  * subdir http request, any merge is discarded when the process exits.  As a
  * 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
@@ -131,6 +136,84 @@
 	int allow_deny;
 } Htaccess_IP;
 
+enum {
+	HTTP_OK = 200,
+	HTTP_MOVED_TEMPORARILY = 302,
+	HTTP_BAD_REQUEST = 400,       /* malformed syntax */
+	HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
+	HTTP_NOT_FOUND = 404,
+	HTTP_FORBIDDEN = 403,
+	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_SWITCHING_PROTOCOLS = 101,
+	HTTP_CREATED = 201,
+	HTTP_ACCEPTED = 202,
+	HTTP_NON_AUTHORITATIVE_INFO = 203,
+	HTTP_NO_CONTENT = 204,
+	HTTP_MULTIPLE_CHOICES = 300,
+	HTTP_MOVED_PERMANENTLY = 301,
+	HTTP_NOT_MODIFIED = 304,
+	HTTP_PAYMENT_REQUIRED = 402,
+	HTTP_BAD_GATEWAY = 502,
+	HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
+	HTTP_RESPONSE_SETSIZE = 0xffffffff
+#endif
+};
+
+static const uint16_t http_response_type[] = {
+	HTTP_OK,
+	HTTP_MOVED_TEMPORARILY,
+	HTTP_REQUEST_TIMEOUT,
+	HTTP_NOT_IMPLEMENTED,
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH 
+	HTTP_UNAUTHORIZED,
+#endif
+	HTTP_NOT_FOUND,
+	HTTP_BAD_REQUEST,
+	HTTP_FORBIDDEN,
+	HTTP_INTERNAL_SERVER_ERROR,
+#if 0   /* not implemented */
+	HTTP_CREATED,
+	HTTP_ACCEPTED,
+	HTTP_NO_CONTENT,
+	HTTP_MULTIPLE_CHOICES,
+	HTTP_MOVED_PERMANENTLY,
+	HTTP_NOT_MODIFIED,
+	HTTP_BAD_GATEWAY,
+	HTTP_SERVICE_UNAVAILABLE,
+#endif
+};
+
+static const struct {
+	const char *name;
+	const char *info;
+} http_response[ARRAY_SIZE(http_response_type)] = {
+	{ "OK", NULL },
+	{ "Found", "Directories must end with a slash" }, /* ?? */
+	{ "Request Timeout", "No request appeared within 60 seconds" },
+	{ "Not Implemented", "The requested method is not recognized" },
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+	{ "Unauthorized", "" },
+#endif
+	{ "Not Found", "The requested URL was not found" },
+	{ "Bad Request", "Unsupported method" },
+	{ "Forbidden", ""  },
+	{ "Internal Server Error", "Internal Server Error" },
+#if 0   /* not implemented */
+	{ "Created" },
+	{ "Accepted" },
+	{ "No Content" },
+	{ "Multiple Choices" },
+	{ "Moved Permanently" },
+	{ "Not Modified" },
+	{ "Bad Gateway", "" },
+	{ "Service Unavailable", "" },
+#endif
+};
+
 struct globals {
 	int verbose;            /* must be int (used by getopt32) */
 	smallint flg_deny_all;
@@ -167,6 +250,9 @@
 #define hdr_buf bb_common_bufsiz1
 	char *hdr_ptr;
 	int hdr_cnt;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+	const char *http_error_page[ARRAY_SIZE(http_response_type)];
+#endif
 };
 #define G (*ptr_to_globals)
 #define verbose           (G.verbose          )
@@ -192,6 +278,7 @@
 #define iobuf             (G.iobuf            )
 #define hdr_ptr           (G.hdr_ptr          )
 #define hdr_cnt           (G.hdr_cnt          )
+#define http_error_page   (G.http_error_page  )
 #define INIT_G() do { \
 	PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
 	USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
@@ -200,70 +287,13 @@
 } while (0)
 
 
-typedef enum {
-	HTTP_OK = 200,
-	HTTP_MOVED_TEMPORARILY = 302,
-	HTTP_BAD_REQUEST = 400,       /* malformed syntax */
-	HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
-	HTTP_NOT_FOUND = 404,
-	HTTP_FORBIDDEN = 403,
-	HTTP_REQUEST_TIMEOUT = 408,
-	HTTP_NOT_IMPLEMENTED = 501,   /* used for unrecognized requests */
-	HTTP_INTERNAL_SERVER_ERROR = 500,
-#if 0 /* future use */
-	HTTP_CONTINUE = 100,
-	HTTP_SWITCHING_PROTOCOLS = 101,
-	HTTP_CREATED = 201,
-	HTTP_ACCEPTED = 202,
-	HTTP_NON_AUTHORITATIVE_INFO = 203,
-	HTTP_NO_CONTENT = 204,
-	HTTP_MULTIPLE_CHOICES = 300,
-	HTTP_MOVED_PERMANENTLY = 301,
-	HTTP_NOT_MODIFIED = 304,
-	HTTP_PAYMENT_REQUIRED = 402,
-	HTTP_BAD_GATEWAY = 502,
-	HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
-	HTTP_RESPONSE_SETSIZE = 0xffffffff
-#endif
-} HttpResponseNum;
 
-typedef struct {
-	HttpResponseNum type;
-	const char *name;
-	const char *info;
-} HttpEnumString;
 
-static const HttpEnumString httpResponseNames[] = {
-	{ HTTP_OK, "OK", NULL },
-	{ HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
-	{ HTTP_REQUEST_TIMEOUT, "Request Timeout",
-		"No request appeared within a reasonable time period." },
-	{ HTTP_NOT_IMPLEMENTED, "Not Implemented",
-		"The requested method is not recognized by this server." },
-#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
-	{ HTTP_UNAUTHORIZED, "Unauthorized", "" },
-#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", "" },
-	{ HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
-		"Internal Server Error" },
-#if 0                               /* not implemented */
-	{ HTTP_CREATED, "Created" },
-	{ HTTP_ACCEPTED, "Accepted" },
-	{ HTTP_NO_CONTENT, "No Content" },
-	{ HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
-	{ HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
-	{ HTTP_NOT_MODIFIED, "Not Modified" },
-	{ HTTP_BAD_GATEWAY, "Bad Gateway", "" },
-	{ HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
-#endif
-};
+#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
 
+/* Prototypes */
+static void send_file_and_exit(const char *url, int headers) ATTRIBUTE_NORETURN;
 
-#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
-
 static void free_llist(has_next_ptr **pptr)
 {
 	has_next_ptr *cur = *pptr;
@@ -382,11 +412,13 @@
  *    .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
  * are also discarded.  That is, previous settings are retained if flag is
  * SUBDIR_PARSE.
+ * Error pages are only parsed on the main config file.
  *
  * path   Path where to look for httpd.conf (without filename).
  * flag   Type of the parse request.
@@ -486,18 +518,6 @@
 
 		if (*p0 == 'a')
 			*p0 = 'A';
-		else if (*p0 != 'D' && *p0 != 'A'
-#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
-			 && *p0 != '/'
-#endif
-#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
-			 && *p0 != '.'
-#endif
-#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
-			 && *p0 != '*'
-#endif
-			)
-			 continue;
 		if (*p0 == 'A' || *p0 == 'D') {
 			/* storing current config IP line */
 			pip = xzalloc(sizeof(Htaccess_IP));
@@ -527,6 +547,30 @@
 			}
 			continue;
 		}
+
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+		if (flag == FIRST_PARSE && *p0 == 'E') {
+			int i;
+			/* error status code */
+			int status = atoi(++p0);
+			/* c already points at the character following ':' in parse loop */
+			// c = strchr(p0, ':'); c++;
+			if (status < HTTP_CONTINUE) {
+				bb_error_msg("config error '%s' in '%s'", buf, cf);
+				continue;
+			}
+
+			/* then error page; find matching status */
+			for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
+				if (http_response_type[i] == status) {
+					http_error_page[i] = concat_path_file((*c == '/') ? NULL : home_httpd, c);
+					break;
+				}
+			}
+			continue;
+		}
+#endif
+
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
 		if (*p0 == '/') {
 			/* make full path from httpd root / current_path / config_line_path */
@@ -813,24 +857,29 @@
  * IE will puke big-time if the headers are not sent in one packet and the
  * second packet is delayed for any reason.
  * responseNum - the result code to send.
- * Return result of write().
  */
-static void send_headers(HttpResponseNum responseNum)
+static void send_headers(int responseNum)
 {
 	static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
 
 	const char *responseString = "";
 	const char *infoString = NULL;
 	const char *mime_type;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+	const char *error_page = 0;
+#endif
 	unsigned i;
 	time_t timer = time(0);
 	char tmp_str[80];
 	int len;
 
-	for (i = 0; i < ARRAY_SIZE(httpResponseNames); i++) {
-		if (httpResponseNames[i].type == responseNum) {
-			responseString = httpResponseNames[i].name;
-			infoString = httpResponseNames[i].info;
+	for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
+		if (http_response_type[i] == responseNum) {
+			responseString = http_response[i].name;
+			infoString = http_response[i].info;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+			error_page = http_error_page[i];
+#endif
 			break;
 		}
 	}
@@ -862,6 +911,20 @@
 				(g_query ? g_query : ""));
 	}
 
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+	if (error_page && !access(error_page, R_OK)) {
+		strcat(iobuf, "\r\n");
+		len += 2;
+
+		if (DEBUG)
+			fprintf(stderr, "headers: '%s'\n", iobuf);
+		full_write(1, iobuf, len);
+		if (DEBUG)
+			fprintf(stderr, "writing error page: '%s'\n", error_page);
+		return send_file_and_exit(error_page, FALSE);
+	}
+#endif
+
 	if (ContentLength != -1) {    /* file */
 		strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod));
 		len += sprintf(iobuf + len, "Last-Modified: %s\r\n%s %"OFF_FMT"d\r\n",
@@ -885,8 +948,8 @@
 	}
 }
 
-static void send_headers_and_exit(HttpResponseNum responseNum) ATTRIBUTE_NORETURN;
-static void send_headers_and_exit(HttpResponseNum responseNum)
+static void send_headers_and_exit(int responseNum) ATTRIBUTE_NORETURN;
+static void send_headers_and_exit(int responseNum)
 {
 	send_headers(responseNum);
 	log_and_exit();
@@ -1279,9 +1342,12 @@
 
 /*
  * Send a file response to a HTTP request, and exit
+ * 
+ * Parameters:
+ * const char *url    The requested URL (with leading /).
+ * headers            Don't send headers before if FALSE.
  */
-static void send_file_and_exit(const char *url) ATTRIBUTE_NORETURN;
-static void send_file_and_exit(const char *url)
+static void send_file_and_exit(const char *url, int headers)
 {
 	static const char *const suffixTable[] = {
 	/* Warning: shorter equivalent suffix in one line must be first */
@@ -1350,10 +1416,12 @@
 	if (f < 0) {
 		if (DEBUG)
 			bb_perror_msg("cannot open '%s'", url);
-		send_headers_and_exit(HTTP_NOT_FOUND);
+		if (headers)
+			send_headers_and_exit(HTTP_NOT_FOUND);
 	}
 
-	send_headers(HTTP_OK);
+	if (headers)
+		send_headers(HTTP_OK);
 
 	/* If you want to know about EPIPE below
 	 * (happens if you abort downloads from local httpd): */
@@ -1789,7 +1857,7 @@
 	 * }
 	 */
 
-	send_file_and_exit(tptr);
+	send_file_and_exit(tptr, TRUE);
 }
 
 /*




More information about the busybox-cvs mailing list