From c4c9537d57fd1ead16214c61525186a096018309 Mon Sep 17 00:00:00 2001 From: caomingjie Date: Thu, 10 Feb 2022 13:14:47 +0800 Subject: [PATCH 1/3] bug:The path of the cookie is changed. More sessions will be created for the same user --- .../java/org/mitre/dsmiley/httpproxy/ProxyServlet.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java index fdf6c7f..a23ef80 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -113,6 +113,9 @@ public class ProxyServlet extends HttpServlet { protected static final String ATTR_TARGET_HOST = ProxyServlet.class.getSimpleName() + ".targetHost"; + /**default root path of cookie**/ + private static String COOKIE_ROOT_PATH = "/"; + /* MISC */ protected boolean doLog = false; @@ -590,7 +593,8 @@ protected void copyProxyCookie(HttpServletRequest servletRequest, protected Cookie createProxyCookie(HttpServletRequest servletRequest, HttpCookie cookie) { String proxyCookieName = getProxyCookieName(cookie); Cookie servletCookie = new Cookie(proxyCookieName, cookie.getValue()); - servletCookie.setPath(buildProxyCookiePath(servletRequest)); //set to the path of the proxy servlet + //set to the path of the proxy servlet + servletCookie.setPath(COOKIE_ROOT_PATH.equals(cookie.getPath()) ? COOKIE_ROOT_PATH : buildProxyCookiePath(servletRequest)); servletCookie.setComment(cookie.getComment()); servletCookie.setMaxAge((int) cookie.getMaxAge()); // don't set cookie domain @@ -621,7 +625,7 @@ protected String buildProxyCookiePath(HttpServletRequest servletRequest) { String path = servletRequest.getContextPath(); // path starts with / or is empty string path += servletRequest.getServletPath(); // servlet path starts with / or is empty string if (path.isEmpty()) { - path = "/"; + path = COOKIE_ROOT_PATH; } return path; } @@ -680,7 +684,7 @@ protected void copyResponseEntity(HttpResponse proxyResponse, HttpServletRespons * but may read or skip fewer bytes. * * To work around this, a flush is issued always if compression - * is handled by apache http client + * is handled by apache http client */ if (doHandleCompression || is.available() == 0 /* next is.read will block */) { os.flush(); From 9d30384c3ca4e536f3b65cefc67650d47fb9bd4d Mon Sep 17 00:00:00 2001 From: caomingjie Date: Thu, 10 Feb 2022 20:17:18 +0800 Subject: [PATCH 2/3] bug:The path of the cookie is changed. More sessions will be created for the same user --- .../mitre/dsmiley/httpproxy/ProxyServlet.java | 66 +++++++++++++++++-- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java index a23ef80..8008d43 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -29,6 +29,7 @@ import org.apache.http.client.methods.AbortableHttpRequest; import org.apache.http.client.utils.URIUtils; import org.apache.http.config.SocketConfig; +import org.apache.http.cookie.SM; import org.apache.http.entity.InputStreamEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicHeader; @@ -46,11 +47,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.ref.SoftReference; import java.net.HttpCookie; import java.net.URI; -import java.util.BitSet; -import java.util.Enumeration; -import java.util.Formatter; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * An HTTP reverse proxy/gateway servlet. It is designed to be extended for customization @@ -140,6 +141,7 @@ public class ProxyServlet extends HttpServlet { protected HttpHost targetHost;//URIUtils.extractHost(targetUriObj); private HttpClient proxyClient; + private static volatile Map>> rootPathCookies = new ConcurrentHashMap<>();// key: sessionId, value: proxyCookies @Override public String getServletInfo() { @@ -522,7 +524,35 @@ protected void copyRequestHeader(HttpServletRequest servletRequest, HttpRequest } else if (!doPreserveCookies && headerName.equalsIgnoreCase(org.apache.http.cookie.SM.COOKIE)) { headerValue = getRealCookie(headerValue); } - proxyRequest.addHeader(headerName, headerValue); + String finalHeaderValue = null; + String sessionId = servletRequest.getSession().getId(); + SoftReference> setSoftReference = rootPathCookies.get(sessionId); + Set proxyCookies = null; + if(setSoftReference != null && (proxyCookies = setSoftReference.get()) != null){ + StringBuilder cookieBuilder = null; + if(SM.COOKIE.equalsIgnoreCase(headerName) || SM.COOKIE2.equalsIgnoreCase(headerName)){ + cookieBuilder = new StringBuilder(headerValue); + }else { + cookieBuilder = new StringBuilder(); + } + for(HttpCookie proxyCookie : proxyCookies){ + if(!cookieBuilder.toString().contains(proxyCookie.toString())){ + if(cookieBuilder.length()!=0){ + cookieBuilder.append("; "); + } + cookieBuilder.append(proxyCookie.toString()); + } + } + finalHeaderValue = cookieBuilder.toString(); + } + if(SM.COOKIE.equalsIgnoreCase(headerName) || SM.COOKIE2.equalsIgnoreCase(headerName)){ + proxyRequest.addHeader(headerName,finalHeaderValue); + }else{ + proxyRequest.addHeader(headerName, headerValue); + if(finalHeaderValue != null){ + proxyRequest.addHeader(SM.COOKIE,finalHeaderValue); + } + } } } @@ -594,13 +624,39 @@ protected Cookie createProxyCookie(HttpServletRequest servletRequest, HttpCookie String proxyCookieName = getProxyCookieName(cookie); Cookie servletCookie = new Cookie(proxyCookieName, cookie.getValue()); //set to the path of the proxy servlet - servletCookie.setPath(COOKIE_ROOT_PATH.equals(cookie.getPath()) ? COOKIE_ROOT_PATH : buildProxyCookiePath(servletRequest)); + String proxyCookiePath = buildProxyCookiePath(servletRequest); + servletCookie.setPath(proxyCookiePath); servletCookie.setComment(cookie.getComment()); servletCookie.setMaxAge((int) cookie.getMaxAge()); // don't set cookie domain servletCookie.setSecure(cookie.getSecure()); servletCookie.setVersion(cookie.getVersion()); servletCookie.setHttpOnly(cookie.isHttpOnly()); + if(COOKIE_ROOT_PATH.equals(cookie.getPath())){ + String sessionId = servletRequest.getSession().getId(); + SoftReference> reference = rootPathCookies.get(sessionId); + List expiredKey = new LinkedList<>(); + if(reference == null || reference.get() == null){ + rootPathCookies.forEach((k,v)->{ + if(v.get() == null){ + expiredKey.add(k); + } + }); + for(String ek : expiredKey){ + rootPathCookies.remove(ek); + } + synchronized (servletRequest.getSession()){ + if((reference = rootPathCookies.get(sessionId)) == null || reference.get() == null){ + Set httpCookiesSet = new HashSet<>(); + reference = new SoftReference>(httpCookiesSet); + rootPathCookies.put(sessionId,reference) ; + } + } + } + if(reference.get() != null){ + reference.get().add(cookie); + } + } return servletCookie; } From d310d8b51ff52efd2761f190bc970f5be060c2f7 Mon Sep 17 00:00:00 2001 From: caomingjie Date: Mon, 14 Feb 2022 15:10:10 +0800 Subject: [PATCH 3/3] bug:The path of the cookie is changed. More sessions will be created for the same user: review code --- .../mitre/dsmiley/httpproxy/ProxyServlet.java | 84 +++++++++++++------ 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java index 8008d43..daef2b7 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -85,6 +85,9 @@ public class ProxyServlet extends HttpServlet { /** A boolean parameter name to keep COOKIES as-is */ public static final String P_PRESERVECOOKIES = "preserveCookies"; + /** A boolean parameter name to cache root path cookies **/ + public static final String P_CACHEROOTPATHCOOKIES = "cacheRootPathCookies"; + /** A boolean parameter name to have auto-handle redirects */ public static final String P_HANDLEREDIRECTS = "http.protocol.handle-redirects"; // ClientPNames.HANDLE_REDIRECTS @@ -125,6 +128,8 @@ public class ProxyServlet extends HttpServlet { protected boolean doSendUrlFragment = true; protected boolean doPreserveHost = false; protected boolean doPreserveCookies = false; + /**Whether to enable cache session cookies, The default value is invalid**/ + protected boolean doCacheRootPathCookies = false; protected boolean doHandleRedirects = false; protected boolean useSystemProperties = true; protected boolean doHandleCompression = false; @@ -141,6 +146,8 @@ public class ProxyServlet extends HttpServlet { protected HttpHost targetHost;//URIUtils.extractHost(targetUriObj); private HttpClient proxyClient; + /**Caches proxy cookies corresponding to the current ssession , If the current session is invalid (session timeout), or the JVM's fullGC chooses to clear the SoftReference data, + * Use the SoftReference function to clear the cookies of an expired session. Avoid memory leaks caused by caching when memory is running low **/ private static volatile Map>> rootPathCookies = new ConcurrentHashMap<>();// key: sessionId, value: proxyCookies @Override @@ -187,6 +194,11 @@ public void init() throws ServletException { this.doPreserveCookies = Boolean.parseBoolean(preserveCookiesString); } + String cacheRootPathCookies = getConfigParam(P_CACHEROOTPATHCOOKIES); + if (cacheRootPathCookies != null) { + this.doCacheRootPathCookies = Boolean.parseBoolean(cacheRootPathCookies); + } + String handleRedirectsString = getConfigParam(P_HANDLEREDIRECTS); if (handleRedirectsString != null) { this.doHandleRedirects = Boolean.parseBoolean(handleRedirectsString); @@ -274,8 +286,8 @@ protected void initTarget() throws ServletException { */ protected HttpClient createHttpClient() { HttpClientBuilder clientBuilder = getHttpClientBuilder() - .setDefaultRequestConfig(buildRequestConfig()) - .setDefaultSocketConfig(buildSocketConfig()); + .setDefaultRequestConfig(buildRequestConfig()) + .setDefaultSocketConfig(buildSocketConfig()); clientBuilder.setMaxConnTotal(maxConnections); clientBuilder.setMaxConnPerRoute(maxConnections); @@ -337,7 +349,7 @@ public void destroy() { @Override protected void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) - throws ServletException, IOException { + 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); @@ -353,7 +365,7 @@ protected void service(HttpServletRequest servletRequest, HttpServletResponse se 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) { + servletRequest.getHeader(HttpHeaders.TRANSFER_ENCODING) != null) { proxyRequest = newProxyRequestWithEntity(method, proxyRequestUri, servletRequest); } else { proxyRequest = new BasicHttpRequest(method, proxyRequestUri); @@ -435,7 +447,7 @@ protected HttpResponse doExecute(HttpServletRequest servletRequest, HttpServletR } protected HttpRequest newProxyRequestWithEntity(String method, String proxyRequestUri, - HttpServletRequest servletRequest) + HttpServletRequest servletRequest) throws IOException { HttpEntityEnclosingRequest eProxyRequest = new BasicHttpEntityEnclosingRequest(method, proxyRequestUri); @@ -472,8 +484,8 @@ protected void closeQuietly(Closeable closeable) { static { hopByHopHeaders = new HeaderGroup(); String[] headers = new String[] { - "Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization", - "TE", "Trailers", "Transfer-Encoding", "Upgrade" }; + "Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization", + "TE", "Trailers", "Transfer-Encoding", "Upgrade" }; for (String header : headers) { hopByHopHeaders.addHeader(new BasicHeader(header, null)); } @@ -524,35 +536,42 @@ protected void copyRequestHeader(HttpServletRequest servletRequest, HttpRequest } else if (!doPreserveCookies && headerName.equalsIgnoreCase(org.apache.http.cookie.SM.COOKIE)) { headerValue = getRealCookie(headerValue); } + if(doCacheRootPathCookies){ + addRootPathCookies(servletRequest, proxyRequest, headerName, headerValue); + }else { + proxyRequest.addHeader(headerName, headerValue); + } + } + } + + /** + * Add root path cookies + * @param servletRequest + * @param proxyRequest + * @param headerName + * @param headerValue + */ + private void addRootPathCookies(HttpServletRequest servletRequest, HttpRequest proxyRequest, String headerName, String headerValue) { + if(SM.COOKIE.equalsIgnoreCase(headerName) || SM.COOKIE2.equalsIgnoreCase(headerName)){ String finalHeaderValue = null; String sessionId = servletRequest.getSession().getId(); SoftReference> setSoftReference = rootPathCookies.get(sessionId); Set proxyCookies = null; if(setSoftReference != null && (proxyCookies = setSoftReference.get()) != null){ - StringBuilder cookieBuilder = null; - if(SM.COOKIE.equalsIgnoreCase(headerName) || SM.COOKIE2.equalsIgnoreCase(headerName)){ - cookieBuilder = new StringBuilder(headerValue); - }else { - cookieBuilder = new StringBuilder(); - } + StringBuilder cookieBuilder = new StringBuilder(headerValue);//Cookies in the current request for(HttpCookie proxyCookie : proxyCookies){ if(!cookieBuilder.toString().contains(proxyCookie.toString())){ if(cookieBuilder.length()!=0){ - cookieBuilder.append("; "); + cookieBuilder.append("; "); } - cookieBuilder.append(proxyCookie.toString()); + cookieBuilder.append(proxyCookie.toString()); //Append cached root path cookies } } finalHeaderValue = cookieBuilder.toString(); } - if(SM.COOKIE.equalsIgnoreCase(headerName) || SM.COOKIE2.equalsIgnoreCase(headerName)){ - proxyRequest.addHeader(headerName,finalHeaderValue); - }else{ - proxyRequest.addHeader(headerName, headerValue); - if(finalHeaderValue != null){ - proxyRequest.addHeader(SM.COOKIE,finalHeaderValue); - } - } + proxyRequest.addHeader(headerName,finalHeaderValue); + }else{ + proxyRequest.addHeader(headerName, headerValue); } } @@ -585,7 +604,7 @@ protected void copyResponseHeaders(HttpResponse proxyResponse, HttpServletReques * This is easily overwritten to filter out certain headers if desired. */ protected void copyResponseHeader(HttpServletRequest servletRequest, - HttpServletResponse servletResponse, Header header) { + HttpServletResponse servletResponse, Header header) { String headerName = header.getName(); if (hopByHopHeaders.containsHeader(headerName)) return; @@ -632,11 +651,24 @@ protected Cookie createProxyCookie(HttpServletRequest servletRequest, HttpCookie servletCookie.setSecure(cookie.getSecure()); servletCookie.setVersion(cookie.getVersion()); servletCookie.setHttpOnly(cookie.isHttpOnly()); + if(doCacheRootPathCookies){ + cacheRootPathCookies(servletRequest, cookie); + } + return servletCookie; + } + + /** + * cache root path cookies + * @param servletRequest + * @param cookie + */ + private void cacheRootPathCookies(HttpServletRequest servletRequest, HttpCookie cookie) { if(COOKIE_ROOT_PATH.equals(cookie.getPath())){ String sessionId = servletRequest.getSession().getId(); SoftReference> reference = rootPathCookies.get(sessionId); List expiredKey = new LinkedList<>(); if(reference == null || reference.get() == null){ + //Facilitate the data that has been cleaned by FULLGC at a time, and remove the expired data rootPathCookies.forEach((k,v)->{ if(v.get() == null){ expiredKey.add(k); @@ -645,6 +677,7 @@ protected Cookie createProxyCookie(HttpServletRequest servletRequest, HttpCookie for(String ek : expiredKey){ rootPathCookies.remove(ek); } + synchronized (servletRequest.getSession()){ if((reference = rootPathCookies.get(sessionId)) == null || reference.get() == null){ Set httpCookiesSet = new HashSet<>(); @@ -654,10 +687,9 @@ protected Cookie createProxyCookie(HttpServletRequest servletRequest, HttpCookie } } if(reference.get() != null){ - reference.get().add(cookie); + reference.get().add(cookie); } } - return servletCookie; } /**