package com.xceptance.xlt.common.util.action.execution; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.http.auth.Credentials; import com.gargoylesoftware.htmlunit.FormEncodingType; import com.gargoylesoftware.htmlunit.HttpMethod; import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.util.NameValuePair; import com.gargoylesoftware.htmlunit.util.UrlUtils; import com.xceptance.common.lang.StringUtils; import com.xceptance.xlt.api.util.XltLogger; import com.xceptance.xlt.common.util.ParameterUtils; import com.xceptance.xlt.common.util.action.data.URLActionData; /** * Builder <br> * Takes an {@link URLActionData} object and builds a {@link WebRequest}. <br> * two alternatives that only differ in the headers: * <ul> * <li>General WebRequest: -> {@link #buildRequest(URLActionData)} * <li>XmlHttpRequest: -> {@link #buildXhrRequest(URLActionData, URL)} * </ul> * * @author matthias mitterreiter */ public class URLActionDataRequestBuilder { public URLActionDataRequestBuilder() { XltLogger.runTimeLogger.debug("Creating new Instance"); } /** * Builds a general WebRequest with the information from action. <br> * If you want some additional XmlHttpRequest Headers, <br> * use {@link #buildXhrRequest(URLActionData, URL)} instead. * * @param action * {@link URLActionData} * @return {@link WebRequest} * @throws IllegalArgumentException * if failed to create a WebRequest */ public WebRequest buildRequest(final URLActionData action) { ParameterUtils.isNotNull(action, "URLAction"); XltLogger.runTimeLogger.debug("Building WebRequest for action: '" + action.getName() + "'"); final WebRequest resultRequest; try { resultRequest = createWebRequestFromURLAction(action); } catch (final Exception e) { throw new IllegalArgumentException( "Failed to create WebRequest for action: " + action.getName() + " Reason: " + e.getMessage(), e); } logWebRequest(resultRequest); return resultRequest; } private WebRequest createWebRequestFromURLAction(final URLActionData action) throws MalformedURLException, UnsupportedEncodingException { final WebRequest resultRequest; resultRequest = createWebRequestFromUrl(action.getUrl()); fillWebRequestWithMethod(resultRequest, action.getMethod()); if (action.hasBody()) { fillWebRequestWithBody(resultRequest, action.getBody(), action.encodeBody()); } fillWebRequestWithHeaders(resultRequest, action.getHeaders()); fillWebRequestWithCookies(resultRequest, action.getCookies()); fillWebRequestWithParameters(resultRequest, action.getParameters(), action.encodeParameters(), action.getMethod()); return resultRequest; } /** * Builds a WebRequest with the information from action.<br> * Additionally adds some XmlHttpRequest Headers. * * @param action * {@link URLActionData} * @param refererUrl * the url, the request refers to. * @return {@link WebRequest} * @throws IllegalArgumentException * if failed to create a WebRequest */ public WebRequest buildXhrRequest(final URLActionData action, final URL refererUrl) { XltLogger.runTimeLogger.debug("Building XhrWebRequest for action: " + action.getName()); WebRequest resultXhrRequest = null; try { resultXhrRequest = buildXhrRequestFromURLActionData(action, refererUrl); } catch (final Exception e) { throw new IllegalArgumentException("Failed to create XhrWebRequest for action: " + action.getName() + e.getMessage(), e); } return resultXhrRequest; } private WebRequest buildXhrRequestFromURLActionData(final URLActionData action, final URL refererUrl) throws UnsupportedEncodingException, MalformedURLException { final WebRequest resultXhrRequest = createWebRequestFromUrl(action.getUrl()); fillWebRequestWithMethod(resultXhrRequest, action.getMethod()); if (action.hasBody()) { fillWebRequestWithBody(resultXhrRequest, action.getBody(), action.encodeBody()); } fillWebRequestWithHeaders(resultXhrRequest, action.getHeaders()); resultXhrRequest.setAdditionalHeader("X-Requested-With", "XMLHttpRequest"); resultXhrRequest.setAdditionalHeader("Referer", refererUrl.toExternalForm()); fillWebRequestWithCookies(resultXhrRequest, action.getCookies()); fillWebRequestWithParameters(resultXhrRequest, action.getParameters(), action.encodeParameters(), action.getMethod()); logWebRequest(resultXhrRequest); return resultXhrRequest; } private void fillWebRequestWithHeaders(final WebRequest request, final List<NameValuePair> headers) { if (!headers.isEmpty()) { for (final NameValuePair header : headers) { request.setAdditionalHeader(header.getName(), header.getValue()); } } } private void fillWebRequestWithCookies(final WebRequest request, final List<NameValuePair> cookies) { if (!cookies.isEmpty()) { String cookieString = ""; for (final NameValuePair cookie : cookies) { cookieString = cookieString + cookie.getName() + "=" + cookie.getValue() + ";"; } request.setAdditionalHeader("Cookie", cookieString); } } private void fillWebRequestWithMethod(final WebRequest request, final HttpMethod method) { request.setHttpMethod(method); } private WebRequest createWebRequestFromUrl(final URL url) { return new WebRequest(url); } private void fillWebRequestWithBody(final WebRequest request, final String body, final Boolean encodeBody) throws UnsupportedEncodingException { String resultBody = body; if (!encodeBody) { resultBody = decodeRequestBody(body); } if (request.getHttpMethod().equals(HttpMethod.POST) || request.getHttpMethod().equals(HttpMethod.PUT)) { request.setRequestBody(resultBody); } } private void fillWebRequestWithParameters(final WebRequest request, final List<NameValuePair> parameters, final boolean encodeParameters, final HttpMethod httpMethod) throws UnsupportedEncodingException, MalformedURLException { if (!parameters.isEmpty()) { List<NameValuePair> resultParameters = new ArrayList<NameValuePair>(parameters); if (!encodeParameters) { resultParameters = decodeRequestParameters(resultParameters); } if (!httpMethod.equals(HttpMethod.POST) || request.getRequestBody() != null) { addParametersToUrl(request, resultParameters); } else { request.setRequestParameters(resultParameters); } } } private void addParametersToUrl(final WebRequest request, final List<NameValuePair> parameters) throws MalformedURLException { String urlString = request.getUrl().toString(); urlString = StringUtils.replace(urlString, "&", "&"); final URL newUrl = new URL(urlString); final String oldQueryString = newUrl.getQuery(); final StringBuilder newQueryString = new StringBuilder(); final boolean insertLeadingAmp = oldQueryString != null && !oldQueryString.isEmpty(); if (insertLeadingAmp) { newQueryString.append(oldQueryString); } for (int index = 0; index < parameters.size(); index++) { final NameValuePair nameValuePair = parameters.get(index); /* * For each pair except the first, an & has to be appended. For the first pair it depends on whether there * is already a query (in which case insertLeadingAmp is true). */ if (index > 0 || insertLeadingAmp) { newQueryString.append('&'); } newQueryString.append(nameValuePair.getName()).append('=').append(nameValuePair.getValue()); } final URL newNewUrl = UrlUtils.getUrlWithNewQuery(newUrl, newQueryString.toString()); request.setUrl(newNewUrl); } private String decodeRequestBody(final String body) throws UnsupportedEncodingException { final String decodedBody = URLDecoder.decode(body, "UTF-8"); return decodedBody; } @SuppressWarnings("unused") private String encodeRequestBody(final String body) throws UnsupportedEncodingException { final String encodedBody = URLEncoder.encode(body, "UTF-8"); return encodedBody; } @SuppressWarnings("unused") private List<NameValuePair> encodeRequestParameters(final List<NameValuePair> parameters) throws UnsupportedEncodingException { final List<NameValuePair> encodedParameters = new ArrayList<NameValuePair>(); for (final NameValuePair parameter : parameters) { final String encodedName = parameter.getName() != null ? URLEncoder.encode(parameter.getName(), "UTF-8") : null; final String encodedValue = parameter.getValue() != null ? URLEncoder.encode(parameter.getValue(), "UTF-8") : null; final NameValuePair encodedParameter = new NameValuePair(encodedName, encodedValue); encodedParameters.add(encodedParameter); } return encodedParameters; } private List<NameValuePair> decodeRequestParameters(final List<NameValuePair> parameters) throws UnsupportedEncodingException { final List<NameValuePair> decodedParameters = new ArrayList<NameValuePair>(); for (final NameValuePair parameter : parameters) { final String decodedName = parameter.getName() != null ? URLDecoder.decode(parameter.getName(), "UTF-8") : null; final String decodedValue = parameter.getValue() != null ? URLDecoder.decode(parameter.getValue(), "UTF-8") : null; final NameValuePair decodedParameter = new NameValuePair(decodedName, decodedValue); decodedParameters.add(decodedParameter); } return decodedParameters; } /** * For debugging purpose. <br> * 'err-streams' the attributes of the WebRequest. <br> * * @param request * The {@link WebRequest} you want to err stream. */ public void logWebRequest(final WebRequest request) { XltLogger.runTimeLogger.debug("------------Webrequest------------"); final String url = request.getUrl().toString(); if (url != null) { XltLogger.runTimeLogger.debug("URL: " + url); } final HttpMethod method = request.getHttpMethod(); if (method != null) { XltLogger.runTimeLogger.debug("Method : " + method); } final String charSet = request.getCharset(); if (charSet != null) { XltLogger.runTimeLogger.debug("Chraset: " + charSet); } final String proxy = request.getProxyHost(); if (proxy != null) { XltLogger.runTimeLogger.debug("Proxy: " + proxy); } final String body = request.getRequestBody(); if (body != null) { XltLogger.runTimeLogger.debug("Body: " + body); } final FormEncodingType encoding = request.getEncodingType(); if (encoding != null) { final String encodingString = encoding.toString(); XltLogger.runTimeLogger.debug("Encoding: " + encodingString); } final URL originalUrl = request.getOriginalURL(); if (originalUrl != null) { final String originalUrlString = originalUrl.toString(); XltLogger.runTimeLogger.debug("OriginalUrl: " + originalUrlString); } final List<NameValuePair> parameters = request.getRequestParameters(); if (!(parameters == null) && !(parameters.isEmpty())) { XltLogger.runTimeLogger.debug("Parameters: "); for (final NameValuePair parameter : parameters) { XltLogger.runTimeLogger.debug("\t" + parameter.getName() + " = " + parameter.getValue()); } } final Map<String, String> headers = request.getAdditionalHeaders(); if (!(headers == null)) { XltLogger.runTimeLogger.debug("Headers: "); for (final Map.Entry<String, String> entry : headers.entrySet()) { XltLogger.runTimeLogger.debug("\t" + entry.getKey() + " : " + entry.getValue()); } } final Credentials credentials = request.getCredentials(); if (credentials != null) { XltLogger.runTimeLogger.debug("Credentials: " + credentials.toString()); } } }