All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.grinder.plugin.http.HTTPUtilitiesImplementation Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
// Copyright (C) 2005 - 2011 Philip Aston
// All rights reserved.
//
// This file is part of The Grinder software distribution. Refer to
// the file LICENSE which is part of The Grinder distribution for
// licensing details. The Grinder distribution is available on the
// Internet at http://grinder.sourceforge.net/
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

package net.grinder.plugin.http;

import static java.util.Collections.emptyList;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.grinder.common.GrinderException;
import net.grinder.plugin.http.tcpproxyfilter.RegularExpressions;
import net.grinder.plugin.http.tcpproxyfilter.RegularExpressionsImplementation;
import net.grinder.plugininterface.PluginProcessContext;
import net.grinder.util.AttributeStringParser;
import net.grinder.util.AttributeStringParserImplementation;
import net.grinder.util.http.URIParser;
import net.grinder.util.http.URIParserImplementation;
import HTTPClient.Codecs;
import HTTPClient.HTTPResponse;
import HTTPClient.NVPair;


/**
 * {@link HTTPUtilities} implementation.
 *
 * @author Philip Aston
 */
class HTTPUtilitiesImplementation implements HTTPUtilities {

  private final URIParser m_uriParser = new URIParserImplementation();
  private final RegularExpressions m_regularExpressions =
    new RegularExpressionsImplementation();
  private final AttributeStringParser m_attributeStringParser =
    new AttributeStringParserImplementation();

  private final ThreadLocal m_parsedBodyThreadLocal =
    new ThreadLocal();
  private final NameValue[] m_emptyNameValues = new NameValue[0];

  private final PluginProcessContext m_processContext;

  public HTTPUtilitiesImplementation(PluginProcessContext processContext) {
    m_processContext = processContext;
  }

  public NVPair basicAuthorizationHeader(String userID, String password) {
    return new NVPair("Authorization",
                      "Basic " +
                      Codecs.base64Encode(userID + ":" + password));
  }

  public HTTPResponse getLastResponse() throws GrinderException {
    final HTTPPluginThreadState threadState =
      (HTTPPluginThreadState)m_processContext.getPluginThreadListener();

    return threadState.getLastResponse();
  }

  public String valueFromLocationURI(final String tokenName)
    throws GrinderException {

    final HTTPResponse response = getLastResponse();

    if (response == null) {
      return "";
    }

    final String location;

    try {
      location = response.getHeader("Location");
    }
    catch (Exception e) {
      throw new AssertionError("HTTPResponse not initialised (" + e + ")");
    }

    final String[] result = { "" };

    if (location != null) {
      m_uriParser.parse(location, new URIParser.AbstractParseListener() {
        public boolean pathParameterNameValue(String name, String value) {
          if (name.equals(tokenName)) {
            result[0] = value;
            return false;
          }

          return true;
        }

        public boolean queryStringNameValue(String name, String value) {
          if (name.equals(tokenName)) {
            result[0] = value;
            return false;
          }

          return true;
        }
      });
    }

    return result[0];
  }

  public String valueFromBodyInput(final String tokenName)
    throws GrinderException {
    return valueFromBodyInput(tokenName, null);
  }

  public String valueFromBodyInput(String tokenName, String afterText)
    throws GrinderException {

    final HTTPResponse response = getLastResponse();

    if (response == null) {
      return "";
    }

    return getParsedBody(response).valueFromBodyInput(tokenName, afterText);
  }

  public List valuesFromBodyInput(final String tokenName)
    throws GrinderException {
    return valuesFromBodyInput(tokenName, null);
  }

  public List valuesFromBodyInput(String tokenName, String afterText)
    throws GrinderException {

    final HTTPResponse response = getLastResponse();

    if (response == null) {
      return emptyList();
    }

    return getParsedBody(response).valuesFromBodyInput(tokenName, afterText);
  }

  public String valueFromHiddenInput(final String tokenName)
    throws GrinderException {
    return valueFromHiddenInput(tokenName, null);
  }

  public String valueFromHiddenInput(String tokenName, String afterText)
    throws GrinderException {

    final HTTPResponse response = getLastResponse();

    if (response == null) {
      return "";
    }

    return getParsedBody(response).valueFromHiddenInput(tokenName, afterText);
  }

  public List valuesFromHiddenInput(final String tokenName)
    throws GrinderException {
    return valuesFromHiddenInput(tokenName, null);
  }

  public List valuesFromHiddenInput(String tokenName, String afterText)
    throws GrinderException {

    final HTTPResponse response = getLastResponse();

    if (response == null) {
      return emptyList();
    }

    return getParsedBody(response).valuesFromHiddenInput(tokenName, afterText);
  }

  public String valueFromBodyURI(final String tokenName)
    throws GrinderException {
    return valueFromBodyURI(tokenName, null);
  }

  public String valueFromBodyURI(final String tokenName, String afterText)
    throws GrinderException {

    final HTTPResponse response = getLastResponse();

    if (response == null) {
      return "";
    }

    return getParsedBody(response).valueFromBodyURI(tokenName, afterText);
  }

  public List valuesFromBodyURI(final String tokenName)
    throws GrinderException {
    return valuesFromBodyURI(tokenName, null);
  }

  public List valuesFromBodyURI(final String tokenName,
                                        String afterText)
    throws GrinderException {

    final HTTPResponse response = getLastResponse();

    if (response == null) {
      return emptyList();
    }

    return getParsedBody(response).valuesFromBodyURI(tokenName, afterText);
  }

  private ParsedBody getParsedBody(HTTPResponse response) {
    final ParsedBody original = m_parsedBodyThreadLocal.get();

    if (original != null && original.isValidForResponse(response)) {
      return original;
    }

    final ParsedBody newParsedBody = new ParsedBody(response);
    m_parsedBodyThreadLocal.set(newParsedBody);
    return newParsedBody;
  }

  /**
   * Cache parse results from a HTTPResponse.
   *
   * 

Specific to a thread, so no need to synchronise.

*/ private final class ParsedBody { private final HTTPResponse m_response; private final String m_body; private final MatchList m_bodyInputMatchList; private final MatchList m_hiddenInputMatchList; private final MatchList m_bodyURIMatchList; public ParsedBody(HTTPResponse response) { m_response = response; try { // This shouldn't fail as we have already read the complete response. m_body = response.getText(); } catch (Exception e) { throw new AssertionError(e); } m_bodyInputMatchList = new InputMatchList(m_regularExpressions.getInputPattern(), m_body); m_hiddenInputMatchList = new InputMatchList(m_regularExpressions.getHiddenInputPattern(), m_body); m_bodyURIMatchList = new BodyURIMatchList(m_body); } public boolean isValidForResponse(HTTPResponse response) { return m_response.equals(response); } public String valueFromBodyInput(String tokenName, String afterText) { return m_bodyInputMatchList.getMatchValue(tokenName, afterText); } public List valuesFromBodyInput(String tokenName, String afterText) { return m_bodyInputMatchList.getMatchValues(tokenName, afterText); } public String valueFromHiddenInput(String tokenName, String afterText) { return m_hiddenInputMatchList.getMatchValue(tokenName, afterText); } public List valuesFromHiddenInput(String tokenName, String afterText) { return m_hiddenInputMatchList.getMatchValues(tokenName, afterText); } public String valueFromBodyURI(String tokenName, String afterText) { return m_bodyURIMatchList.getMatchValue(tokenName, afterText); } public List valuesFromBodyURI(String tokenName, String afterText) { return m_bodyURIMatchList.getMatchValues(tokenName, afterText); } } private static final class NameValue { private final String m_name; private final String m_value; public NameValue(String name, String value) { m_name = name; m_value = value; } public String getName() { return m_name; } public String getValue() { return m_value; } } private static final class Match { private final int m_position; private final String m_value; public Match(int position, String value) { m_position = position; m_value = value; } public int getPosition() { return m_position; } public String getValue() { return m_value; } } private static class CachedMatchList { private final List m_matchesByPosition = new ArrayList(); private int m_lastPosition = -1; public void addMatch(Match match) { m_matchesByPosition.add(match); m_lastPosition = match.getPosition(); } public Match getMatchFrom(int startFrom) { if (m_lastPosition >= startFrom) { for (Match match : m_matchesByPosition) { if (match.getPosition() >= startFrom) { return match; } } } return null; } } private interface MatchList { String getMatchValue(String tokenName, String afterText); List getMatchValues(String tokenName, String afterText); } private static class CachedValueMap { private final Map m_map = new HashMap(); public CachedMatchList get(String tokenName) { final CachedMatchList existing = m_map.get(tokenName); if (existing != null) { return existing; } final CachedMatchList newCachedValueList = new CachedMatchList(); m_map.put(tokenName, newCachedValueList); return newCachedValueList; } } private abstract static class AbstractMatchList implements MatchList { private final String m_body; private final Matcher m_matcher; private final CachedValueMap m_cache = new CachedValueMap(); public AbstractMatchList(Pattern pattern, String body) { m_body = body; m_matcher = pattern.matcher(body); } public String getMatchValue(String tokenName, String afterText) { final int startFrom = getStartFrom(afterText); if (startFrom == -1) { return ""; } final CachedMatchList cachedValueList = m_cache.get(tokenName); final Match match = getMatch(cachedValueList, tokenName, startFrom); return match != null ? match.getValue() : ""; } public List getMatchValues(String tokenName, String afterText) { int startFrom = getStartFrom(afterText); if (startFrom == -1) { return emptyList(); } final CachedMatchList cachedValueList = m_cache.get(tokenName); final List result = new ArrayList(); while (true) { final Match match = getMatch(cachedValueList, tokenName, startFrom); if (match == null) { return result; } result.add(match.getValue()); startFrom = match.getPosition() + 1; } } private Match getMatch(CachedMatchList cachedValueList, String tokenName, int startFrom) { final Match existingMatch = cachedValueList.getMatchFrom(startFrom); if (existingMatch != null) { return existingMatch; } Match result = null; // Cache miss, parse more of the body. while (result == null && m_matcher.find()) { final NameValue[] nameValueArray = parseMatch(); final int matchPosition = m_matcher.start(); for (int i = 0; i < nameValueArray.length; ++i) { final String name = nameValueArray[i].getName(); final Match match = new Match(matchPosition, nameValueArray[i].getValue()); if (name.equals(tokenName)) { cachedValueList.addMatch(match); if (result == null && matchPosition >= startFrom) { result = match; } } else { m_cache.get(name).addMatch(match); } } } return result; } private int getStartFrom(String text) { // afterText parameter is infrequently used, so memoizing this // method would cost more than it saved. return text == null ? 0 : m_body.indexOf(text); } protected final Matcher getMatcher() { return m_matcher; } protected abstract NameValue[] parseMatch(); } private final class InputMatchList extends AbstractMatchList { public InputMatchList(Pattern pattern, String body) { super(pattern, body); } protected NameValue[] parseMatch() { final AttributeStringParser.AttributeMap map = m_attributeStringParser.parse(getMatcher().group()); final String name = map.get("name"); final String value = map.get("value"); if (name != null && value != null) { return new NameValue[] { new NameValue(name, value) }; } return m_emptyNameValues; } } private final class BodyURIMatchList extends AbstractMatchList { public BodyURIMatchList(String body) { super(m_regularExpressions.getHyperlinkURIPattern(), body); } protected NameValue[] parseMatch() { final List result = new ArrayList(); final String uri = getMatcher().group(1); m_uriParser.parse(uri, new URIParser.AbstractParseListener() { public boolean pathParameterNameValue(String name, String value) { result.add(new NameValue(name, value)); return true; } public boolean queryStringNameValue(String name, String value) { result.add(new NameValue(name, value)); return true; } }); return result.toArray(new NameValue[result.size()]); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy