背景
反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
简单来说,你的反向代理服务器会接收请求,但其自身不处理该请求,而是对请求经过一些处理,例如添加日志、缓存、身份验证等服务,然后再将请求转发到相应的应用服务器中进行处理,最后将处理结果返回。
HTTP-Proxy-Servlet是基于Filter进行服务代理,只需要进行相关的配置之后,就能进行服务代理,同时核心文件只有2个,容易理解和进行改动,但是存在的问题是只能代理一个url,不能代理多个不同的url,这部分是可以基于源码进行改造。
源码目录
- http-proxy-servlet的源码文件非常简单,只有两个文件。
- ProxyServlet.java重写了spring的HttpServlet,参数初始化都在ProxyServlet初始化。
- 针对改造我们只需要改造ProxyServlet.java文件即可。
源码改造
- http-proxy-servlet的代理配置针对代理URL只能定位到一个URL,如果想内部逻辑判断转发多个URL只能进行代码改造。
- 核心改造逻辑在于改造init()方法、initTarget()方法、service()方法。
- initTarget()方法内部解析spring的配置初始化核心对象。
- service()方法内部根据逻辑判断重定向逻辑。
public class ProxyServlet extends HttpServlet {
// 省略相关代码
protected String targetUri;
protected URI targetUriObj;//new URI(targetUri)
protected HttpHost targetHost;//URIUtils.extractHost(targetUriObj);
// 晴天哥新增代码
protected String nTargetUri;
protected URI nTargetUriObj;//new URI(nTargetUri)
protected HttpHost nTargetHost;//URIUtils.extractHost(nTargetUriObj);
private static AtomicLong counter = new AtomicLong(0);
private HttpClient proxyClient;
@Override
public void init() throws ServletException {
// 省略相关代码
// 核心在于初始化这个链接
initTarget();//sets target*
proxyClient = createHttpClient(buildRequestConfig());
}
protected RequestConfig buildRequestConfig() {
// 省略相关代码
}
protected void initTarget() throws ServletException {
targetUri = getConfigParam(P_TARGET_URI);
if (targetUri == null) {
throw new ServletException(P_TARGET_URI + " is required.");
}
//test it's valid
try {
targetUriObj = new URI(targetUri);
} catch (Exception e) {
throw new ServletException("Trying to process targetUri init parameter: "+e,e);
}
targetHost = URIUtils.extractHost(targetUriObj);
// 晴天哥新增代码
nTargetUri = getConfigParam(N_P_TARGET_URI);
if (null == nTargetUri) {
throw new ServletException(N_P_TARGET_URI + " is required.");
}
try {
nTargetUriObj = new URI(nTargetUri);
} catch (Exception e) {
throw new ServletException("Trying to process targetUri init parameter: "+e,e);
}
nTargetHost = URIUtils.extractHost(nTargetUriObj);
}
protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws ServletException, IOException {
// 晴天哥新增代码
long flag = counter.incrementAndGet();
if (servletRequest.getAttribute(ATTR_TARGET_URI) == null) {
if (0 == flag % 2) {
servletRequest.setAttribute(ATTR_TARGET_URI, targetUri);
} else {
servletRequest.setAttribute(ATTR_TARGET_URI, nTargetUri);
}
}
if (servletRequest.getAttribute(ATTR_TARGET_HOST) == null) {
if (0 == flag % 2) {
servletRequest.setAttribute(ATTR_TARGET_HOST, targetHost);
} else {
servletRequest.setAttribute(ATTR_TARGET_HOST, nTargetHost);
}
}
String method = servletRequest.getMethod();
String proxyRequestUri = rewriteUrlFromRequest(servletRequest);
HttpRequest proxyRequest;
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 {
// 执行请求部分逻辑
proxyResponse = doExecute(servletRequest, servletResponse, proxyRequest);
// 省略相关代码
} catch (Exception e) {
handleRequestException(proxyRequest, e);
}
}
}
demo
- 1、修改源码后mvn打包生成jar包。
- 2、引入pom.xml依赖包
<dependencies>
<dependency>
<groupId>org.mitre.dsmiley.httpproxy</groupId>
<artifactId>smiley-http-proxy-servlet</artifactId>
<version>1.11-SNAPSHOT</version>
</dependency>
</dependencies>
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>solr</servlet-name>
<servlet-class>org.mitre.dsmiley.httpproxy.ProxyServlet</servlet-class>
<init-param>
<param-name>targetUri</param-name>
<param-value>http://sports.sina.com.cn:80/nba/</param-value>
</init-param>
<init-param>
<param-name>n_targetUri</param-name>
<param-value>http://sports.qq.com:80</param-value>
</init-param>
<init-param>
<param-name>log</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>solr</servlet-name>
<url-pattern>/solr/*</url-pattern>
</servlet-mapping>
</web-app>
效果图
- localhost:8080/solr的请求可以定向到新浪体育或腾讯体育地址
参考地址
https://github.com/lebron374/HTTP-Proxy-Servlet