Package org.apache.jmeter.protocol.http.sampler

Source Code of org.apache.jmeter.protocol.http.sampler.HTTPSampler

/*
* 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.sampler;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.net.BindException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;

import org.apache.jmeter.protocol.http.control.AuthManager;
import org.apache.jmeter.protocol.http.control.Authorization;
import org.apache.jmeter.protocol.http.control.CookieManager;
import org.apache.jmeter.protocol.http.control.Header;
import org.apache.jmeter.protocol.http.control.HeaderManager;

import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.testelement.property.PropertyIterator;

import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.util.SSLManager;

import org.apache.jorphan.logging.LoggingManager;

import org.apache.log.Logger;

/**
* A sampler which understands all the parts necessary to read statistics about
* HTTP requests, including cookies and authentication.
*
*/
public class HTTPSampler extends HTTPSamplerBase {
    private static final Logger log = LoggingManager.getLoggerForClass();

  private static final int MAX_CONN_RETRIES =
    JMeterUtils.getPropDefault("http.java.sampler.retries" // $NON-NLS-1$
        ,10); // Maximum connection retries

  static {
    log.info("Maximum connection retries = "+MAX_CONN_RETRIES); // $NON-NLS-1$
  }
 
  private static final byte[] NULL_BA = new byte[0];// can share these

  /** Handles writing of a post request */
    private PostWriter postWriter;

  /**
   * Constructor for the HTTPSampler object.
     *
     * Consider using HTTPSamplerFactory.newInstance() instead
   */
  public HTTPSampler() {
  }

  /**
   * Set request headers in preparation to opening a connection.
   *
   * @param conn
   *            <code>URLConnection</code> to set headers on
   * @exception IOException
   *                if an I/O exception occurs
   */
  protected void setPostHeaders(URLConnection conn) throws IOException {
    postWriter = new PostWriter();
    postWriter.setHeaders(conn, this);
  }

    private void setPutHeaders(URLConnection conn)
     {
         String filename = getFilename();
         if ((filename != null) && (filename.trim().length() > 0))
         {
             conn.setRequestProperty(HEADER_CONTENT_TYPE, getMimetype());
             conn.setDoOutput(true);
             conn.setDoInput(true);
        }
    }

  /**
   * Send POST data from <code>Entry</code> to the open connection.
   *
   * @param connection
   *            <code>URLConnection</code> where POST data should be sent
     * @return a String show what was posted. Will not contain actual file upload content
   * @exception IOException
   *                if an I/O exception occurs
   */
  protected String sendPostData(URLConnection connection) throws IOException {
    return postWriter.sendPostData(connection, this);
  }

    private void sendPutData(URLConnection conn) throws IOException {
        String filename = getFilename();
        if ((filename != null) && (filename.trim().length() > 0)) {
            OutputStream out = conn.getOutputStream();
            byte[] buf = new byte[1024];
            int read;
            InputStream in = new BufferedInputStream(new FileInputStream(filename));
            while ((read = in.read(buf)) > 0) {
                out.write(buf, 0, read);
            }
            in.close();
            out.flush();
            out.close();
        }
    }


  /**
   * Returns an <code>HttpURLConnection</code> fully ready to attempt
   * connection. This means it sets the request method (GET or POST), headers,
   * cookies, and authorization for the URL request.
   * <p>
   * The request infos are saved into the sample result if one is provided.
   *
   * @param u
   *            <code>URL</code> of the URL request
   * @param method
   *            GET, POST etc
   * @param res
   *            sample result to save request infos to
   * @return <code>HttpURLConnection</code> ready for .connect
   * @exception IOException
   *                if an I/O Exception occurs
   */
  protected HttpURLConnection setupConnection(URL u, String method, HTTPSampleResult res) throws IOException {
        SSLManager sslmgr = null;
        if (PROTOCOL_HTTPS.equalsIgnoreCase(u.getProtocol())) {
            try {
                sslmgr=SSLManager.getInstance(); // N.B. this needs to be done before opening the connection
            } catch (Exception e) {
                log.warn("Problem creating the SSLManager: ", e);
            }
        }
   
        HttpURLConnection conn = (HttpURLConnection) u.openConnection();
        // Update follow redirects setting just for this connection
        conn.setInstanceFollowRedirects(getAutoRedirects());

        if (PROTOCOL_HTTPS.equalsIgnoreCase(u.getProtocol())) {
      try {
        if (null != sslmgr){
            sslmgr.setContext(conn); // N.B. must be done after opening connection
        }
      } catch (Exception e) {
        log.warn("Problem setting the SSLManager for the connection: ", e);
      }
    }

    // a well-bahaved browser is supposed to send 'Connection: close'
    // with the last request to an HTTP server. Instead, most browsers
    // leave it to the server to close the connection after their
    // timeout period. Leave it to the JMeter user to decide.
    if (getUseKeepAlive()) {
      conn.setRequestProperty(HEADER_CONNECTION, KEEP_ALIVE);
    } else {
      conn.setRequestProperty(HEADER_CONNECTION, CONNECTION_CLOSE);
    }

    conn.setRequestMethod(method);
    setConnectionHeaders(conn, u, getHeaderManager());
    String cookies = setConnectionCookie(conn, u, getCookieManager());

        setConnectionAuthorization(conn, u, getAuthManager());

    if (method.equals(POST)) {
      setPostHeaders(conn);
    } else if (method.equals(PUT)) {
            setPutHeaders(conn);
        }
       
        if (res != null) {
            res.setURL(u);
            res.setHTTPMethod(method);
            res.setRequestHeaders(getConnectionHeaders(conn));
            res.setCookies(cookies);
        }
       
    return conn;
  }

  /**
   * Reads the response from the URL connection.
   *
   * @param conn
   *            URL from which to read response
   * @return response content
   * @exception IOException
   *                if an I/O exception occurs
   */
  protected byte[] readResponse(HttpURLConnection conn, SampleResult res) throws IOException {
    byte[] readBuffer = getThreadContext().getReadBuffer();
    BufferedInputStream in;

        if ((conn.getContentLength() == 0)
          && JMeterUtils.getPropDefault("httpsampler.obey_contentlength", // $NON-NLS-1$
          false)) {
            log.info("Content-Length: 0, not reading http-body");
      res.setResponseHeaders(getResponseHeaders(conn));
      return NULL_BA;
    }

    try {
            // works OK even if ContentEncoding is null
      if (ENCODING_GZIP.equals(conn.getContentEncoding())) {
        in = new BufferedInputStream(new GZIPInputStream(conn.getInputStream()));
      } else {
        in = new BufferedInputStream(conn.getInputStream());
      }
    } catch (IOException e) {
      if (! (e.getCause() instanceof FileNotFoundException))
      {
        log.error("readResponse: "+e.toString());
        Throwable cause = e.getCause();
        if (cause != null){
            log.error("Cause: "+cause);
        }
      }
      // Normal InputStream is not available
      InputStream errorStream = conn.getErrorStream();
      if (errorStream == null) {
        log.info("Error Response Code: "+conn.getResponseCode()+", Server sent no Errorpage");
        res.setResponseHeaders(getResponseHeaders(conn));
        return NULL_BA;
      }
      else {
        log.info("Error Response Code: "+conn.getResponseCode());
      }
      in = new BufferedInputStream(errorStream);
    } catch (Exception e) {
      log.error("readResponse: "+e.toString());
      Throwable cause = e.getCause();
      if (cause != null){
          log.error("Cause: "+cause);
      }
      in = new BufferedInputStream(conn.getErrorStream());
    }
    java.io.ByteArrayOutputStream w = new ByteArrayOutputStream();
    int x = 0;
    boolean first = true;
    while ((x = in.read(readBuffer)) > -1) {
      if (first) {
        res.latencyEnd();
        first = false;
      }
      w.write(readBuffer, 0, x);
    }
    in.close();
    w.flush();
    w.close();
    return w.toByteArray();
  }

  /**
   * Gets the ResponseHeaders from the URLConnection
   *
   * @param conn
   *            connection from which the headers are read
   * @return string containing the headers, one per line
   */
  protected String getResponseHeaders(HttpURLConnection conn) {
    StringBuffer headerBuf = new StringBuffer();
    headerBuf.append(conn.getHeaderField(0));// Leave header as is
    // headerBuf.append(conn.getHeaderField(0).substring(0, 8));
    // headerBuf.append(" ");
    // headerBuf.append(conn.getResponseCode());
    // headerBuf.append(" ");
    // headerBuf.append(conn.getResponseMessage());
    headerBuf.append("\n"); //$NON-NLS-1$

        String hfk;
    for (int i = 1; (hfk=conn.getHeaderFieldKey(i)) != null; i++) {
            // TODO - why is this not saved? A: it might be a proxy server specific field.
            // If JMeter is using a proxy, the browser wouldn't know about that.
            if (!TRANSFER_ENCODING.equalsIgnoreCase(hfk)) {
                headerBuf.append(hfk);
                headerBuf.append(": "); // $NON-NLS-1$
                headerBuf.append(conn.getHeaderField(i));
                headerBuf.append("\n"); // $NON-NLS-1$
            }
    }
    return headerBuf.toString();
  }

  /**
   * Extracts all the required cookies for that particular URL request and
   * sets them in the <code>HttpURLConnection</code> passed in.
   *
   * @param conn
   *            <code>HttpUrlConnection</code> which represents the URL
   *            request
   * @param u
   *            <code>URL</code> of the URL request
   * @param cookieManager
   *            the <code>CookieManager</code> containing all the cookies
   *            for this <code>UrlConfig</code>
   */
  private String setConnectionCookie(HttpURLConnection conn, URL u, CookieManager cookieManager) {
    String cookieHeader = null;
    if (cookieManager != null) {
      cookieHeader = cookieManager.getCookieHeaderForURL(u);
      if (cookieHeader != null) {
        conn.setRequestProperty(HEADER_COOKIE, cookieHeader);
      }
    }
    return cookieHeader;
  }

  /**
   * Extracts all the required headers for that particular URL request and
   * sets them in the <code>HttpURLConnection</code> passed in
   *
   * @param conn
   *            <code>HttpUrlConnection</code> which represents the URL
   *            request
   * @param u
   *            <code>URL</code> of the URL request
   * @param headerManager
   *            the <code>HeaderManager</code> containing all the cookies
   *            for this <code>UrlConfig</code>
   */
  private void setConnectionHeaders(HttpURLConnection conn, URL u, HeaderManager headerManager) {
        // Add all the headers from the HeaderManager
    if (headerManager != null) {
      CollectionProperty headers = headerManager.getHeaders();
      if (headers != null) {
        PropertyIterator i = headers.iterator();
        while (i.hasNext()) {
          Header header = (Header) i.next().getObjectValue();
          String n = header.getName();
          String v = header.getValue();
          conn.addRequestProperty(n, v);
        }
      }
    }
  }
   
    /**
     * Get all the headers for the <code>HttpURLConnection</code> passed in
     *
     * @param conn
     *            <code>HttpUrlConnection</code> which represents the URL
     *            request
     * @return the headers as a string
     */
    private String getConnectionHeaders(HttpURLConnection conn) {
        // Get all the request properties, which are the headers set on the connection
        StringBuffer hdrs = new StringBuffer(100);
        Map requestHeaders = conn.getRequestProperties();
        Set headerFields = requestHeaders.entrySet();
        for(Iterator i = headerFields.iterator(); i.hasNext();) {
          Map.Entry entry = (Map.Entry)i.next();
          String headerKey=(String) entry.getKey();
            // Exclude the COOKIE header, since cookie is reported separately in the sample
            if(!HEADER_COOKIE.equalsIgnoreCase(headerKey)) {           
              List values = (List) entry.getValue();// value is a List of Strings
              for (int j=0;j<values.size();j++){               
                    hdrs.append(headerKey);
                    hdrs.append(": "); // $NON-NLS-1$               
                    hdrs.append((String) values.get(j));
                    hdrs.append("\n"); // $NON-NLS-1$
              }
            }
        }
        return hdrs.toString();
    }

  /**
   * Extracts all the required authorization for that particular URL request
   * and sets it in the <code>HttpURLConnection</code> passed in.
   *
   * @param conn
   *            <code>HttpUrlConnection</code> which represents the URL
   *            request
   * @param u
   *            <code>URL</code> of the URL request
   * @param authManager
   *            the <code>AuthManager</code> containing all the cookies for
   *            this <code>UrlConfig</code>
   */
  private void setConnectionAuthorization(HttpURLConnection conn, URL u, AuthManager authManager) {
    if (authManager != null) {
      Authorization auth = authManager.getAuthForURL(u);
      if (auth != null) {
        conn.setRequestProperty(HEADER_AUTHORIZATION, auth.toBasicHeader());
      }
    }
  }

  /**
   * Samples the URL passed in and stores the result in
   * <code>HTTPSampleResult</code>, following redirects and downloading
   * page resources as appropriate.
   * <p>
   * When getting a redirect target, redirects are not followed and resources
   * are not downloaded. The caller will take care of this.
   *
   * @param url
   *            URL to sample
   * @param method
   *            HTTP method: GET, POST,...
   * @param areFollowingRedirect
   *            whether we're getting a redirect target
   * @param frameDepth
   *            Depth of this target in the frame structure. Used only to
   *            prevent infinite recursion.
   * @return results of the sampling
   */
  protected HTTPSampleResult sample(URL url, String method, boolean areFollowingRedirect, int frameDepth) {
    HttpURLConnection conn = null;

    String urlStr = url.toString();
    log.debug("Start : sample" + urlStr);

    HTTPSampleResult res = new HTTPSampleResult();
    res.setMonitor(isMonitor());
       
    res.setSampleLabel(urlStr);
    res.sampleStart(); // Count the retries as well in the time
    try {
      // Sampling proper - establish the connection and read the response:
      // Repeatedly try to connect:
      int retry;
      for (retry = 1; retry <= MAX_CONN_RETRIES; retry++) {
        try {
          conn = setupConnection(url, method, res);
          // Attempt the connection:
          conn.connect();
          break;
        } catch (BindException e) {
          if (retry >= MAX_CONN_RETRIES) {
            log.error("Can't connect", e);
            throw e;
          }
          log.debug("Bind exception, try again");
          conn.disconnect();
          this.setUseKeepAlive(false);
          continue; // try again
        } catch (IOException e) {
          log.debug("Connection failed, giving up");
          throw e;
        }
      }
      if (retry > MAX_CONN_RETRIES) {
        // This should never happen, but...
        throw new BindException();
      }
      // Nice, we've got a connection. Finish sending the request:
      if (method.equals(POST)) {
        String postBody = sendPostData(conn);
        res.setQueryString(postBody);
      } else if (method.equals(PUT)) {
                sendPutData(conn);
            }
      // Request sent. Now get the response:
      byte[] responseData = readResponse(conn, res);

      res.sampleEnd();
      // Done with the sampling proper.

      // Now collect the results into the HTTPSampleResult:

      res.setResponseData(responseData);

      int errorLevel = conn.getResponseCode();
            String respMsg = conn.getResponseMessage();
            if (errorLevel == -1){// Bug 38902 - sometimes -1 seems to be returned unnecessarily
            String hdr=conn.getHeaderField(0);
            if (hdr == null) hdr="(null)"// $NON-NLS-1$
              if (respMsg != null) {// Bug 41902 - NPE
                  try {
                      errorLevel = Integer.parseInt(respMsg.substring(0, 3));
                      log.warn("ResponseCode==-1; parsed "+respMsg+ " as "+errorLevel);
                    } catch (NumberFormatException e) {
                      log.warn("ResponseCode==-1; could not parse "+respMsg+" hdr: "+hdr);
                    }
              } else {
                respMsg=hdr; // for result
                    log.warn("ResponseCode==-1 & null ResponseMessage. Header(0)= "+hdr);
              }
            }
            if (errorLevel == -1) {
              res.setResponseCode("(null)"); // $NON-NLS-1$
            } else {
          res.setResponseCode(Integer.toString(errorLevel));
            }
      res.setSuccessful(isSuccessCode(errorLevel));

      res.setResponseMessage(respMsg);

      String ct = conn.getContentType();
      res.setContentType(ct);// e.g. text/html; charset=ISO-8859-1
            res.setEncodingAndType(ct);

      res.setResponseHeaders(getResponseHeaders(conn));
      if (res.isRedirect()) {
        res.setRedirectLocation(conn.getHeaderField(HEADER_LOCATION));
      }

            // If we redirected automatically, the URL may have changed
            if (getAutoRedirects()){
                res.setURL(conn.getURL());
            }
           
      // Store any cookies received in the cookie manager:
      saveConnectionCookies(conn, url, getCookieManager());

      res = resultProcessing(areFollowingRedirect, frameDepth, res);

      log.debug("End : sample");
      return res;
    } catch (IOException e) {
      res.sampleEnd();
      // We don't want to continue using this connection, even if KeepAlive is set
            if (conn != null) { // May not exist
              conn.disconnect();
            }
            conn=null; // Don't process again
      return errorResult(e, res);
    } finally {
      // calling disconnect doesn't close the connection immediately,
      // but indicates we're through with it. The JVM should close
      // it when necessary.
      disconnect(conn); // Disconnect unless using KeepAlive
    }
  }

  protected void disconnect(HttpURLConnection conn) {
    if (conn != null) {
      String connection = conn.getHeaderField(HEADER_CONNECTION);
      String protocol = conn.getHeaderField(0);
      if ((connection == null && (protocol == null || !protocol.startsWith(HTTP_1_1)))
          || (connection != null && connection.equalsIgnoreCase(CONNECTION_CLOSE))) {
        conn.disconnect();
      }
    }
  }

  /**
   * From the <code>HttpURLConnection</code>, store all the "set-cookie"
   * key-pair values in the cookieManager of the <code>UrlConfig</code>.
   *
   * @param conn
   *            <code>HttpUrlConnection</code> which represents the URL
   *            request
   * @param u
   *            <code>URL</code> of the URL request
   * @param cookieManager
   *            the <code>CookieManager</code> containing all the cookies
   *            for this <code>UrlConfig</code>
   */
  private void saveConnectionCookies(HttpURLConnection conn, URL u, CookieManager cookieManager) {
    if (cookieManager != null) {
      for (int i = 1; conn.getHeaderFieldKey(i) != null; i++) {
        if (conn.getHeaderFieldKey(i).equalsIgnoreCase(HEADER_SET_COOKIE)) {
          cookieManager.addCookieFromHeader(conn.getHeaderField(i), u);
        }
      }
    }
  }
}
TOP

Related Classes of org.apache.jmeter.protocol.http.sampler.HTTPSampler

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.