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