Java做个反向代理服务器(配置文件,Cors方式跨域)(二)

2018-09-02  本文已影响0人  冰鱼飞鸟

接上一篇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

上一篇下一篇

猜你喜欢

热点阅读