web proxy server by jetty |
Jetty9을 활용하면 쉽게 Web Proxy Server를 구축할 수 있다.
아래의 소스는 ProxyServlet을 활용하여 특정 호스트 이름으로 들어오는 웹요청을 우리가 원하는 응답으로 바꾸거나 변조하는 예제를 담고 있다.
아래의 소스는 ProxyServlet을 활용하여 특정 호스트 이름으로 들어오는 웹요청을 우리가 원하는 응답으로 바꾸거나 변조하는 예제를 담고 있다.
Maven
<dependency>
<groupid>org.eclipse.jetty</groupid>
<artifactid>jetty-server</artifactid>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupid>org.eclipse.jetty</groupid>
<artifactid>jetty-proxy</artifactid>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupid>org.eclipse.jetty</groupid>
<artifactid>jetty-servlet</artifactid>
<version>${jetty.version}</version>
</dependency>
Main
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8080);
server.addConnector(connector);
ConnectHandler proxy = new ConnectHandler();
server.setHandler(proxy);
CustomProxyServlet customProxyServlet = new CustomProxyServlet();
customProxyServlet.addHostFilter("maybe.somewhere.com", new DefaultFilterableHost()).referHostFilterByUrl("maybe.somewhere.com", "mightbe.somewhere.com");
// Setup proxy servlet
ServletContextHandler context = new ServletContextHandler(proxy, "/", ServletContextHandler.SESSIONS);
ServletHolder proxyServlet = new ServletHolder(customProxyServlet);
proxyServlet.setInitParameter("maxThreads", "10");
context.addServlet(proxyServlet, "/*");
server.start();
server.join();
Filterable Host Interface
public interface FilterableHost {
boolean canHandle(URL url);
void process(final HttpServletRequest request, final HttpServletResponse response);
}
Default Filterable Host
public class DefaultFilterableHost implements FilterableHost {
@Override
public boolean canHandle(URL url) {
return url.getPath().endsWith("/somepath");
}
@Override
public void process(HttpServletRequest request, HttpServletResponse response) {
response.setStatus(HttpStatus.OK_200);
response.addHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.KEEP_ALIVE.asString());
response.addHeader(HttpHeader.TRANSFER_ENCODING.asString(), HttpHeaderValue.CHUNKED.asString());
response.getOutputStream().write("some your revised response");
return response;
}
}
Proxy servlet
public class CustomProxyServlet extends ProxyServlet {
private final Map filterMap;
public CustomProxyServlet() {
this.filterMap = new HashMap<>();
}
public CustomProxyServlet referHostFilterByUrl(String fromUrl, String toUrl) {
if(filterMap.containsKey(fromUrl)) {
filterMap.put(toUrl, filterMap.get(fromUrl));
return this;
} else {
throw new IllegalStateException("can't find " + fromUrl + " in filterMap. you should put hostFilter for " + fromUrl + " first");
}
}
public CustomProxyServlet addHostFilter(String url, FilterableHost filter) {
if(filterMap.containsKey(url) == false) {
filterMap.put(url, filter);
return this;
} else{
throw new IllegalStateException("host url " + url + " already exists");
}
}
@Override
protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
URL url = new URL(request.getRequestURL().toString()); // Extract host from URL
if(filterMap.containsKey(url.getHost())) {
final FilterableHost filterableHost = filterMap.get(url.getHost());
if(filterableHost.canHandle(url)) {
final AsyncContext asyncContext = request.startAsync();
// We do not timeout the continuation, but the proxy request
asyncContext.setTimeout(0);
filterableHost.process(request, response);
asyncContext.complete();
}
} else {
super.service(request, response);
}
}
@Override
protected Response.Listener newProxyResponseListener(HttpServletRequest request, HttpServletResponse response)
{
return new CustomProxyServletProxyResponseListener(request, response);
}
protected class CustomProxyServletProxyResponseListener extends Response.Listener.Adapter
{
private final HttpServletRequest request;
private final HttpServletResponse response;
protected CustomProxyServletProxyResponseListener(HttpServletRequest request, HttpServletResponse response)
{
this.request = request;
this.response = response;
}
@Override
public void onBegin(Response proxyResponse)
{
response.setStatus(proxyResponse.getStatus());
}
@Override
public void onHeaders(Response proxyResponse)
{
onServerResponseHeaders(request, response, proxyResponse);
}
@Override
public void onContent(final Response proxyResponse, ByteBuffer content, final Callback callback)
{
byte[] buffer;
int offset;
int length = content.remaining();
if (content.hasArray())
{
buffer = content.array();
offset = content.arrayOffset();
}
else
{
buffer = new byte[length];
content.get(buffer);
offset = 0;
}
onResponseContent(request, response, proxyResponse, buffer, offset, length, new Callback.Nested(callback)
{
@Override
public void failed(Throwable x)
{
super.failed(x);
proxyResponse.abort(x);
}
});
}
@Override
public void onComplete(Result result)
{
if (result.isSucceeded())
onProxyResponseSuccess(request, response, result.getResponse());
else
onProxyResponseFailure(request, response, result.getResponse(), result.getFailure());
}
}
}
Full source: https://gist.github.com/magicsih/cd9fa8b96e58fa7a8f977c82ad7a8d5f