smiley-http-proxy-servlet核心由httpclient改为okHttp

源组件有个问题,代理ceph文件系统时,文件名中包含+ ( )这些符号,会拼接在请求路径上,正常应该unicode转码,但是在组件核心请求到ceph时,httpclient发送请求会在请求前自动解码+(%2B),从而造成下载文件失败。我的解决方案是将核心代码中的httpclient、改为oKHttp。不多说了,直接上代码。

创建文件FileProxyServlet.java

import lombok.extern.slf4j.Slf4j;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.http.Header;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.message.HeaderGroup;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpCookie;
import java.net.URI;
import java.util.BitSet;
import java.util.Enumeration;
import java.util.Formatter;
import java.util.Iterator;

/**
 * @创建人 zinc.xin
 * @创建时间 
 * @描述
 */
@Slf4j
public class CephFileProxyServlet  extends HttpServlet {
    final OkHttpClient client = new OkHttpClient();
    public static final String PROXY_LOG = "log";
    public static final String PROXY_TARGET_URI = "targetUri";
    protected static final String FACT_TARGET_URI = CephFileProxyServlet.class.getSimpleName() + ".targetUri";
    protected static final String FACT_TARGET_HOST = CephFileProxyServlet.class.getSimpleName() + ".targetHost";
    protected boolean fLog = false;
    protected boolean fForwardIP = true;
    protected boolean fSendUrlFragment = true;
    protected boolean fPreserveHost = false;
    protected boolean fPreserveCookies = false;
    protected boolean fHandleRedirects = false;
    protected boolean useSysProp = true;
    protected boolean fHandleCompression = false;

    protected int connTimeout = -1;
    protected int readTimeout = -1;
    protected int connRequestTimeout = -1;
    protected int maxConns = -1;
    protected String targetUri;
    protected URI targetUriObj;
    protected HttpHost targetHost;
    protected static final HeaderGroup hopByHopHeaders = new HeaderGroup();
    protected static final BitSet asciiQueryChars;

    public CephFileProxyServlet() {
    }

    public String getServletInfo() {
        return "A proxy servlet forawrod by okHttp";
    }

    protected String getTargetUri(HttpServletRequest servletRequest) {
        return (String)servletRequest.getAttribute(FACT_TARGET_URI);
    }

    protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
        return (HttpHost)servletRequest.getAttribute(FACT_TARGET_HOST);
    }

    protected String getConfigParam(String key) {
        return this.getServletConfig().getInitParameter(key);
    }

    public void init() throws ServletException {
        String fLog = this.getConfigParam("log");
        if (fLog != null) {
            this.fLog = Boolean.parseBoolean(fLog);
        }

        String fForwardIP = this.getConfigParam("forwardip");
        if (fForwardIP != null) {
            this.fForwardIP = Boolean.parseBoolean(fForwardIP);
        }

        String fPreserveHost = this.getConfigParam("preserveHost");
        if (fPreserveHost != null) {
            this.fPreserveHost = Boolean.parseBoolean(fPreserveHost);
        }

        String fPreserveCookies = this.getConfigParam("preserveCookies");
        if (fPreserveCookies != null) {
            this.fPreserveCookies = Boolean.parseBoolean(fPreserveCookies);
        }

        String fHandleRedirects = this.getConfigParam("http.protocol.handle-redirects");
        if (fHandleRedirects != null) {
            this.fHandleRedirects = Boolean.parseBoolean(fHandleRedirects);
        }

        String connTimeout = this.getConfigParam("http.socket.timeout");
        if (connTimeout != null) {
            this.connTimeout = Integer.parseInt(connTimeout);
        }

        String readTimeout = this.getConfigParam("http.read.timeout");
        if (readTimeout != null) {
            this.readTimeout = Integer.parseInt(readTimeout);
        }

        String connRequestTimeout = this.getConfigParam("http.connectionrequest.timeout");
        if (connRequestTimeout != null) {
            this.connRequestTimeout = Integer.parseInt(connRequestTimeout);
        }

        String maxConns = this.getConfigParam("http.maxConnections");
        if (maxConns != null) {
            this.maxConns = Integer.parseInt(maxConns);
        }

        String useSysProp = this.getConfigParam("useSystemProperties");
        if (useSysProp != null) {
            this.useSysProp = Boolean.parseBoolean(useSysProp);
        }

        String fHandleCompression = this.getConfigParam("handleCompression");
        if (fHandleCompression != null) {
            this.fHandleCompression = Boolean.parseBoolean(fHandleCompression);
        }

        this.initTarget();
    }

    protected void initTarget() throws ServletException {
        this.targetUri = this.getConfigParam("targetUri");
        if (this.targetUri == null) {
            throw new ServletException("targetUri is required.");
        } else {
            try {
                this.targetUriObj = new URI(this.targetUri);
            } catch (Exception var2) {
                throw new ServletException("Trying to process targetUri init parameter: " + var2, var2);
            }

            this.targetHost = URIUtils.extractHost(this.targetUriObj);
        }
    }

    protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException {
        if (servletRequest.getAttribute(FACT_TARGET_URI) == null) {
            servletRequest.setAttribute(FACT_TARGET_URI, this.targetUri);
        }

        if (servletRequest.getAttribute(FACT_TARGET_HOST) == null) {
            servletRequest.setAttribute(FACT_TARGET_HOST, this.targetHost);
        }

        String method = servletRequest.getMethod();
        String proxyRequestUri = this.rewriteUrlFromRequest(servletRequest);
        Object proxyRequest;
        if (servletRequest.getHeader("Content-Length") == null && servletRequest.getHeader("Transfer-Encoding") == null) {
            proxyRequest = new BasicHttpRequest(method, proxyRequestUri);
        } else {
            proxyRequest = this.newProxyRequestWithEntity(method, proxyRequestUri, servletRequest);
        }

        this.copyRequestHeaders(servletRequest, (HttpRequest)proxyRequest);
        this.setXForwardedForHeader(servletRequest, (HttpRequest)proxyRequest);


        //创建一个request对象
        Request request = new Request.Builder()
                .url(((HttpRequest)proxyRequest).getRequestLine().getUri())
                .get()
                .build();
        //获取响应并把响应体返回
        try (Response response = client.newCall(request).execute()) {
            int statusCode = response.code();
            //TO-DO 没找到对应的proxyResponse.getStatusLine().getReasonPhrase()
            servletResponse.setStatus(statusCode, response.cacheControl().toString());
            this.copyResponseHeaders(response, servletRequest, servletResponse);
            if (statusCode == 304) {
                servletResponse.setIntHeader("Content-Length", 0);
            } else {
                this.copyResponseEntity(response, servletResponse, (HttpRequest)proxyRequest, servletRequest);
            }
        }
        catch (Exception e){
            log.error("error:{}", e.getMessage(), e);
        }
    }

    protected HttpRequest newProxyRequestWithEntity(String method, String proxyRequestUri, HttpServletRequest servletRequest) throws IOException {
        HttpEntityEnclosingRequest eProxyRequest = new BasicHttpEntityEnclosingRequest(method, proxyRequestUri);
        eProxyRequest.setEntity(new InputStreamEntity(servletRequest.getInputStream(), this.getContentLength(servletRequest)));
        return eProxyRequest;
    }

    private long getContentLength(HttpServletRequest request) {
        String contentLengthHeader = request.getHeader("Content-Length");
        return contentLengthHeader != null ? Long.parseLong(contentLengthHeader) : -1L;
    }

    protected void copyRequestHeaders(HttpServletRequest servletRequest, HttpRequest proxyRequest) {
        Enumeration enumerationOfHeaderNames = servletRequest.getHeaderNames();

        while(enumerationOfHeaderNames.hasMoreElements()) {
            String headerName = (String)enumerationOfHeaderNames.nextElement();
            this.copyRequestHeader(servletRequest, proxyRequest, headerName);
        }

    }

    protected void copyRequestHeader(HttpServletRequest servletRequest, HttpRequest proxyRequest, String headerName) {
        if (!headerName.equalsIgnoreCase("Content-Length")) {
            if (!hopByHopHeaders.containsHeader(headerName)) {
                if (!this.fHandleCompression || !headerName.equalsIgnoreCase("Accept-Encoding")) {
                    String headerValue;
                    for(Enumeration headers = servletRequest.getHeaders(headerName); headers.hasMoreElements(); proxyRequest.addHeader(headerName, headerValue)) {
                        headerValue = (String)headers.nextElement();
                        if (!this.fPreserveHost && headerName.equalsIgnoreCase("Host")) {
                            HttpHost host = this.getTargetHost(servletRequest);
                            headerValue = host.getHostName();
                            if (host.getPort() != -1) {
                                headerValue = headerValue + ":" + host.getPort();
                            }
                        } else if (!this.fPreserveCookies && headerName.equalsIgnoreCase("Cookie")) {
                            headerValue = this.getRealCookie(headerValue);
                        }
                    }
                }
            }
        }
    }

    private void setXForwardedForHeader(HttpServletRequest servletRequest, HttpRequest proxyRequest) {
        if (this.fForwardIP) {
            String forHeaderName = "X-Forwarded-For";
            String forHeader = servletRequest.getRemoteAddr();
            String existingForHeader = servletRequest.getHeader(forHeaderName);
            if (existingForHeader != null) {
                forHeader = existingForHeader + ", " + forHeader;
            }

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

    }

    protected void copyResponseHeaders(Response response, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
        Headers headers = response.headers();
        headers.forEach(header -> {
            Header hr = new BasicHeader(header.getFirst(), header.getSecond());
            this.copyResponseHeader(servletRequest, servletResponse, hr);
        });
    }

    protected void copyResponseHeader(HttpServletRequest servletRequest, HttpServletResponse servletResponse, Header header) {
        String headerName = header.getName();
        if (!hopByHopHeaders.containsHeader(headerName)) {
            String headerValue = header.getValue();
            if (!headerName.equalsIgnoreCase("Set-Cookie") && !headerName.equalsIgnoreCase("Set-Cookie2")) {
                if (headerName.equalsIgnoreCase("Location")) {
                    servletResponse.addHeader(headerName, this.rewriteUrlFromResponse(servletRequest, headerValue));
                } else {
                    servletResponse.addHeader(headerName, headerValue);
                }
            } else {
                this.copyProxyCookie(servletRequest, servletResponse, headerValue);
            }

        }
    }

    protected void copyProxyCookie(HttpServletRequest servletRequest, HttpServletResponse servletResponse, String headerValue) {
        Iterator var4 = HttpCookie.parse(headerValue).iterator();

        while(var4.hasNext()) {
            HttpCookie cookie = (HttpCookie)var4.next();
            Cookie servletCookie = this.createProxyCookie(servletRequest, cookie);
            servletResponse.addCookie(servletCookie);
        }

    }

    protected Cookie createProxyCookie(HttpServletRequest servletRequest, HttpCookie cookie) {
        String proxyCookieName = this.getProxyCookieName(cookie);
        Cookie servletCookie = new Cookie(proxyCookieName, cookie.getValue());
        servletCookie.setPath(this.buildProxyCookiePath(servletRequest));
        servletCookie.setComment(cookie.getComment());
        servletCookie.setMaxAge((int)cookie.getMaxAge());
        servletCookie.setSecure(cookie.getSecure());
        servletCookie.setVersion(cookie.getVersion());
        servletCookie.setHttpOnly(cookie.isHttpOnly());
        return servletCookie;
    }

    protected String getProxyCookieName(HttpCookie cookie) {
        return this.fPreserveCookies ? cookie.getName() : this.getCookieNamePrefix(cookie.getName()) + cookie.getName();
    }

    protected String buildProxyCookiePath(HttpServletRequest servletRequest) {
        String path = servletRequest.getContextPath() + servletRequest.getServletPath();
        return path.isEmpty() ? "/" : path;
    }

    protected String getRealCookie(String cookieValue) {
        StringBuilder esCookie = new StringBuilder();
        String[] cookies = cookieValue.split("[;,]");

        for(String cookie : cookies){
            String[] cookieSplit = cookie.split("=");
            if (cookieSplit.length == 2) {
                String cookieName = cookieSplit[0].trim();
                if (cookieName.startsWith(this.getCookieNamePrefix(cookieName))) {
                    cookieName = cookieName.substring(this.getCookieNamePrefix(cookieName).length());
                    if (esCookie.length() > 0) {
                        esCookie.append("; ");
                    }
                    esCookie.append(cookieName).append("=").append(cookieSplit[1].trim());
                }
            }
        }

        return esCookie.toString();
    }

    protected String getCookieNamePrefix(String name) {
        return "!Proxy!" + this.getServletConfig().getServletName();
    }

    protected void copyResponseEntity(Response response, HttpServletResponse servletResponse, HttpRequest proxyRequest, HttpServletRequest servletRequest) throws IOException {
        InputStream is = response.body().byteStream();
        OutputStream os = servletResponse.getOutputStream();
        byte[] buffer = new byte[10240];
        while(true) {
            do {
                int read;
                if ((read = is.read(buffer)) == -1) {
                    return;
                }

                os.write(buffer, 0, read);
            } while(!this.fHandleCompression && is.available() != 0);

            os.flush();
        }
    }

    protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) throws UnsupportedEncodingException {
        StringBuilder uri = new StringBuilder(500);
        uri.append(this.getTargetUri(servletRequest));
        String pathInfo = this.rewritePathInfoFromRequest(servletRequest);
        pathInfo = pathInfo.substring(0, pathInfo.lastIndexOf("/") + 1);
        String fileName = servletRequest.getRequestURI();
        fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
        if (pathInfo != null) {
            uri.append(this.encodeUriQuery(pathInfo, true));
        }

        String queryString = servletRequest.getQueryString();
        String fragment = null;
        if (queryString != null) {
            int fragIdx = queryString.indexOf(35);
            if (fragIdx >= 0) {
                fragment = queryString.substring(fragIdx + 1);
                queryString = queryString.substring(0, fragIdx);
            }
        }

        queryString = this.rewriteQueryStringFromRequest(servletRequest, queryString);
        if (queryString != null && queryString.length() > 0) {
            uri.append('?');
            uri.append(this.encodeUriQuery(queryString, false));
        }

        if (this.fSendUrlFragment && fragment != null) {
            uri.append('#');
            uri.append(this.encodeUriQuery(fragment, false));
        }
        String url = uri.toString();
        String params = url.substring(url.indexOf("?"));
        return url.substring(0, url.indexOf("?")) + fileName + params;
    }

    protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
        return queryString;
    }

    protected String rewritePathInfoFromRequest(HttpServletRequest servletRequest) {
        return servletRequest.getPathInfo();
    }

    protected String rewriteUrlFromResponse(HttpServletRequest servletRequest, String theUrl) {
        String targetUri = this.getTargetUri(servletRequest);
        if (theUrl.startsWith(targetUri)) {
            StringBuffer curUrl = servletRequest.getRequestURL();
            int pos;
            if ((pos = curUrl.indexOf("://")) >= 0 && (pos = curUrl.indexOf("/", pos + 3)) >= 0) {
                curUrl.setLength(pos);
            }

            curUrl.append(servletRequest.getContextPath());
            curUrl.append(servletRequest.getServletPath());
            curUrl.append(theUrl, targetUri.length(), theUrl.length());
            return curUrl.toString();
        } else {
            return theUrl;
        }
    }

    protected CharSequence encodeUriQuery(CharSequence in, boolean encodePercent) {
        StringBuilder outBuf = null;
        Formatter formatter = null;

        for(int i = 0; i < in.length(); ++i) {
            char c = in.charAt(i);
            boolean escape = true;
            if (c < 128) {
                if (asciiQueryChars.get(c) && (!encodePercent || c != '%')) {
                    escape = false;
                }
            } else if (!Character.isISOControl(c) && !Character.isSpaceChar(c)) {
                escape = false;
            }

            if (!escape) {
                if (outBuf != null) {
                    outBuf.append(c);
                }
            } else {
                if (outBuf == null) {
                    outBuf = new StringBuilder(in.length() + 15);
                    outBuf.append(in, 0, i);
                    formatter = new Formatter(outBuf);
                }

                formatter.format("%%%02X", Integer.valueOf(c));
            }
        }

        return (CharSequence)(outBuf != null ? outBuf : in);
    }

    static {
        String[] headers = new String[]{"Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization", "TE", "Trailers", "Transfer-Encoding", "Upgrade"};

        for(String header : headers) {
            hopByHopHeaders.addHeader(new BasicHeader(header, (String)null));
        }

        char[] c_unreserved = "_-!.~'()*".toCharArray();
        char[] c_punct = ",;:$&+=".toCharArray();
        char[] c_reserved = "/@".toCharArray();
        asciiQueryChars = new BitSet(128);

        char c;
        for(c = 'a'; c <= 'z'; ++c) {
            asciiQueryChars.set(c);
        }

        for(c = 'A'; c <= 'Z'; ++c) {
            asciiQueryChars.set(c);
        }

        for(c = '0'; c <= '9'; ++c) {
            asciiQueryChars.set(c);
        }

        for(char s : c_unreserved) {
            asciiQueryChars.set(s);
        }

        for(char s : c_punct) {
            asciiQueryChars.set(s);
        }

        for(char s : c_reserved) {
            asciiQueryChars.set(s);
        }

        asciiQueryChars.set(37);
    }
}

我用的是springboot框架,将其加载。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @创建人 zinc.xin
 * @创建时间 
 * @描述
 */
@Configuration
public class ProxyServletConfig {

    // 下载
    private String servletUrlDownload = "/myproxy/*";

    @Value("${ceph.endpoint}")
    private String targetUrlDownload;

    @Bean
    @SuppressWarnings(value={"unchecked", "rawtypes"})
    public ServletRegistrationBean proxyServletRegistrationUploading(){
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new CephFileProxyServlet(), servletUrlDownload);
        //这里名字必须不一样
        registrationBean.setName("suitDown");
        registrationBean.addInitParameter(CephFileProxyServlet.PROXY_TARGET_URI, targetUrlDownload);
        registrationBean.addInitParameter(CephFileProxyServlet.PROXY_LOG, "true");
        return registrationBean;
    }


}

发送下载GET请求  /myproxy/config/myFile-2022-03-01+0800.xml?Ased=sdrfefs&stime=2342342dsddd

请求会转发到  ${ceph.endpoint}/config/myFile-2022-03-01+0800.xml?Ased=sdrfefs&stime=2342342dsddd

你可能感兴趣的:(http,网络协议,网络,proxy)