Skip to content

Commit

Permalink
Merge pull request #39 from norbertroamsys/fix/urlParamDecoding
Browse files Browse the repository at this point in the history
Fix URL decoding of path parameters
  • Loading branch information
ni-c committed Feb 9, 2022
2 parents 97bb44d + c6557a0 commit 6563120
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 48 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -7,7 +7,7 @@
<groupId>com.roamsys.opensource</groupId>
<artifactId>swaggerapi</artifactId>
<packaging>jar</packaging>
<version>5.0.0-SNAPSHOT</version>
<version>6.0.0-SNAPSHOT</version>
<organization>
<name>ROAMSYS S.A.</name>
<url>http://www.roamsys.com</url>
Expand Down
93 changes: 46 additions & 47 deletions src/main/java/com/roamsys/swagger/SwaggerAPIServlet.java
Expand Up @@ -10,8 +10,6 @@
import com.roamsys.swagger.data.SwaggerExceptionHandler;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
Expand Down Expand Up @@ -53,21 +51,27 @@ public class SwaggerAPIServlet extends HttpServlet {
protected void processRequest(final HttpServletRequest request, final HttpServletResponse response, final HTTPMethod method) throws ServletException, IOException {
final SwaggerAPIConfig config = (SwaggerAPIConfig) request.getSession().getServletContext().getAttribute(SwaggerAPIConfig.SERVLET_ATTRIBUTE_NAME);

final String uri = request.getRequestURI();
final String path = uri.substring(request.getContextPath().length() + request.getServletPath().length(), uri.length());

// register exception handler for API
final String exceptionHandlerClass = getServletConfig().getInitParameter("exceptionHandler");
SwaggerExceptionHandler exceptionHandler = config.getExceptionHandler();
if (!StringUtils.isEmpty(exceptionHandlerClass)) {
try {
final Class<?> clazz = Class.forName(exceptionHandlerClass);
config.setExceptionHandler((SwaggerExceptionHandler) clazz.getConstructor().newInstance());
exceptionHandler = (SwaggerExceptionHandler) clazz.getConstructor().newInstance();
config.setExceptionHandler(exceptionHandler);
} catch (final Exception ex) {
//log error with default exception handler
config.getExceptionHandler().handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Could not instantiate Swagger exception handler [" + exceptionHandlerClass + "]. Default exception handler will be used.", ex);
// log error with default exception handler
exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Could not instantiate Swagger exception handler [" + exceptionHandlerClass + "]. Default exception handler will be used.", ex);
}
}

// get the URL decoded path to be called
final String path = request.getPathInfo();
if (path == null) {
exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Invalid empty path", null);
return;
}

// enables cross-origin-access, if allowed in config
if (config.isCrossOriginAccessAllowed()) {
response.addHeader("Access-Control-Allow-Origin", "*");
Expand All @@ -80,11 +84,9 @@ protected void processRequest(final HttpServletRequest request, final HttpServle
response.setContentType(config.getDefaultContentType());
}

/**
* try to authenticate the API call
*/
// try to authenticate the API call
if (config.getAuthorizationHandler() != null && !config.getAuthorizationHandler().isRequestAuthorized(request, response)) {
config.getExceptionHandler().handleException(response, HttpServletResponse.SC_UNAUTHORIZED, "Invalid authorization key", null);
exceptionHandler.handleException(response, HttpServletResponse.SC_UNAUTHORIZED, "Invalid authorization key", null);
return;
}

Expand All @@ -104,7 +106,7 @@ protected void processRequest(final HttpServletRequest request, final HttpServle
// API method calls
final int basePathEndPos = path.indexOf("/", 1);
if (basePathEndPos == -1) {
config.getExceptionHandler().handleException(response, HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Invalid base URL", null);
exceptionHandler.handleException(response, HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Invalid base URL", null);
return;
}

Expand All @@ -116,16 +118,16 @@ protected void processRequest(final HttpServletRequest request, final HttpServle
for (final SwaggerAPIModelData api : config.getAPIsFor(basePath)) {
// Find matching API model and method
if (api.getHTTPMethod() == method) {
final Matcher m = api.matchPath(path);
if (m.matches()) {
final Matcher pathParamMatcher = api.matchPath(path);
if (pathParamMatcher.matches()) {
matchFound = true;
response.setStatus(HttpServletResponse.SC_OK);

// Set up variables for parameter collection
final List<SwaggerAPIParameterData> paramsData = api.getParameters();
final int parameterCount = paramsData.size();
final Object[] arguments = new Object[parameterCount + 1];
arguments[0] = new SwaggerAPIContext(this, request, response, config.getExceptionHandler());
arguments[0] = new SwaggerAPIContext(this, request, response, exceptionHandler);

// Collect parameters
int getParamIndex = 0;
Expand All @@ -136,8 +138,8 @@ protected void processRequest(final HttpServletRequest request, final HttpServle
try {
switch (paramData.getParamType()) {
case PATH:
if (m.groupCount() >= getParamIndex + 1) {
arguments[i] = convertParamToArgument(paramData.getDataType(), m.group(1 + getParamIndex++));
if (pathParamMatcher.groupCount() >= getParamIndex + 1) {
arguments[i] = convertParamToArgument(paramData.getDataType(), pathParamMatcher.group(1 + getParamIndex++));
}
break;

Expand All @@ -158,7 +160,7 @@ protected void processRequest(final HttpServletRequest request, final HttpServle
throw new IllegalArgumentException("Handling for parameter type \"" + paramData.getParamType().name() + "\" not yet implemented.");
}
} catch (final ParseException ex) {
config.getExceptionHandler().handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Invalid value for parameter " + paramData.getName(), ex);
exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Invalid value for parameter " + paramData.getName(), ex);
return;
}
}
Expand All @@ -167,21 +169,21 @@ protected void processRequest(final HttpServletRequest request, final HttpServle
try {
api.getMethod().invoke(api.getAPIModelClass(), arguments);
} catch (final IllegalAccessException ex) {
config.getExceptionHandler().handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Called method is not visible", ex);
exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Called method is not visible", ex);
} catch (final IllegalArgumentException ex) {
config.getExceptionHandler().handleException(response, HttpServletResponse.SC_NOT_ACCEPTABLE, "Illegal parameters for called method. See server error log for details.", ex);
exceptionHandler.handleException(response, HttpServletResponse.SC_NOT_ACCEPTABLE, "Illegal parameters for called method. See server error log for details.", ex);
} catch (final InvocationTargetException ex) {
config.getExceptionHandler().handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Error calling method. See server error log for details.", ex);
exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Error calling method. See server error log for details.", ex);
} catch (final Throwable ex) {
config.getExceptionHandler().handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Internal server error for called method. See server error log for details.", ex);
exceptionHandler.handleException(response, HttpServletResponse.SC_BAD_REQUEST, "Internal server error for called method. See server error log for details.", ex);
}
break;
}
}
}
}
if (!matchFound) {
config.getExceptionHandler().handleException(response, HttpServletResponse.SC_NOT_IMPLEMENTED, "Called method does not exist", null);
exceptionHandler.handleException(response, HttpServletResponse.SC_NOT_IMPLEMENTED, "Called method does not exist", null);
}
}

Expand All @@ -194,35 +196,32 @@ protected void processRequest(final HttpServletRequest request, final HttpServle
}

/**
* Convert the swagger API parameter to a method argument class type
* depending on the swagger API data type
* Convert the swagger API parameter to a method argument class type depending on the swagger API data type.
*
* @param dataType the swagger API parameter data type
* @param paramValue the value of the parameter as string
* @return the argument
*/
private Object convertParamToArgument(final DataType dataType, final String paramValue) throws ParseException, IOException {
switch (dataType) {
case STRING:
return paramValue;
case INTEGER:
return Integer.parseInt(paramValue);
case LONG:
return Long.parseLong(paramValue);
case BOOLEAN:
return Boolean.valueOf(paramValue);
case DATE:
return new SimpleDateFormat(DATE_FORMAT).parse(paramValue);
case DATETIME:
final SimpleDateFormat dateTimeFormat = new SimpleDateFormat(DATE_TIME_FORMAT);
try {
return dateTimeFormat.parse(paramValue);
} catch (final ParseException ex) {
// Swagger UI will encode the URL - let's try with decoded value again
return dateTimeFormat.parse(URLDecoder.decode(paramValue, StandardCharsets.UTF_8.name()));
}
default:
throw new IllegalArgumentException("Handling for data type \"" + dataType.name() + "\" not yet implemented.");
if (StringUtils.isEmpty(paramValue)) {
return null;
} else {
switch (dataType) {
case STRING:
return paramValue;
case INTEGER:
return Integer.parseInt(paramValue);
case LONG:
return Long.parseLong(paramValue);
case BOOLEAN:
return Boolean.valueOf(paramValue);
case DATE:
return new SimpleDateFormat(DATE_FORMAT).parse(paramValue);
case DATETIME:
return new SimpleDateFormat(DATE_TIME_FORMAT).parse(paramValue);
default:
throw new IllegalArgumentException("Handling for data type \"" + dataType.name() + "\" not yet implemented.");
}
}
}

Expand Down

0 comments on commit 6563120

Please sign in to comment.