Skip to content

Commit

Permalink
Merge pull request #1893 from mattremmel/master
Browse files Browse the repository at this point in the history
Added forward proxy support  and included an example
  • Loading branch information
s-ludwig committed Nov 6, 2017
2 parents 8972ceb + 1b9a1b7 commit 41ac1a4
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 21 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);
}
87 changes: 66 additions & 21 deletions http/vibe/http/proxy.d
Original file line number Diff line number Diff line change
Expand Up @@ -22,51 +22,70 @@ import std.exception;
TODO:
- use a client pool
- implement a path based reverse proxy
- implement a forward proxy
*/

/**
Transparently forwards all requests to the proxy to a destination_host.
Transparently forwards all requests to the proxy to another host.
You can use the hostName field in the 'settings' to combine multiple internal HTTP servers
into one public web server with multiple virtual hosts.
The configurations set in 'settings' and 'proxy_settings' determines the exact
behavior.
*/
void listenHTTPReverseProxy(HTTPServerSettings settings, HTTPReverseProxySettings proxy_settings)
void listenHTTPProxy(HTTPServerSettings settings, HTTPProxySettings proxy_settings)
{
// disable all advanced parsing in the server
settings.options = HTTPServerOption.None;
listenHTTP(settings, reverseProxyRequest(proxy_settings));
listenHTTP(settings, proxyRequest(proxy_settings));
}
/// ditto
// Compatibility alias - will be deprecated soon.
alias listenHTTPReverseProxy = listenHTTPProxy;

/**
Transparently forwards all requests to the proxy to a destination_host.
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, string destination_host, ushort destination_port)
{
URL url;
url.schema = "http";
url.host = destination_host;
url.port = destination_port;
auto proxy_settings = new HTTPReverseProxySettings;
auto proxy_settings = new HTTPProxySettings(ProxyMode.reverse);
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) {
auto proxy_settings = new HTTPProxySettings(ProxyMode.forward);
proxy_settings.handleConnectRequests = true;
listenHTTPProxy(settings, proxy_settings);
}

/**
Returns a HTTP request handler that forwards any request to the specified host/port.
Returns a HTTP request handler that forwards any request to the specified or requested host/port.
*/
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.proxyMode == ProxyMode.reverse) {
url.localURI = req.requestURL;
}
else {
url = URL(req.requestURL);
}

//handle connect tunnels
if (req.method == HTTPMethod.CONNECT) {
Expand Down Expand Up @@ -185,38 +204,55 @@ 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;
}
/// ditto
/// Compatibility alias - will be deprecated soon
alias reverseProxyRequest = proxyRequest;

/**
Returns a HTTP request handler that forwards any request to the specified host/port.
*/
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(ProxyMode.reverse);
settings.destination = url;
return reverseProxyRequest(settings);
return proxyRequest(settings);
}

/// ditto
HTTPServerRequestDelegateS reverseProxyRequest(URL destination)
{
auto settings = new HTTPReverseProxySettings;
auto settings = new HTTPProxySettings(ProxyMode.reverse);
settings.destination = destination;
return reverseProxyRequest(settings);
return proxyRequest(settings);
}

/**
Returns a HTTP request handler that forwards any request to the requested host/port.
*/
HTTPServerRequestDelegateS forwardProxyRequest() {
return proxyRequest(new HTTPProxySettings(ProxyMode.forward));
}

/**
Enum to represent the two modes a proxy can operate as.
*/
enum ProxyMode {forward, reverse}

/**
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 @@ -228,8 +264,17 @@ final class HTTPReverseProxySettings {

/// The destination URL to forward requests to
URL destination = URL("http", InetPath(""));
/// The mode of the proxy i.e forward, reverse
ProxyMode proxyMode;
/// Avoids compressed transfers between proxy and destination hosts
bool avoidCompressedRequests;
/// Handle CONNECT requests for creating a tunnel to the destination host
bool handleConnectRequests;

/// Empty default constructor for backwards compatibility - will be deprecated soon.
this() { }
/// Explicitly sets the proxy mode.
this(ProxyMode mode) { proxyMode = mode; }
}
/// Compatibility alias - will be deprecated soon.
alias HTTPReverseProxySettings = HTTPProxySettings;

0 comments on commit 41ac1a4

Please sign in to comment.