HttpsURLConnectionTest.java revision f6c387128427e121477c1b32ad35cdcaa5101ba3
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.apache.harmony.luni.tests.internal.net.www.protocol.https;
19
20import dalvik.annotation.AndroidOnly;
21import dalvik.annotation.KnownFailure;
22import dalvik.annotation.TestTargetClass;
23import dalvik.annotation.TestTargets;
24import dalvik.annotation.TestLevel;
25import dalvik.annotation.TestTargetNew;
26
27import java.io.BufferedInputStream;
28import java.io.File;
29import java.io.FileInputStream;
30import java.io.FileNotFoundException;
31import java.io.FileOutputStream;
32import java.io.IOException;
33import java.io.InputStream;
34import java.io.OutputStream;
35import java.io.PrintStream;
36import java.net.Authenticator;
37import java.net.HttpURLConnection;
38import java.net.InetSocketAddress;
39import java.net.PasswordAuthentication;
40import java.net.Proxy;
41import java.net.ServerSocket;
42import java.net.Socket;
43import java.net.SocketTimeoutException;
44import java.net.URL;
45import java.security.KeyStore;
46import java.security.cert.Certificate;
47import java.util.Arrays;
48import javax.net.ssl.HostnameVerifier;
49import javax.net.ssl.HttpsURLConnection;
50import javax.net.ssl.KeyManagerFactory;
51import javax.net.ssl.SSLContext;
52import javax.net.ssl.SSLServerSocket;
53import javax.net.ssl.SSLSession;
54import javax.net.ssl.SSLSocket;
55import javax.net.ssl.SSLSocketFactory;
56import javax.net.ssl.TrustManagerFactory;
57
58import junit.framework.TestCase;
59
60/**
61 * Implementation independent test for HttpsURLConnection.
62 * The test needs certstore file placed in system classpath
63 * and named as "key_store." + the type of the
64 * default KeyStore installed in the system in lower case.
65 * <br>
66 * For example: if default KeyStore type in the system is BKS
67 * (i.e. java.security file sets up the property keystore.type=BKS),
68 * thus classpath should point to the directory with "key_store.bks"
69 * file.
70 * <br>
71 * This certstore file should contain self-signed certificate
72 * generated by keytool utility in a usual way.
73 * <br>
74 * The password to the certstore should be "password" (without quotes).
75 */
76@TestTargetClass(HttpsURLConnection.class)
77@AndroidOnly("we only have a .bks key store in the test resources")
78public class HttpsURLConnectionTest extends TestCase {
79
80    // the password to the store
81    private static final String KS_PASSWORD = "password";
82
83    // turn on/off logging
84    private static final boolean DO_LOG = false;
85
86    // read/connection timeout value
87    private static final int TIMEOUT = 5000;
88
89    // OK response code
90    private static final int OK_CODE = 200;
91
92    // Not Found response code
93    private static final int NOT_FOUND_CODE = 404;
94
95    // Proxy authentication required response code
96    private static final int AUTHENTICATION_REQUIRED_CODE = 407;
97
98    // fields keeping the system values of corresponding properties
99    private static String systemKeyStoreType;
100
101    private static String systemKeyStore;
102
103    private static String systemKeyStorePassword;
104
105    private static String systemTrustStoreType;
106
107    private static String systemTrustStore;
108
109    private static String systemTrustStorePassword;
110
111    private static File store;
112
113    static {
114        try {
115            store = File.createTempFile("key_store", "bks");
116        } catch (Exception e) {
117            // ignore
118        }
119    }
120
121    /**
122     * Checks that HttpsURLConnection's default SSLSocketFactory is operable.
123     */
124    @TestTargetNew(
125        level = TestLevel.PARTIAL_COMPLETE,
126        notes = "Verifies that HttpsURLConnection's default SSLSocketFactory is operable.",
127        method = "getDefaultSSLSocketFactory",
128        args = {}
129    )
130    public void testGetDefaultSSLSocketFactory() throws Exception {
131        // set up the properties defining the default values needed by SSL stuff
132        setUpStoreProperties();
133
134        try {
135            SSLSocketFactory defaultSSLSF = HttpsURLConnection
136                    .getDefaultSSLSocketFactory();
137            ServerSocket ss = new ServerSocket(0);
138            Socket s = defaultSSLSF
139                    .createSocket("localhost", ss.getLocalPort());
140            ss.accept();
141            s.close();
142            ss.close();
143        } finally {
144            // roll the properties back to system values
145            tearDownStoreProperties();
146        }
147    }
148
149    /**
150     * Checks if HTTPS connection performs initial SSL handshake with the
151     * server working over SSL, sends encrypted HTTP request,
152     * and receives expected HTTP response. After HTTPS session if finished
153     * test checks connection state parameters established by
154     * HttpsURLConnection.
155     */
156    @TestTargetNew(
157        level = TestLevel.PARTIAL_COMPLETE,
158        notes = "Verifies  if HTTPS connection performs initial SSL handshake with the server working over SSL, sends encrypted HTTP request, and receives expected HTTP response.",
159        method = "setDefaultHostnameVerifier",
160        args = {javax.net.ssl.HostnameVerifier.class}
161    )
162    @KnownFailure("VM aborts after more than one test was run.")
163    public void testHttpsConnection() throws Throwable {
164        // set up the properties defining the default values needed by SSL stuff
165        setUpStoreProperties();
166
167        try {
168            // create the SSL server socket acting as a server
169            SSLContext ctx = getContext();
170            ServerSocket ss = ctx.getServerSocketFactory()
171                    .createServerSocket(0);
172
173            // create the HostnameVerifier to check hostname verification
174            TestHostnameVerifier hnv = new TestHostnameVerifier();
175            HttpsURLConnection.setDefaultHostnameVerifier(hnv);
176
177            // create url connection to be tested
178            URL url = new URL("https://localhost:" + ss.getLocalPort());
179            HttpsURLConnection connection = (HttpsURLConnection) url
180                    .openConnection();
181
182            // perform the interaction between the peers
183            SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
184
185            // check the connection state
186            checkConnectionStateParameters(connection, peerSocket);
187
188            // should silently exit
189            connection.connect();
190        } finally {
191            // roll the properties back to system values
192            tearDownStoreProperties();
193        }
194    }
195
196    /**
197     * Tests the behaviour of HTTPS connection in case of unavailability
198     * of requested resource.
199     */
200    @TestTargets({
201        @TestTargetNew(
202            level = TestLevel.PARTIAL,
203            notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.",
204            method = "setDoInput",
205            args = {boolean.class}
206        ),
207        @TestTargetNew(
208            level = TestLevel.PARTIAL,
209            notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.",
210            method = "setConnectTimeout",
211            args = {int.class}
212        ),
213        @TestTargetNew(
214            level = TestLevel.PARTIAL,
215            notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.",
216            method = "setReadTimeout",
217            args = {int.class}
218        )
219    })
220    @KnownFailure("VM aborts after more than one test was run.")
221    public void testHttpsConnection_Not_Found_Response() throws Throwable {
222        // set up the properties defining the default values needed by SSL stuff
223        setUpStoreProperties();
224
225        try {
226            // create the SSL server socket acting as a server
227            SSLContext ctx = getContext();
228            ServerSocket ss = ctx.getServerSocketFactory()
229                    .createServerSocket(0);
230
231            // create the HostnameVerifier to check hostname verification
232            TestHostnameVerifier hnv = new TestHostnameVerifier();
233            HttpsURLConnection.setDefaultHostnameVerifier(hnv);
234
235            // create url connection to be tested
236            URL url = new URL("https://localhost:" + ss.getLocalPort());
237            HttpsURLConnection connection = (HttpsURLConnection) url
238                    .openConnection();
239
240            try {
241                doInteraction(connection, ss, NOT_FOUND_CODE);
242                fail("Expected exception was not thrown.");
243            } catch (FileNotFoundException e) {
244                if (DO_LOG) {
245                    System.out.println("Expected exception was thrown: "
246                            + e.getMessage());
247                }
248            }
249
250            // should silently exit
251            connection.connect();
252        } finally {
253            // roll the properties back to system values
254            tearDownStoreProperties();
255        }
256    }
257
258    /**
259     * Tests possibility to set up the default SSLSocketFactory
260     * to be used by HttpsURLConnection.
261     */
262    @TestTargetNew(
263        level = TestLevel.PARTIAL_COMPLETE,
264        notes = "Verifies possibility to set up the default SSLSocketFactory to be used by HttpsURLConnection.",
265        method = "setDefaultSSLSocketFactory",
266        args = {javax.net.ssl.SSLSocketFactory.class}
267    )
268    public void testSetDefaultSSLSocketFactory() throws Throwable {
269        // create the SSLServerSocket which will be used by server side
270        SSLContext ctx = getContext();
271        SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory()
272                .createServerSocket(0);
273
274        SSLSocketFactory socketFactory = (SSLSocketFactory) ctx
275                .getSocketFactory();
276        // set up the factory as default
277        HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
278        // check the result
279        assertSame("Default SSLSocketFactory differs from expected",
280                socketFactory, HttpsURLConnection.getDefaultSSLSocketFactory());
281
282        // create the HostnameVerifier to check hostname verification
283        TestHostnameVerifier hnv = new TestHostnameVerifier();
284        HttpsURLConnection.setDefaultHostnameVerifier(hnv);
285
286        // create HttpsURLConnection to be tested
287        URL url = new URL("https://localhost:" + ss.getLocalPort());
288        HttpsURLConnection connection = (HttpsURLConnection) url
289                .openConnection();
290
291        TestHostnameVerifier hnv_late = new TestHostnameVerifier();
292        // late initialization: should not be used for created connection
293        HttpsURLConnection.setDefaultHostnameVerifier(hnv_late);
294
295        // perform the interaction between the peers
296        SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
297        // check the connection state
298        checkConnectionStateParameters(connection, peerSocket);
299        // check the verification process
300        assertTrue("Hostname verification was not done", hnv.verified);
301        assertFalse(
302                "Hostname verification should not be done by this verifier",
303                hnv_late.verified);
304        // check the used SSLSocketFactory
305        assertSame("Default SSLSocketFactory should be used",
306                HttpsURLConnection.getDefaultSSLSocketFactory(), connection
307                        .getSSLSocketFactory());
308
309        // should silently exit
310        connection.connect();
311    }
312
313    /**
314     * Tests possibility to set up the SSLSocketFactory
315     * to be used by HttpsURLConnection.
316     */
317    @TestTargetNew(
318        level = TestLevel.PARTIAL_COMPLETE,
319        notes = "Verifies possibility to set up the SSLSocketFactory to be used by HttpsURLConnection.",
320        method = "setSSLSocketFactory",
321        args = {javax.net.ssl.SSLSocketFactory.class}
322    )
323    public void testSetSSLSocketFactory() throws Throwable {
324        // create the SSLServerSocket which will be used by server side
325        SSLContext ctx = getContext();
326        SSLServerSocket ss = (SSLServerSocket) ctx.getServerSocketFactory()
327                .createServerSocket(0);
328
329        // create the HostnameVerifier to check hostname verification
330        TestHostnameVerifier hnv = new TestHostnameVerifier();
331        HttpsURLConnection.setDefaultHostnameVerifier(hnv);
332
333        // create HttpsURLConnection to be tested
334        URL url = new URL("https://localhost:" + ss.getLocalPort());
335        HttpsURLConnection connection = (HttpsURLConnection) url
336                .openConnection();
337
338        SSLSocketFactory socketFactory = (SSLSocketFactory) ctx
339                .getSocketFactory();
340        connection.setSSLSocketFactory(socketFactory);
341
342        TestHostnameVerifier hnv_late = new TestHostnameVerifier();
343        // late initialization: should not be used for created connection
344        HttpsURLConnection.setDefaultHostnameVerifier(hnv_late);
345
346        // perform the interaction between the peers
347        SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
348        // check the connection state
349        checkConnectionStateParameters(connection, peerSocket);
350        // check the verification process
351        assertTrue("Hostname verification was not done", hnv.verified);
352        assertFalse(
353                "Hostname verification should not be done by this verifier",
354                hnv_late.verified);
355        // check the used SSLSocketFactory
356        assertNotSame("Default SSLSocketFactory should not be used",
357                HttpsURLConnection.getDefaultSSLSocketFactory(), connection
358                        .getSSLSocketFactory());
359        assertSame("Result differs from expected", socketFactory, connection
360                .getSSLSocketFactory());
361
362        // should silently exit
363        connection.connect();
364    }
365
366    /**
367     * Tests the behaviour of HttpsURLConnection in case of retrieving
368     * of the connection state parameters before connection has been made.
369     */
370    @TestTargets({
371        @TestTargetNew(
372            level = TestLevel.PARTIAL_COMPLETE,
373            notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.",
374            method = "getCipherSuite",
375            args = {}
376        ),
377        @TestTargetNew(
378            level = TestLevel.PARTIAL_COMPLETE,
379            notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.",
380            method = "getPeerPrincipal",
381            args = {}
382        ),
383        @TestTargetNew(
384            level = TestLevel.PARTIAL_COMPLETE,
385            notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.",
386            method = "getLocalPrincipal",
387            args = {}
388        ),
389        @TestTargetNew(
390            level = TestLevel.PARTIAL_COMPLETE,
391            notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.",
392            method = "getServerCertificates",
393            args = {}
394        ),
395        @TestTargetNew(
396            level = TestLevel.PARTIAL_COMPLETE,
397            notes = "Verifies the behaviour of HttpsURLConnection in case of retrieving of the connection state parameters before connection has been made.",
398            method = "getLocalCertificates",
399            args = {}
400        )
401    })
402    public void testUnconnectedStateParameters() throws Throwable {
403        // create HttpsURLConnection to be tested
404        URL url = new URL("https://localhost:55555");
405        HttpsURLConnection connection = (HttpsURLConnection) url
406                .openConnection();
407
408        try {
409            connection.getCipherSuite();
410            fail("Expected IllegalStateException was not thrown");
411        } catch (IllegalStateException e) {}
412        try {
413            connection.getPeerPrincipal();
414            fail("Expected IllegalStateException was not thrown");
415        } catch (IllegalStateException e) {}
416        try {
417            connection.getLocalPrincipal();
418            fail("Expected IllegalStateException was not thrown");
419        } catch (IllegalStateException e) {}
420
421        try {
422            connection.getServerCertificates();
423            fail("Expected IllegalStateException was not thrown");
424        } catch (IllegalStateException e) {}
425        try {
426            connection.getLocalCertificates();
427            fail("Expected IllegalStateException was not thrown");
428        } catch (IllegalStateException e) {}
429    }
430
431    /**
432     * Tests if setHostnameVerifier() method replaces default verifier.
433     */
434    @TestTargetNew(
435        level = TestLevel.PARTIAL_COMPLETE,
436        notes = "Verifies if setHostnameVerifier() method replaces default verifier.",
437        method = "setHostnameVerifier",
438        args = {javax.net.ssl.HostnameVerifier.class}
439    )
440    @KnownFailure("VM aborts after more than one test was run.")
441    public void testSetHostnameVerifier() throws Throwable {
442        // setting up the properties pointing to the key/trust stores
443        setUpStoreProperties();
444
445        try {
446            // create the SSLServerSocket which will be used by server side
447            SSLServerSocket ss = (SSLServerSocket) getContext()
448                    .getServerSocketFactory().createServerSocket(0);
449
450            // create the HostnameVerifier to check that Hostname verification
451            // is done
452            TestHostnameVerifier hnv = new TestHostnameVerifier();
453            HttpsURLConnection.setDefaultHostnameVerifier(hnv);
454
455            // create HttpsURLConnection to be tested
456            URL url = new URL("https://localhost:" + ss.getLocalPort());
457            HttpsURLConnection connection = (HttpsURLConnection) url
458                    .openConnection();
459
460            TestHostnameVerifier hnv_late = new TestHostnameVerifier();
461            // replace default verifier
462            connection.setHostnameVerifier(hnv_late);
463
464            // perform the interaction between the peers and check the results
465            SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
466            assertTrue("Hostname verification was not done", hnv_late.verified);
467            assertFalse(
468                    "Hostname verification should not be done by this verifier",
469                    hnv.verified);
470            checkConnectionStateParameters(connection, peerSocket);
471
472            // should silently exit
473            connection.connect();
474        } finally {
475            // roll the properties back to system values
476            tearDownStoreProperties();
477        }
478    }
479
480    /**
481     * Tests the behaviour in case of sending the data to the server.
482     */
483    @TestTargetNew(
484        level = TestLevel.PARTIAL,
485        notes = "Verifies the behaviour in case of sending the data to the server.",
486        method = "setDoOutput",
487        args = {boolean.class}
488    )
489    @KnownFailure("VM aborts after more than one test was run.")
490    public void test_doOutput() throws Throwable {
491        // setting up the properties pointing to the key/trust stores
492        setUpStoreProperties();
493
494        try {
495            // create the SSLServerSocket which will be used by server side
496            SSLServerSocket ss = (SSLServerSocket) getContext()
497                    .getServerSocketFactory().createServerSocket(0);
498
499            // create the HostnameVerifier to check that Hostname verification
500            // is done
501            TestHostnameVerifier hnv = new TestHostnameVerifier();
502            HttpsURLConnection.setDefaultHostnameVerifier(hnv);
503
504            // create HttpsURLConnection to be tested
505            URL url = new URL("https://localhost:" + ss.getLocalPort());
506            HttpsURLConnection connection = (HttpsURLConnection) url
507                    .openConnection();
508            connection.setDoOutput(true);
509
510            // perform the interaction between the peers and check the results
511            SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
512            checkConnectionStateParameters(connection, peerSocket);
513
514            // should silently exit
515            connection.connect();
516        } finally {
517            // roll the properties back to system values
518            tearDownStoreProperties();
519        }
520    }
521
522    /**
523     * Tests HTTPS connection process made through the proxy server.
524     */
525    @TestTargets({
526        @TestTargetNew(
527            level = TestLevel.PARTIAL,
528            notes = "Verifies HTTPS connection process made through the proxy server.",
529            method = "setDoInput",
530            args = {boolean.class}
531        ),
532        @TestTargetNew(
533            level = TestLevel.PARTIAL,
534            notes = "Verifies HTTPS connection process made through the proxy server.",
535            method = "setConnectTimeout",
536            args = {int.class}
537        ),
538        @TestTargetNew(
539            level = TestLevel.PARTIAL,
540            notes = "Verifies HTTPS connection process made through the proxy server.",
541            method = "setReadTimeout",
542            args = {int.class}
543        )
544    })
545    @KnownFailure("VM aborts after more than one test was run.")
546    public void testProxyConnection() throws Throwable {
547        // setting up the properties pointing to the key/trust stores
548        setUpStoreProperties();
549
550        try {
551            // create the SSLServerSocket which will be used by server side
552            ServerSocket ss = new ServerSocket(0);
553
554            // create the HostnameVerifier to check that Hostname verification
555            // is done
556            TestHostnameVerifier hnv = new TestHostnameVerifier();
557            HttpsURLConnection.setDefaultHostnameVerifier(hnv);
558
559            // create HttpsURLConnection to be tested
560            URL url = new URL("https://requested.host:55556/requested.data");
561            HttpsURLConnection connection = (HttpsURLConnection) url
562                    .openConnection(new Proxy(Proxy.Type.HTTP,
563                            new InetSocketAddress("localhost", ss
564                                    .getLocalPort())));
565
566            // perform the interaction between the peers and check the results
567            SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
568            checkConnectionStateParameters(connection, peerSocket);
569
570            // should silently exit
571            connection.connect();
572        } finally {
573            // roll the properties back to system values
574            tearDownStoreProperties();
575        }
576    }
577
578    /**
579     * Tests HTTPS connection process made through the proxy server.
580     * Proxy server needs authentication.
581     */
582    @TestTargets({
583        @TestTargetNew(
584            level = TestLevel.PARTIAL,
585            notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.",
586            method = "setDoInput",
587            args = {boolean.class}
588        ),
589        @TestTargetNew(
590            level = TestLevel.PARTIAL,
591            notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.",
592            method = "setConnectTimeout",
593            args = {int.class}
594        ),
595        @TestTargetNew(
596            level = TestLevel.PARTIAL,
597            notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication.",
598            method = "setReadTimeout",
599            args = {int.class}
600        )
601    })
602    @KnownFailure("VM aborts after more than one test was run.")
603    public void testProxyAuthConnection() throws Throwable {
604        // setting up the properties pointing to the key/trust stores
605        setUpStoreProperties();
606
607        try {
608            // create the SSLServerSocket which will be used by server side
609            ServerSocket ss = new ServerSocket(0);
610
611            // create the HostnameVerifier to check that Hostname verification
612            // is done
613            TestHostnameVerifier hnv = new TestHostnameVerifier();
614            HttpsURLConnection.setDefaultHostnameVerifier(hnv);
615
616            Authenticator.setDefault(new Authenticator() {
617
618                protected PasswordAuthentication getPasswordAuthentication() {
619                    return new PasswordAuthentication("user", "password"
620                            .toCharArray());
621                }
622            });
623
624            // create HttpsURLConnection to be tested
625            URL url = new URL("https://requested.host:55555/requested.data");
626            HttpsURLConnection connection = (HttpsURLConnection) url
627                    .openConnection(new Proxy(Proxy.Type.HTTP,
628                            new InetSocketAddress("localhost", ss
629                                    .getLocalPort())));
630
631            // perform the interaction between the peers and check the results
632            SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
633            checkConnectionStateParameters(connection, peerSocket);
634
635            // should silently exit
636            connection.connect();
637        } finally {
638            // roll the properties back to system values
639            tearDownStoreProperties();
640        }
641    }
642
643    /**
644     * Tests HTTPS connection process made through the proxy server.
645     * 2 HTTPS connections are opened for one URL. For the first time
646     * the connection is opened through one proxy,
647     * for the second time through another.
648     */
649    @TestTargets({
650        @TestTargetNew(
651            level = TestLevel.PARTIAL_COMPLETE,
652            notes = "Verifies HTTPS connection process made through the proxy server.",
653            method = "getCipherSuite",
654            args = {}
655        ),
656        @TestTargetNew(
657            level = TestLevel.PARTIAL_COMPLETE,
658            notes = "Verifies HTTPS connection process made through the proxy server.",
659            method = "getLocalPrincipal",
660            args = {}
661        ),
662        @TestTargetNew(
663            level = TestLevel.PARTIAL_COMPLETE,
664            notes = "Verifies HTTPS connection process made through the proxy server.",
665            method = "getPeerPrincipal",
666            args = {}
667        )
668    })
669    @KnownFailure("VM aborts after more than one test was run.")
670    public void testConsequentProxyConnection() throws Throwable {
671        // setting up the properties pointing to the key/trust stores
672        setUpStoreProperties();
673
674        try {
675            // create the SSLServerSocket which will be used by server side
676            ServerSocket ss = new ServerSocket(0);
677
678            // create the HostnameVerifier to check that Hostname verification
679            // is done
680            TestHostnameVerifier hnv = new TestHostnameVerifier();
681            HttpsURLConnection.setDefaultHostnameVerifier(hnv);
682
683            // create HttpsURLConnection to be tested
684            URL url = new URL("https://requested.host:55555/requested.data");
685            HttpsURLConnection connection = (HttpsURLConnection) url
686                    .openConnection(new Proxy(Proxy.Type.HTTP,
687                            new InetSocketAddress("localhost", ss
688                                    .getLocalPort())));
689
690            // perform the interaction between the peers and check the results
691            SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss);
692            checkConnectionStateParameters(connection, peerSocket);
693
694            // create another SSLServerSocket which will be used by server side
695            ss = new ServerSocket(0);
696
697            connection = (HttpsURLConnection) url.openConnection(new Proxy(
698                    Proxy.Type.HTTP, new InetSocketAddress("localhost", ss
699                            .getLocalPort())));
700
701            // perform the interaction between the peers and check the results
702            peerSocket = (SSLSocket) doInteraction(connection, ss);
703            checkConnectionStateParameters(connection, peerSocket);
704        } finally {
705            // roll the properties back to system values
706            tearDownStoreProperties();
707        }
708    }
709
710    /**
711     * Tests HTTPS connection process made through the proxy server.
712     * Proxy server needs authentication.
713     * Client sends data to the server.
714     */
715    @TestTargets({
716        @TestTargetNew(
717            level = TestLevel.PARTIAL,
718            notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.",
719            method = "setDoInput",
720            args = {boolean.class}
721        ),
722        @TestTargetNew(
723            level = TestLevel.PARTIAL,
724            notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.",
725            method = "setConnectTimeout",
726            args = {int.class}
727        ),
728        @TestTargetNew(
729            level = TestLevel.PARTIAL,
730            notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.",
731            method = "setReadTimeout",
732            args = {int.class}
733        ),
734        @TestTargetNew(
735            level = TestLevel.PARTIAL,
736            notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication. Client sends data to the server.",
737            method = "setDoOutput",
738            args = {boolean.class}
739        )
740    })
741    @KnownFailure("VM aborts after more than one test was run.")
742    public void testProxyAuthConnection_doOutput() throws Throwable {
743        // setting up the properties pointing to the key/trust stores
744        setUpStoreProperties();
745
746        try {
747            // create the SSLServerSocket which will be used by server side
748            ServerSocket ss = new ServerSocket(0);
749
750            // create the HostnameVerifier to check that Hostname verification
751            // is done
752            TestHostnameVerifier hnv = new TestHostnameVerifier();
753            HttpsURLConnection.setDefaultHostnameVerifier(hnv);
754
755            Authenticator.setDefault(new Authenticator() {
756
757                protected PasswordAuthentication getPasswordAuthentication() {
758                    return new PasswordAuthentication("user", "password"
759                            .toCharArray());
760                }
761            });
762
763            // create HttpsURLConnection to be tested
764            URL url = new URL("https://requested.host:55554/requested.data");
765            HttpsURLConnection connection = (HttpsURLConnection) url
766                    .openConnection(new Proxy(Proxy.Type.HTTP,
767                            new InetSocketAddress("localhost", ss
768                                    .getLocalPort())));
769            connection.setDoOutput(true);
770
771            // perform the interaction between the peers and check the results
772            SSLSocket peerSocket = (SSLSocket) doInteraction(connection, ss,
773                    OK_CODE, true);
774            checkConnectionStateParameters(connection, peerSocket);
775        } finally {
776            // roll the properties back to system values
777            tearDownStoreProperties();
778        }
779    }
780
781    /**
782     * Tests HTTPS connection process made through the proxy server.
783     * Proxy server needs authentication but client fails to authenticate
784     * (Authenticator was not set up in the system).
785     */
786    @TestTargets({
787        @TestTargetNew(
788            level = TestLevel.PARTIAL,
789            notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication but client fails to authenticate (Authenticator was not set up in the system).",
790            method = "setDoInput",
791            args = {boolean.class}
792        ),
793        @TestTargetNew(
794            level = TestLevel.PARTIAL,
795            notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication but client fails to authenticate (Authenticator was not set up in the system).",
796            method = "setConnectTimeout",
797            args = {int.class}
798        ),
799        @TestTargetNew(
800            level = TestLevel.PARTIAL,
801            notes = "Verifies HTTPS connection process made through the proxy server. Proxy server needs authentication but client fails to authenticate (Authenticator was not set up in the system).",
802            method = "setReadTimeout",
803            args = {int.class}
804        )
805    })
806    @KnownFailure("VM aborts after more than one test was run.")
807    public void testProxyAuthConnectionFailed() throws Throwable {
808        // setting up the properties pointing to the key/trust stores
809        setUpStoreProperties();
810
811        try {
812            // create the SSLServerSocket which will be used by server side
813            ServerSocket ss = new ServerSocket(0);
814
815            // create the HostnameVerifier to check that Hostname verification
816            // is done
817            TestHostnameVerifier hnv = new TestHostnameVerifier();
818            HttpsURLConnection.setDefaultHostnameVerifier(hnv);
819
820            // create HttpsURLConnection to be tested
821            URL url = new URL("https://requested.host:55555/requested.data");
822            HttpURLConnection connection = (HttpURLConnection) url
823                    .openConnection(new Proxy(Proxy.Type.HTTP,
824                            new InetSocketAddress("localhost", ss
825                                    .getLocalPort())));
826
827            // perform the interaction between the peers and check the results
828            try {
829                doInteraction(connection, ss, AUTHENTICATION_REQUIRED_CODE,
830                        true);
831            } catch (IOException e) {
832                // SSL Tunnelling failed
833                if (DO_LOG) {
834                    System.out.println("Got expected IOException: "
835                            + e.getMessage());
836                }
837            }
838        } finally {
839            // roll the properties back to system values
840            tearDownStoreProperties();
841        }
842    }
843
844    /**
845     * Tests the behaviour of HTTPS connection in case of unavailability
846     * of requested resource.
847     */
848    @TestTargets({
849        @TestTargetNew(
850            level = TestLevel.PARTIAL,
851            notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.",
852            method = "setDoInput",
853            args = {boolean.class}
854        ),
855        @TestTargetNew(
856            level = TestLevel.PARTIAL,
857            notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.",
858            method = "setConnectTimeout",
859            args = {int.class}
860        ),
861        @TestTargetNew(
862            level = TestLevel.PARTIAL,
863            notes = "Verifies the behaviour of HTTPS connection in case of unavailability of requested resource.",
864            method = "setReadTimeout",
865            args = {int.class}
866        )
867    })
868    @KnownFailure("VM aborts after more than one test was run.")
869    public void testProxyConnection_Not_Found_Response() throws Throwable {
870        // setting up the properties pointing to the key/trust stores
871        setUpStoreProperties();
872
873        try {
874            // create the SSLServerSocket which will be used by server side
875            ServerSocket ss = new ServerSocket(0);
876
877            // create the HostnameVerifier to check that Hostname verification
878            // is done
879            TestHostnameVerifier hnv = new TestHostnameVerifier();
880            HttpsURLConnection.setDefaultHostnameVerifier(hnv);
881
882            // create HttpsURLConnection to be tested
883            URL url = new URL("https://localhost:" + ss.getLocalPort());
884            HttpURLConnection connection = (HttpURLConnection) url
885                    .openConnection(new Proxy(Proxy.Type.HTTP,
886                            new InetSocketAddress("localhost", ss
887                                    .getLocalPort())));
888
889            try {
890                doInteraction(connection, ss, NOT_FOUND_CODE); // NOT FOUND
891                fail("Expected exception was not thrown.");
892            } catch (FileNotFoundException e) {
893                if (DO_LOG) {
894                    System.out.println("Expected exception was thrown: "
895                            + e.getMessage());
896                }
897            }
898        } finally {
899            // roll the properties back to system values
900            tearDownStoreProperties();
901        }
902    }
903
904    // ---------------------------------------------------------------------
905    // ------------------------ Staff Methods ------------------------------
906    // ---------------------------------------------------------------------
907
908    /**
909     * Log the name of the test case to be executed.
910     */
911    public void setUp() throws Exception {
912        if (DO_LOG) {
913            System.out.println();
914            System.out.println("------------------------");
915            System.out.println("------ " + getName());
916            System.out.println("------------------------");
917        }
918
919        if (store != null) {
920            String ksFileName = "org/apache/harmony/luni/tests/key_store."
921                    + KeyStore.getDefaultType().toLowerCase();
922            InputStream in = ClassLoader.getSystemClassLoader()
923                    .getResourceAsStream(ksFileName);
924            FileOutputStream out = new FileOutputStream(store);
925            BufferedInputStream bufIn = new BufferedInputStream(in, 8192);
926            while (bufIn.available() > 0) {
927                byte[] buf = new byte[128];
928                int read = bufIn.read(buf);
929                out.write(buf, 0, read);
930            }
931            bufIn.close();
932            out.close();
933        } else {
934            fail("couldn't set up key store");
935        }
936    }
937
938    public void tearDown() {
939        if (store != null) {
940            store.delete();
941        }
942    }
943
944    /**
945     * Checks the HttpsURLConnection getter's values and compares
946     * them with actual corresponding values of remote peer.
947     */
948    public static void checkConnectionStateParameters(
949            HttpsURLConnection clientConnection, SSLSocket serverPeer)
950            throws Exception {
951        SSLSession session = serverPeer.getSession();
952
953        assertEquals(session.getCipherSuite(), clientConnection
954                .getCipherSuite());
955
956        assertEquals(session.getLocalPrincipal(), clientConnection
957                .getPeerPrincipal());
958
959        assertEquals(session.getPeerPrincipal(), clientConnection
960                .getLocalPrincipal());
961
962        Certificate[] serverCertificates = clientConnection
963                .getServerCertificates();
964        Certificate[] localCertificates = session.getLocalCertificates();
965        assertTrue("Server certificates differ from expected", Arrays.equals(
966                serverCertificates, localCertificates));
967
968        localCertificates = clientConnection.getLocalCertificates();
969        serverCertificates = session.getPeerCertificates();
970        assertTrue("Local certificates differ from expected", Arrays.equals(
971                serverCertificates, localCertificates));
972    }
973
974    /**
975     * Returns the file name of the key/trust store. The key store file
976     * (named as "key_store." + extension equals to the default KeyStore
977     * type installed in the system in lower case) is searched in classpath.
978     * @throws AssertionFailedError if property was not set
979     * or file does not exist.
980     */
981    private static String getKeyStoreFileName() {
982        return store.getAbsolutePath();
983    }
984
985    /**
986     * Builds and returns the context used for secure socket creation.
987     */
988    private static SSLContext getContext() throws Exception {
989        String type = KeyStore.getDefaultType();
990        SSLContext ctx;
991
992        String keyStore = getKeyStoreFileName();
993        File keyStoreFile = new File(keyStore);
994
995        FileInputStream fis = new FileInputStream(keyStoreFile);
996
997        KeyStore ks = KeyStore.getInstance(type);
998        ks.load(fis, KS_PASSWORD.toCharArray());
999
1000        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
1001                .getDefaultAlgorithm());
1002        kmf.init(ks, KS_PASSWORD.toCharArray());
1003
1004        TrustManagerFactory tmf = TrustManagerFactory
1005                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
1006        tmf.init(ks);
1007
1008        ctx = SSLContext.getInstance("TLSv1");
1009        ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
1010
1011        return ctx;
1012    }
1013
1014    /**
1015     * Sets up the properties pointing to the key store and trust store
1016     * and used as default values by JSSE staff. This is needed to test
1017     * HTTPS behaviour in the case of default SSL Socket Factories.
1018     */
1019    private static void setUpStoreProperties() throws Exception {
1020        String type = KeyStore.getDefaultType();
1021
1022        systemKeyStoreType = System.getProperty("javax.net.ssl.keyStoreType");
1023        systemKeyStore = System.getProperty("javax.net.ssl.keyStore");
1024        systemKeyStorePassword = System
1025                .getProperty("javax.net.ssl.keyStorePassword");
1026
1027        systemTrustStoreType = System
1028                .getProperty("javax.net.ssl.trustStoreType");
1029        systemTrustStore = System.getProperty("javax.net.ssl.trustStore");
1030        systemTrustStorePassword = System
1031                .getProperty("javax.net.ssl.trustStorePassword");
1032
1033        System.setProperty("javax.net.ssl.keyStoreType", type);
1034        System.setProperty("javax.net.ssl.keyStore", getKeyStoreFileName());
1035        System.setProperty("javax.net.ssl.keyStorePassword", KS_PASSWORD);
1036
1037        System.setProperty("javax.net.ssl.trustStoreType", type);
1038        System.setProperty("javax.net.ssl.trustStore", getKeyStoreFileName());
1039        System.setProperty("javax.net.ssl.trustStorePassword", KS_PASSWORD);
1040    }
1041
1042    /**
1043     * Rolls back the values of system properties.
1044     */
1045    private static void tearDownStoreProperties() {
1046        if (systemKeyStoreType == null) {
1047            System.clearProperty("javax.net.ssl.keyStoreType");
1048        } else {
1049            System
1050                    .setProperty("javax.net.ssl.keyStoreType",
1051                            systemKeyStoreType);
1052        }
1053        if (systemKeyStore == null) {
1054            System.clearProperty("javax.net.ssl.keyStore");
1055        } else {
1056            System.setProperty("javax.net.ssl.keyStore", systemKeyStore);
1057        }
1058        if (systemKeyStorePassword == null) {
1059            System.clearProperty("javax.net.ssl.keyStorePassword");
1060        } else {
1061            System.setProperty("javax.net.ssl.keyStorePassword",
1062                    systemKeyStorePassword);
1063        }
1064
1065        if (systemTrustStoreType == null) {
1066            System.clearProperty("javax.net.ssl.trustStoreType");
1067        } else {
1068            System.setProperty("javax.net.ssl.trustStoreType",
1069                    systemTrustStoreType);
1070        }
1071        if (systemTrustStore == null) {
1072            System.clearProperty("javax.net.ssl.trustStore");
1073        } else {
1074            System.setProperty("javax.net.ssl.trustStore", systemTrustStore);
1075        }
1076        if (systemTrustStorePassword == null) {
1077            System.clearProperty("javax.net.ssl.trustStorePassword");
1078        } else {
1079            System.setProperty("javax.net.ssl.trustStorePassword",
1080                    systemTrustStorePassword);
1081        }
1082    }
1083
1084    /**
1085     * Performs interaction between client's HttpURLConnection and
1086     * servers side (ServerSocket).
1087     */
1088    public static Socket doInteraction(
1089            final HttpURLConnection clientConnection,
1090            final ServerSocket serverSocket) throws Throwable {
1091        return doInteraction(clientConnection, serverSocket, OK_CODE, false);
1092    }
1093
1094    /**
1095     * Performs interaction between client's HttpURLConnection and
1096     * servers side (ServerSocket). Server will response with specified
1097     * response code.
1098     */
1099    public static Socket doInteraction(
1100            final HttpURLConnection clientConnection,
1101            final ServerSocket serverSocket, final int responseCode)
1102            throws Throwable {
1103        return doInteraction(clientConnection, serverSocket, responseCode,
1104                false);
1105    }
1106
1107    /**
1108     * Performs interaction between client's HttpURLConnection and
1109     * servers side (ServerSocket). Server will response with specified
1110     * response code.
1111     * @param doAuthentication specifies
1112     * if the server needs client authentication.
1113     */
1114    public static Socket doInteraction(
1115            final HttpURLConnection clientConnection,
1116            final ServerSocket serverSocket, final int responseCode,
1117            final boolean doAuthentication) throws Throwable {
1118
1119        // set up the connection
1120        clientConnection.setDoInput(true);
1121        clientConnection.setConnectTimeout(TIMEOUT);
1122        clientConnection.setReadTimeout(TIMEOUT);
1123
1124        ServerWork server = new ServerWork(serverSocket, responseCode,
1125                doAuthentication);
1126
1127        ClientConnectionWork client = new ClientConnectionWork(clientConnection);
1128
1129        server.start();
1130        client.start();
1131
1132        client.join();
1133        server.join();
1134
1135        if (client.thrown != null) {
1136            if (responseCode != OK_CODE) { // not OK response expected
1137                // it is probably expected exception, keep it as is
1138                throw client.thrown;
1139            }
1140            if ((client.thrown instanceof SocketTimeoutException)
1141                    && (server.thrown != null)) {
1142                // server's exception is more informative in this case
1143                throw new Exception(server.thrown);
1144            } else {
1145                throw new Exception(client.thrown);
1146            }
1147        }
1148        if (server.thrown != null) {
1149            throw server.thrown;
1150        }
1151        return server.peerSocket;
1152    }
1153
1154    /**
1155     * The host name verifier used in test.
1156     */
1157    static class TestHostnameVerifier implements HostnameVerifier {
1158
1159        boolean verified = false;
1160
1161        public boolean verify(String hostname, SSLSession session) {
1162            if (DO_LOG) {
1163                System.out.println("***> verification " + hostname + " "
1164                        + session.getPeerHost());
1165            }
1166            verified = true;
1167            return true;
1168        }
1169    }
1170
1171    /**
1172     * The base class for mock Client and Server.
1173     */
1174    static class Work extends Thread {
1175
1176        /**
1177         * The header of OK HTTP response.
1178         */
1179        static String responseHead = "HTTP/1.1 200 OK\n";
1180
1181        /**
1182         * The content of the response.
1183         */
1184        static String plainResponseContent = "<HTML>\n"
1185                + "<HEAD><TITLE>Plain Response Content</TITLE></HEAD>\n"
1186                + "</HTML>";
1187
1188        /**
1189         * The tail of the response.
1190         */
1191        static String plainResponseTail = "Content-type: text/html\n"
1192                + "Content-length: " + plainResponseContent.length() + "\n\n"
1193                + plainResponseContent;
1194
1195        /**
1196         * The response message to be sent in plain (HTTP) format.
1197         */
1198        static String plainResponse = responseHead + plainResponseTail;
1199
1200        /**
1201         * The content of the response to be sent during HTTPS session.
1202         */
1203        static String httpsResponseContent = "<HTML>\n"
1204                + "<HEAD><TITLE>HTTPS Response Content</TITLE></HEAD>\n"
1205                + "</HTML>";
1206
1207        /**
1208         * The tail of the response to be sent during HTTPS session.
1209         */
1210        static String httpsResponseTail = "Content-type: text/html\n"
1211                + "Content-length: " + httpsResponseContent.length() + "\n\n"
1212                + httpsResponseContent;
1213
1214        /**
1215         * The response requiring client's proxy authentication.
1216         */
1217        static String respAuthenticationRequired = "HTTP/1.0 407 Proxy authentication required\n"
1218                + "Proxy-authenticate: Basic realm=\"localhost\"\n\n";
1219
1220        /**
1221         * The data to be posted by client to the server.
1222         */
1223        static String clientsData = "_.-^ Client's Data ^-._";
1224
1225        /**
1226         * The exception thrown during peers interaction.
1227         */
1228        protected Throwable thrown;
1229
1230        /**
1231         * The print stream used for debug log.
1232         * If it is null debug info will not be printed.
1233         */
1234        private PrintStream out = new PrintStream(System.out);
1235
1236        /**
1237         * Prints log message.
1238         */
1239        public synchronized void log(String message) {
1240            if (DO_LOG && (out != null)) {
1241                System.out.println("[" + getName() + "]: " + message);
1242            }
1243        }
1244    }
1245
1246    /**
1247     * The class used for server side works.
1248     */
1249    static class ServerWork extends Work {
1250
1251        // the server socket used for connection
1252        private ServerSocket serverSocket;
1253
1254        // the socket connected with client peer
1255        private Socket peerSocket;
1256
1257        // indicates if the server acts as proxy server
1258        private boolean actAsProxy;
1259
1260        // indicates if the server needs proxy authentication
1261        private boolean needProxyAuthentication;
1262
1263        // response code to be send to the client peer
1264        private int responseCode;
1265
1266        /**
1267         * Creates the thread acting as a server side.
1268         */
1269        public ServerWork(ServerSocket serverSocket) {
1270            // the server does not require proxy authentication
1271            // and sends OK_CODE (OK) response code
1272            this(serverSocket, OK_CODE, false);
1273        }
1274
1275        /**
1276         * Creates the thread acting as a server side.
1277         * @param serverSocket the server socket to be used during connection
1278         * @param responseCode the response code to be sent to the client
1279         * @param needProxyAuthentication
1280         * indicates if the server needs proxy authentication
1281         */
1282        public ServerWork(ServerSocket serverSocket, int responseCode,
1283                boolean needProxyAuthentication) {
1284            this.serverSocket = serverSocket;
1285            this.responseCode = responseCode;
1286            this.needProxyAuthentication = needProxyAuthentication;
1287            // will act as a proxy server if the specified server socket
1288            // is not a secure server socket
1289            if (serverSocket instanceof SSLServerSocket) {
1290                // demand client to send its certificate
1291                ((SSLServerSocket) serverSocket).setNeedClientAuth(true);
1292                // work as a HTTPS server, not as HTTP proxy
1293                this.actAsProxy = false;
1294            } else {
1295                this.actAsProxy = true;
1296            }
1297            this.actAsProxy = !(serverSocket instanceof SSLServerSocket);
1298            setName(this.actAsProxy ? "Proxy Server" : "Server");
1299        }
1300
1301        /**
1302         * Closes the connection.
1303         */
1304        public void closeSocket(Socket socket) {
1305            try {
1306                socket.getInputStream().close();
1307            } catch (IOException e) {}
1308            try {
1309                socket.getOutputStream().close();
1310            } catch (IOException e) {}
1311            try {
1312                socket.close();
1313            } catch (IOException e) {}
1314        }
1315
1316        /**
1317         * Performs the actual server work.
1318         * If some exception occurs during the work it will be
1319         * stored in the <code>thrown</code> field.
1320         */
1321        public void run() {
1322            // the buffer used for reading the messages
1323            byte[] buff = new byte[2048];
1324            // the number of bytes read into the buffer
1325            int num;
1326            try {
1327                // configure the server socket to avoid blocking
1328                serverSocket.setSoTimeout(TIMEOUT);
1329                // accept client connection
1330                peerSocket = serverSocket.accept();
1331                // configure the client connection to avoid blocking
1332                peerSocket.setSoTimeout(TIMEOUT);
1333                log("Client connection ACCEPTED");
1334
1335                InputStream is = peerSocket.getInputStream();
1336                OutputStream os = peerSocket.getOutputStream();
1337
1338                num = is.read(buff);
1339                String message = new String(buff, 0, num);
1340                log("Got request:\n" + message);
1341                log("------------------");
1342
1343                if (!actAsProxy) {
1344                    // Act as Server (not Proxy) side
1345                    if (message.startsWith("POST")) {
1346                        // client connection sent some data
1347                        log("try to read client data");
1348                        num = is.read(buff);
1349                        message = new String(buff, 0, num);
1350                        log("client's data: '" + message + "'");
1351                        // check the received data
1352                        assertEquals(clientsData, message);
1353                    }
1354                    // just send the response
1355                    os
1356                            .write(("HTTP/1.1 " + responseCode + "\n" + httpsResponseTail)
1357                                    .getBytes());
1358                    // and return
1359                    log("Work is DONE");
1360                    return;
1361                }
1362
1363                // Do proxy work
1364                if (needProxyAuthentication) {
1365                    log("Authentication required ...");
1366                    // send Authentication Request
1367                    os.write(respAuthenticationRequired.getBytes());
1368                    // read response
1369                    num = is.read(buff);
1370                    if (num == -1) {
1371                        // this connection was closed,
1372                        // do clean up and create new one:
1373                        closeSocket(peerSocket);
1374                        peerSocket = serverSocket.accept();
1375                        peerSocket.setSoTimeout(TIMEOUT);
1376                        log("New client connection ACCEPTED");
1377                        is = peerSocket.getInputStream();
1378                        os = peerSocket.getOutputStream();
1379                        num = is.read(buff);
1380                    }
1381                    message = new String(buff, 0, num);
1382                    log("Got authenticated request:\n" + message);
1383                    log("------------------");
1384                    // check provided authorization credentials
1385                    assertTrue("Received message does not contain "
1386                            + "authorization credentials", message
1387                            .toLowerCase().indexOf("proxy-authorization:") > 0);
1388                }
1389
1390                // The content of this response will reach proxied HTTPUC
1391                // but will not reach proxied HTTPSUC
1392                // In case of HTTP connection it will be the final message,
1393                // in case of HTTPS connection this message will just indicate
1394                // that connection with remote host has been done
1395                // (i.e. SSL tunnel has been established).
1396                os.write(plainResponse.getBytes());
1397                log("Sent OK RESPONSE");
1398
1399                if (message.startsWith("CONNECT")) { // request for SSL tunnel
1400                    log("Perform SSL Handshake...");
1401                    // create sslSocket acting as a remote server peer
1402                    SSLSocket sslSocket = (SSLSocket) getContext()
1403                            .getSocketFactory().createSocket(peerSocket,
1404                                    "localhost", peerSocket.getPort(), true); // do autoclose
1405                    sslSocket.setUseClientMode(false);
1406                    // demand client authentication
1407                    sslSocket.setNeedClientAuth(true);
1408                    sslSocket.startHandshake();
1409                    peerSocket = sslSocket;
1410                    is = peerSocket.getInputStream();
1411                    os = peerSocket.getOutputStream();
1412
1413                    // read the HTTP request sent by secure connection
1414                    // (HTTPS request)
1415                    num = is.read(buff);
1416                    message = new String(buff, 0, num);
1417                    log("[Remote Server] Request from SSL tunnel:\n" + message);
1418                    log("------------------");
1419
1420                    if (message.startsWith("POST")) {
1421                        // client connection sent some data
1422                        log("[Remote Server] try to read client data");
1423                        num = is.read(buff);
1424                        message = new String(buff, 0, num);
1425                        log("[Remote Server] client's data: '" + message + "'");
1426                        // check the received data
1427                        assertEquals(clientsData, message);
1428                    }
1429
1430                    log("[Remote Server] Sending the response by SSL tunnel..");
1431                    // send the response with specified response code
1432                    os
1433                            .write(("HTTP/1.1 " + responseCode + "\n" + httpsResponseTail)
1434                                    .getBytes());
1435                }
1436                log("Work is DONE");
1437            } catch (Throwable e) {
1438                if (DO_LOG) {
1439                    e.printStackTrace();
1440                }
1441                thrown = e;
1442            } finally {
1443                closeSocket(peerSocket);
1444                try {
1445                    serverSocket.close();
1446                } catch (IOException e) {}
1447            }
1448        }
1449    }
1450
1451    /**
1452     * The class used for client side works. It could be used to test
1453     * both HttpURLConnection and HttpsURLConnection.
1454     */
1455    static class ClientConnectionWork extends Work {
1456
1457        // connection to be used to contact the server side
1458        private HttpURLConnection connection;
1459
1460        /**
1461         * Creates the thread acting as a client side.
1462         * @param connection connection to be used to contact the server side
1463         */
1464        public ClientConnectionWork(HttpURLConnection connection) {
1465            this.connection = connection;
1466            setName("Client Connection");
1467            log("Created over connection: " + connection.getClass());
1468        }
1469
1470        /**
1471         * Performs the actual client work.
1472         * If some exception occurs during the work it will be
1473         * stored in the <code>thrown<code> field.
1474         */
1475        public void run() {
1476            try {
1477                log("Opening the connection..");
1478                connection.connect();
1479                log("Connection has been ESTABLISHED, using proxy: "
1480                        + connection.usingProxy());
1481                if (connection.getDoOutput()) {
1482                    // connection configured to post data, do so
1483                    connection.getOutputStream().write(clientsData.getBytes());
1484                }
1485                // read the content of HTTP(s) response
1486                InputStream is = connection.getInputStream();
1487                log("Input Stream obtained");
1488                byte[] buff = new byte[2048];
1489                int num = 0;
1490                int byt = 0;
1491                while ((num < buff.length) && (is.available() > 0)
1492                        && ((byt = is.read()) != -1)) {
1493                    buff[num++] = (byte) byt;
1494                }
1495                String message = new String(buff, 0, num);
1496                log("Got content:\n" + message);
1497                log("------------------");
1498                log("Response code: " + connection.getResponseCode());
1499
1500                if (connection instanceof HttpsURLConnection) {
1501                    assertEquals(httpsResponseContent, message);
1502                } else {
1503                    assertEquals(plainResponseContent, message);
1504                }
1505            } catch (Throwable e) {
1506                if (DO_LOG) {
1507                    e.printStackTrace();
1508                }
1509                thrown = e;
1510            }
1511        }
1512    }
1513}
1514