View | Details | Raw Unified | Return to bug 58807
Collapse All | Expand All

(-)src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java (-24 / +41 lines)
Lines 483-490 Link Here
483
483
484
    private volatile HttpUriRequest currentRequest; // Accessed from multiple threads
484
    private volatile HttpUriRequest currentRequest; // Accessed from multiple threads
485
485
486
    private volatile boolean resetSSLContext;
487
488
    protected HTTPHC4Impl(HTTPSamplerBase testElement) {
486
    protected HTTPHC4Impl(HTTPSamplerBase testElement) {
489
        super(testElement);
487
        super(testElement);
490
    }
488
    }
Lines 539-544 Link Here
539
            log.debug("Start : sample {} method {} followingRedirect {} depth {}", 
537
            log.debug("Start : sample {} method {} followingRedirect {} depth {}", 
540
                    url, method, areFollowingRedirect, frameDepth);            
538
                    url, method, areFollowingRedirect, frameDepth);            
541
        }
539
        }
540
        JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables();
542
541
543
        HTTPSampleResult res = createSampleResult(url, method);
542
        HTTPSampleResult res = createSampleResult(url, method);
544
543
Lines 549-555 Link Here
549
        clientContext.setAttribute(CONTEXT_ATTRIBUTE_AUTH_MANAGER, getAuthManager());
548
        clientContext.setAttribute(CONTEXT_ATTRIBUTE_AUTH_MANAGER, getAuthManager());
550
549
551
        try {
550
        try {
552
            httpClient = setupClient(url, clientContext);
551
            httpClient = setupClient(jMeterVariables, url, clientContext);
553
            URI uri = url.toURI();
552
            URI uri = url.toURI();
554
            if (method.equals(HTTPConstants.POST)) {
553
            if (method.equals(HTTPConstants.POST)) {
555
                httpRequest = new HttpPost(uri);
554
                httpRequest = new HttpPost(uri);
Lines 589-595 Link Here
589
            return res;
588
            return res;
590
        }
589
        }
591
590
592
        setupClientContextBeforeSample(localContext);
591
        setupClientContextBeforeSample(jMeterVariables, localContext);
593
        
592
        
594
        res.sampleStart();
593
        res.sampleStart();
595
594
Lines 609-615 Link Here
609
608
610
            // Needs to be done after execute to pick up all the headers
609
            // Needs to be done after execute to pick up all the headers
611
            final HttpRequest request = (HttpRequest) localContext.getAttribute(HttpCoreContext.HTTP_REQUEST);
610
            final HttpRequest request = (HttpRequest) localContext.getAttribute(HttpCoreContext.HTTP_REQUEST);
612
            extractClientContextAfterSample(localContext);
611
            extractClientContextAfterSample(jMeterVariables, localContext);
613
            // We've finished with the request, so we can add the LocalAddress to it for display
612
            // We've finished with the request, so we can add the LocalAddress to it for display
614
            if (localAddress != null) {
613
            if (localAddress != null) {
615
                request.addHeader(HEADER_LOCAL_ADDRESS, localAddress.toString());
614
                request.addHeader(HEADER_LOCAL_ADDRESS, localAddress.toString());
Lines 718-731 Link Here
718
    /**
717
    /**
719
     * Store in JMeter Variables the UserToken so that the SSL context is reused
718
     * Store in JMeter Variables the UserToken so that the SSL context is reused
720
     * See <a href="https://bz.apache.org/bugzilla/show_bug.cgi?id=57804">Bug 57804</a>
719
     * See <a href="https://bz.apache.org/bugzilla/show_bug.cgi?id=57804">Bug 57804</a>
720
     * @param jMeterVariables {@link JMeterVariables}
721
     * @param localContext {@link HttpContext}
721
     * @param localContext {@link HttpContext}
722
     */
722
     */
723
    private void extractClientContextAfterSample(HttpContext localContext) {
723
    private void extractClientContextAfterSample(JMeterVariables jMeterVariables, HttpContext localContext) {
724
        Object userToken = localContext.getAttribute(HttpClientContext.USER_TOKEN);
724
        Object userToken = localContext.getAttribute(HttpClientContext.USER_TOKEN);
725
        if(userToken != null) {
725
        if(userToken != null) {
726
            log.debug("Extracted from HttpContext user token:{} storing it as JMeter variable:{}", userToken, JMETER_VARIABLE_USER_TOKEN);
726
            log.debug("Extracted from HttpContext user token:{} storing it as JMeter variable:{}", userToken, JMETER_VARIABLE_USER_TOKEN);
727
            // During recording JMeterContextService.getContext().getVariables() is null
727
            // During recording JMeterContextService.getContext().getVariables() is null
728
            JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables();
729
            if (jMeterVariables != null) {
728
            if (jMeterVariables != null) {
730
                jMeterVariables.putObject(JMETER_VARIABLE_USER_TOKEN, userToken); 
729
                jMeterVariables.putObject(JMETER_VARIABLE_USER_TOKEN, userToken); 
731
            }
730
            }
Lines 735-746 Link Here
735
    /**
734
    /**
736
     * Configure the UserToken so that the SSL context is reused
735
     * Configure the UserToken so that the SSL context is reused
737
     * See <a href="https://bz.apache.org/bugzilla/show_bug.cgi?id=57804">Bug 57804</a>
736
     * See <a href="https://bz.apache.org/bugzilla/show_bug.cgi?id=57804">Bug 57804</a>
737
     * @param jMeterVariables {@link JMeterVariables}
738
     * @param localContext {@link HttpContext}
738
     * @param localContext {@link HttpContext}
739
     */
739
     */
740
    private void setupClientContextBeforeSample(HttpContext localContext) {
740
    private void setupClientContextBeforeSample(JMeterVariables jMeterVariables, HttpContext localContext) {
741
        Object userToken = null;
741
        Object userToken = null;
742
        // During recording JMeterContextService.getContext().getVariables() is null
742
        // During recording JMeterContextService.getContext().getVariables() is null
743
        JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables();
744
        if(jMeterVariables != null) {
743
        if(jMeterVariables != null) {
745
            userToken = jMeterVariables.getObject(JMETER_VARIABLE_USER_TOKEN);            
744
            userToken = jMeterVariables.getObject(JMETER_VARIABLE_USER_TOKEN);            
746
        }
745
        }
Lines 934-940 Link Here
934
        }
933
        }
935
    }
934
    }
936
935
937
    private CloseableHttpClient setupClient(URL url, HttpClientContext clientContext) throws GeneralSecurityException {
936
    private CloseableHttpClient setupClient(JMeterVariables jMeterVariables, URL url, HttpClientContext clientContext) throws GeneralSecurityException {
938
937
939
        Map<HttpClientKey, Pair<CloseableHttpClient, PoolingHttpClientConnectionManager>> mapHttpClientPerHttpClientKey = 
938
        Map<HttpClientKey, Pair<CloseableHttpClient, PoolingHttpClientConnectionManager>> mapHttpClientPerHttpClientKey = 
940
                HTTPCLIENTS_CACHE_PER_THREAD_AND_HTTPCLIENTKEY.get();
939
                HTTPCLIENTS_CACHE_PER_THREAD_AND_HTTPCLIENTKEY.get();
Lines 972-978 Link Here
972
            httpClient = pair != null ? pair.getLeft() : null;
971
            httpClient = pair != null ? pair.getLeft() : null;
973
        }
972
        }
974
973
975
        resetSSLStateIfNeeded(url, clientContext, pair);
974
        resetStateIfNeeded(jMeterVariables, clientContext, mapHttpClientPerHttpClientKey);
976
975
977
        if (httpClient == null) { // One-time init for this client
976
        if (httpClient == null) { // One-time init for this client
978
            DnsResolver resolver = this.testElement.getDNSResolver();
977
            DnsResolver resolver = this.testElement.getDNSResolver();
Lines 1080-1100 Link Here
1080
     *  <li>Close current Idle or Expired connections that hold SSL State</li>
1079
     *  <li>Close current Idle or Expired connections that hold SSL State</li>
1081
     *  <li>Remove HttpClientContext.USER_TOKEN from {@link HttpClientContext}</li>
1080
     *  <li>Remove HttpClientContext.USER_TOKEN from {@link HttpClientContext}</li>
1082
     * </ul>
1081
     * </ul>
1083
     * @param url {@link URL}
1082
     * @param jMeterVariables {@link JMeterVariables}
1084
     * @param clientContext {@link HttpClientContext}
1083
     * @param clientContext {@link HttpClientContext}
1085
     * @param pair {@link Pair} holding {@link CloseableHttpClient} and {@link PoolingHttpClientConnectionManager}
1084
     * @param mapHttpClientPerHttpClientKey Map of {@link Pair} holding {@link CloseableHttpClient} and {@link PoolingHttpClientConnectionManager}
1086
     */
1085
     */
1087
    private void resetSSLStateIfNeeded(URL url, HttpClientContext clientContext,
1086
    private void resetStateIfNeeded(JMeterVariables jMeterVariables, 
1088
            Pair<CloseableHttpClient, PoolingHttpClientConnectionManager> pair) {
1087
            HttpClientContext clientContext,
1089
        if (resetSSLContext && HTTPConstants.PROTOCOL_HTTPS.equalsIgnoreCase(url.getProtocol())) {
1088
            Map<HttpClientKey, Pair<CloseableHttpClient, PoolingHttpClientConnectionManager>> mapHttpClientPerHttpClientKey) {
1090
            if(pair != null) {
1089
        if (resetSSLContext.get()) {
1091
                PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = pair.getRight();
1090
            closeCurrentConnections(mapHttpClientPerHttpClientKey);
1092
                poolingHttpClientConnectionManager.closeExpiredConnections();
1093
                poolingHttpClientConnectionManager.closeIdleConnections(1L, TimeUnit.MICROSECONDS);
1094
            }
1095
            clientContext.removeAttribute(HttpClientContext.USER_TOKEN);
1091
            clientContext.removeAttribute(HttpClientContext.USER_TOKEN);
1096
            ((JsseSSLManager) SSLManager.getInstance()).resetContext();
1092
            ((JsseSSLManager) SSLManager.getInstance()).resetContext();
1097
            resetSSLContext = false;
1093
            resetSSLContext.set(false);
1094
        }
1095
        if (closeConnections.get()) {
1096
            closeCurrentConnections(mapHttpClientPerHttpClientKey);
1097
            clientContext.removeAttribute(HttpClientContext.USER_TOKEN);
1098
            closeConnections.set(false);
1099
        }
1100
    }
1101
1102
    /**
1103
     * @param mapHttpClientPerHttpClientKey
1104
     */
1105
    private void closeCurrentConnections(
1106
            Map<HttpClientKey, Pair<CloseableHttpClient, PoolingHttpClientConnectionManager>> mapHttpClientPerHttpClientKey) {
1107
        for (Pair<CloseableHttpClient, PoolingHttpClientConnectionManager> pair :
1108
                mapHttpClientPerHttpClientKey.values()) {
1109
            PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = pair.getRight();
1110
            poolingHttpClientConnectionManager.closeExpiredConnections();
1111
            poolingHttpClientConnectionManager.closeIdleConnections(1L, TimeUnit.MICROSECONDS);
1098
        }
1112
        }
1099
    }
1113
    }
1100
1114
Lines 1700-1707 Link Here
1700
1714
1701
    @Override
1715
    @Override
1702
    protected void notifyFirstSampleAfterLoopRestart() {
1716
    protected void notifyFirstSampleAfterLoopRestart() {
1703
        log.debug("notifyFirstSampleAfterLoopRestart");
1717
        log.debug("notifyFirstSampleAfterLoopRestart called "
1704
        resetSSLContext = !USE_CACHED_SSL_CONTEXT;
1718
                + "with config(reuse.http.connections={}, https.use.cached.ssl.context={})",
1719
                REUSE_HTTP_CONNECTIONS, USE_CACHED_SSL_CONTEXT);
1720
        closeConnections.set(!REUSE_HTTP_CONNECTIONS);
1721
        resetSSLContext.set(!USE_CACHED_SSL_CONTEXT);
1705
    }
1722
    }
1706
1723
1707
    @Override
1724
    @Override
(-)src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHCAbstractImpl.java (+18 lines)
Lines 78-87 Link Here
78
    // -1 means not defined
78
    // -1 means not defined
79
    protected static final int SO_TIMEOUT = JMeterUtils.getPropDefault("httpclient.timeout", -1);
79
    protected static final int SO_TIMEOUT = JMeterUtils.getPropDefault("httpclient.timeout", -1);
80
    
80
    
81
    // Control reuse of HTTP connections in subsequent iterations
82
    protected static final boolean REUSE_HTTP_CONNECTIONS = 
83
            JMeterUtils.getPropDefault("reuse.http.connections", false);//$NON-NLS-1$
84
81
    // Control reuse of cached SSL Context in subsequent iterations
85
    // Control reuse of cached SSL Context in subsequent iterations
82
    protected static final boolean USE_CACHED_SSL_CONTEXT = 
86
    protected static final boolean USE_CACHED_SSL_CONTEXT = 
83
            JMeterUtils.getPropDefault("https.use.cached.ssl.context", true);//$NON-NLS-1$
87
            JMeterUtils.getPropDefault("https.use.cached.ssl.context", true);//$NON-NLS-1$
84
88
89
    /**
90
     *  Whether SSL State/Context should be reset
91
     *  Shared state for any HC based implementation, because SSL contexts are the same 
92
     */
93
    protected static final ThreadLocal<Boolean> resetSSLContext =
94
            ThreadLocal.withInitial(() -> Boolean.FALSE);
95
96
    /**
97
     *  Whether connections should be closed
98
     *  Shared state for any HC based implementation, 
99
     */
100
    protected static final ThreadLocal<Boolean> closeConnections =
101
            ThreadLocal.withInitial(() -> Boolean.FALSE);
102
    
85
    static {
103
    static {
86
        if(!StringUtils.isEmpty(JMeterUtils.getProperty("httpclient.timeout"))) { //$NON-NLS-1$
104
        if(!StringUtils.isEmpty(JMeterUtils.getProperty("httpclient.timeout"))) { //$NON-NLS-1$
87
            log.warn("You're using property 'httpclient.timeout' that will soon be deprecated for HttpClient3.1, you should either set "
105
            log.warn("You're using property 'httpclient.timeout' that will soon be deprecated for HttpClient3.1, you should either set "
(-)src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerProxy.java (-8 / +3 lines)
Lines 36-43 Link Here
36
36
37
    private transient HTTPAbstractImpl impl;
37
    private transient HTTPAbstractImpl impl;
38
    
38
    
39
    private transient volatile boolean notifyFirstSampleAfterLoopRestart;
40
41
    public HTTPSamplerProxy(){
39
    public HTTPSamplerProxy(){
42
        super();
40
        super();
43
    }
41
    }
Lines 66-76 Link Here
66
                return errorResult(ex, new HTTPSampleResult());
64
                return errorResult(ex, new HTTPSampleResult());
67
            }
65
            }
68
        }
66
        }
69
        // see https://bz.apache.org/bugzilla/show_bug.cgi?id=51380
70
        if(notifyFirstSampleAfterLoopRestart) {
71
            impl.notifyFirstSampleAfterLoopRestart();
72
            notifyFirstSampleAfterLoopRestart = false;
73
        }
74
        return impl.sample(u, method, areFollowingRedirect, depth);
67
        return impl.sample(u, method, areFollowingRedirect, depth);
75
    }
68
    }
76
69
Lines 97-102 Link Here
97
     */
90
     */
98
    @Override
91
    @Override
99
    public void testIterationStart(LoopIterationEvent event) {
92
    public void testIterationStart(LoopIterationEvent event) {
100
        notifyFirstSampleAfterLoopRestart = true;
93
        if (impl != null) {
94
            impl.notifyFirstSampleAfterLoopRestart();
95
        }
101
    }
96
    }
102
}
97
}

Return to bug 58807