Skip to content

Commit

Permalink
More customizable reverse proxy, with built-in configuration.
Browse files Browse the repository at this point in the history
  • Loading branch information
nmihajlovski committed Mar 6, 2017
1 parent 10c0af9 commit f2ae8a3
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 26 deletions.
Expand Up @@ -58,6 +58,7 @@ public Config map(String name) throws Exception {
public static final Config APP = section("app"); public static final Config APP = section("app");
public static final Config GUI = section("gui"); public static final Config GUI = section("gui");
public static final Config HTTP = section("http"); public static final Config HTTP = section("http");
public static final Config REVERSE_PROXY = section("reverse-proxy");
public static final Config NET = section("net"); public static final Config NET = section("net");
public static final Config ON = section("on"); public static final Config ON = section("on");
public static final Config ADMIN = section("admin"); public static final Config ADMIN = section("admin");
Expand Down
7 changes: 7 additions & 0 deletions rapidoid-commons/src/main/resources/built-in-config.yml
Expand Up @@ -117,6 +117,13 @@ http:
server: true server: true
contentType: true contentType: true


reverse-proxy:
timeout: 10000
retryDelay: 300
maxConnections: 100
maxConnectionsPerRoute: 100
reuseConnections: true

token: token:
ttl: 0 # unlimited ttl: 0 # unlimited


Expand Down
Expand Up @@ -3,6 +3,8 @@
import org.rapidoid.RapidoidThing; import org.rapidoid.RapidoidThing;
import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since; import org.rapidoid.annotation.Since;
import org.rapidoid.config.Conf;
import org.rapidoid.config.Config;
import org.rapidoid.http.HttpClient; import org.rapidoid.http.HttpClient;
import org.rapidoid.util.LazyInit; import org.rapidoid.util.LazyInit;


Expand Down Expand Up @@ -32,11 +34,17 @@
@Since("5.2.0") @Since("5.2.0")
public abstract class AbstractReverseProxyBean<T> extends RapidoidThing { public abstract class AbstractReverseProxyBean<T> extends RapidoidThing {


private volatile boolean reuseConnections = true; private static final Config CFG = Conf.REVERSE_PROXY;


private volatile int maxConnTotal = 100; private volatile long retryDelay = CFG.entry("retryDelay").or(300);


private volatile int maxConnPerRoute = 100; private volatile long timeout = CFG.entry("timeout").or(10000);

private volatile boolean reuseConnections = CFG.entry("reuseConnections").or(true);

private volatile int maxConnections = CFG.entry("maxConnections").or(100);

private volatile int maxConnectionsPerRoute = CFG.entry("maxConnectionsPerRoute").or(100);


private final LazyInit<HttpClient> client = new LazyInit<HttpClient>(new Callable<HttpClient>() { private final LazyInit<HttpClient> client = new LazyInit<HttpClient>(new Callable<HttpClient>() {
@Override @Override
Expand All @@ -61,22 +69,40 @@ public T reuseConnections(boolean reuseConnections) {
return me(); return me();
} }


public int maxConnTotal() { public long retryDelay() {
return maxConnTotal; return retryDelay;
} }


public T maxConnTotal(int maxConnTotal) { public AbstractReverseProxyBean retryDelay(int retryDelay) {
this.maxConnTotal = maxConnTotal; this.retryDelay = retryDelay;
return me(); return this;
} }


public int maxConnPerRoute() { public long timeout() {
return maxConnPerRoute; return timeout;
} }


public T maxConnPerRoute(int maxConnPerRoute) { public AbstractReverseProxyBean timeout(int timeout) {
this.maxConnPerRoute = maxConnPerRoute; this.timeout = timeout;
return me(); return this;
}

public int maxConnections() {
return maxConnections;
}

public AbstractReverseProxyBean maxConnections(int maxConnections) {
this.maxConnections = maxConnections;
return this;
}

public int maxConnectionsPerRoute() {
return maxConnectionsPerRoute;
}

public AbstractReverseProxyBean maxConnectionsPerRoute(int maxConnectionsPerRoute) {
this.maxConnectionsPerRoute = maxConnectionsPerRoute;
return this;
} }


public HttpClient client() { public HttpClient client() {
Expand Down
Expand Up @@ -8,6 +8,7 @@
import org.rapidoid.job.Jobs; import org.rapidoid.job.Jobs;
import org.rapidoid.log.LogLevel; import org.rapidoid.log.LogLevel;
import org.rapidoid.u.U; import org.rapidoid.u.U;
import org.rapidoid.util.Msc;


import java.io.IOException; import java.io.IOException;
import java.net.ConnectException; import java.net.ConnectException;
Expand Down Expand Up @@ -37,10 +38,6 @@
@Since("5.2.0") @Since("5.2.0")
public class ReverseProxy extends AbstractReverseProxyBean<ReverseProxy> implements ReqRespHandler { public class ReverseProxy extends AbstractReverseProxyBean<ReverseProxy> implements ReqRespHandler {


private static final int RETRY_AFTER_MS = 300;

private static final int TIMEOUT_MS = 10000;

private final ProxyMapping mapping; private final ProxyMapping mapping;


public ReverseProxy(ProxyMapping mapping) { public ReverseProxy(ProxyMapping mapping) {
Expand All @@ -64,10 +61,11 @@ protected ProxyMapping findMapping(Req req) {
return mapping; // customizable for more complex logic return mapping; // customizable for more complex logic
} }


protected void process(final Req req, final Resp resp, final ProxyMapping mapping, final int attempts, final long since) { private void process(final Req req, final Resp resp, final ProxyMapping mapping, final int attempts, final long since) {
final String targetUrl = mapping.getTargetUrl(req); final String targetUrl = mapping.getTargetUrl(req);


Map<String, String> headers = req.headers(); Map<String, String> headers = U.map(req.headers());

headers.remove("transfer-encoding"); headers.remove("transfer-encoding");
headers.remove("content-length"); headers.remove("content-length");


Expand Down Expand Up @@ -107,12 +105,12 @@ public void onDone(HttpResp result, Throwable error) {
}); });
} }


protected void handleError(Throwable error, final Req req, final Resp resp, final ProxyMapping mapping, final int attempts, final long since) { private void handleError(Throwable error, final Req req, final Resp resp, final ProxyMapping mapping, final int attempts, final long since) {
if (error instanceof ConnectException || error instanceof IOException) { if (error instanceof ConnectException || error instanceof IOException) {


if (HttpUtils.isGetReq(req) && (U.time() - since < TIMEOUT_MS)) { if (HttpUtils.isGetReq(req) && !Msc.timedOut(since, timeout())) {


Jobs.after(RETRY_AFTER_MS).milliseconds(new Runnable() { Jobs.after(retryDelay()).milliseconds(new Runnable() {
@Override @Override
public void run() { public void run() {
process(req, resp, mapping, attempts + 1, since); process(req, resp, mapping, attempts + 1, since);
Expand All @@ -134,8 +132,8 @@ protected HttpClient createClient() {
return HTTP.client() return HTTP.client()
.reuseConnections(reuseConnections()) .reuseConnections(reuseConnections())
.keepCookies(false) .keepCookies(false)
.maxConnTotal(maxConnTotal()) .maxConnTotal(maxConnections())
.maxConnPerRoute(maxConnPerRoute()); .maxConnPerRoute(maxConnectionsPerRoute());
} }


} }
Expand Up @@ -6,6 +6,7 @@
import org.rapidoid.log.Log; import org.rapidoid.log.Log;
import org.rapidoid.setup.On; import org.rapidoid.setup.On;
import org.rapidoid.setup.OnRoute; import org.rapidoid.setup.OnRoute;
import org.rapidoid.setup.Setup;
import org.rapidoid.u.U; import org.rapidoid.u.U;


import java.util.List; import java.util.List;
Expand Down Expand Up @@ -59,15 +60,15 @@ public ReverseProxyMapDSL to(List<String> upstreams) {
return this; return this;
} }


public ReverseProxy add() { public ReverseProxy addTo(Setup setup) {
Log.info("!Reverse proxy mapping", "!uriPrefix", uriPrefix, "!upstreams", upstreams); Log.info("!Reverse proxy mapping", "!uriPrefix", uriPrefix, "!upstreams", upstreams);


ReverseProxy proxy = createReverseProxy(); ReverseProxy proxy = createReverseProxy();


U.must(uriPrefix.startsWith("/"), "The URI prefix must start with '/'"); U.must(uriPrefix.startsWith("/"), "The URI prefix must start with '/'");


String path = uriPrefix.equals("/") ? "/*" : uriPrefix + "/*"; String path = uriPrefix.equals("/") ? "/*" : uriPrefix + "/*";
OnRoute route = On.any(path); OnRoute route = setup.any(path);


if (roles != null) route.roles(roles); if (roles != null) route.roles(roles);
if (cacheTTL != null) route.cacheTTL(cacheTTL); if (cacheTTL != null) route.cacheTTL(cacheTTL);
Expand All @@ -78,6 +79,10 @@ public ReverseProxy add() {
return proxy; return proxy;
} }


public ReverseProxy add() {
return addTo(On.setup());
}

private ReverseProxy createReverseProxy() { private ReverseProxy createReverseProxy() {
List<ProxyUpstream> proxyUpstreams = U.list(); List<ProxyUpstream> proxyUpstreams = U.list();


Expand Down

0 comments on commit f2ae8a3

Please sign in to comment.