接上一篇Java不到10行代码做个简单的反向代理服务器(一)
1.配置每个代理服务器至少需要一个代理访问的地址,和一个目标服务器的地址。我们需要代理多个。为了方便扩展,键值对形式的配置文件显然不是很适合。这里采用xml格式,配置文件格式初步设计成这样。
<Proxy>
<defaultSettings>
<port>8081</port>
<basePath>/</basePath>
</defaultSettings>
<ProxyServices>
<ProxyService>
<proxyUrl>/jianshu/*</proxyUrl>
<targetUrl>https://www.jianshu.com</targetUrl>
</ProxyService>
<ProxyService>
<proxyUrl>/baidu/*</proxyUrl>
<targetUrl>https://www.baidu.com</targetUrl>
</ProxyService>
<ProxyService>
<proxyUrl>/test/*</proxyUrl>
<targetUrl>http://localhost:9090</targetUrl>
</ProxyService>
</ProxyServices>
</Proxy>
2.用Cors的方式支持跨域。
原ProxyServlet并没有这个功能,所以我们继承ProxyServlet,给其扩展一下。
先阅读一下ProxyServlet的service的源码如下
@Override
protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws ServletException, IOException {
//initialize request attributes from caches if unset by a subclass by this point
if (servletRequest.getAttribute(ATTR_TARGET_URI) == null) {
servletRequest.setAttribute(ATTR_TARGET_URI, targetUri);
}
if (servletRequest.getAttribute(ATTR_TARGET_HOST) == null) {
servletRequest.setAttribute(ATTR_TARGET_HOST, targetHost);
}
// Make the Request
//note: we won't transfer the protocol version because I'm not sure it would truly be compatible
String method = servletRequest.getMethod();
String proxyRequestUri = rewriteUrlFromRequest(servletRequest);
HttpRequest proxyRequest;
//spec: RFC 2616, sec 4.3: either of these two headers signal that there is a message body.
if (servletRequest.getHeader(HttpHeaders.CONTENT_LENGTH) != null ||
servletRequest.getHeader(HttpHeaders.TRANSFER_ENCODING) != null) {
proxyRequest = newProxyRequestWithEntity(method, proxyRequestUri, servletRequest);
} else {
proxyRequest = new BasicHttpRequest(method, proxyRequestUri);
}
copyRequestHeaders(servletRequest, proxyRequest);
setXForwardedForHeader(servletRequest, proxyRequest);
HttpResponse proxyResponse = null;
try {
// Execute the request
proxyResponse = doExecute(servletRequest, servletResponse, proxyRequest);
// Process the response:
// Pass the response code. This method with the "reason phrase" is deprecated but it's the
// only way to pass the reason along too.
int statusCode = proxyResponse.getStatusLine().getStatusCode();
//noinspection deprecation
servletResponse.setStatus(statusCode, proxyResponse.getStatusLine().getReasonPhrase());
// Copying response headers to make sure SESSIONID or other Cookie which comes from the remote
// server will be saved in client when the proxied url was redirected to another one.
// See issue [#51](https://github.com/mitre/HTTP-Proxy-Servlet/issues/51)
copyResponseHeaders(proxyResponse, servletRequest, servletResponse);
if (statusCode == HttpServletResponse.SC_NOT_MODIFIED) {
// 304 needs special handling. See:
// http://www.ics.uci.edu/pub/ietf/http/rfc1945.html#Code304
// Don't send body entity/content!
servletResponse.setIntHeader(HttpHeaders.CONTENT_LENGTH, 0);
} else {
// Send the content to the client
copyResponseEntity(proxyResponse, servletResponse, proxyRequest, servletRequest);
}
} catch (Exception e) {
handleRequestException(proxyRequest, e);
} finally {
// make sure the entire entity was consumed, so the connection is released
if (proxyResponse != null)
consumeQuietly(proxyResponse.getEntity());
//Note: Don't need to close servlet outputStream:
// http://stackoverflow.com/questions/1159168/should-one-call-close-on-httpservletresponse-getoutputstream-getwriter
}
}
它是调用httpClient的api来完成转发请求的。
servlet中是ServletRequest和ServletResponse。
httpClient中是HttpRequest和HttpResponse。
其service方法中做的内容是
1.根据ServletRequest构建httpRequest。(内容的拷贝)
2.根据httpRequest去获取httpResponse。
3.把httpResponse的内容填充到ServletResponse中。(也是内容的拷贝)
所以我们要往response中setHeader的话,可以在第三步之前在httpResponse中setHeader,也可以在第三步之后往ServletResponse中setHeader。
通过阅读源码可以得知httpResponse就是调用doExecute方法返回的,所以我们重写doExecute方法。
public class MyProxyServlet extends ProxyServlet{
@Override
protected HttpResponse doExecute(HttpServletRequest servletRequest, HttpServletResponse servletResponse,
HttpRequest proxyRequest) throws IOException {
HttpResponse response = super.doExecute(servletRequest, servletResponse, proxyRequest);
String origin = servletRequest.getHeader("origin");
response.setHeader("Access-Control-Allow-Origin",origin);
response.setHeader("Access-Control-Allow-Credentials","true");
response.setHeader("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers",
"Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Mx-ReqToken,X-Requested-With");
return response;
}
}
最后,项目的地址:proxyservice
网友评论