Skip to content

Commit

Permalink
Added forward proxy support and included an example
Browse files Browse the repository at this point in the history
  • Loading branch information
mattremmel committed Aug 17, 2017
1 parent ba71a9a commit 5537d6e
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 13 deletions.
8 changes: 8 additions & 0 deletions examples/http_forward_proxy/dub.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "http-forward-proxy-example",
"description": "Sets up a simple forward proxy.",
"dependencies": {
"vibe-d:http": {"path": "../../"}
},
"versions": ["VibeDefaultMain"]
}
13 changes: 13 additions & 0 deletions examples/http_forward_proxy/source/app.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import vibe.appmain;
import vibe.http.proxy;
import vibe.http.server;


shared static this()
{
auto settings = new HTTPServerSettings;
settings.port = 8080;
settings.bindAddresses = ["::1", "127.0.0.1"];

listenHTTPForwardProxy(settings);
}
61 changes: 48 additions & 13 deletions http/vibe/http/proxy.d
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import std.exception;
TODO:
- use a client pool
- implement a path based reverse proxy
- implement a forward proxy
*/

/**
Expand All @@ -31,7 +30,7 @@ import std.exception;
You can use the hostName field in the 'settings' to combine multiple internal HTTP servers
into one public web server with multiple virtual hosts.
*/
void listenHTTPReverseProxy(HTTPServerSettings settings, HTTPReverseProxySettings proxy_settings)
void listenHTTPReverseProxy(HTTPServerSettings settings, HTTPProxySettings proxy_settings)
{
// disable all advanced parsing in the server
settings.options = HTTPServerOption.None;
Expand All @@ -44,29 +43,45 @@ void listenHTTPReverseProxy(HTTPServerSettings settings, string destination_host
url.schema = "http";
url.host = destination_host;
url.port = destination_port;
auto proxy_settings = new HTTPReverseProxySettings;
auto proxy_settings = new HTTPProxySettings;
proxy_settings.reverseProxyMode = true;
proxy_settings.destination = url;
listenHTTPReverseProxy(settings, proxy_settings);
}

/**
Transparently forwards all requests to the proxy to the requestURL of the request.
*/
void listenHTTPForwardProxy(HTTPServerSettings settings) {
listenHTTP(settings, forwardProxyRequest(new HTTPProxySettings));
}

/// ditto
void listenHTTPForwardProxy(HTTPServerSettings settings, HTTPProxySettings proxy_settings) {
listenHTTP(settings, forwardProxyRequest(proxy_settings));
}

/**
Returns a HTTP request handler that forwards any request to the specified host/port.
Generic proxy delegate that will work with both forward and reverse proxies
*/
HTTPServerRequestDelegateS reverseProxyRequest(HTTPReverseProxySettings settings)
HTTPServerRequestDelegateS proxyRequest(HTTPProxySettings settings)
{
static immutable string[] non_forward_headers = ["Content-Length", "Transfer-Encoding", "Content-Encoding", "Connection"];
static InetHeaderMap non_forward_headers_map;
if (non_forward_headers_map.length == 0)
foreach (n; non_forward_headers)
non_forward_headers_map[n] = "";

auto url = settings.destination;

void handleRequest(scope HTTPServerRequest req, scope HTTPServerResponse res)
@safe {
auto rurl = url;
rurl.localURI = req.requestURL;
auto url = settings.destination;

if (settings.reverseProxyMode) {
url.localURI = req.requestURL;
}
else {
url = URL(req.requestURL);
}

//handle connect tunnels
if (req.method == HTTPMethod.CONNECT) {
Expand Down Expand Up @@ -185,38 +200,56 @@ HTTPServerRequestDelegateS reverseProxyRequest(HTTPReverseProxySettings settings
else cres.bodyReader.pipe(res.bodyWriter);
}

try requestHTTP(rurl, &setupClientRequest, &handleClientResponse);
try requestHTTP(url, &setupClientRequest, &handleClientResponse);
catch (Exception e) {
throw new HTTPStatusException(HTTPStatus.badGateway, "Connection to upstream server failed: "~e.msg);
}
}

return &handleRequest;
}

/**
Returns a HTTP request handler that acts as a forward proxy
*/
HTTPServerRequestDelegateS forwardProxyRequest(HTTPProxySettings settings)
{
return proxyRequest(settings);
}

/**
Returns a HTTP request handler that forwards any request to the specified host/port.
*/
HTTPServerRequestDelegateS reverseProxyRequest(HTTPProxySettings settings)
{
return proxyRequest(settings);
}
/// ditto
HTTPServerRequestDelegateS reverseProxyRequest(string destination_host, ushort destination_port)
{
URL url;
url.schema = "http";
url.host = destination_host;
url.port = destination_port;
auto settings = new HTTPReverseProxySettings;
auto settings = new HTTPProxySettings;
settings.reverseProxyMode = true;
settings.destination = url;
return reverseProxyRequest(settings);
}

/// ditto
HTTPServerRequestDelegateS reverseProxyRequest(URL destination)
{
auto settings = new HTTPReverseProxySettings;
auto settings = new HTTPProxySettings;
settings.reverseProxyMode = true;
settings.destination = destination;
return reverseProxyRequest(settings);
}

/**
Provides advanced configuration facilities for reverse proxy servers.
*/
final class HTTPReverseProxySettings {
final class HTTPProxySettings {
/// Scheduled for deprecation - use `destination.host` instead.
@property string destinationHost() const { return destination.host; }
/// ditto
Expand All @@ -226,6 +259,8 @@ final class HTTPReverseProxySettings {
/// ditto
@property void destinationPort(ushort port) { destination.port = port; }

/// Whether the proxy should act as forward or reverse proxy
bool reverseProxyMode = false;
/// The destination URL to forward requests to
URL destination = URL("http", InetPath(""));
/// Avoids compressed transfers between proxy and destination hosts
Expand Down

0 comments on commit 5537d6e

Please sign in to comment.