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