Package org.apache.jmeter.protocol.http.proxy

Source Code of org.apache.jmeter.protocol.http.proxy.HttpRequestHdr

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.apache.jmeter.protocol.http.proxy;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.CharUtils;
import org.apache.jmeter.protocol.http.config.MultipartUrlConfig;
import org.apache.jmeter.protocol.http.control.Header;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui;
import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui2;
import org.apache.jmeter.protocol.http.gui.HeaderPanel;
import org.apache.jmeter.protocol.http.sampler.HTTPSampler2;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerFactory;
import org.apache.jmeter.protocol.http.util.ConversionUtils;
import org.apache.jmeter.protocol.http.util.HTTPConstants;
import org.apache.jmeter.protocol.http.util.HTTPFileArg;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

//For unit tests, @see TestHttpRequestHdr

/**
* The headers of the client HTTP request.
*
*/
public class HttpRequestHdr {
    private static final Logger log = LoggingManager.getLoggerForClass();

    private static final String HTTP = "http"; // $NON-NLS-1$
    private static final String HTTPS = "https"; // $NON-NLS-1$
    private static final String PROXY_CONNECTION = "proxy-connection"; // $NON-NLS-1$
    private static final String CONTENT_TYPE = "content-type"; // $NON-NLS-1$
    private static final String CONTENT_LENGTH = "content-length"; // $NON-NLS-1$

    /** Filetype to be used for the temporary binary files*/
    private static final String binaryFileSuffix =
        JMeterUtils.getPropDefault("proxy.binary.filesuffix",// $NON-NLS-1$
                                   ".binary"); // $NON-NLS-1$
   
    /** Which content-types will be treated as binary (exact match) */
    private static final Set binaryContentTypes = new HashSet();
   
    /** Where to store the temporary binary files */
    private static final String binaryDirectory =
        JMeterUtils.getPropDefault("proxy.binary.directory",// $NON-NLS-1$
                JMeterUtils.getProperty("user.dir")); // $NON-NLS-1$ proxy.binary.filetype=binary
   
    static {
        String binaries = JMeterUtils.getPropDefault("proxy.binary.types", // $NON-NLS-1$
                "application/x-amf,application/x-java-serialized-object"); // $NON-NLS-1$
        if (binaries.length() > 0){
            StringTokenizer s = new StringTokenizer(binaries,"|, ");// $NON-NLS-1$
            while (s.hasMoreTokens()){
               binaryContentTypes.add(s.nextToken());
            }
        }
    }

  /**
   * Http Request method. Such as get or post.
   */
  private String method = ""; // $NON-NLS-1$

  /**
   * The requested url. The universal resource locator that hopefully uniquely
   * describes the object or service the client is requesting.
   */
  private String url = ""; // $NON-NLS-1$

  /**
   * Version of http being used. Such as HTTP/1.0.
   */
  private String version = ""; // NOTREAD // $NON-NLS-1$

    private byte[] rawPostData;

  private final Map headers = new HashMap();

  private final HTTPSamplerBase sampler;

  private HeaderManager headerManager;
 
  /*
   * Optionally number the requests
   */
  private static final boolean numberRequests =
        JMeterUtils.getPropDefault("proxy.number.requests", false); // $NON-NLS-1$

  private static volatile int requestNumber = 0;// running number

  public HttpRequestHdr() {
    this.sampler = HTTPSamplerFactory.newInstance();
  }
 
  /**
   * @param sampler the http sampler
   */
  public HttpRequestHdr(HTTPSamplerBase sampler) {
    this.sampler = sampler;
  }
 
  /**
   * Parses a http header from a stream.
   *
   * @param in
   *            the stream to parse.
   * @return array of bytes from client.
   */
  public byte[] parse(InputStream in) throws IOException {
    boolean inHeaders = true;
    int readLength = 0;
    int dataLength = 0;
    boolean firstLine = true;
    ByteArrayOutputStream clientRequest = new ByteArrayOutputStream();
    ByteArrayOutputStream line = new ByteArrayOutputStream();
    int x;
    while ((inHeaders || readLength < dataLength) && ((x = in.read()) != -1)) {
      line.write(x);
      clientRequest.write(x);
      if (firstLine && !CharUtils.isAscii((char) x)){// includes \n
        throw new IllegalArgumentException("Only ASCII supported in headers (perhaps SSL was used?)");
      }
      if (inHeaders && (byte) x == (byte) '\n') { // $NON-NLS-1$
        if (line.size() < 3) {
          inHeaders = false;
          firstLine = false; // cannot be first line either
        }
        if (firstLine) {
          parseFirstLine(line.toString());
          firstLine = false;
        } else {
            // parse other header lines, looking for Content-Length
          final int contentLen = parseLine(line.toString());
          if (contentLen > 0) {
              dataLength = contentLen; // Save the last valid content length one
          }
        }
                if (log.isDebugEnabled()){
            log.debug("Client Request Line: " + line.toString());
                }
        line.reset();
      } else if (!inHeaders) {
        readLength++;
      }
    }
        // Keep the raw post data
        rawPostData = line.toByteArray();

        if (log.isDebugEnabled()){
            log.debug("rawPostData in default JRE encoding: " + new String(rawPostData));
        log.debug("Request: " + clientRequest.toString());
        }
    return clientRequest.toByteArray();
  }

  private void parseFirstLine(String firstLine) {
        if (log.isDebugEnabled()) {
        log.debug("browser request: " + firstLine);
        }
        if (!CharUtils.isAsciiAlphanumeric(firstLine.charAt(0))) {
          throw new IllegalArgumentException("Unrecognised header line (probably used HTTPS)");
        }
    StringTokenizer tz = new StringTokenizer(firstLine);
    method = getToken(tz).toUpperCase(java.util.Locale.ENGLISH);
    url = getToken(tz);
    if (url.toLowerCase(java.util.Locale.ENGLISH).startsWith(HTTPConstants.PROTOCOL_HTTPS)) {
      throw new IllegalArgumentException("Cannot handle https URLS: " + url);
    }
    version = getToken(tz);
        if (log.isDebugEnabled()) {
        log.debug("parser input:  " + firstLine);
        log.debug("parsed method: " + method);
        log.debug("parsed url:    " + url);
        log.debug("parsed version:" + version);
        }
        if ("CONNECT".equalsIgnoreCase(method)){
          throw new IllegalArgumentException("Cannot handle CONNECT - probably used HTTPS");         
        }
  }

    /*
     * Split line into name/value pairs and store in headers if relevant
     * If name = "content-length", then return value as int, else return 0
     */
  private int parseLine(String nextLine) {
    StringTokenizer tz;
    tz = new StringTokenizer(nextLine);
    String token = getToken(tz);
    // look for termination of HTTP command
    if (0 == token.length()) {
      return 0;
    }
    String trimmed = token.trim();
        String name = trimmed.substring(0, trimmed.length() - 1);// drop ':'
    String value = getRemainder(tz);
    headers.put(name.toLowerCase(java.util.Locale.ENGLISH), new Header(name, value));
    if (name.equalsIgnoreCase(CONTENT_LENGTH)) {
      return Integer.parseInt(value);
    }
    return 0;
  }

  private HeaderManager createHeaderManager() {
    HeaderManager manager = new HeaderManager();
    Iterator keys = headers.keySet().iterator();
    while (keys.hasNext()) {
      String key = (String) keys.next();
      if (!key.equals(PROXY_CONNECTION) && !key.equals(CONTENT_LENGTH)) {
        manager.add((Header) headers.get(key));
      }
    }
    manager.setName("Browser-derived headers");
    manager.setProperty(TestElement.TEST_CLASS, HeaderManager.class.getName());
    manager.setProperty(TestElement.GUI_CLASS, HeaderPanel.class.getName());
    return manager;
  }

  public HeaderManager getHeaderManager() {
    if(headerManager == null) {
      headerManager = createHeaderManager();
    }
    return headerManager;
  }

    public HTTPSamplerBase getSampler(Map pageEncodings, Map formEncodings)
            throws MalformedURLException, IOException {
    // Damn! A whole new GUI just to instantiate a test element?
    // Isn't there a beter way?
    HttpTestSampleGui tempGui = null;
    // Create the corresponding gui for the sampler class
    if(sampler instanceof HTTPSampler2) {
      tempGui = new HttpTestSampleGui2();
    }
    else {
      tempGui = new HttpTestSampleGui();
    }
    sampler.setProperty(TestElement.GUI_CLASS, tempGui.getClass().getName());

        // Populate the sampler
        populateSampler(pageEncodings, formEncodings);
   
    tempGui.configure(sampler);
    tempGui.modifyTestElement(sampler);
    // Defaults
    sampler.setFollowRedirects(false);
    sampler.setUseKeepAlive(true);
   
        if (log.isDebugEnabled()) {
        log.debug("getSampler: sampler path = " + sampler.getPath());
        }
    return sampler;
  }
   
    /**
     *
     * @return the sampler
     * @throws MalformedURLException
     * @throws IOException
     * @throws ProtocolException
     * @deprecated use the getSampler(HashMap pageEncodings, HashMap formEncodings) instead, since
     * that properly handles the encodings of the page
     */
    public HTTPSamplerBase getSampler() throws MalformedURLException, IOException, ProtocolException {
        return getSampler(null, null);
    }

  private String getContentType() {
    Header contentTypeHeader = (Header) headers.get(CONTENT_TYPE);
    if (contentTypeHeader != null) {
      return contentTypeHeader.getValue();
    }
        return null;
  }

    private boolean isMultipart(String contentType) {
        if (contentType != null && contentType.startsWith(HTTPConstants.MULTIPART_FORM_DATA)) {
            return true;
        }
        return false;
    }

    private MultipartUrlConfig getMultipartConfig(String contentType) {
        if(isMultipart(contentType)) {
            // Get the boundary string for the multiparts from the content type
            String boundaryString = contentType.substring(contentType.toLowerCase(java.util.Locale.ENGLISH).indexOf("boundary=") + "boundary=".length());
            return new MultipartUrlConfig(boundaryString);
        }
        return null;
    }

    private void populateSampler(Map pageEncodings, Map formEncodings)
            throws MalformedURLException, UnsupportedEncodingException {       
        sampler.setDomain(serverName());
        if (log.isDebugEnabled()) {
        log.debug("Proxy: setting server: " + sampler.getDomain());
        }
    sampler.setMethod(method);
    log.debug("Proxy: setting method: " + sampler.getMethod());
    sampler.setPort(serverPort());
        if (log.isDebugEnabled()) {
            log.debug("Proxy: setting port: " + sampler.getPort());
        }
    if (url.indexOf("//") > -1) {
      String protocol = url.substring(0, url.indexOf(":"));
            if (log.isDebugEnabled()) {
          log.debug("Proxy: setting protocol to : " + protocol);
            }
      sampler.setProtocol(protocol);
    } else if (sampler.getPort() == HTTPConstants.DEFAULT_HTTPS_PORT) {
      sampler.setProtocol(HTTPS);
            if (log.isDebugEnabled()) {
          log.debug("Proxy: setting protocol to https");
            }
    } else {
            if (log.isDebugEnabled()) {
          log.debug("Proxy setting default protocol to: http");
            }
      sampler.setProtocol(HTTP);
    }
       
        URL pageUrl = null;
        if(sampler.isProtocolDefaultPort()) {           
            pageUrl = new URL(sampler.getProtocol(), sampler.getDomain(), getPath());
        }
        else {
            pageUrl = new URL(sampler.getProtocol(), sampler.getDomain(), sampler.getPort(), getPath());
        }
        String urlWithoutQuery = getUrlWithoutQuery(pageUrl);
       

        // Check if the request itself tells us what the encoding is
        String contentEncoding = null;
        String requestContentEncoding = ConversionUtils.getEncodingFromContentType(getContentType());
        if(requestContentEncoding != null) {
            contentEncoding = requestContentEncoding;
        }
        else {       
            // Check if we know the encoding of the page
            if (pageEncodings != null) {
                synchronized (pageEncodings) {
                    contentEncoding = (String) pageEncodings.get(urlWithoutQuery);
                }
            }
            // Check if we know the encoding of the form
            if (formEncodings != null) {
                synchronized (formEncodings) {
                    String formEncoding = (String) formEncodings.get(urlWithoutQuery);
                    // Form encoding has priority over page encoding
                    if (formEncoding != null) {
                        contentEncoding = formEncoding;
                    }
                }
            }
        }

        // Get the post data using the content encoding of the request
        String postData = null;
        if (log.isDebugEnabled()) {
            if(contentEncoding != null) {
                log.debug("Using encoding " + contentEncoding + " for request body");
            }
            else {
                log.debug("No encoding found, using JRE default encoding for request body");
            }
        }
        if (contentEncoding != null) {
            postData = new String(rawPostData, contentEncoding);
        } else {
            // Use default encoding
            postData = new String(rawPostData);
        }

        if(contentEncoding != null) {
            sampler.setPath(getPath(), contentEncoding);
        }
        else {
            // Although the spec says UTF-8 should be used for encoding URL parameters,
            // most browser use ISO-8859-1 for default if encoding is not known.
            // We use null for contentEncoding, then the url parameters will be added
            // with the value in the URL, and the "encode?" flag set to false
            sampler.setPath(getPath(), null);
        }
        if (log.isDebugEnabled()) {
            log.debug("Proxy: setting path: " + sampler.getPath());
        }
        if (numberRequests) {
            requestNumber++;
            sampler.setName(requestNumber + " " + sampler.getPath());
        } else {
            sampler.setName(sampler.getPath());
        }
       
        // Set the content encoding
        if(contentEncoding != null) {
            sampler.setContentEncoding(contentEncoding);
        }
       
        // If it was a HTTP GET request, then all parameters in the URL
        // has been handled by the sampler.setPath above, so we just need
        // to do parse the rest of the request if it is not a GET request
        if(!HTTPConstants.GET.equals(method)) {
            // Check if it was a multipart http post request
            final String contentType = getContentType();
            MultipartUrlConfig urlConfig = getMultipartConfig(contentType);
            if (urlConfig != null) {
                urlConfig.parseArguments(postData);
                // Tell the sampler to do a multipart post
                sampler.setDoMultipartPost(true);
                // Remove the header for content-type and content-length, since
                // those values will most likely be incorrect when the sampler
                // performs the multipart request, because the boundary string
                // will change
                getHeaderManager().removeHeaderNamed(CONTENT_TYPE);
                getHeaderManager().removeHeaderNamed(CONTENT_LENGTH);
           
                // Set the form data
                sampler.setArguments(urlConfig.getArguments());
                // Set the file uploads
                sampler.setHTTPFiles(urlConfig.getHTTPFileArgs().asArray());
            } else if (postData.trim().startsWith("<?")) {
                // Not sure if this is needed anymore. I assume these requests
                // do not have HTTPConstants.APPLICATION_X_WWW_FORM_URLENCODED as content type,
                // and they would therefore be catched by the last else if of these if else if tests
                sampler.addNonEncodedArgument("", postData, ""); //used when postData is pure xml (ex. an xml-rpc call)
            } else if (contentType == null || contentType.startsWith(HTTPConstants.APPLICATION_X_WWW_FORM_URLENCODED) ){
                // It is the most common post request, with parameter name and values
                // We also assume this if no content type is present, to be most backwards compatible,
                // but maybe we should only parse arguments if the content type is as expected
                sampler.parseArguments(postData.trim(), contentEncoding); //standard name=value postData
            } else if (postData.length() > 0) {
                if (isBinaryContent(contentType)) {
                    try {
                        File tempDir = new File(binaryDirectory);
                        File out = File.createTempFile(method, binaryFileSuffix, tempDir);
                        FileUtils.writeByteArrayToFile(out,rawPostData);
                        HTTPFileArg [] files = {new HTTPFileArg(out.getPath(),"",contentType)};
                        sampler.setHTTPFiles(files);
                    } catch (IOException e) {
                        log.warn("Could not create binary file: "+e);
                    }
                } else {
                    // Just put the whole postbody as the value of a parameter
                    sampler.addNonEncodedArgument("", postData, ""); //used when postData is pure xml (ex. an xml-rpc call)
                }
            }
        }
        if (log.isDebugEnabled()) {
        log.debug("sampler path = " + sampler.getPath());
        }
  }

  private boolean isBinaryContent(String contentType) {
        if (contentType == null) return false;
        return binaryContentTypes.contains(contentType);
    }

    //
  // Parsing Methods
  //

  /**
   * Find the //server.name from an url.
   *
   * @return server's internet name
   */
  private String serverName() {
    // chop to "server.name:x/thing"
    String str = url;
    int i = str.indexOf("//"); // $NON-NLS-1$
    if (i > 0) {
      str = str.substring(i + 2);
    }
    // chop to server.name:xx
    i = str.indexOf("/"); // $NON-NLS-1$
    if (0 < i) {
      str = str.substring(0, i);
    }
    // chop to server.name
    i = str.indexOf(":"); // $NON-NLS-1$
    if (0 < i) {
      str = str.substring(0, i);
    }
    return str;
  }

  // TODO replace repeated substr() above and below with more efficient method.
 
  /**
   * Find the :PORT from http://server.ect:PORT/some/file.xxx
   *
   * @return server's port (or UNSPECIFIED if not found)
   */
  private int serverPort() {
    String str = url;
    // chop to "server.name:x/thing"
    int i = str.indexOf("//");
    if (i > 0) {
      str = str.substring(i + 2);
    }
    // chop to server.name:xx
    i = str.indexOf("/");
    if (0 < i) {
      str = str.substring(0, i);
    }
    // chop XX
    i = str.indexOf(":");
    if (0 < i) {
      return Integer.parseInt(str.substring(i + 1).trim());
    }
    return HTTPSamplerBase.UNSPECIFIED_PORT;
  }

  /**
   * Find the /some/file.xxxx from http://server.ect:PORT/some/file.xxx
   *
   * @return the path
   */
  private String getPath() {
    String str = url;
    int i = str.indexOf("//");
    if (i > 0) {
      str = str.substring(i + 2);
    }
    i = str.indexOf("/");
    if (i < 0) {
      return "";
    }
    return str.substring(i);
  }

  /**
   * Returns the url string extracted from the first line of the client request.
   *
   * @return the url
   */
  public String getUrl(){
    return url;
  }
 
  /**
   * Returns the next token in a string.
   *
   * @param tk
   *            String that is partially tokenized.
   * @return The remainder
   */
  private String getToken(StringTokenizer tk) {
    if (tk.hasMoreTokens()) {
      return tk.nextToken();
    }
    return "";// $NON-NLS-1$
  }

  /**
   * Returns the remainder of a tokenized string.
   *
   * @param tk
   *            String that is partially tokenized.
   * @return The remainder
   */
  private String getRemainder(StringTokenizer tk) {
    StringBuffer strBuff = new StringBuffer();
    if (tk.hasMoreTokens()) {
      strBuff.append(tk.nextToken());
    }
    while (tk.hasMoreTokens()) {
            strBuff.append(" "); // $NON-NLS-1$
            strBuff.append(tk.nextToken());
    }
    return strBuff.toString();
  }

    private String getUrlWithoutQuery(URL _url) {
        String fullUrl = _url.toString();
        String urlWithoutQuery = fullUrl;
        String query = _url.getQuery();
        if(query != null) {
            // Get rid of the query and the ?
            urlWithoutQuery = urlWithoutQuery.substring(0, urlWithoutQuery.length() - query.length() - 1);
        }
        return urlWithoutQuery;
    }
}
TOP

Related Classes of org.apache.jmeter.protocol.http.proxy.HttpRequestHdr

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.