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

(-)/bin/jmeter.properties (+6 lines)
Lines 419-424 Link Here
419
# use command-line flags for user-name and password
419
# use command-line flags for user-name and password
420
#http.proxyDomain=NTLM domain, if required by HTTPClient sampler
420
#http.proxyDomain=NTLM domain, if required by HTTPClient sampler
421
421
422
# SSL configuration
423
#proxy.cert.directory=./
424
#proxy.cert.file=server.p12
425
#proxy.cert.keystorepass=password
426
#proxy.cert.keypassword=password
427
422
#---------------------------------------------------------------------------
428
#---------------------------------------------------------------------------
423
# HTTPSampleResponse Parser configuration
429
# HTTPSampleResponse Parser configuration
424
#---------------------------------------------------------------------------
430
#---------------------------------------------------------------------------
(-)/build.xml (+2 lines)
Lines 995-1000 Link Here
995
    <include name="${dest.jar.jmeter}/mirror-server.*"/>
995
    <include name="${dest.jar.jmeter}/mirror-server.*"/>
996
    <include name="${dest.jar.jmeter}/shutdown.*"/>
996
    <include name="${dest.jar.jmeter}/shutdown.*"/>
997
    <include name="${dest.jar.jmeter}/stoptest.*"/>
997
    <include name="${dest.jar.jmeter}/stoptest.*"/>
998
  	<!-- Fake SSL cert for JMeter proxy recorder in https -->
999
  	<include name="${dest.jar.jmeter}/server.p12"/>
998
    <!-- Exclude any files that might be present from testing the release -->
1000
    <!-- Exclude any files that might be present from testing the release -->
999
    <exclude name="${dest.jar.jmeter}/*.log"/>
1001
    <exclude name="${dest.jar.jmeter}/*.log"/>
1000
    <include name="${dest.jar}/"/>
1002
    <include name="${dest.jar}/"/>
(-)/src/protocol/http/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java (-10 / +20 lines)
Lines 95-100 Link Here
95
     * Http Request method. Such as get or post.
95
     * Http Request method. Such as get or post.
96
     */
96
     */
97
    private String method = ""; // $NON-NLS-1$
97
    private String method = ""; // $NON-NLS-1$
98
    
99
    private String paramHttps = ""; // $NON-NLS-1$
98
100
99
    /**
101
    /**
100
     * The requested url. The universal resource locator that hopefully uniquely
102
     * The requested url. The universal resource locator that hopefully uniquely
Lines 192-206 Link Here
192
        if (log.isDebugEnabled()) {
194
        if (log.isDebugEnabled()) {
193
            log.debug("browser request: " + firstLine);
195
            log.debug("browser request: " + firstLine);
194
        }
196
        }
195
        if (!CharUtils.isAsciiAlphanumeric(firstLine.charAt(0))) {
196
            throw new IllegalArgumentException("Unrecognised header line (probably used HTTPS)");
197
        }
198
        StringTokenizer tz = new StringTokenizer(firstLine);
197
        StringTokenizer tz = new StringTokenizer(firstLine);
199
        method = getToken(tz).toUpperCase(java.util.Locale.ENGLISH);
198
        method = getToken(tz).toUpperCase(java.util.Locale.ENGLISH);
200
        url = getToken(tz);
199
        url = getToken(tz);
201
        if (url.toLowerCase(java.util.Locale.ENGLISH).startsWith(HTTPConstants.PROTOCOL_HTTPS)) {
202
            throw new IllegalArgumentException("Cannot handle https URLS: " + url);
203
        }
204
        version = getToken(tz);
200
        version = getToken(tz);
205
        if (log.isDebugEnabled()) {
201
        if (log.isDebugEnabled()) {
206
            log.debug("parser input:  " + firstLine);
202
            log.debug("parser input:  " + firstLine);
Lines 208-216 Link Here
208
            log.debug("parsed url:    " + url);
204
            log.debug("parsed url:    " + url);
209
            log.debug("parsed version:" + version);
205
            log.debug("parsed version:" + version);
210
        }
206
        }
211
        if ("CONNECT".equalsIgnoreCase(method)){
207
        // SSL connection
212
            throw new IllegalArgumentException("Cannot handle CONNECT - probably used HTTPS");
208
        if (getMethod().startsWith(HTTPConstants.CONNECT)) {
209
            paramHttps = url;
210
        } 
211
        if (url.startsWith("/")) {
212
            url = HTTPS + "://" + paramHttps + url; // $NON-NLS-1$
213
        }
213
        }
214
        log.debug("First Line: " + url);
214
    }
215
    }
215
216
216
    /*
217
    /*
Lines 414-420 Link Here
414
        if (log.isDebugEnabled()) {
415
        if (log.isDebugEnabled()) {
415
            log.debug("Proxy: setting path: " + sampler.getPath());
416
            log.debug("Proxy: setting path: " + sampler.getPath());
416
        }
417
        }
417
        if (numberRequests) {
418
        if (!HTTPConstants.CONNECT.equals(getMethod()) && numberRequests) {
418
            requestNumber++;
419
            requestNumber++;
419
            sampler.setName(requestNumber + " " + sampler.getPath());
420
            sampler.setName(requestNumber + " " + sampler.getPath());
420
        } else {
421
        } else {
Lines 429-435 Link Here
429
        // If it was a HTTP GET request, then all parameters in the URL
430
        // If it was a HTTP GET request, then all parameters in the URL
430
        // has been handled by the sampler.setPath above, so we just need
431
        // has been handled by the sampler.setPath above, so we just need
431
        // to do parse the rest of the request if it is not a GET request
432
        // to do parse the rest of the request if it is not a GET request
432
        if(!HTTPConstants.GET.equals(method)) {
433
        if((!HTTPConstants.CONNECT.equals(getMethod())) && (!HTTPConstants.GET.equals(method))) {
433
            // Check if it was a multipart http post request
434
            // Check if it was a multipart http post request
434
            final String contentType = getContentType();
435
            final String contentType = getContentType();
435
            MultipartUrlConfig urlConfig = getMultipartConfig(contentType);
436
            MultipartUrlConfig urlConfig = getMultipartConfig(contentType);
Lines 567-572 Link Here
567
    public String getUrl(){
568
    public String getUrl(){
568
        return url;
569
        return url;
569
    }
570
    }
571
    
572
    /**
573
     * Returns the method string extracted from the first line of the client request.
574
     *
575
     * @return the url
576
     */
577
    public String getMethod(){
578
        return method.toUpperCase();
579
    }
570
580
571
    /**
581
    /**
572
     * Returns the next token in a string.
582
     * Returns the next token in a string.
(-)/src/protocol/http/org/apache/jmeter/protocol/http/proxy/Proxy.java (-3 / +143 lines)
Lines 21-33 Link Here
21
import java.io.BufferedInputStream;
21
import java.io.BufferedInputStream;
22
import java.io.BufferedOutputStream;
22
import java.io.BufferedOutputStream;
23
import java.io.DataOutputStream;
23
import java.io.DataOutputStream;
24
import java.io.File;
25
import java.io.FileInputStream;
26
import java.io.FileNotFoundException;
24
import java.io.IOException;
27
import java.io.IOException;
28
import java.io.InputStream;
25
import java.io.OutputStream;
29
import java.io.OutputStream;
26
import java.net.Socket;
30
import java.net.Socket;
31
import java.net.URL;
27
import java.net.UnknownHostException;
32
import java.net.UnknownHostException;
28
import java.net.URL;
33
import java.security.KeyStore;
34
import java.util.HashMap;
29
import java.util.Map;
35
import java.util.Map;
30
36
37
import javax.net.ssl.KeyManagerFactory;
38
import javax.net.ssl.SSLContext;
39
import javax.net.ssl.SSLSocket;
40
import javax.net.ssl.SSLSocketFactory;
41
31
import org.apache.jmeter.protocol.http.control.HeaderManager;
42
import org.apache.jmeter.protocol.http.control.HeaderManager;
32
import org.apache.jmeter.protocol.http.parser.HTMLParseException;
43
import org.apache.jmeter.protocol.http.parser.HTMLParseException;
33
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
44
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
Lines 64-70 Link Here
64
    private static final String PROXY_HEADERS_REMOVE_DEFAULT = "If-Modified-Since,If-None-Match,Host"; // $NON-NLS-1$
75
    private static final String PROXY_HEADERS_REMOVE_DEFAULT = "If-Modified-Since,If-None-Match,Host"; // $NON-NLS-1$
65
76
66
    private static final String PROXY_HEADERS_REMOVE_SEPARATOR = ","; // $NON-NLS-1$
77
    private static final String PROXY_HEADERS_REMOVE_SEPARATOR = ","; // $NON-NLS-1$
78
    
79
    // for ssl connection
80
    private static final String KEYSTORE_INSTANCE = "PKCS12"; // $NON-NLS-1$
81
    
82
    private static final String KEYMANAGERFACTORY_INSTANCE = "SunX509"; // $NON-NLS-1$
83
    
84
    private static final String SSLCONTEXT_INSTANCE = "SSLv3"; // $NON-NLS-1$
85
    
86
    // Haspmap to save ssl connection between Jmeter proxy and browser
87
    private static HashMap hashHost = new HashMap();
88
    
89
    // Proxy configuration SSL
90
    private static String CERT_DIRECTORY = JMeterUtils.getPropDefault("proxy.cert.directory", "./"); // $NON-NLS-1$ $NON-NLS-2$
91
    
92
    private static String CERT_FILE = JMeterUtils.getPropDefault("proxy.cert.file", "server.p12"); // $NON-NLS-1$ $NON-NLS-2$
93
    
94
    private static char[] KEYSTORE_PASSWORD = JMeterUtils.getPropDefault("proxy.cert.keystorepass", "password").toCharArray(); // $NON-NLS-1$ $NON-NLS-2$
95
96
    private static char[] KEY_PASSWORD = JMeterUtils.getPropDefault("proxy.cert.keypassword","password").toCharArray(); // $NON-NLS-1$ $NON-NLS-2$
67
97
98
    // Use with SSL connection
99
    private OutputStream outStreamClient = null;
100
    
68
    static {
101
    static {
69
        String removeList = JMeterUtils.getPropDefault(PROXY_HEADERS_REMOVE,PROXY_HEADERS_REMOVE_DEFAULT);
102
        String removeList = JMeterUtils.getPropDefault(PROXY_HEADERS_REMOVE,PROXY_HEADERS_REMOVE_DEFAULT);
70
        headersToRemove = JOrphanUtils.split(removeList,PROXY_HEADERS_REMOVE_SEPARATOR);
103
        headersToRemove = JOrphanUtils.split(removeList,PROXY_HEADERS_REMOVE_SEPARATOR);
Lines 161-169 Link Here
161
        SampleResult result = null;
194
        SampleResult result = null;
162
        HeaderManager headers = null;
195
        HeaderManager headers = null;
163
196
164
        try {
197
        try {   
198
            // Now, parse only first line
165
            request.parse(new BufferedInputStream(clientSocket.getInputStream()));
199
            request.parse(new BufferedInputStream(clientSocket.getInputStream()));
166
200
            outStreamClient = clientSocket.getOutputStream();
201
            
202
            if ((request.getMethod().startsWith(HTTPConstants.CONNECT)) && (outStreamClient != null)) {
203
                log.debug("Method CONNECT => SSL");
204
                // write a OK reponse to browser, to engage SSL exchange
205
                outStreamClient.write(("HTTP/1.0 200 OK\r\n\r\n").getBytes()); // $NON-NLS-1$
206
                outStreamClient.flush();
207
               // With ssl request, url is host:port (without https:// or path)
208
                String[] param = request.getUrl().split(":");  // $NON-NLS-1$
209
                if (param.length == 2) {
210
                    log.debug("Start to negociate SSL connection, host: " + param[0]);
211
                    clientSocket = startSSL(clientSocket, param[0]);
212
                } else {
213
                    log.warn("In SSL request, unable to find host and port in CONNECT request");
214
                }
215
                // Re-parse (now it's the http request over SSL)
216
                request.parse(new BufferedInputStream(clientSocket.getInputStream()));
217
            }
218
            
167
            // Populate the sampler. It is the same sampler as we sent into
219
            // Populate the sampler. It is the same sampler as we sent into
168
            // the constructor of the HttpRequestHdr instance above
220
            // the constructor of the HttpRequestHdr instance above
169
            request.getSampler(pageEncodings, formEncodings);
221
            request.getSampler(pageEncodings, formEncodings);
Lines 225-230 Link Here
225
                    "To record https requests, see " +
277
                    "To record https requests, see " +
226
                    "<a href=\"http://jakarta.apache.org/jmeter/usermanual/component_reference.html#HTTP_Proxy_Server\">HTTP Proxy Server documentation</a>"));
278
                    "<a href=\"http://jakarta.apache.org/jmeter/usermanual/component_reference.html#HTTP_Proxy_Server\">HTTP Proxy Server documentation</a>"));
227
            result = generateErrorResult(result, e); // Generate result (if nec.) and populate it
279
            result = generateErrorResult(result, e); // Generate result (if nec.) and populate it
280
        } catch (IOException ioe) {
281
            log.warn("IOE, certainly during response OK to CONNECT: anti-phishing method browser (request canceled by browser)."
282
                    + " Please accept fake JMeter SSL cert", ioe);
228
        } catch (Exception e) {
283
        } catch (Exception e) {
229
            log.error("Exception when processing sample", e);
284
            log.error("Exception when processing sample", e);
230
            writeErrorToClient(HttpReplyHdr.formTimeout());
285
            writeErrorToClient(HttpReplyHdr.formTimeout());
Lines 253-259 Link Here
253
            sampler.threadFinished(); // Needed for HTTPSampler2
308
            sampler.threadFinished(); // Needed for HTTPSampler2
254
        }
309
        }
255
    }
310
    }
311
    
312
    /**
313
     * SSL connection hashmap
314
     * @param host
315
     * @return a ssl socket factory
316
     */
317
    private SSLSocketFactory getSSLSocketFactory(String host) {
318
        synchronized (hashHost) {
319
            if (hashHost.containsKey(host)) {
320
                log.debug("Good, already in map, host=" + host);
321
                return (SSLSocketFactory) hashHost.get(host);
322
            }
323
            InputStream in = getCertificat();
324
            if (in != null) {
325
                KeyStore ks = null;
326
                KeyManagerFactory kmf = null;
327
                SSLContext sslcontext = null;
328
                try {
329
                    ks = KeyStore.getInstance(KEYSTORE_INSTANCE);
330
                    ks.load(in, KEYSTORE_PASSWORD);
331
                    kmf = KeyManagerFactory
332
                            .getInstance(KEYMANAGERFACTORY_INSTANCE);
333
                    kmf.init(ks, KEY_PASSWORD);
334
                    sslcontext = SSLContext.getInstance(SSLCONTEXT_INSTANCE);
335
                    sslcontext.init(kmf.getKeyManagers(), null, null);
336
                    SSLSocketFactory sslFactory = sslcontext.getSocketFactory();
337
                    hashHost.put(host, sslFactory);
338
                    log.info("KeyStore for SSL load OK and put host in map ("+host+")");
339
                    return sslFactory;
340
                } catch (Exception e) {
341
                    log.error("Exception with keystore: " + e);
342
                }
343
            } else {
344
                throw new NullPointerException("Unable to read keystore");
345
            }
346
            return null;
347
        }
348
    }
256
349
350
    /**
351
     * Negociate a SSL connection
352
     * @param sock socket in
353
     * @param host
354
     * @return a new client socket over ssl
355
     * @throws Exception if negociation failed
356
     */
357
    private Socket startSSL(Socket sock, String host) throws Exception {
358
        SSLSocketFactory sslFactory = getSSLSocketFactory(host);
359
        SSLSocket secureSocket;
360
        if (sslFactory != null) {
361
        try {
362
            secureSocket = (SSLSocket) sslFactory.createSocket(sock, sock
363
                    .getInetAddress().getHostName(), sock.getPort(), true);
364
            secureSocket.setUseClientMode(false);
365
            log.debug("SSL transaction ok with cipher: " + secureSocket.getSession().getCipherSuite());
366
            return secureSocket;
367
        } catch (Exception e) {
368
            log.error("Error in SSL socket negociation: ", e);
369
            throw e;
370
        }
371
        } else {
372
            log.warn("Unable to negociate SSL transaction, no keystore");
373
            throw new Exception("Unable to negociate SSL transaction, no keystore");
374
        }
375
    }
376
    
377
    /**
378
     * Load (fake) cert file 
379
     * @return stream to key cert
380
     */
381
    private InputStream getCertificat() {
382
        File certFile = new File(CERT_DIRECTORY + CERT_FILE);
383
        InputStream in = null;
384
        if (certFile.exists() && certFile.canRead()) {
385
            try {
386
                log.info("Keystore file: "+CERT_DIRECTORY+CERT_FILE);
387
                in = new FileInputStream(certFile);
388
            } catch (FileNotFoundException e) {
389
                log.error("No server cert file found", e);
390
            }
391
        } else {
392
            throw new NullPointerException("No keystore found");
393
        }
394
        return in;
395
    }
396
257
    private SampleResult generateErrorResult(SampleResult result, Exception e) {
397
    private SampleResult generateErrorResult(SampleResult result, Exception e) {
258
        if (result == null) {
398
        if (result == null) {
259
            result = new SampleResult();
399
            result = new SampleResult();
(-)/src/protocol/http/org/apache/jmeter/protocol/http/util/HTTPConstantsInterface.java (+1 lines)
Lines 38-43 Link Here
38
    public static final String OPTIONS = "OPTIONS"; // $NON-NLS-1$
38
    public static final String OPTIONS = "OPTIONS"; // $NON-NLS-1$
39
    public static final String TRACE = "TRACE"; // $NON-NLS-1$
39
    public static final String TRACE = "TRACE"; // $NON-NLS-1$
40
    public static final String DELETE = "DELETE"; // $NON-NLS-1$
40
    public static final String DELETE = "DELETE"; // $NON-NLS-1$
41
    public static final String CONNECT = "CONNECT"; // $NON-NLS-1$
41
    public static final String HEADER_AUTHORIZATION = "Authorization"; // $NON-NLS-1$
42
    public static final String HEADER_AUTHORIZATION = "Authorization"; // $NON-NLS-1$
42
    public static final String HEADER_COOKIE = "Cookie"; // $NON-NLS-1$
43
    public static final String HEADER_COOKIE = "Cookie"; // $NON-NLS-1$
43
    public static final String HEADER_CONNECTION = "Connection"; // $NON-NLS-1$
44
    public static final String HEADER_CONNECTION = "Connection"; // $NON-NLS-1$

Return to bug 47622