diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java index 2bde2c1deaa..2c112940f8b 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java @@ -760,15 +760,15 @@ public class HttpURI _decodedPath = path; } - public void setPathQuery(String path) + public void setPathQuery(String pathQuery) { _uri = null; _path = null; _decodedPath = null; _param = null; _fragment = null; - if (path != null) - parse(State.PATH, path); + if (pathQuery != null) + parse(State.PATH, pathQuery); } public void setQuery(String query) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java index 4da7cc87e67..c4bc0095aee 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java @@ -25,6 +25,7 @@ import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.server.handler.ContextHandler.Context; import org.eclipse.jetty.util.thread.Scheduler; @@ -32,6 +33,7 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable { private final Context _context; private final AsyncContextState _asyncContext; + private final HttpURI _baseURI; private volatile HttpChannelState _state; private ServletContext _dispatchContext; private String _dispatchPath; @@ -39,11 +41,17 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable private Throwable _throwable; public AsyncContextEvent(Context context, AsyncContextState asyncContext, HttpChannelState state, Request baseRequest, ServletRequest request, ServletResponse response) + { + this (context, asyncContext, state, baseRequest, request, response, null); + } + + public AsyncContextEvent(Context context, AsyncContextState asyncContext, HttpChannelState state, Request baseRequest, ServletRequest request, ServletResponse response, HttpURI baseURI) { super(null, request, response, null); _context = context; _asyncContext = asyncContext; _state = state; + _baseURI = baseURI; // If we haven't been async dispatched before if (baseRequest.getAttribute(AsyncContext.ASYNC_REQUEST_URI) == null) @@ -74,6 +82,11 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable } } + public HttpURI getBaseURI() + { + return _baseURI; + } + public ServletContext getSuspendedContext() { return _context; @@ -94,14 +107,6 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable return _dispatchContext == null ? _context : _dispatchContext; } - /** - * @return The path in the context (encoded with possible query string) - */ - public String getPath() - { - return _dispatchPath; - } - public void setTimeoutTask(Scheduler.Task task) { _timeoutTask = task; @@ -137,6 +142,14 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable _dispatchContext = context; } + /** + * @return The path in the context (encoded with possible query string) + */ + public String getDispatchPath() + { + return _dispatchPath; + } + /** * @param path encoded URI */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index 6abada24f58..07f2d318be4 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -1598,7 +1598,10 @@ public class Request implements HttpServletRequest { MetaData.Request metadata = _metaData; if (metadata != null) + { metadata.setURI(uri); + _queryParameters = null; + } } public UserIdentity getUserIdentity() @@ -2178,17 +2181,8 @@ public class Request implements HttpServletRequest HttpChannelState state = getHttpChannelState(); if (_async == null) _async = new AsyncContextState(state); - AsyncContextEvent event = new AsyncContextEvent(_context, _async, state, this, servletRequest, servletResponse); + AsyncContextEvent event = new AsyncContextEvent(_context, _async, state, this, servletRequest, servletResponse, getHttpURI()); event.setDispatchContext(getServletContext()); - - String uri = unwrap(servletRequest).getRequestURI(); - if (_contextPath != null && uri.startsWith(_contextPath)) - uri = uri.substring(_contextPath.length()); - else - // TODO probably need to strip encoded context from requestURI, but will do this for now: - uri = URIUtil.encodePath(URIUtil.addPaths(getServletPath(), getPathInfo())); - - event.setDispatchPath(uri); state.startAsync(event); return _async; } @@ -2391,7 +2385,7 @@ public class Request implements HttpServletRequest setQueryString(oldQuery); else if (oldQuery == null) setQueryString(newQuery); - else + else if (oldQueryParams.keySet().stream().anyMatch(newQueryParams.keySet()::contains)) { // Build the new merged query string, parameters in the // new query string hide parameters in the old query string. @@ -2413,6 +2407,10 @@ public class Request implements HttpServletRequest } setQueryString(mergedQuery.toString()); } + else + { + setQueryString(newQuery + '&' + oldQuery); + } } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index d9ade6e27d4..1566d6ba2f7 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -47,6 +47,8 @@ import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.Jetty; import org.eclipse.jetty.util.MultiException; +import org.eclipse.jetty.util.MultiMap; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.Uptime; import org.eclipse.jetty.util.annotation.ManagedAttribute; @@ -563,22 +565,74 @@ public class Server extends HandlerWrapper implements Attributes { final HttpChannelState state = channel.getRequest().getHttpChannelState(); final AsyncContextEvent event = state.getAsyncContextEvent(); - final Request baseRequest = channel.getRequest(); - final String path = event.getPath(); + final HttpURI baseUri = event.getBaseURI(); + String encodedPathQuery = event.getDispatchPath(); - if (path != null) + if (encodedPathQuery == null && baseUri == null) { - // this is a dispatch with a path - ServletContext context = event.getServletContext(); - String query = baseRequest.getQueryString(); - baseRequest.setURIPathQuery(URIUtil.addEncodedPaths(context == null ? null : URIUtil.encodePath(context.getContextPath()), path)); - HttpURI uri = baseRequest.getHttpURI(); - baseRequest.setPathInfo(uri.getDecodedPath()); - if (uri.getQuery() != null) - baseRequest.mergeQueryParameters(query, uri.getQuery(), true); //we have to assume dispatch path and query are UTF8 + // Simple case, no request modification or merging needed + handleAsync(channel, event, baseRequest); + return; } + // this is a dispatch with either a provided URI and/or a dispatched path + // We will have to modify the request and then revert + final ServletContext context = event.getServletContext(); + final HttpURI oldUri = baseRequest.getHttpURI(); + final String oldQuery = baseRequest.getQueryString(); + final MultiMap oldQueryParams = baseRequest.getQueryParameters(); + try + { + baseRequest.resetParameters(); + HttpURI newUri = baseUri == null ? new HttpURI(oldUri) : baseUri; + if (encodedPathQuery == null) + { + baseRequest.setHttpURI(newUri); + } + else + { + if (context != null && !StringUtil.isEmpty(context.getContextPath())) + encodedPathQuery = URIUtil.addEncodedPaths(URIUtil.encodePath(context.getContextPath()), encodedPathQuery); + + if (newUri.getQuery() == null) + { + // parse new path and query + newUri.setPathQuery(encodedPathQuery); + baseRequest.setHttpURI(newUri); + } + else + { + // do we have a new query in the encodedPathQuery + int q = encodedPathQuery.indexOf('?'); + if (q < 0) + { + // No query, so we can just set the encoded path + newUri.setPath(encodedPathQuery); + baseRequest.setHttpURI(newUri); + } + else + { + newUri.setPath(encodedPathQuery.substring(0, q)); + baseRequest.setHttpURI(newUri); + baseRequest.mergeQueryParameters(oldQuery, encodedPathQuery.substring(q + 1), true); + } + } + } + + baseRequest.setPathInfo(newUri.getDecodedPath()); + handleAsync(channel, event, baseRequest); + } + finally + { + baseRequest.setHttpURI(oldUri); + baseRequest.setQueryParameters(oldQueryParams); + baseRequest.resetParameters(); + } + } + + private void handleAsync(HttpChannel channel, AsyncContextEvent event, Request baseRequest) throws IOException, ServletException + { final String target = baseRequest.getPathInfo(); final HttpServletRequest request = Request.unwrap(event.getSuppliedRequest()); final HttpServletResponse response = Response.unwrap(event.getSuppliedResponse()); diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java index ac16a759a6a..f04cffee271 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java @@ -163,7 +163,7 @@ public class AsyncServletTest String response = process("sleep=200", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?sleep=200", "initial")); assertContains("SLEPT", response); assertFalse(__history.contains("onTimeout")); @@ -173,7 +173,7 @@ public class AsyncServletTest @Test public void testNonAsync() throws Exception { - String response = process("", null); + String response = process(null, null); assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( "REQUEST /ctx/path/info", @@ -186,7 +186,7 @@ public class AsyncServletTest public void testAsyncNotSupportedNoAsync() throws Exception { _expectedCode = "200 "; - String response = process("noasync", "", null); + String response = process("noasync", null, null); assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( "REQUEST /ctx/noasync/info", @@ -205,9 +205,9 @@ public class AsyncServletTest String response = process("noasync", "start=200", null); assertThat(response, Matchers.startsWith("HTTP/1.1 500 ")); assertThat(__history, contains( - "REQUEST /ctx/noasync/info", + "REQUEST /ctx/noasync/info?start=200", "initial", - "ERROR /ctx/error/custom", + "ERROR /ctx/error/custom?start=200", "!initial" )); @@ -224,11 +224,11 @@ public class AsyncServletTest String response = process("start=200", null); assertThat(response, Matchers.startsWith("HTTP/1.1 500 Server Error")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=200", "initial", "start", "onTimeout", - "ERROR /ctx/error/custom", + "ERROR /ctx/error/custom?start=200", "!initial", "onComplete")); @@ -241,12 +241,12 @@ public class AsyncServletTest String response = process("start=200&timeout=dispatch", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=200&timeout=dispatch", "initial", "start", "onTimeout", "dispatch", - "ASYNC /ctx/path/info", + "ASYNC /ctx/path/info?start=200&timeout=dispatch", "!initial", "onComplete")); @@ -260,12 +260,12 @@ public class AsyncServletTest String response = process("start=200&timeout=error", null); assertThat(response, startsWith("HTTP/1.1 500 Server Error")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=200&timeout=error", "initial", "start", "onTimeout", "error", - "ERROR /ctx/error/custom", + "ERROR /ctx/error/custom?start=200&timeout=error", "!initial", "onComplete")); @@ -278,7 +278,7 @@ public class AsyncServletTest String response = process("start=200&timeout=complete", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=200&timeout=complete", "initial", "start", "onTimeout", @@ -294,11 +294,11 @@ public class AsyncServletTest String response = process("start=200&dispatch=10", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=200&dispatch=10", "initial", "start", "dispatch", - "ASYNC /ctx/path/info", + "ASYNC /ctx/path/info?start=200&dispatch=10", "!initial", "onComplete")); assertFalse(__history.contains("onTimeout")); @@ -310,11 +310,11 @@ public class AsyncServletTest String response = process("start=200&dispatch=0", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=200&dispatch=0", "initial", "start", "dispatch", - "ASYNC /ctx/path/info", + "ASYNC /ctx/path/info?start=200&dispatch=0", "!initial", "onComplete")); } @@ -326,11 +326,11 @@ public class AsyncServletTest String response = process("start=200&throw=1", null); assertThat(response, startsWith("HTTP/1.1 500 Server Error")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=200&throw=1", "initial", "start", "onError", - "ERROR /ctx/error/custom", + "ERROR /ctx/error/custom?start=200&throw=1", "!initial", "onComplete")); assertContains("ERROR DISPATCH: /ctx/error/custom", response); @@ -342,7 +342,7 @@ public class AsyncServletTest String response = process("start=200&complete=50", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=200&complete=50", "initial", "start", "complete", @@ -358,7 +358,7 @@ public class AsyncServletTest String response = process("start=200&complete=0", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=200&complete=0", "initial", "start", "complete", @@ -374,16 +374,16 @@ public class AsyncServletTest String response = process("start=1000&dispatch=10&start2=1000&dispatch2=10", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=1000&dispatch=10&start2=1000&dispatch2=10", "initial", "start", "dispatch", - "ASYNC /ctx/path/info", + "ASYNC /ctx/path/info?start=1000&dispatch=10&start2=1000&dispatch2=10", "!initial", "onStartAsync", "start", "dispatch", - "ASYNC /ctx/path/info", + "ASYNC /ctx/path/info?start=1000&dispatch=10&start2=1000&dispatch2=10", "!initial", "onComplete")); assertContains("DISPATCHED", response); @@ -395,11 +395,11 @@ public class AsyncServletTest String response = process("start=1000&dispatch=10&start2=1000&complete2=10", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=1000&dispatch=10&start2=1000&complete2=10", "initial", "start", "dispatch", - "ASYNC /ctx/path/info", + "ASYNC /ctx/path/info?start=1000&dispatch=10&start2=1000&complete2=10", "!initial", "onStartAsync", "start", @@ -415,16 +415,16 @@ public class AsyncServletTest String response = process("start=1000&dispatch=10&start2=10", null); assertThat(response, startsWith("HTTP/1.1 500 Server Error")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=1000&dispatch=10&start2=10", "initial", "start", "dispatch", - "ASYNC /ctx/path/info", + "ASYNC /ctx/path/info?start=1000&dispatch=10&start2=10", "!initial", "onStartAsync", "start", "onTimeout", - "ERROR /ctx/error/custom", + "ERROR /ctx/error/custom?start=1000&dispatch=10&start2=10", "!initial", "onComplete")); assertContains("ERROR DISPATCH: /ctx/error/custom", response); @@ -436,16 +436,16 @@ public class AsyncServletTest String response = process("start=10&start2=1000&dispatch2=10", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=10&start2=1000&dispatch2=10", "initial", "start", "onTimeout", - "ERROR /ctx/error/custom", + "ERROR /ctx/error/custom?start=10&start2=1000&dispatch2=10", "!initial", "onStartAsync", "start", "dispatch", - "ASYNC /ctx/path/info", + "ASYNC /ctx/path/info?start=10&start2=1000&dispatch2=10", "!initial", "onComplete")); assertContains("DISPATCHED", response); @@ -457,11 +457,11 @@ public class AsyncServletTest String response = process("start=10&start2=1000&complete2=10", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=10&start2=1000&complete2=10", "initial", "start", "onTimeout", - "ERROR /ctx/error/custom", + "ERROR /ctx/error/custom?start=10&start2=1000&complete2=10", "!initial", "onStartAsync", "start", @@ -478,16 +478,16 @@ public class AsyncServletTest String response = process("start=10&start2=10", null); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=10&start2=10", "initial", "start", "onTimeout", - "ERROR /ctx/path/error", + "ERROR /ctx/path/error?start=10&start2=10", "!initial", "onStartAsync", "start", "onTimeout", - "ERROR /ctx/path/error", + "ERROR /ctx/path/error?start=10&start2=10", "!initial", "onComplete")); // Error Page Loop! assertContains("AsyncContext timeout", response); @@ -499,11 +499,11 @@ public class AsyncServletTest String response = process("wrap=true&start=200&dispatch=20", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?wrap=true&start=200&dispatch=20", "initial", "start", "dispatch", - "ASYNC /ctx/path/info", + "ASYNC /ctx/path/info?wrap=true&start=200&dispatch=20", "wrapped REQ RSP", "!initial", "onComplete")); @@ -516,11 +516,11 @@ public class AsyncServletTest String response = process("start=200&dispatch=20&path=/p%20th3", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=200&dispatch=20&path=/p%20th3", "initial", "start", "dispatch", - "ASYNC /ctx/p%20th3", + "ASYNC /ctx/p%20th3?start=200&dispatch=20&path=/p%20th3", "!initial", "onComplete")); assertContains("DISPATCHED", response); @@ -532,13 +532,13 @@ public class AsyncServletTest String response = process("fwd", "start=200&dispatch=20", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "FWD REQUEST /ctx/fwd/info", - "FORWARD /ctx/path1", + "FWD REQUEST /ctx/fwd/info?start=200&dispatch=20", + "FORWARD /ctx/path1?forward=true&start=200&dispatch=20", "initial", "start", "dispatch", - "FWD ASYNC /ctx/fwd/info", - "FORWARD /ctx/path1", + "FWD ASYNC /ctx/fwd/info?start=200&dispatch=20", + "FORWARD /ctx/path1?forward=true&start=200&dispatch=20", "!initial", "onComplete")); assertContains("DISPATCHED", response); @@ -550,12 +550,12 @@ public class AsyncServletTest String response = process("fwd", "start=200&dispatch=20&path=/path2", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "FWD REQUEST /ctx/fwd/info", - "FORWARD /ctx/path1", + "FWD REQUEST /ctx/fwd/info?start=200&dispatch=20&path=/path2", + "FORWARD /ctx/path1?forward=true&start=200&dispatch=20&path=/path2", "initial", "start", "dispatch", - "ASYNC /ctx/path2", + "ASYNC /ctx/path2?start=200&dispatch=20&path=/path2", "!initial", "onComplete")); assertContains("DISPATCHED", response); @@ -567,12 +567,12 @@ public class AsyncServletTest String response = process("fwd", "wrap=true&start=200&dispatch=20", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "FWD REQUEST /ctx/fwd/info", - "FORWARD /ctx/path1", + "FWD REQUEST /ctx/fwd/info?wrap=true&start=200&dispatch=20", + "FORWARD /ctx/path1?forward=true&wrap=true&start=200&dispatch=20", "initial", "start", "dispatch", - "ASYNC /ctx/path1", + "ASYNC /ctx/path1?forward=true&wrap=true&start=200&dispatch=20", "wrapped REQ RSP", "!initial", "onComplete")); @@ -585,12 +585,12 @@ public class AsyncServletTest String response = process("fwd", "wrap=true&start=200&dispatch=20&path=/path2", null); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "FWD REQUEST /ctx/fwd/info", - "FORWARD /ctx/path1", + "FWD REQUEST /ctx/fwd/info?wrap=true&start=200&dispatch=20&path=/path2", + "FORWARD /ctx/path1?forward=true&wrap=true&start=200&dispatch=20&path=/path2", "initial", "start", "dispatch", - "ASYNC /ctx/path2", + "ASYNC /ctx/path2?forward=true&wrap=true&start=200&dispatch=20&path=/path2", "wrapped REQ RSP", "!initial", "onComplete")); @@ -619,12 +619,12 @@ public class AsyncServletTest __latch.await(1, TimeUnit.SECONDS); assertThat(response, startsWith("HTTP/1.1 200 OK")); assertThat(__history, contains( - "REQUEST /ctx/path/info", + "REQUEST /ctx/path/info?start=2000&dispatch=1500", "initial", "start", "async-read=10", "dispatch", - "ASYNC /ctx/path/info", + "ASYNC /ctx/path/info?start=2000&dispatch=1500", "!initial", "onComplete")); } @@ -685,10 +685,10 @@ public class AsyncServletTest @Override public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { - historyAdd("FWD " + request.getDispatcherType() + " " + request.getRequestURI()); + historyAdd("FWD " + request.getDispatcherType() + " " + URIUtil.addPathQuery(request.getRequestURI(), request.getQueryString())); if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper) historyAdd("wrapped" + ((request instanceof ServletRequestWrapper) ? " REQ" : "") + ((response instanceof ServletResponseWrapper) ? " RSP" : "")); - request.getServletContext().getRequestDispatcher("/path1").forward(request, response); + request.getServletContext().getRequestDispatcher("/path1?forward=true").forward(request, response); } } @@ -711,7 +711,7 @@ public class AsyncServletTest // ignored } - historyAdd(request.getDispatcherType() + " " + request.getRequestURI()); + historyAdd(request.getDispatcherType() + " " + URIUtil.addPathQuery(request.getRequestURI(),request.getQueryString())); if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper) historyAdd("wrapped" + ((request instanceof ServletRequestWrapper) ? " REQ" : "") + ((response instanceof ServletResponseWrapper) ? " RSP" : "")); diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index 1909e102242..6ae6f609cd9 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -714,6 +714,20 @@ public class URIUtil return buf.toString(); } + /** Add a path and a query string + * @param path The path which may already contain contain a query + * @param query The query string or null if no query to be added + * @return The path with any non null query added after a '?' or '&' as appropriate. + */ + public static String addPathQuery(String path, String query) + { + if (query == null) + return path; + if (path.indexOf('?') >= 0) + return path + '&' + query; + return path + '?' + query; + } + /** * Given a URI, attempt to get the last segment. *