it.staiger.jmeter.protocol.http.sampler.HTTPHC4DynamicFilePost.java Source code

Java tutorial

Introduction

Here is the source code for it.staiger.jmeter.protocol.http.sampler.HTTPHC4DynamicFilePost.java

Source

/*
 * @@@LICENSE
 *
 */

package it.staiger.jmeter.protocol.http.sampler;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.security.auth.Subject;

import it.staiger.jmeter.protocol.http.sampler.DynamicHttpPostSampler;
import it.staiger.jmeter.protocol.http.util.VariableFileArg;
import it.staiger.jmeter.services.FileContentServer;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpConnection;
import org.apache.http.HttpConnectionMetrics;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.NTCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.client.protocol.ResponseContentEncoding;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.entity.mime.FormBodyPart;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.conn.SchemeRegistryFactory;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.DefaultedHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.SyncBasicHttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl;
import org.apache.jmeter.protocol.http.sampler.HTTPSampleResult;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
import org.apache.jmeter.protocol.http.sampler.MeasuringConnectionManager;
import org.apache.jmeter.protocol.http.sampler.HttpClientDefaultParameters;
import org.apache.jmeter.protocol.http.control.AuthManager;
import org.apache.jmeter.protocol.http.control.CacheManager;
import org.apache.jmeter.protocol.http.control.CookieManager;
import org.apache.jmeter.protocol.http.util.HC4TrustAllSSLSocketFactory;
import org.apache.jmeter.protocol.http.util.HTTPArgument;
import org.apache.jmeter.protocol.http.util.HTTPConstants;
import org.apache.jmeter.protocol.http.util.HTTPFileArg;
import org.apache.jmeter.protocol.http.util.SlowHC4SSLSocketFactory;
import org.apache.jmeter.protocol.http.util.SlowHC4SocketFactory;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.property.PropertyIterator;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.util.JsseSSLManager;
import org.apache.jmeter.util.SSLManager;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

/**
 * HTTP Sampler using Apache HttpClient 4.x.
 *
 */
public class HTTPHC4DynamicFilePost extends HTTPHC4Impl {

    private static final Logger log = LoggingManager.getLoggerForClass();

    /** retry count to be used (default 0); 0 = disable retries */
    private static final int RETRY_COUNT = JMeterUtils.getPropDefault("httpclient4.retrycount", 0);

    /** Idle timeout to be applied to connections if no Keep-Alive header is sent by the server (default 0 = disable) */
    private static final int IDLE_TIMEOUT = JMeterUtils.getPropDefault("httpclient4.idletimeout", 0);

    private static final String CONTEXT_METRICS = "jmeter_metrics"; // TODO hack for metrics related to HTTPCLIENT-1081, to be removed later

    protected final DynamicHttpPostSampler testElement;

    private static final ConnectionKeepAliveStrategy IDLE_STRATEGY = new DefaultConnectionKeepAliveStrategy() {
        @Override
        public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
            long duration = super.getKeepAliveDuration(response, context);
            if (duration <= 0 && IDLE_TIMEOUT > 0) {// none found by the superclass
                log.debug("Setting keepalive to " + IDLE_TIMEOUT);
                return IDLE_TIMEOUT;
            }
            return duration; // return the super-class value
        }

    };

    /**
     * Special interceptor made to keep metrics when connection is released for some method like HEAD
     * Otherwise calling directly ((HttpConnection) localContext.getAttribute(ExecutionContext.HTTP_CONNECTION)).getMetrics();
     * would throw org.apache.http.impl.conn.ConnectionShutdownException
     * See https://bz.apache.org/jira/browse/HTTPCLIENT-1081
     */
    private static final HttpResponseInterceptor METRICS_SAVER = new HttpResponseInterceptor() {
        @Override
        public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
            HttpConnection conn = (HttpConnection) context.getAttribute(ExecutionContext.HTTP_CONNECTION);
            HttpConnectionMetrics metrics = conn.getMetrics();
            context.setAttribute(CONTEXT_METRICS, metrics);
        }
    };
    private static final HttpRequestInterceptor METRICS_RESETTER = new HttpRequestInterceptor() {
        @Override
        public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
            HttpConnection conn = (HttpConnection) context.getAttribute(ExecutionContext.HTTP_CONNECTION);
            HttpConnectionMetrics metrics = conn.getMetrics();
            metrics.reset();
        }
    };

    /**
     * 1 HttpClient instance per combination of (HttpClient,HttpClientKey)
     */
    private static final ThreadLocal<Map<HttpClientKey, HttpClient>> HTTPCLIENTS_CACHE_PER_THREAD_AND_HTTPCLIENTKEY = new ThreadLocal<Map<HttpClientKey, HttpClient>>() {
        @Override
        protected Map<HttpClientKey, HttpClient> initialValue() {
            return new HashMap<HttpClientKey, HttpClient>();
        }
    };

    // Scheme used for slow HTTP sockets. Cannot be set as a default, because must be set on an HttpClient instance.
    private static final Scheme SLOW_HTTP;

    // We always want to override the HTTPS scheme, because we want to trust all certificates and hosts
    private static final Scheme HTTPS_SCHEME;

    /*
     * Create a set of default parameters from the ones initially created.
     * This allows the defaults to be overridden if necessary from the properties file.
     */
    private static final HttpParams DEFAULT_HTTP_PARAMS;

    static {
        log.info("HTTP request retry count = " + RETRY_COUNT);

        DEFAULT_HTTP_PARAMS = new SyncBasicHttpParams(); // Could we drop the Sync here?
        DEFAULT_HTTP_PARAMS.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false);
        DefaultHttpClient.setDefaultHttpParams(DEFAULT_HTTP_PARAMS);

        // Process Apache HttpClient parameters file
        String file = JMeterUtils.getProperty("hc.parameters.file"); // $NON-NLS-1$
        if (file != null) {
            HttpClientDefaultParameters.load(file, DEFAULT_HTTP_PARAMS);
        }

        // Set up HTTP scheme override if necessary
        if (CPS_HTTP > 0) {
            log.info("Setting up HTTP SlowProtocol, cps=" + CPS_HTTP);
            SLOW_HTTP = new Scheme(HTTPConstants.PROTOCOL_HTTP, HTTPConstants.DEFAULT_HTTP_PORT,
                    new SlowHC4SocketFactory(CPS_HTTP));
        } else {
            SLOW_HTTP = null;
        }

        // We always want to override the HTTPS scheme
        Scheme https = null;
        if (CPS_HTTPS > 0) {
            log.info("Setting up HTTPS SlowProtocol, cps=" + CPS_HTTPS);
            try {
                https = new Scheme(HTTPConstants.PROTOCOL_HTTPS, HTTPConstants.DEFAULT_HTTPS_PORT,
                        new SlowHC4SSLSocketFactory(CPS_HTTPS));
            } catch (GeneralSecurityException e) {
                log.warn("Failed to initialise SLOW_HTTPS scheme, cps=" + CPS_HTTPS, e);
            }
        } else {
            log.info("Setting up HTTPS TrustAll scheme");
            try {
                https = new Scheme(HTTPConstants.PROTOCOL_HTTPS, HTTPConstants.DEFAULT_HTTPS_PORT,
                        new HC4TrustAllSSLSocketFactory());
            } catch (GeneralSecurityException e) {
                log.warn("Failed to initialise HTTPS TrustAll scheme", e);
            }
        }
        HTTPS_SCHEME = https;
        if (localAddress != null) {
            DEFAULT_HTTP_PARAMS.setParameter(ConnRoutePNames.LOCAL_ADDRESS, localAddress);
        }

    }

    private volatile HttpUriRequest currentRequest; // Accessed from multiple threads

    private volatile boolean resetSSLContext;

    /*
        protected HTTPHC4wPropAsFileImpl(HTTPSamplerBase testElement) {
    super(testElement);
        }
    */
    protected HTTPHC4DynamicFilePost(DynamicHttpPostSampler testElement) {
        super((HTTPSamplerBase) testElement);
        this.testElement = testElement;
    }

    @Override
    protected HTTPSampleResult sample(URL url, String method, boolean areFollowingRedirect, int frameDepth) {

        if (log.isDebugEnabled()) {
            log.debug("Start : sample " + url.toString());
            log.debug("method " + method + " followingRedirect " + areFollowingRedirect + " depth " + frameDepth);
        }

        HTTPSampleResult res = createSampleResult(url, method);

        HttpClient httpClient = setupClient(url, res);

        HttpRequestBase httpRequest = null;
        try {
            URI uri = url.toURI();
            if (method.equals(HTTPConstants.POST)) {
                httpRequest = new HttpPost(uri);
            } else {
                throw new IllegalArgumentException("Unexpected method: '" + method + "'");
            }
            setupRequest(url, httpRequest, res); // can throw IOException
        } catch (Exception e) {
            res.sampleStart();
            res.sampleEnd();
            errorResult(e, res);
            return res;
        }

        HttpContext localContext = new BasicHttpContext();

        res.sampleStart();

        final CacheManager cacheManager = getCacheManager();
        if (cacheManager != null && HTTPConstants.GET.equalsIgnoreCase(method)) {
            if (cacheManager.inCache(url)) {
                return updateSampleResultForResourceInCache(res);
            }
        }

        try {
            currentRequest = httpRequest;

            // attach POST data
            String postBody = sendPostData((HttpPost) httpRequest);
            res.setQueryString(postBody);

            // perform the sample
            HttpResponse httpResponse = executeRequest(httpClient, httpRequest, localContext, url);

            // parse response and fill the SampleResult with all info
            parseResponse(httpRequest, localContext, res, httpResponse);

            // Store any cookies received in the cookie manager:
            saveConnectionCookies(httpResponse, res.getURL(), getCookieManager());

            // Save cache information
            if (cacheManager != null) {
                cacheManager.saveDetails(httpResponse, res);
            }

            // Follow redirects and download page resources if appropriate:
            res = resultProcessing(areFollowingRedirect, frameDepth, res);

        } catch (IOException e) {
            log.debug("IOException", e);
            if (res.getEndTime() == 0) {
                res.sampleEnd();
            }
            // pick up headers if failed to execute the request
            if (res.getRequestHeaders() != null) {
                log.debug("Overwriting request old headers: " + res.getRequestHeaders());
            }
            res.setRequestHeaders(
                    getConnectionHeaders((HttpRequest) localContext.getAttribute(ExecutionContext.HTTP_REQUEST)));
            errorResult(e, res);
            return res;
        } catch (RuntimeException e) {
            log.debug("RuntimeException", e);
            if (res.getEndTime() == 0) {
                res.sampleEnd();
            }
            errorResult(e, res);
            return res;
        } finally {
            currentRequest = null;
        }
        return res;
    }

    /**
     * Execute request either as is or under PrivilegedAction 
     * if a Subject is available for url
     * @param httpClient
     * @param httpRequest
     * @param localContext
     * @param url
     * @return
     * @throws IOException
     * @throws ClientProtocolException
     */
    private HttpResponse executeRequest(final HttpClient httpClient, final HttpRequestBase httpRequest,
            final HttpContext localContext, final URL url) throws IOException, ClientProtocolException {
        AuthManager authManager = getAuthManager();
        if (authManager != null) {
            Subject subject = authManager.getSubjectForUrl(url);
            if (subject != null) {
                try {
                    return Subject.doAs(subject, new PrivilegedExceptionAction<HttpResponse>() {

                        @Override
                        public HttpResponse run() throws Exception {
                            return httpClient.execute(httpRequest, localContext);
                        }
                    });
                } catch (PrivilegedActionException e) {
                    log.error("Can't execute httpRequest with subject:" + subject, e);
                    throw new RuntimeException("Can't execute httpRequest with subject:" + subject, e);
                }
            }
        }
        return httpClient.execute(httpRequest, localContext);
    }

    /**
     * Holder class for all fields that define an HttpClient instance;
     * used as the key to the ThreadLocal map of HttpClient instances.
     */
    private static final class HttpClientKey {

        private final String target; // protocol://[user:pass@]host:[port]
        private final boolean hasProxy;
        private final String proxyHost;
        private final int proxyPort;
        private final String proxyUser;
        private final String proxyPass;

        private final int hashCode; // Always create hash because we will always need it

        /**
         * @param url URL Only protocol and url authority are used (protocol://[user:pass@]host:[port])
         * @param hasProxy has proxy
         * @param proxyHost proxy host
         * @param proxyPort proxy port
         * @param proxyUser proxy user
         * @param proxyPass proxy password
         */
        public HttpClientKey(URL url, boolean hasProxy, String proxyHost, int proxyPort, String proxyUser,
                String proxyPass) {
            // N.B. need to separate protocol from authority otherwise http://server would match https://erver (<= sic, not typo error)
            // could use separate fields, but simpler to combine them
            this.target = url.getProtocol() + "://" + url.getAuthority();
            this.hasProxy = hasProxy;
            this.proxyHost = proxyHost;
            this.proxyPort = proxyPort;
            this.proxyUser = proxyUser;
            this.proxyPass = proxyPass;
            this.hashCode = getHash();
        }

        private int getHash() {
            int hash = 17;
            hash = hash * 31 + (hasProxy ? 1 : 0);
            if (hasProxy) {
                hash = hash * 31 + getHash(proxyHost);
                hash = hash * 31 + proxyPort;
                hash = hash * 31 + getHash(proxyUser);
                hash = hash * 31 + getHash(proxyPass);
            }
            hash = hash * 31 + target.hashCode();
            return hash;
        }

        // Allow for null strings
        private int getHash(String s) {
            return s == null ? 0 : s.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (!(obj instanceof HttpClientKey)) {
                return false;
            }
            HttpClientKey other = (HttpClientKey) obj;
            if (this.hasProxy) { // otherwise proxy String fields may be null
                return this.hasProxy == other.hasProxy && this.proxyPort == other.proxyPort
                        && this.proxyHost.equals(other.proxyHost) && this.proxyUser.equals(other.proxyUser)
                        && this.proxyPass.equals(other.proxyPass) && this.target.equals(other.target);
            }
            // No proxy, so don't check proxy fields
            return this.hasProxy == other.hasProxy && this.target.equals(other.target);
        }

        @Override
        public int hashCode() {
            return hashCode;
        }

        // For debugging
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(target);
            if (hasProxy) {
                sb.append(" via ");
                sb.append(proxyUser);
                sb.append("@");
                sb.append(proxyHost);
                sb.append(":");
                sb.append(proxyPort);
            }
            return sb.toString();
        }
    }

    private HttpClient setupClient(URL url, SampleResult res) {

        Map<HttpClientKey, HttpClient> mapHttpClientPerHttpClientKey = HTTPCLIENTS_CACHE_PER_THREAD_AND_HTTPCLIENTKEY
                .get();

        final String host = url.getHost();
        final String proxyHost = getProxyHost();
        final int proxyPort = getProxyPortInt();

        boolean useStaticProxy = isStaticProxy(host);
        boolean useDynamicProxy = isDynamicProxy(proxyHost, proxyPort);

        // Lookup key - must agree with all the values used to create the HttpClient.
        HttpClientKey key = new HttpClientKey(url, (useStaticProxy || useDynamicProxy),
                useDynamicProxy ? proxyHost : PROXY_HOST, useDynamicProxy ? proxyPort : PROXY_PORT,
                useDynamicProxy ? getProxyUser() : PROXY_USER, useDynamicProxy ? getProxyPass() : PROXY_PASS);

        HttpClient httpClient = mapHttpClientPerHttpClientKey.get(key);

        if (httpClient != null && resetSSLContext
                && HTTPConstants.PROTOCOL_HTTPS.equalsIgnoreCase(url.getProtocol())) {
            ((AbstractHttpClient) httpClient).clearRequestInterceptors();
            ((AbstractHttpClient) httpClient).clearResponseInterceptors();
            httpClient.getConnectionManager().closeIdleConnections(1L, TimeUnit.MICROSECONDS);
            httpClient = null;
            JsseSSLManager sslMgr = (JsseSSLManager) SSLManager.getInstance();
            sslMgr.resetContext();
            resetSSLContext = false;
        }

        if (httpClient == null) { // One-time init for this client

            HttpParams clientParams = new DefaultedHttpParams(new BasicHttpParams(), DEFAULT_HTTP_PARAMS);

            DnsResolver resolver = this.testElement.getDNSResolver();
            if (resolver == null) {
                resolver = new SystemDefaultDnsResolver();
            }
            ClientConnectionManager connManager = new MeasuringConnectionManager(
                    SchemeRegistryFactory.createDefault(), resolver);

            httpClient = new DefaultHttpClient(connManager, clientParams) {
                @Override
                protected HttpRequestRetryHandler createHttpRequestRetryHandler() {
                    return new DefaultHttpRequestRetryHandler(RETRY_COUNT, false); // set retry count
                }
            };
            if (IDLE_TIMEOUT > 0) {
                ((AbstractHttpClient) httpClient).setKeepAliveStrategy(IDLE_STRATEGY);
            }
            ((AbstractHttpClient) httpClient).addResponseInterceptor(new ResponseContentEncoding());
            ((AbstractHttpClient) httpClient).addResponseInterceptor(METRICS_SAVER); // HACK
            ((AbstractHttpClient) httpClient).addRequestInterceptor(METRICS_RESETTER);

            // Override the defualt schemes as necessary
            SchemeRegistry schemeRegistry = httpClient.getConnectionManager().getSchemeRegistry();

            if (SLOW_HTTP != null) {
                schemeRegistry.register(SLOW_HTTP);
            }

            if (HTTPS_SCHEME != null) {
                schemeRegistry.register(HTTPS_SCHEME);
            }

            // Set up proxy details
            if (useDynamicProxy) {
                HttpHost proxy = new HttpHost(proxyHost, proxyPort);
                clientParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
                String proxyUser = getProxyUser();

                if (proxyUser.length() > 0) {
                    ((AbstractHttpClient) httpClient).getCredentialsProvider().setCredentials(
                            new AuthScope(proxyHost, proxyPort),
                            new NTCredentials(proxyUser, getProxyPass(), localHost, PROXY_DOMAIN));
                }
            } else if (useStaticProxy) {
                HttpHost proxy = new HttpHost(PROXY_HOST, PROXY_PORT);
                clientParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
                if (PROXY_USER.length() > 0) {
                    ((AbstractHttpClient) httpClient).getCredentialsProvider().setCredentials(
                            new AuthScope(PROXY_HOST, PROXY_PORT),
                            new NTCredentials(PROXY_USER, PROXY_PASS, localHost, PROXY_DOMAIN));
                }
            }

            // Bug 52126 - we do our own cookie handling
            clientParams.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);

            if (log.isDebugEnabled()) {
                log.debug("Created new HttpClient: @" + System.identityHashCode(httpClient) + " " + key.toString());
            }

            mapHttpClientPerHttpClientKey.put(key, httpClient); // save the agent for next time round
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Reusing the HttpClient: @" + System.identityHashCode(httpClient) + " " + key.toString());
            }
        }

        MeasuringConnectionManager connectionManager = (MeasuringConnectionManager) httpClient
                .getConnectionManager();
        connectionManager.setSample(res);

        // TODO - should this be done when the client is created?
        // If so, then the details need to be added as part of HttpClientKey
        setConnectionAuthorization(httpClient, url, getAuthManager(), key);

        return httpClient;
    }

    /**
     * Gets the ResponseHeaders
     *
     * @param response
     *            containing the headers
     * @return string containing the headers, one per line
     */
    private String getResponseHeaders(HttpResponse response) {
        StringBuilder headerBuf = new StringBuilder();
        Header[] rh = response.getAllHeaders();
        headerBuf.append(response.getStatusLine());// header[0] is not the status line...
        headerBuf.append("\n"); // $NON-NLS-1$

        for (int i = 0; i < rh.length; i++) {
            headerBuf.append(rh[i].getName());
            headerBuf.append(": "); // $NON-NLS-1$
            headerBuf.append(rh[i].getValue());
            headerBuf.append("\n"); // $NON-NLS-1$
        }
        return headerBuf.toString();
    }

    /**
     * Get all the request headers for the <code>HttpMethod</code>
     *
     * @param method
     *            <code>HttpMethod</code> which represents the request
     * @return the headers as a string
     */
    private String getConnectionHeaders(HttpRequest method) {
        if (method != null) {
            // Get all the request headers
            StringBuilder hdrs = new StringBuilder(100);
            Header[] requestHeaders = method.getAllHeaders();
            for (int i = 0; i < requestHeaders.length; i++) {
                // Exclude the COOKIE header, since cookie is reported separately in the sample
                if (!HTTPConstants.HEADER_COOKIE.equalsIgnoreCase(requestHeaders[i].getName())) {
                    hdrs.append(requestHeaders[i].getName());
                    hdrs.append(": "); // $NON-NLS-1$
                    hdrs.append(requestHeaders[i].getValue());
                    hdrs.append("\n"); // $NON-NLS-1$
                }
            }

            return hdrs.toString();
        }
        return ""; ////$NON-NLS-1$
    }

    /**
     * Setup credentials for url AuthScope but keeps Proxy AuthScope credentials
     * @param client HttpClient
     * @param url URL
     * @param authManager {@link AuthManager}
     * @param key key
     */
    private void setConnectionAuthorization(HttpClient client, URL url, AuthManager authManager,
            HttpClientKey key) {
        CredentialsProvider credentialsProvider = ((AbstractHttpClient) client).getCredentialsProvider();
        if (authManager != null) {
            if (authManager.hasAuthForURL(url)) {
                authManager.setupCredentials(client, url, credentialsProvider, localHost);
            } else {
                credentialsProvider.clear();
            }
        } else {
            Credentials credentials = null;
            AuthScope authScope = null;
            if (key.hasProxy && !StringUtils.isEmpty(key.proxyUser)) {
                authScope = new AuthScope(key.proxyHost, key.proxyPort);
                credentials = credentialsProvider.getCredentials(authScope);
            }
            credentialsProvider.clear();
            if (credentials != null) {
                credentialsProvider.setCredentials(authScope, credentials);
            }
        }
    }

    // Helper class so we can generate request data without dumping entire file contents to SampleResult
    private static class ViewableByteBody extends ByteArrayBody {
        private boolean hideFileData;

        public ViewableByteBody(byte[] content, String mimeType, String fileName) {
            super(content, mimeType, fileName);
            hideFileData = false;
        }

        @Override
        public void writeTo(final OutputStream out) throws IOException {
            if (hideFileData) {
                out.write("<actual file content, not shown here>".getBytes());// encoding does not really matter here
            } else {
                super.writeTo(out);
            }
        }
    }

    /**
     * 
     * @param post {@link HttpPost}
     * @return String posted body if computable
     * @throws IOException if sending the data fails due to I/O
     */
    @Override
    protected String sendPostData(HttpPost post) throws IOException {
        // Buffer to hold the post body, except file content
        StringBuilder postedBody = new StringBuilder(1000);
        FileContentServer contentServer = FileContentServer.getServer();
        HTTPFileArg staticFiles[] = getHTTPFiles();
        HTTPFileArg dynFiles[] = testElement.getDynamicFiles();
        VariableFileArg variableFiles[] = testElement.getVariableFiles();
        String[] attachmentsNumber = null;
        boolean thresholdCheck = testElement.getRecordType() >= testElement.getThreshold();

        final String contentEncoding = getContentEncodingOrNull();
        final boolean haveContentEncoding = contentEncoding != null;
        boolean hasContent = false;

        // If a content encoding is specified, we use that as the
        // encoding of any parameter values
        Charset charset = null;
        if (haveContentEncoding) {
            charset = Charset.forName(contentEncoding);
        }

        // Write the request to our own stream
        MultipartEntity multiPart = new MultipartEntity(
                getDoBrowserCompatibleMultipart() ? HttpMultipartMode.BROWSER_COMPATIBLE : HttpMultipartMode.STRICT,
                null, charset);
        // Create the parts
        // Add any parameters
        PropertyIterator args = getArguments().iterator();
        while (args.hasNext()) {
            HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
            String parameterName = arg.getName();
            if (arg.isSkippable(parameterName)) {
                continue;
            }
            FormBodyPart formPart;
            StringBody stringBody = new StringBody(arg.getValue(), charset);
            formPart = new FormBodyPart(arg.getName(), stringBody);
            multiPart.addPart(formPart);
            hasContent = true;
        }
        /*
         * set parameters controlled by threshold
         */
        if (!testElement.getArgumentThreshold() || thresholdCheck) {
            args = testElement.getOwnArguments().iterator();
            while (args.hasNext()) {
                HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
                String parameterName = arg.getName();
                if (arg.isSkippable(parameterName)) {
                    continue;
                }
                FormBodyPart formPart;
                StringBody stringBody = new StringBody(arg.getValue(), charset);
                formPart = new FormBodyPart(arg.getName(), stringBody);
                multiPart.addPart(formPart);
                hasContent = true;
            }
        }

        // Add all files
        // Cannot retrieve parts once added to the MultiPartEntity, so have to save them here.
        ViewableByteBody[] viewableByteBodies = new ViewableByteBody[variableFiles.length + staticFiles.length
                + dynFiles.length];

        int i = 0;

        //Variable Files
        if (!testElement.getVariableThreshold() || thresholdCheck)
            for (int j = 0; j < variableFiles.length; j++, i++) {
                VariableFileArg file = variableFiles[j];

                viewableByteBodies[i] = new ViewableByteBody(file.getContent().getBytes(), file.getMimeType(),
                        file.getName());
                multiPart.addPart(file.getParamName(), viewableByteBodies[i]);
                hasContent = true;
            }

        //Static Files
        if (!testElement.getStaticThreshold() || thresholdCheck)
            for (i = 0; i < staticFiles.length; i++) {
                HTTPFileArg file = staticFiles[i];

                viewableByteBodies[i] = new ViewableByteBody(contentServer.getFileContent(file.getPath()),
                        file.getMimeType(), new File(file.getPath()).getName());
                multiPart.addPart(file.getParamName(), viewableByteBodies[i]);
                hasContent = true;
            }

        //Dynamic Files
        if (!testElement.getDynamicThreshold() || thresholdCheck) {

            attachmentsNumber = testElement.getAttachmentNumbers().split(",");

            for (int j = 0; j < attachmentsNumber.length && !attachmentsNumber[j].isEmpty(); i++, j++) {
                int fileNum = Integer.parseInt(attachmentsNumber[j]) - 1;
                if (fileNum >= dynFiles.length) {
                    log.warn("trying to send file out of dynamic files range (" + Integer.toString(fileNum + 1)
                            + " of " + Integer.toString(dynFiles.length) + ")\nfile was skipped");
                    i--;
                    continue;
                }
                HTTPFileArg file = dynFiles[fileNum];

                viewableByteBodies[i] = new ViewableByteBody(contentServer.getFileContent(file.getPath()),
                        file.getMimeType(), new File(file.getPath()).getName());
                multiPart.addPart(file.getParamName(), viewableByteBodies[i]);
                hasContent = true;
            }
        }

        post.setEntity(multiPart);
        if (!hasContent)
            log.warn("POST has no content!");

        if (multiPart.isRepeatable()) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();

            //stop the content from appearing in sampler result
            if (!testElement.getLogFiles()) {
                for (ViewableByteBody fileBody : viewableByteBodies) {
                    if (fileBody != null)
                        fileBody.hideFileData = true;
                    else
                        break;
                }
            }

            multiPart.writeTo(bos);

            //Set it back, in order for the content to be sent
            if (!testElement.getLogFiles()) {
                for (ViewableByteBody fileBody : viewableByteBodies) {
                    if (fileBody != null)
                        fileBody.hideFileData = false;
                    else
                        break;
                }
            }
            bos.flush();
            // We get the posted bytes using the encoding used to create it
            postedBody.append(new String(bos.toByteArray(), contentEncoding == null ? "US-ASCII" // $NON-NLS-1$ this is the default used by HttpClient
                    : contentEncoding));
            bos.close();
        } else {
            postedBody.append("<Multipart was not repeatable, cannot view what was sent>"); // $NON-NLS-1$
        }

        return postedBody.toString();
    }

    /**
     * Parses the result and fills the SampleResult with its info
     * @param httpRequest the executed request
     * @param localContext the Http context which was used
     * @param res the SampleResult which is to be filled
     * @param httpResponse the response which is to be parsed
     * @throws IllegalStateException
     * @throws IOException
     */
    private void parseResponse(HttpRequestBase httpRequest, HttpContext localContext, HTTPSampleResult res,
            HttpResponse httpResponse) throws IllegalStateException, IOException {

        // Needs to be done after execute to pick up all the headers
        final HttpRequest request = (HttpRequest) localContext.getAttribute(ExecutionContext.HTTP_REQUEST);
        // We've finished with the request, so we can add the LocalAddress to it for display
        final InetAddress localAddr = (InetAddress) httpRequest.getParams()
                .getParameter(ConnRoutePNames.LOCAL_ADDRESS);
        if (localAddr != null) {
            request.addHeader(HEADER_LOCAL_ADDRESS, localAddr.toString());
        }
        res.setRequestHeaders(getConnectionHeaders(request));

        Header contentType = httpResponse.getLastHeader(HTTPConstants.HEADER_CONTENT_TYPE);
        if (contentType != null) {
            String ct = contentType.getValue();
            res.setContentType(ct);
            res.setEncodingAndType(ct);
        }
        HttpEntity entity = httpResponse.getEntity();
        if (entity != null) {
            InputStream instream = entity.getContent();
            res.setResponseData(readResponse(res, instream, (int) entity.getContentLength()));
        }

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

        // Now collect the results into the HTTPSampleResult:
        StatusLine statusLine = httpResponse.getStatusLine();
        int statusCode = statusLine.getStatusCode();
        res.setResponseCode(Integer.toString(statusCode));
        res.setResponseMessage(statusLine.getReasonPhrase());
        res.setSuccessful(isSuccessCode(statusCode));

        res.setResponseHeaders(getResponseHeaders(httpResponse));
        if (res.isRedirect()) {
            final Header headerLocation = httpResponse.getLastHeader(HTTPConstants.HEADER_LOCATION);
            if (headerLocation == null) { // HTTP protocol violation, but avoids NPE
                throw new IllegalArgumentException(
                        "Missing location header in redirect for " + httpRequest.getRequestLine());
            }
            String redirectLocation = headerLocation.getValue();
            res.setRedirectLocation(redirectLocation);
        }

        // record some sizes to allow HTTPSampleResult.getBytes() with different options
        HttpConnectionMetrics metrics = (HttpConnectionMetrics) localContext.getAttribute(CONTEXT_METRICS);
        long headerBytes = res.getResponseHeaders().length() // condensed length (without \r)
                + httpResponse.getAllHeaders().length // Add \r for each header
                + 1 // Add \r for initial header
                + 2; // final \r\n before data
        long totalBytes = metrics.getReceivedBytesCount();
        res.setHeadersSize((int) headerBytes);
        res.setBodySize((int) (totalBytes - headerBytes));
        if (log.isDebugEnabled()) {
            log.debug("ResponseHeadersSize=" + res.getHeadersSize() + " Content-Length=" + res.getBodySize()
                    + " Total=" + (res.getHeadersSize() + res.getBodySize()));
        }

        // If we redirected automatically, the URL may have changed
        if (getAutoRedirects()) {
            HttpUriRequest req = (HttpUriRequest) localContext.getAttribute(ExecutionContext.HTTP_REQUEST);
            HttpHost target = (HttpHost) localContext.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
            URI redirectURI = req.getURI();
            if (redirectURI.isAbsolute()) {
                res.setURL(redirectURI.toURL());
            } else {
                res.setURL(new URL(new URL(target.toURI()), redirectURI.toString()));
            }
        }
    }

    /**
     * 
     * @return the value of {@link #getContentEncoding()}; forced to null if empty
     */
    private String getContentEncodingOrNull() {
        return getContentEncoding(null);
    }

    /**
     * @param dflt the default to be used
     * @return the value of {@link #getContentEncoding()}; default if null or empty
     */
    private String getContentEncoding(String dflt) {
        String ce = getContentEncoding();
        if (isNullOrEmptyTrimmed(ce)) {
            return dflt;
        } else {
            return ce;
        }
    }

    private void saveConnectionCookies(HttpResponse method, URL u, CookieManager cookieManager) {
        if (cookieManager != null) {
            Header[] hdrs = method.getHeaders(HTTPConstants.HEADER_SET_COOKIE);
            for (Header hdr : hdrs) {
                cookieManager.addCookieFromHeader(hdr.getValue(), u);
            }
        }
    }

    @Override
    public boolean interrupt() {
        HttpUriRequest request = currentRequest;
        if (request != null) {
            currentRequest = null; // don't try twice
            try {
                request.abort();
            } catch (UnsupportedOperationException e) {
                log.warn("Could not abort pending request", e);
            }
        }
        return request != null;
    }
}