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

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

/*
* 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.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.net.URL;
import java.util.Map;

import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.parser.HTMLParseException;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerFactory;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

/**
* Thread to handle one client request. Gets the request from the client and
* passes it on to the server, then sends the response back to the client.
* Information about the request and response is stored so it can be used in a
* JMeter test plan.
*
* @author mike
* @version $Revision: 552959 $ Last updated: $Date: 2007-07-03 20:39:51 +0100 (Tue, 03 Jul 2007) $ Created
*          June 8, 2001
*/
public class Proxy extends Thread {
    private static final Logger log = LoggingManager.getLoggerForClass();

    private static final String NEW_LINE = "\n"; // $NON-NLS-1$

  /** Socket to client. */
  private Socket clientSocket = null;

  /** Target to receive the generated sampler. */
  private ProxyControl target;

  /** Whether or not to capture the HTTP headers. */
  private boolean captureHttpHeaders;

  /** Whether to try to spoof as https **/
  private boolean httpsSpoof;

    /** Reference to Deamon's Map of url string to page character encoding of that page */
    private Map pageEncodings;
    /** Reference to Deamon's Map of url string to character encoding for the form */
    private Map formEncodings;

  /**
   * Default constructor - used by newInstance call in Daemon
   */
  public Proxy() {
  }

  /**
   * Create and configure a new Proxy object.
   *
   * @param clientSocket
   *            the socket connection to the client
   * @param target
   *            the ProxyControl which will receive the generated sampler
   */
  Proxy(Socket clientSocket, ProxyControl target) {
    configure(clientSocket, target);
  }

  /**
   * Configure the Proxy.
   *
   * @param clientSocket
   *            the socket connection to the client
   * @param target
   *            the ProxyControl which will receive the generated sampler
   */
  void configure(Socket _clientSocket, ProxyControl _target) {
        configure(_clientSocket, _target, null, null);
    }
   
    /**
     * Configure the Proxy.
     *
     * @param clientSocket
     *            the socket connection to the client
     * @param target
     *            the ProxyControl which will receive the generated sampler
     * @param pageEncodings
     *            reference to the Map of Deamon, with mappings from page urls to encoding used
     * @param formEncodingsEncodings
     *            reference to the Map of Deamon, with mappings from form action urls to encoding used
     */
    void configure(Socket clientSocket, ProxyControl target, Map pageEncodings, Map formEncodings) {
        this.target = target;
        this.clientSocket = clientSocket;
        this.captureHttpHeaders = target.getCaptureHttpHeaders();
        this.httpsSpoof = target.getHttpsSpoof();
        this.pageEncodings = pageEncodings;
        this.formEncodings = formEncodings;
    }

  /**
   * Main processing method for the Proxy object
   */
  public void run() {
    // Check which HTTPSampler class we should use
    String httpSamplerName = HTTPSamplerFactory.DEFAULT_CLASSNAME;
    if(target.getSamplerTypeName() == ProxyControl.SAMPLER_TYPE_HTTP_SAMPLER) {
      httpSamplerName = HTTPSamplerFactory.HTTP_SAMPLER_JAVA;
    }
    else if(target.getSamplerTypeName() == ProxyControl.SAMPLER_TYPE_HTTP_SAMPLER2) {
      httpSamplerName = HTTPSamplerFactory.HTTP_SAMPLER_APACHE;
    }
    // Instantiate the sampler
    HTTPSamplerBase sampler = HTTPSamplerFactory.newInstance(httpSamplerName);
   
    HttpRequestHdr request = new HttpRequestHdr(sampler);
    SampleResult result = null;
    HeaderManager headers = null;

    try {
      request.parse(new BufferedInputStream(clientSocket.getInputStream()));

      // Populate the sampler. It is the same sampler as we sent into
      // the constructor of the HttpRequestHdr instance above
            request.getSampler(pageEncodings, formEncodings);

      /*
       * Create a Header Manager to ensure that the browsers headers are
       * captured and sent to the server
       */
      headers = request.getHeaderManager();
      sampler.setHeaderManager(headers);
     
      /*
       * If we are trying to spoof https, change the protocol
       */
      if (httpsSpoof) {
        sampler.setProtocol("https");
      }
      sampler.threadStarted(); // Needed for HTTPSampler2
      result = sampler.sample();
     
      /*
       * If we're dealing with text data, and if we're spoofing https,
       * replace all occurences of "https" with "http" for the client.
       */
      if (httpsSpoof && SampleResult.TEXT.equals(result.getDataType()))
      {
        String noHttpsResult = new String(result.getResponseData());
        result.setResponseData(noHttpsResult.replaceAll("https", "http").getBytes());
      }

            // Find the page encoding and possibly encodings for forms in the page
            // in the response from the web server
            String pageEncoding = addPageEncoding(result);
            addFormEncodings(result, pageEncoding);

      writeToClient(result, new BufferedOutputStream(clientSocket.getOutputStream()));
      /*
       * We don't want to store any cookies in the generated test plan
       */
      headers.removeHeaderNamed("cookie");// Always remove cookies // $NON-NLS-1$
    } catch (UnknownHostException uhe) {
      log.warn("Server Not Found.", uhe);
      writeErrorToClient(HttpReplyHdr.formServerNotFound());
    } catch (Exception e) {
      log.error("", e);
      writeErrorToClient(HttpReplyHdr.formTimeout());
    } finally {
      if (log.isDebugEnabled()) {
        log.debug("Will deliver sample " + sampler.getName());
      }
      target.deliverSampler(sampler, new TestElement[] { captureHttpHeaders ? headers : null }, result);
      try {
        clientSocket.close();
      } catch (Exception e) {
        log.error("", e);
      }
      sampler.threadFinished(); // Needed for HTTPSampler2
    }
  }

  /**
   * Write output to the output stream, then flush and close the stream.
   *
   * @param inBytes
   *            the bytes to write
   * @param out
   *            the output stream to write to
   * @throws IOException
   *             if an IOException occurs while writing
   */
  private void writeToClient(SampleResult res, OutputStream out) throws IOException {
    try {
      String responseHeaders = massageResponseHeaders(res, res.getResponseHeaders());
            out.write(responseHeaders.getBytes());
      out.write('\n'); // $NON-NLS-1$
      out.write(res.getResponseData());
      out.flush();
      log.debug("Done writing to client");
    } catch (IOException e) {
      log.error("", e);
      throw e;
    } finally {
      try {
        out.close();
      } catch (Exception ex) {
        log.warn("Error while closing socket", ex);
      }
    }
  }

  /**
   * In the event the content was gzipped and unpacked, the content-encoding
   * header must be removed and the content-length header should be corrected.
   *
   * @param res
   * @param headers
   * @return
   */
  private String massageResponseHeaders(SampleResult res, String headers) {
    int encodingHeaderLoc = headers.indexOf(": gzip"); // $NON-NLS-1$
    String newHeaders = headers;
    if (encodingHeaderLoc > -1) {
      int end = headers.indexOf(NEW_LINE, encodingHeaderLoc);
      int begin = headers.lastIndexOf(NEW_LINE, encodingHeaderLoc);
      newHeaders = newHeaders.substring(0, begin) + newHeaders.substring(end);
      int lengthIndex = newHeaders.indexOf("ength: "); // $NON-NLS-1$
      end = newHeaders.indexOf(NEW_LINE, lengthIndex);
      newHeaders = newHeaders.substring(0, lengthIndex + 7) + res.getResponseData().length
          + newHeaders.substring(end);
    }
    return newHeaders;
  }

  /**
   * Write an error message to the client. The message should be the full HTTP
   * response.
   *
   * @param message
   *            the message to write
   */
  private void writeErrorToClient(String message) {
    try {
      OutputStream sockOut = clientSocket.getOutputStream();
      DataOutputStream out = new DataOutputStream(sockOut);
      out.writeBytes(message);
      out.flush();
    } catch (Exception e) {
      log.warn("Exception while writing error", e);
    }
  }

    /**
     * Add the page encoding of the sample result to the Map with page encodings
     *
     * @param result the sample result to check
     * @return the page encoding found for the sample result, or null
     */
    private String addPageEncoding(SampleResult result) {
        String pageEncoding = getContentEncoding(result);
        if(pageEncoding != null) {
            String urlWithoutQuery = getUrlWithoutQuery(result.getURL());
            synchronized(pageEncodings) {
                pageEncodings.put(urlWithoutQuery, pageEncoding);
            }
        }
        return pageEncoding;
    }
   
    /**
     * Add the form encodings for all forms in the sample result
     *
     * @param result the sample result to check
     * @param pageEncoding the encoding used for the sample result page
     */
    private void addFormEncodings(SampleResult result, String pageEncoding) {
        FormCharSetFinder finder = new FormCharSetFinder();
        try {
            finder.addFormActionsAndCharSet(result.getResponseDataAsString(), formEncodings, pageEncoding);
        }
        catch (HTMLParseException parseException) {
            log.debug("Unable to parse response, could not find any form character set encodings");
        }
    }

    /**
     * Get the value of the charset of the content-type header of the sample result
     *
     * @param res the sample result to find the charset for
     * @return the charset found, or null
     */
    private String getContentEncoding(SampleResult res) {
        String contentTypeHeader = res.getContentType();
        String charSet = null;
        if (contentTypeHeader != null) {
            int charSetStartPos = contentTypeHeader.toLowerCase().indexOf("charset=");
            if (charSetStartPos > 0) {
                charSet = contentTypeHeader.substring(charSetStartPos + "charset=".length());
                if (charSet != null) {
                    if (charSet.trim().length() > 0) {
                        charSet = charSet.trim();
                    } else {
                        charSet = null;
                    }
                }
            }
        }
        return charSet;
    }

    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.Proxy

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.