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