All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.deepexi.devops.proxy.support.httpclient.HttpClientProxy Maven / Gradle / Ivy

package com.deepexi.devops.proxy.support.httpclient;

import com.deepexi.devops.proxy.AbstractProxy;
import com.deepexi.devops.proxy.FullHttpRequest;
import com.deepexi.devops.proxy.RequestContext;
import com.deepexi.devops.proxy.exception.*;
import lombok.Getter;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpCookie;
import java.util.Enumeration;
import java.util.Objects;

import static org.apache.http.HttpHeaders.CONTENT_LENGTH;
import static org.apache.http.HttpHeaders.HOST;
import static org.apache.http.cookie.SM.SET_COOKIE;
import static org.apache.http.cookie.SM.SET_COOKIE2;
import static org.apache.http.protocol.HTTP.TRANSFER_ENCODING;

/**
 * 基于 HttpClient 实现的 Proxy
 *
 * @author linyuan - [email protected]
 * @since 2019-12-20
 */
public class HttpClientProxy extends AbstractProxy {

    @Getter
    private CloseableHttpClient httpClient;
    protected FullHttpRequest proxyRequest;
    protected HttpResponse proxyResponse;

    public HttpClientProxy(RequestContext requestContext, CloseableHttpClient httpClient) {
        super(requestContext);
        this.httpClient = httpClient;
    }

    @Override
    public void proxy() throws ProxyRequestException {
        checkTargetHost();
        initProxyRequest();
        setXForwardedForHeader();
        doProxyRequest();
    }

    @Override
    public void response() throws ProxyResponseException {
        try {
            setResponseStatus();
            setResponseHeaders();
            setResponseEntity();
        } finally {
            if (Objects.nonNull(proxyResponse)) {
                EntityUtils.consumeQuietly(proxyResponse.getEntity());
            }
        }
    }

    @Override
    public void destroy() throws ProxyDestroyException {
        try {
            if (Objects.nonNull(httpClient)) {
                httpClient.close();
            }
        } catch (Exception e) {
            throw new ProxyDestroyException(String.format("HttpClientProxy对象资源销毁失败,错误信息:", e.getMessage()), getRequestContext());
        }
    }

    protected void setResponseStatus() {
        getRequestContext()
                .getResponse()
                .setStatus(proxyResponse.getStatusLine().getStatusCode());
    }

    protected void setResponseHeaders() {
        for (Header header : proxyResponse.getAllHeaders()) {
            copyResponseHeader(header);
        }
    }

    protected void setResponseEntity() {
        try {
            HttpEntity entity = proxyResponse.getEntity();
            if (entity != null) {
                OutputStream stream = getRequestContext().getResponse().getOutputStream();
                entity.writeTo(stream);
            }
        } catch (IOException e) {
            throw new ProxyResponseException(String.format("更新响应体失败,错误信息:", e.getMessage()), getRequestContext());
        }
    }

    private void copyResponseHeader(Header header) {
        String headerName = header.getName();
        if (getSkipHeaders().containsHeader(headerName)) {
            return;
        }
        String headerValue = header.getValue();
        if (headerName.equalsIgnoreCase(SET_COOKIE) || headerName.equalsIgnoreCase(SET_COOKIE2)) {
            copyProxyCookie(headerValue);
        } else {
            getRequestContext()
                    .getResponse()
                    .addHeader(headerName, headerValue);
        }
    }

    private void copyProxyCookie(String headerValue) {
        HttpServletRequest request = getRequestContext().getRequest();
        String path = request.getContextPath() + request.getServletPath();
        if (path.isEmpty()) {
            path = "/";
        }

        for (HttpCookie cookie : HttpCookie.parse(headerValue)) {
            String proxyCookieName = cookie.getName();
            Cookie servletCookie = new Cookie(proxyCookieName, cookie.getValue());
            servletCookie.setComment(cookie.getComment());
            servletCookie.setMaxAge((int) cookie.getMaxAge());
            servletCookie.setPath(path);
            servletCookie.setSecure(cookie.getSecure());
            servletCookie.setVersion(cookie.getVersion());
            servletCookie.setHttpOnly(cookie.isHttpOnly());
            getRequestContext().getResponse().addCookie(servletCookie);
        }
    }

    protected void doProxyRequest() {
        try {
            proxyResponse = httpClient.execute(proxyRequest);
        } catch (Exception e) {
            throw new ProxyRequestException(e.getMessage(), getRequestContext());
        }
    }

    protected void initProxyRequest() {
        proxyRequest = new FullHttpRequest();
        proxyRequest.setMethod(getRequestContext().getRequest().getMethod());
        proxyRequest.setURI(getRequestContext().getProxyURI());
        proxyRequest.setEntity(getRequestBody());
        initRequestHeaders();
        setXForwardedForHeader();
    }

    protected void initRequestHeaders() {
        Enumeration headerNames = getRequestContext().getRequest().getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            copyProxyRequestHeader(headerName);
        }
    }

    protected void setXForwardedForHeader() {
        HttpServletRequest request = getRequestContext().getRequest();
        String forHeaderName = "X-Forwarded-For";
        String forHeader = request.getRemoteAddr();
        String existingForHeader = request.getHeader(forHeaderName);
        if (existingForHeader != null) {
            forHeader = existingForHeader + ", " + forHeader;
        }
        proxyRequest.setHeader(forHeaderName, forHeader);

        String protoHeaderName = "X-Forwarded-Proto";
        String protoHeader = request.getScheme();
        proxyRequest.setHeader(protoHeaderName, protoHeader);
    }

    protected HttpEntity getRequestBody() {
        try {
            HttpServletRequest request = getRequestContext().getRequest();
            if (request.getHeader(CONTENT_LENGTH) != null || request.getHeader(TRANSFER_ENCODING) != null) {
                InputStreamEntity entity = new InputStreamEntity(request.getInputStream(), getContentLength());
                entity.setContentType(request.getHeader(HttpHeaders.CONTENT_TYPE));
                entity.setContentEncoding(request.getHeader(HttpHeaders.CONTENT_ENCODING));
                int parseInt = Integer.parseInt(request.getHeader(CONTENT_LENGTH));
                if (parseInt == -1) {
                    entity.setChunked(false);
                }
                return entity;
            }
            return null;
        } catch (IOException e) {
            throw new RequestBodyException(String.format("获取请求体失败,错误信息:", e.getMessage()), getRequestContext());
        }
    }

    private void copyProxyRequestHeader(String headerName) {
        if (headerName.equalsIgnoreCase(CONTENT_LENGTH)) {
            return;
        }
        if (getSkipHeaders().containsHeader(headerName)) {
            return;
        }
        Enumeration headers = getRequestContext().getRequest().getHeaders(headerName);
        while (headers.hasMoreElements()) {
            String headerValue = headers.nextElement();
            if (headerName.equalsIgnoreCase(HOST)) {
                headerValue = getRequestContext().getTargetHost().toAddr();
            }
            proxyRequest.addHeader(headerName, headerValue);
        }
    }

    private long getContentLength() {
        String contentLengthHeader = getRequestContext().getRequest().getHeader(CONTENT_LENGTH);
        return (contentLengthHeader != null) ? Long.parseLong(contentLengthHeader) : -1L;
    }

    private void checkTargetHost() {
        if (Objects.isNull(getRequestContext().getTargetHost())) {
            throw new NotTargetHostException("缺少目标主机信息", getRequestContext());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy