源组件有个问题,代理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