/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.service.http.api.utils;
import static java.net.URLEncoder.encode;
import static java.nio.charset.StandardCharsets.UTF_8;
import org.mule.runtime.api.exception.MuleRuntimeException;
import org.mule.service.http.api.domain.ParameterMap;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
/**
* Provides helper methods for encoding and decoding http request and response content.
*
* @since 4.0
*/
public final class HttpEncoderDecoderUtils {
private HttpEncoderDecoderUtils() {
// Nothing to do
}
private static final String SPACE_ENTITY = "%20";
/**
* Extracts the path (what's left of the {@code ?} character) from the passed uri.
*
* @param uri the uri to extract the path from
* @return the path form the uri
*/
public static String extractPath(String uri) {
String path = uri;
int i = path.indexOf('?');
if (i > -1) {
path = path.substring(0, i);
}
return path;
}
/**
* Extracts the query parameters (what's right of the {@code ?} character) from the passed uri.
*
* @param uri the uri to extract the parameters from
* @return the parameters form the uri
*/
public static String extractQueryParams(String uri) {
int i = uri.indexOf("?");
String queryString = "";
if (i > -1) {
queryString = uri.substring(i + 1);
}
return queryString;
}
/**
* Converts a query-string from a request url into a {@link ParameterMap}.
* <p>
* This is the inverse of {@link #encodeQueryString(Map)}.
*
* @param queryString the query string to parse
* @return a map representation of the {@code queryString}
*/
public static ParameterMap decodeQueryString(String queryString) {
return decodeUrlEncodedBody(queryString, UTF_8);
}
/**
* Converts a map to a request url query-string form.
* <p>
* This is the inverse of {@link #decodeQueryString(String)}.
*
* @param parameters a map representation of the {@code queryString}
* @return the generated query string
*/
public static String encodeQueryString(Map<String, String> parameters) {
return encodeString(parameters, UTF_8);
}
/**
* Converts an url-encoded body into a {@link ParameterMap} with a given encoding.
* <p>
* This is the inverse of {@link #encodeString(String, Charset)}.
*
* @param queryString the string to parse
* @param encoding {@link URLDecoder#decode(String, String)}.
* @return a map representation of the {@code queryString}
*/
public static ParameterMap decodeUrlEncodedBody(String queryString, Charset encoding) {
ParameterMap queryParams = new ParameterMap();
if (queryString != null && queryString.trim().length() > 0) {
String[] pairs = queryString.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
if (idx != -1) {
addParam(queryParams, pair.substring(0, idx), pair.substring(idx + 1), encoding);
} else {
addParam(queryParams, pair, null, encoding);
}
}
}
return queryParams;
}
/**
* Decodes uri params from a request path
*
* @param pathWithUriParams path with uri param place holders
* @param requestPath request path
* @return a map with the uri params present in the request path with the values decoded.
*/
public static ParameterMap decodeUriParams(String pathWithUriParams, String requestPath) {
ParameterMap uriParams = new ParameterMap();
if (pathWithUriParams.contains("{")) {
final String[] requestPathParts = requestPath.split("/");
final String[] listenerPathParts = pathWithUriParams.split("/");
int longerPathSize = Math.min(requestPathParts.length, listenerPathParts.length);
// split will return an empty string as first path before /
for (int i = 1; i < longerPathSize; i++) {
final String listenerPart = listenerPathParts[i];
if (listenerPart.startsWith("{") && listenerPart.endsWith("}")) {
String parameterName = listenerPart.substring(1, listenerPart.length() - 1);
String parameterValue = requestPathParts[i];
uriParams.put(parameterName, decode(parameterValue, UTF_8));
}
}
}
return uriParams;
}
private static void addParam(ParameterMap queryParams, String name, String value, Charset encoding) {
queryParams.put(decode(name, encoding), decode(value, encoding));
}
private static String decode(String text, Charset encoding) {
if (text == null) {
return null;
}
try {
return URLDecoder.decode(text, encoding.name());
} catch (UnsupportedEncodingException e) {
throw new MuleRuntimeException(e);
}
}
/**
* Converts a map to a request url query-string form.
* <p>
* This is the inverse of {@link #decodeUrlEncodedBody(String, Charset)}.
*
* @param parameters a map representation of the {@code queryString}
* @param encoding {@link URLDecoder#decode(String, String)}.
* @return the generated query string
*/
public static String encodeString(Map parameters, Charset encoding) {
String body;
StringBuilder result = new StringBuilder();
for (Map.Entry<?, ?> entry : (Set<Map.Entry<?, ?>>) ((parameters).entrySet())) {
String paramName = entry.getKey().toString();
Object paramValue = entry.getValue();
Iterable paramValues = paramValue instanceof Iterable ? (Iterable) paramValue : Arrays.asList(paramValue);
for (Object value : paramValues) {
try {
paramName = encode(paramName, encoding.name());
paramValue = value != null ? encode(value.toString(), encoding.name()) : null;
} catch (UnsupportedEncodingException e) {
throw new MuleRuntimeException(e);
}
if (result.length() > 0) {
result.append("&");
}
result.append(paramName);
if (paramValue != null) {
// Allowing parameters name with no value assigned
result.append("=");
result.append(paramValue);
}
}
}
body = result.toString();
return body;
}
/**
* Encodes spaces in a path, replacing them by %20.
*
* @param path Path that may contain spaces
* @return The path with all spaces replaced by %20.
*/
public static String encodeSpaces(String path) {
return path.replaceAll(" ", SPACE_ENTITY);
}
/**
* Appends a query parameter to an URL that may or may not contain query parameters already.
*
* @param url base URL to apply the new query parameter
* @param queryParamName query parameter name
* @param queryParamValue query parameter value
* @return a new string with the query parameter appended
*/
public static String appendQueryParam(String url, String queryParamName, String queryParamValue) {
try {
return (url.contains("?") ? url + "&" : url + "?")
+ encode(queryParamName, UTF_8.name()) + "=" + encode(queryParamValue, UTF_8.name());
} catch (UnsupportedEncodingException e) {
throw new MuleRuntimeException(e);
}
}
}