Skip to content

Commit bd64ced

Browse files
committed
[UNDERTOW-1591][UNDERTOW-2123][UNDERTOW-2206] IAE trying to decode a requestPath
1 parent 52577d1 commit bd64ced

File tree

13 files changed

+888
-174
lines changed

13 files changed

+888
-174
lines changed

core/src/main/java/io/undertow/server/Connectors.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@
3232
import io.undertow.util.StatusCodes;
3333
import io.undertow.util.URLUtils;
3434
import io.undertow.connector.PooledByteBuffer;
35+
import org.xnio.OptionMap;
3536
import org.xnio.channels.StreamSourceChannel;
3637
import org.xnio.conduits.ConduitStreamSinkChannel;
3738

3839
import java.io.IOException;
40+
import java.nio.charset.StandardCharsets;
3941
import java.util.Date;
4042
import java.util.concurrent.Executor;
4143
import java.util.concurrent.RejectedExecutionException;
@@ -447,13 +449,33 @@ public static void setExchangeRequestPath(final HttpServerExchange exchange, fin
447449
throw new RuntimeException(e);
448450
}
449451
}
450-
/**
451-
* Sets the request path and query parameters, decoding to the requested charset.
452-
*
453-
* @param exchange The exchange
454-
* @param encodedPath The encoded path
455-
* @param charset The charset
456-
*/
452+
453+
/**
454+
* Sets the request path and query parameters, decoding to the requested charset.
455+
* All the options are retrieved from the exchange undertow options.
456+
*
457+
* @param exchange The exchange
458+
* @param encodedPath The encoded path to decode
459+
* @param decodeBuffer The decode buffer to use
460+
* @throws ParameterLimitException
461+
*/
462+
public static void setExchangeRequestPath(final HttpServerExchange exchange, final String encodedPath, StringBuilder decodeBuffer) throws ParameterLimitException {
463+
final OptionMap options = exchange.getConnection().getUndertowOptions();
464+
setExchangeRequestPath(exchange, encodedPath,
465+
options.get(UndertowOptions.URL_CHARSET, StandardCharsets.UTF_8.name()),
466+
options.get(UndertowOptions.DECODE_URL, true),
467+
options.get(UndertowOptions.ALLOW_ENCODED_SLASH, false),
468+
decodeBuffer,
469+
options.get(UndertowOptions.MAX_PARAMETERS, UndertowOptions.DEFAULT_MAX_PARAMETERS));
470+
}
471+
472+
/**
473+
* Sets the request path and query parameters, decoding to the requested charset.
474+
*
475+
* @param exchange The exchange
476+
* @param encodedPath The encoded path
477+
* @param charset The charset
478+
*/
457479
public static void setExchangeRequestPath(final HttpServerExchange exchange, final String encodedPath, final String charset, boolean decode, final boolean allowEncodedSlash, StringBuilder decodeBuffer, int maxParameters) throws ParameterLimitException {
458480
boolean requiresDecode = false;
459481
final StringBuilder pathBuilder = new StringBuilder();

core/src/main/java/io/undertow/server/protocol/http/HttpRequestParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ final void handlePathParameters(ByteBuffer buffer, ParseState state, HttpServerE
619619
handleParsedParam(param, stringBuilder.substring(pos), exchange, urlDecodeRequired, state);
620620
final String path = stringBuilder.toString();
621621
// the canonicalPathStart should be the current length to not add anything to it
622-
parsePathComplete(state, exchange, path.length(), state.parseState, urlDecodeRequired, path);
622+
parsePathComplete(state, exchange, path.length(), state.parseState, state.urlDecodeRequired, path);
623623
state.state = ParseState.VERSION;
624624
state.nextQueryParam = null;
625625
if (next == '?') {

servlet/src/main/java/io/undertow/servlet/spec/AsyncContextImpl.java

Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@
2121
import java.io.IOException;
2222
import java.util.ArrayDeque;
2323
import java.util.Deque;
24-
import java.util.HashMap;
2524
import java.util.List;
26-
import java.util.Map;
2725
import java.util.concurrent.CopyOnWriteArrayList;
2826
import java.util.concurrent.Executor;
2927
import java.util.concurrent.TimeUnit;
@@ -61,8 +59,10 @@
6159
import io.undertow.servlet.handlers.ServletDebugPageHandler;
6260
import io.undertow.servlet.handlers.ServletPathMatch;
6361
import io.undertow.servlet.handlers.ServletRequestContext;
62+
import io.undertow.servlet.util.DispatchUtils;
6463
import io.undertow.util.CanonicalPathUtils;
6564
import io.undertow.util.Headers;
65+
import io.undertow.util.ParameterLimitException;
6666
import io.undertow.util.SameThreadExecutor;
6767
import io.undertow.util.StatusCodes;
6868
import io.undertow.util.WorkerUtils;
@@ -219,53 +219,15 @@ public void dispatch(final ServletContext context, final String path) {
219219

220220
HttpServletRequestImpl requestImpl = servletRequestContext.getOriginalRequest();
221221
HttpServletResponseImpl responseImpl = servletRequestContext.getOriginalResponse();
222-
final HttpServerExchange exchange = requestImpl.getExchange();
223222

224-
exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY).setDispatcherType(DispatcherType.ASYNC);
225-
226-
requestImpl.setAttribute(ASYNC_REQUEST_URI, requestImpl.getOriginalRequestURI());
227-
requestImpl.setAttribute(ASYNC_CONTEXT_PATH, requestImpl.getOriginalContextPath());
228-
requestImpl.setAttribute(ASYNC_SERVLET_PATH, requestImpl.getOriginalServletPath());
229-
requestImpl.setAttribute(ASYNC_QUERY_STRING, requestImpl.getOriginalQueryString());
230-
231-
String newQueryString = "";
232-
int qsPos = path.indexOf("?");
233-
String newServletPath = path;
234-
if (qsPos != -1) {
235-
newQueryString = newServletPath.substring(qsPos + 1);
236-
newServletPath = newServletPath.substring(0, qsPos);
237-
}
238-
String newRequestUri = context.getContextPath() + newServletPath;
239-
240-
//todo: a more efficient impl
241-
Map<String, Deque<String>> newQueryParameters = new HashMap<>();
242-
for (String part : newQueryString.split("&")) {
243-
String name = part;
244-
String value = "";
245-
int equals = part.indexOf('=');
246-
if (equals != -1) {
247-
name = part.substring(0, equals);
248-
value = part.substring(equals + 1);
249-
}
250-
Deque<String> queue = newQueryParameters.get(name);
251-
if (queue == null) {
252-
newQueryParameters.put(name, queue = new ArrayDeque<>(1));
253-
}
254-
queue.add(value);
223+
ServletPathMatch info;
224+
try {
225+
info = DispatchUtils.dispatchAsync(path, requestImpl, responseImpl, (ServletContextImpl) context);
226+
} catch (ParameterLimitException e) {
227+
throw new IllegalStateException(e);
255228
}
256-
requestImpl.setQueryParameters(newQueryParameters);
257-
258-
requestImpl.getExchange().setRelativePath(newServletPath);
259-
requestImpl.getExchange().setQueryString(newQueryString);
260-
requestImpl.getExchange().setRequestPath(newRequestUri);
261-
requestImpl.getExchange().setRequestURI(newRequestUri);
262-
requestImpl.setServletContext((ServletContextImpl) context);
263-
responseImpl.setServletContext((ServletContextImpl) context);
264229

265230
Deployment deployment = requestImpl.getServletContext().getDeployment();
266-
ServletPathMatch info = deployment.getServletPaths().getServletHandlerByPath(newServletPath);
267-
requestImpl.getExchange().getAttachment(ServletRequestContext.ATTACHMENT_KEY).setServletPathMatch(info);
268-
269231
dispatchAsyncRequest(deployment.getServletDispatcher(), info, exchange);
270232
}
271233

servlet/src/main/java/io/undertow/servlet/spec/RequestDispatcherImpl.java

Lines changed: 25 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
import io.undertow.servlet.handlers.ServletRequestContext;
4646
import io.undertow.servlet.handlers.ServletChain;
4747
import io.undertow.servlet.handlers.ServletPathMatch;
48-
import io.undertow.util.QueryParameterUtils;
48+
import io.undertow.servlet.util.DispatchUtils;
49+
import io.undertow.util.ParameterLimitException;
4950

5051
/**
5152
* @author Stuart Douglas
@@ -55,32 +56,20 @@ public class RequestDispatcherImpl implements RequestDispatcher {
5556
private final String path;
5657
private final ServletContextImpl servletContext;
5758
private final ServletChain chain;
58-
private final ServletPathMatch pathMatch;
5959
private final boolean named;
6060

6161
public RequestDispatcherImpl(final String path, final ServletContextImpl servletContext) {
6262
this.path = path;
6363
this.servletContext = servletContext;
64-
String basePath = path;
65-
int qPos = basePath.indexOf("?");
66-
if (qPos != -1) {
67-
basePath = basePath.substring(0, qPos);
68-
}
69-
int mPos = basePath.indexOf(";");
70-
if(mPos != -1) {
71-
basePath = basePath.substring(0, mPos);
72-
}
73-
this.pathMatch = servletContext.getDeployment().getServletPaths().getServletHandlerByPath(basePath);
74-
this.chain = pathMatch.getServletChain();
7564
this.named = false;
65+
this.chain = null;
7666
}
7767

7868
public RequestDispatcherImpl(final ServletChain chain, final ServletContextImpl servletContext) {
7969
this.chain = chain;
8070
this.named = true;
8171
this.servletContext = servletContext;
8272
this.path = null;
83-
this.pathMatch = null;
8473
}
8574

8675

@@ -169,8 +158,6 @@ private void forwardImpl(ServletRequest request, ServletResponse response, Servl
169158
final ServletRequest oldRequest = servletRequestContext.getServletRequest();
170159
final ServletResponse oldResponse = servletRequestContext.getServletResponse();
171160

172-
Map<String, Deque<String>> queryParameters = requestImpl.getQueryParameters();
173-
174161
request.removeAttribute(INCLUDE_REQUEST_URI);
175162
request.removeAttribute(INCLUDE_CONTEXT_PATH);
176163
request.removeAttribute(INCLUDE_SERVLET_PATH);
@@ -181,38 +168,14 @@ private void forwardImpl(ServletRequest request, ServletResponse response, Servl
181168
final String oldRequestPath = requestImpl.getExchange().getRequestPath();
182169
final String oldPath = requestImpl.getExchange().getRelativePath();
183170
final ServletPathMatch oldServletPathMatch = requestImpl.getExchange().getAttachment(ServletRequestContext.ATTACHMENT_KEY).getServletPathMatch();
184-
if (!named) {
185-
186-
//only update if this is the first forward
187-
if (request.getAttribute(FORWARD_REQUEST_URI) == null) {
188-
requestImpl.setAttribute(FORWARD_REQUEST_URI, requestImpl.getRequestURI());
189-
requestImpl.setAttribute(FORWARD_CONTEXT_PATH, requestImpl.getContextPath());
190-
requestImpl.setAttribute(FORWARD_SERVLET_PATH, requestImpl.getServletPath());
191-
requestImpl.setAttribute(FORWARD_PATH_INFO, requestImpl.getPathInfo());
192-
requestImpl.setAttribute(FORWARD_QUERY_STRING, requestImpl.getQueryString());
193-
}
194171

195-
int qsPos = path.indexOf("?");
196-
String newServletPath = path;
197-
if (qsPos != -1) {
198-
String newQueryString = newServletPath.substring(qsPos + 1);
199-
newServletPath = newServletPath.substring(0, qsPos);
200-
201-
String encoding = QueryParameterUtils.getQueryParamEncoding(servletRequestContext.getExchange());
202-
Map<String, Deque<String>> newQueryParameters = QueryParameterUtils.mergeQueryParametersWithNewQueryString(queryParameters, newQueryString, encoding);
203-
requestImpl.getExchange().setQueryString(newQueryString);
204-
requestImpl.setQueryParameters(newQueryParameters);
172+
ServletPathMatch pathMatch = null;
173+
if (!named) {
174+
try {
175+
pathMatch = DispatchUtils.dispatchForward(path, requestImpl, responseImpl, servletContext);
176+
} catch (ParameterLimitException e) {
177+
throw new ServletException(e);
205178
}
206-
String newRequestUri = servletContext.getContextPath() + newServletPath;
207-
208-
209-
210-
requestImpl.getExchange().setRelativePath(newServletPath);
211-
requestImpl.getExchange().setRequestPath(newRequestUri);
212-
requestImpl.getExchange().setRequestURI(newRequestUri);
213-
requestImpl.getExchange().getAttachment(ServletRequestContext.ATTACHMENT_KEY).setServletPathMatch(pathMatch);
214-
requestImpl.setServletContext(servletContext);
215-
responseImpl.setServletContext(servletContext);
216179
}
217180

218181
try {
@@ -241,9 +204,7 @@ private void forwardImpl(ServletRequest request, ServletResponse response, Servl
241204
}
242205
}
243206
}
244-
} catch (ServletException e) {
245-
throw e;
246-
} catch (IOException e) {
207+
} catch (ServletException | IOException e) {
247208
throw e;
248209
} catch (Exception e) {
249210
throw new RuntimeException(e);
@@ -348,32 +309,19 @@ private void includeImpl(ServletRequest request, ServletResponse response, Servl
348309
Object queryString = null;
349310
Map<String, Deque<String>> queryParameters = requestImpl.getQueryParameters();
350311

312+
ServletPathMatch pathMatch = null;
351313
if (!named) {
352314
requestUri = request.getAttribute(INCLUDE_REQUEST_URI);
353315
contextPath = request.getAttribute(INCLUDE_CONTEXT_PATH);
354316
servletPath = request.getAttribute(INCLUDE_SERVLET_PATH);
355317
pathInfo = request.getAttribute(INCLUDE_PATH_INFO);
356318
queryString = request.getAttribute(INCLUDE_QUERY_STRING);
357319

358-
int qsPos = path.indexOf("?");
359-
String newServletPath = path;
360-
if (qsPos != -1) {
361-
String newQueryString = newServletPath.substring(qsPos + 1);
362-
newServletPath = newServletPath.substring(0, qsPos);
363-
364-
String encoding = QueryParameterUtils.getQueryParamEncoding(servletRequestContext.getExchange());
365-
Map<String, Deque<String>> newQueryParameters = QueryParameterUtils.mergeQueryParametersWithNewQueryString(queryParameters, newQueryString, encoding);
366-
requestImpl.setQueryParameters(newQueryParameters);
367-
requestImpl.setAttribute(INCLUDE_QUERY_STRING, newQueryString);
368-
} else {
369-
requestImpl.setAttribute(INCLUDE_QUERY_STRING, "");
320+
try {
321+
pathMatch = DispatchUtils.dispatchInclude(path, requestImpl, responseImpl, servletContext);
322+
} catch (ParameterLimitException e) {
323+
throw new ServletException(e);
370324
}
371-
String newRequestUri = servletContext.getContextPath() + newServletPath;
372-
373-
requestImpl.setAttribute(INCLUDE_REQUEST_URI, newRequestUri);
374-
requestImpl.setAttribute(INCLUDE_CONTEXT_PATH, servletContext.getContextPath());
375-
requestImpl.setAttribute(INCLUDE_SERVLET_PATH, pathMatch.getMatched());
376-
requestImpl.setAttribute(INCLUDE_PATH_INFO, pathMatch.getRemaining());
377325
}
378326
boolean inInclude = responseImpl.isInsideInclude();
379327
responseImpl.setInsideInclude(true);
@@ -386,10 +334,9 @@ private void includeImpl(ServletRequest request, ServletResponse response, Servl
386334
try {
387335
servletRequestContext.setServletRequest(request);
388336
servletRequestContext.setServletResponse(response);
389-
servletContext.getDeployment().getServletDispatcher().dispatchToServlet(requestImpl.getExchange(), chain, DispatcherType.INCLUDE);
390-
} catch (ServletException e) {
391-
throw e;
392-
} catch (IOException e) {
337+
servletContext.getDeployment().getServletDispatcher().dispatchToServlet(requestImpl.getExchange(),
338+
named? chain : pathMatch.getServletChain(), DispatcherType.INCLUDE);
339+
} catch (ServletException|IOException e) {
393340
throw e;
394341
} catch (Exception e) {
395342
throw new RuntimeException(e);
@@ -452,59 +399,20 @@ private void error(ServletRequestContext servletRequestContext, final ServletReq
452399

453400
final ServletRequest oldRequest = servletRequestContext.getServletRequest();
454401
final ServletResponse oldResponse = servletRequestContext.getServletResponse();
455-
servletRequestContext.setDispatcherType(DispatcherType.ERROR);
456-
457-
//only update if this is the first forward, add forward attrs too
458-
if (request.getAttribute(FORWARD_REQUEST_URI) == null) {
459-
requestImpl.setAttribute(FORWARD_REQUEST_URI, requestImpl.getRequestURI());
460-
requestImpl.setAttribute(FORWARD_CONTEXT_PATH, requestImpl.getContextPath());
461-
requestImpl.setAttribute(FORWARD_SERVLET_PATH, requestImpl.getServletPath());
462-
requestImpl.setAttribute(FORWARD_PATH_INFO, requestImpl.getPathInfo());
463-
requestImpl.setAttribute(FORWARD_QUERY_STRING, requestImpl.getQueryString());
464-
}
465-
requestImpl.setAttribute(ERROR_REQUEST_URI, requestImpl.getRequestURI());
466-
requestImpl.setAttribute(ERROR_SERVLET_NAME, servletName);
467-
if (exception != null) {
468-
if (exception instanceof ServletException && ((ServletException)exception).getRootCause() != null) {
469-
requestImpl.setAttribute(ERROR_EXCEPTION, ((ServletException) exception).getRootCause());
470-
requestImpl.setAttribute(ERROR_EXCEPTION_TYPE, ((ServletException) exception).getRootCause().getClass());
471-
} else {
472-
requestImpl.setAttribute(ERROR_EXCEPTION, exception);
473-
requestImpl.setAttribute(ERROR_EXCEPTION_TYPE, exception.getClass());
474-
}
475-
}
476-
requestImpl.setAttribute(ERROR_MESSAGE, message);
477-
requestImpl.setAttribute(ERROR_STATUS_CODE, responseImpl.getStatus());
478-
479-
int qsPos = path.indexOf("?");
480-
String newServletPath = path;
481-
if (qsPos != -1) {
482-
Map<String, Deque<String>> queryParameters = requestImpl.getQueryParameters();
483-
String newQueryString = newServletPath.substring(qsPos + 1);
484-
newServletPath = newServletPath.substring(0, qsPos);
485-
486-
String encoding = QueryParameterUtils.getQueryParamEncoding(servletRequestContext.getExchange());
487-
Map<String, Deque<String>> newQueryParameters = QueryParameterUtils.mergeQueryParametersWithNewQueryString(queryParameters, newQueryString, encoding);
488-
requestImpl.getExchange().setQueryString(newQueryString);
489-
requestImpl.setQueryParameters(newQueryParameters);
490-
}
491-
String newRequestUri = servletContext.getContextPath() + newServletPath;
492402

493-
requestImpl.getExchange().setRelativePath(newServletPath);
494-
requestImpl.getExchange().setRequestPath(newRequestUri);
495-
requestImpl.getExchange().setRequestURI(newRequestUri);
496-
requestImpl.getExchange().getAttachment(ServletRequestContext.ATTACHMENT_KEY).setServletPathMatch(pathMatch);
497-
requestImpl.setServletContext(servletContext);
498-
responseImpl.setServletContext(servletContext);
403+
ServletPathMatch pathMatch;
404+
try {
405+
pathMatch = DispatchUtils.dispatchError(path, servletName, exception, message, requestImpl, responseImpl, servletContext);
406+
} catch (ParameterLimitException e) {
407+
throw new ServletException(e);
408+
}
499409

500410
try {
501411
try {
502412
servletRequestContext.setServletRequest(request);
503413
servletRequestContext.setServletResponse(response);
504414
servletContext.getDeployment().getServletDispatcher().dispatchToPath(requestImpl.getExchange(), pathMatch, DispatcherType.ERROR);
505-
} catch (ServletException e) {
506-
throw e;
507-
} catch (IOException e) {
415+
} catch (ServletException | IOException e) {
508416
throw e;
509417
} catch (Exception e) {
510418
throw new RuntimeException(e);

0 commit comments

Comments
 (0)