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

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

作者: 冰鱼飞鸟 | 来源:发表于2018-09-02 00:27 被阅读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

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/dzdpwftx.html