[prev in list] [next in list] [prev in thread] [next in thread] 

List:       busybox
Subject:    [PATCH] httpd: Support reverse proxy
From:       Alex Landau <landau_alex () yahoo ! com>
Date:       2007-10-15 14:08:13
Message-ID: 81742.63281.qm () web62511 ! mail ! re1 ! yahoo ! com
[Download RAW message or body]

Hi,

The attached patch adds support for reverse proxying (see mod_proxy of Apache).
If compiled in and httpd.conf contains the line
P:/url/:http://somehost:1234/other/path/
then all requests for /url/somefile will be proxied to
http://somehost:1234/other/path/somefile.
This might be useful (and is useful in my case) when there is another HTTP server running
on the local machine, and one does not want to expose two ports - one for that server and
one for busybox's. With this patch, busybox can proxy requests to the other server which
can now listen on 127.0.0.1. The user will not notice that there are 2 servers involved.

bloatcheck:
   text    data     bss     dec     hex filename
  93978    4032    1952   99962   1867a busybox_old
  94834    4064    1952  100850   189f2 busybox_unstripped

Regards,
Alex


       
____________________________________________________________________________________
Looking for a deal? Find great prices on flights and hotels with Yahoo! FareChase.
http://farechase.yahoo.com/
["httpd-reverse-proxy.patch" (text/x-patch)]

Index: networking/httpd.c
===================================================================
--- networking/httpd.c	(revision 20259)
+++ networking/httpd.c	(working copy)
@@ -43,6 +43,8 @@
  * 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
+ * # When /url/somefile is requested, reverse proxy it to \
http://hostname[:port]/new/path/somefile + * P:/url/:http://hostname[:port]/new/path/ \
                
  * /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/
@@ -139,6 +141,14 @@
 	int allow_deny;
 } Htaccess_IP;
 
+/* Must have "next" as a first member */
+typedef struct Htaccess_Proxy {
+	struct Htaccess_Proxy *next;
+	char *url_from;
+	char *host_port;
+	char *url_to;
+} Htaccess_Proxy;
+
 enum {
 	HTTP_OK = 200,
 	HTTP_PARTIAL_CONTENT = 206,
@@ -270,6 +280,9 @@
 #if ENABLE_FEATURE_HTTPD_ERROR_PAGES
 	const char *http_error_page[ARRAY_SIZE(http_response_type)];
 #endif
+#if ENABLE_FEATURE_HTTPD_PROXY
+	Htaccess_Proxy *proxy;
+#endif
 };
 #define G (*ptr_to_globals)
 #define verbose           (G.verbose          )
@@ -301,6 +314,7 @@
 #define hdr_ptr           (G.hdr_ptr          )
 #define hdr_cnt           (G.hdr_cnt          )
 #define http_error_page   (G.http_error_page  )
+#define proxy             (G.proxy            )
 #define INIT_G() do { \
 	PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
 	USE_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
@@ -441,6 +455,7 @@
  *    [adAD]:from      # ip address allow/deny, * for wildcard
  *    /path:user:pass  # username/password
  *    Ennn:error.html  # error page for status nnn
+ *    P:/url/:http://host:port/other/url/ # reverse proxy
  *
  * Any previous IP rules are discarded.
  * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
@@ -594,6 +609,41 @@
 		}
 #endif
 
+#if ENABLE_FEATURE_HTTPD_PROXY
+		if (flag == FIRST_PARSE && *p0 == 'P') {
+			char *url_from, *host_port, *url_to;
+			Htaccess_Proxy *proxy_entry;
+
+			url_from = c;
+			if ((c = strchr(c, ':')) == NULL) {
+				bb_error_msg("config error '%s' in '%s'", buf, cf);
+				continue;
+			}
+			*c++ = '\0';
+			if (strncmp(c, "http://", 7) == 0)
+				c += 7;
+			host_port = c;
+			if (host_port == NULL) {
+				bb_error_msg("config error '%s' in '%s'", buf, cf);
+				continue;
+			}
+			if ((c = strchr(c, '/')) == NULL) {
+				bb_error_msg("config error '%s' in '%s'", buf, cf);
+				continue;
+			}
+			*c = '\0';
+			url_to = c;
+			proxy_entry = xzalloc(sizeof(Htaccess_Proxy));
+			proxy_entry->url_from = xstrdup(url_from);
+			proxy_entry->host_port = xstrdup(host_port);
+			*c = '/';
+			proxy_entry->url_to = xstrdup(url_to);
+			proxy_entry->next = proxy;
+			proxy = proxy_entry;
+			continue;
+		}
+#endif
+
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
 		if (*p0 == '/') {
 			/* make full path from httpd root / current_path / config_line_path */
@@ -1033,7 +1083,7 @@
 	return count;
 }
 
-#if ENABLE_FEATURE_HTTPD_CGI
+#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
 
 /* gcc 4.2.1 fares better with NOINLINE */
 static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int \
post_len) ATTRIBUTE_NORETURN; @@ -1207,7 +1257,10 @@
 	} /* while (1) */
 	log_and_exit();
 }
+#endif
 
+#if ENABLE_FEATURE_HTTPD_CGI
+
 static void setenv1(const char *name, const char *value)
 {
 	setenv(name, value ? value : "", 1);
@@ -1655,6 +1708,18 @@
 }
 #endif  /* FEATURE_HTTPD_BASIC_AUTH */
 
+#if ENABLE_FEATURE_HTTPD_PROXY
+static Htaccess_Proxy *find_proxy_entry(const char *url)
+{
+	Htaccess_Proxy *p;
+	for (p = proxy; p; p = p->next) {
+		if (strncmp(url, p->url_from, strlen(p->url_from)) == 0)
+			return p;
+	}
+	return NULL;
+}
+#endif
+
 /*
  * Handle timeouts
  */
@@ -1680,10 +1745,20 @@
 	int ip_allowed;
 #if ENABLE_FEATURE_HTTPD_CGI
 	const char *prequest;
-	unsigned long length = 0;
 	char *cookie = 0;
 	char *content_type = 0;
+#elif ENABLE_FEATURE_HTTPD_PROXY
+#define prequest request_GET
 #endif
+#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
+	unsigned long length = 0;
+#endif
+#if ENABLE_FEATURE_HTTPD_PROXY
+	char *headers = xmalloc(IOBUF_SIZE);
+	char *headers_ptr = headers;
+	int http_minor_version = 0;
+	Htaccess_Proxy *proxy_entry;
+#endif
 	struct sigaction sa;
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
 	int credentials = -1;  /* if not required this is Ok */
@@ -1749,8 +1824,10 @@
 	http_major_version = -1;
 	tptr = strchrnul(urlp, ' ');
 	/* Is it " HTTP/"? */
-	if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0)
+	if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) {
 		http_major_version = (tptr[6] - '0');
+		USE_FEATURE_HTTPD_PROXY(http_minor_version = (tptr[8] - '0'));
+	}
 	*tptr = '\0';
 
 	/* Copy URL from after "GET "/"POST " to stack-allocated char[] */
@@ -1840,8 +1917,20 @@
 				break; /* EOF or error or empty line */
 			if (DEBUG)
 				bb_error_msg("header: '%s'", iobuf);
+#if ENABLE_FEATURE_HTTPD_PROXY
+			if (headers_ptr - headers < IOBUF_SIZE) {
+				int len = strlen(iobuf);
+				if (len > IOBUF_SIZE - (headers_ptr - headers) - 2)
+					len = IOBUF_SIZE - (headers_ptr - headers) - 2;
+				memcpy(headers_ptr, iobuf, len);
+				headers_ptr += len;
+				headers_ptr[0] = '\r';
+				headers_ptr[1] = '\n';
+				headers_ptr += 2;
+			}
+#endif
 
-#if ENABLE_FEATURE_HTTPD_CGI
+#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
 			/* try and do our best to parse more lines */
 			if ((STRNCASECMP(iobuf, "Content-length:") == 0)) {
 				/* extra read only for POST */
@@ -1858,7 +1947,10 @@
 					if (tptr[0] || errno || length > INT_MAX)
 						send_headers_and_exit(HTTP_BAD_REQUEST);
 				}
-			} else if (STRNCASECMP(iobuf, "Cookie:") == 0) {
+			}
+#endif
+#if ENABLE_FEATURE_HTTPD_CGI
+			else if (STRNCASECMP(iobuf, "Cookie:") == 0) {
 				cookie = strdup(skip_whitespace(iobuf + sizeof("Cookie:")-1));
 			} else if (STRNCASECMP(iobuf, "Content-Type:") == 0) {
 				content_type = strdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1));
@@ -1921,6 +2013,28 @@
 		send_headers_and_exit(HTTP_MOVED_TEMPORARILY);
 	}
 
+#if ENABLE_FEATURE_HTTPD_PROXY
+	if ((proxy_entry = find_proxy_entry(urlcopy)) != NULL) {
+		int proxy_fd;
+		len_and_sockaddr *lsa;
+		char *url;
+		if ((proxy_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+			send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+		if ((lsa = host2sockaddr(proxy_entry->host_port, 80)) == NULL)
+			send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+		if (connect(proxy_fd, &lsa->sa, lsa->len) < 0)
+			send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+		url = xmalloc(strlen(urlcopy) - strlen(proxy_entry->url_from) + \
strlen(proxy_entry->url_to) + 1); +		strcpy(url, proxy_entry->url_to);
+		strcat(url, urlcopy + strlen(proxy_entry->url_from));
+		dprintf(proxy_fd, "%s %s%s%s HTTP/%d.%d\r\n", prequest, url, 
+				(g_query ? "?" : ""), (g_query ? g_query : ""), http_major_version, \
http_minor_version); +		write(proxy_fd, headers, headers_ptr - headers);
+		write(proxy_fd, "\r\n", 2);
+		cgi_io_loop_and_exit(proxy_fd, dup(proxy_fd), length);
+	}
+#endif
+
 	tptr = urlcopy + 1;      /* skip first '/' */
 
 #if ENABLE_FEATURE_HTTPD_CGI
Index: networking/Config.in
===================================================================
--- networking/Config.in	(revision 20259)
+++ networking/Config.in	(working copy)
@@ -192,6 +192,18 @@
 	  '/path/e404.html' file instead of the terse '404 NOT FOUND'
 	  message.
 
+config FEATURE_HTTPD_PROXY
+	bool "Enable support for reverse proxy"
+	default n
+	depends on HTTPD
+	help
+	  This option allows you to define URLs that will be forwarded
+	  to another HTTP server. To setup add the following line to the
+	  configuration file
+	        P:/url/:http://hostname[:port]/new/path/
+	  Then a request to /url/myfile will be forwarded to
+	  http://hostname[:port]/new/path/myfile.
+
 config IFCONFIG
 	bool "ifconfig"
 	default n



_______________________________________________
busybox mailing list
busybox@busybox.net
http://busybox.net/cgi-bin/mailman/listinfo/busybox

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic