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.http;
19
20import dalvik.annotation.SideEffect;
21
22import java.io.IOException;
23import java.net.Authenticator;
24import java.net.HttpURLConnection;
25import java.net.InetSocketAddress;
26import java.net.PasswordAuthentication;
27import java.net.Proxy;
28import java.net.ProxySelector;
29import java.net.ServerSocket;
30import java.net.Socket;
31import java.net.SocketAddress;
32import java.net.SocketTimeoutException;
33import java.net.URI;
34import java.net.URL;
35import java.util.ArrayList;
36
37import junit.framework.TestCase;
38
39
40/**
41 * Tests for <code>HTTPURLConnection</code> class constructors and methods.
42 *
43 */
44public class HttpURLConnectionTest extends TestCase {
45
46    private final static Object bound = new Object();
47
48    static class MockServer extends Thread {
49        ServerSocket serverSocket;
50        boolean accepted = false;
51        boolean started = false;
52
53        public MockServer(String name) throws IOException {
54            super(name);
55            serverSocket = new ServerSocket(0);
56            serverSocket.setSoTimeout(1000);
57        }
58
59        public int port() {
60            return serverSocket.getLocalPort();
61        }
62
63        @Override
64        public void run() {
65            try {
66                synchronized (bound) {
67                    started = true;
68                    bound.notify();
69                }
70                try {
71                    serverSocket.accept().close();
72                    accepted = true;
73                } catch (SocketTimeoutException ignore) {
74                }
75                serverSocket.close();
76            } catch (IOException e) {
77                throw new RuntimeException(e);
78            }
79        }
80    }
81
82    static class MockProxyServer extends MockServer {
83
84        boolean acceptedAuthorizedRequest;
85
86        public MockProxyServer(String name) throws Exception {
87            super(name);
88        }
89
90        @Override
91        public void run() {
92            try {
93                Socket socket = serverSocket.accept();
94                socket.setSoTimeout(1000);
95                byte[] buff = new byte[1024];
96                int num = socket.getInputStream().read(buff);
97                socket.getOutputStream().write((
98                    "HTTP/1.0 407 Proxy authentication required\n"
99                  + "Proxy-authenticate: Basic realm=\"remotehost\"\n\n")
100                        .getBytes());
101                num = socket.getInputStream().read(buff);
102                if (num == -1) {
103                    // this connection was closed, create new one:
104                    socket = serverSocket.accept();
105                    socket.setSoTimeout(1000);
106                    num = socket.getInputStream().read(buff);
107                }
108                String request = new String(buff, 0, num);
109                acceptedAuthorizedRequest =
110                    request.toLowerCase().indexOf("proxy-authorization:") > 0;
111                if (acceptedAuthorizedRequest) {
112                    socket.getOutputStream().write((
113                            "HTTP/1.1 200 OK\n\n").getBytes());
114                }
115            } catch (IOException e) {
116            }
117        }
118    }
119
120    /**
121     * ProxySelector implementation used in the test.
122     */
123    static class TestProxySelector extends ProxySelector {
124        // proxy port
125        private int proxy_port;
126        // server port
127        private int server_port;
128
129        /**
130         * Creates proxy selector instance.
131         * Selector will return the proxy, only if the connection
132         * is made to localhost:server_port. Otherwise it will
133         * return NO_PROXY.
134         * Address of the returned proxy will be localhost:proxy_port.
135         */
136        public TestProxySelector(int server_port, int proxy_port) {
137            this.server_port = server_port;
138            this.proxy_port = proxy_port;
139        }
140
141        @Override
142        public java.util.List<Proxy> select(URI uri) {
143            Proxy proxy = Proxy.NO_PROXY;
144            if (("localhost".equals(uri.getHost()))
145                    && (server_port == uri.getPort())) {
146                proxy = new Proxy(Proxy.Type.HTTP,
147                            new InetSocketAddress("localhost", proxy_port));
148            }
149            ArrayList<Proxy> result = new ArrayList<Proxy>();
150            result.add(proxy);
151            return result;
152        }
153
154        @Override
155        public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
156            // do nothing
157        }
158    }
159
160    /**
161     * org.apache.harmony.luni.internal.net.www.http.getOutputStream()
162     */
163    public void testGetOutputStream() throws Exception {
164        // Regression for HARMONY-482
165        MockServer httpServer =
166            new MockServer("ServerSocket for HttpURLConnectionTest");
167        httpServer.start();
168        synchronized(bound) {
169            if (!httpServer.started) {
170                bound.wait(5000);
171            }
172        }
173        HttpURLConnection c = (HttpURLConnection)
174            new URL("http://localhost:" + httpServer.port()).openConnection();
175        c.setDoOutput(true);
176        //use new String("POST") instead of simple "POST" to obtain other
177        //object instances then those that are in HttpURLConnection classes
178        c.setRequestMethod(new String("POST"));
179        c.getOutputStream();
180        httpServer.join();
181    }
182
183
184    /**
185     * Test checks if the proxy specified in openConnection
186     * method will be used for connection to the server
187     */
188    public void testUsingProxy() throws Exception {
189        // Regression for HARMONY-570
190        MockServer server = new MockServer("server");
191        MockServer proxy = new MockServer("proxy");
192
193        URL url = new URL("http://localhost:" + server.port());
194
195        HttpURLConnection connection = (HttpURLConnection) url
196                .openConnection(new Proxy(Proxy.Type.HTTP,
197                        new InetSocketAddress("localhost",
198                            proxy.port())));
199        connection.setConnectTimeout(2000);
200        connection.setReadTimeout(2000);
201
202        server.start();
203        synchronized(bound) {
204            if (!server.started) bound.wait(5000);
205        }
206        proxy.start();
207        synchronized(bound) {
208            if (!proxy.started) bound.wait(5000);
209        }
210
211        connection.connect();
212
213        // wait while server and proxy run
214        server.join();
215        proxy.join();
216
217        assertTrue("Connection does not use proxy", connection.usingProxy());
218        assertTrue("Proxy server was not used", proxy.accepted);
219
220        HttpURLConnection huc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
221        assertFalse(huc.usingProxy());
222    }
223
224    /**
225     * Test checks if the proxy provided by proxy selector
226     * will be used for connection to the server
227     */
228    public void testUsingProxySelector() throws Exception {
229        // Regression for HARMONY-570
230        MockServer server = new MockServer("server");
231        MockServer proxy = new MockServer("proxy");
232
233        URL url = new URL("http://localhost:" + server.port());
234
235        // keep default proxy selector
236        ProxySelector defPS = ProxySelector.getDefault();
237        // replace selector
238        ProxySelector.setDefault(
239                new TestProxySelector(server.port(), proxy.port()));
240
241        try {
242            HttpURLConnection connection =
243                (HttpURLConnection) url.openConnection();
244            connection.setConnectTimeout(2000);
245            connection.setReadTimeout(2000);
246
247            server.start();
248            synchronized(bound) {
249                if (!server.started) bound.wait(5000);
250            }
251            proxy.start();
252            synchronized(bound) {
253                if (!proxy.started) bound.wait(5000);
254            }
255            connection.connect();
256
257            // wait while server and proxy run
258            server.join();
259            proxy.join();
260
261            assertTrue("Connection does not use proxy",
262                                            connection.usingProxy());
263            assertTrue("Proxy server was not used", proxy.accepted);
264        } finally {
265            // restore default proxy selector
266            ProxySelector.setDefault(defPS);
267        }
268    }
269    @SideEffect("Suffers from side effect of other, currently unknown test")
270    public void testProxyAuthorization() throws Exception {
271        // Set up test Authenticator
272        Authenticator.setDefault(new Authenticator() {
273            @Override
274            protected PasswordAuthentication getPasswordAuthentication() {
275                return new PasswordAuthentication(
276                    "user", "password".toCharArray());
277            }
278        });
279
280        try {
281            MockProxyServer proxy = new MockProxyServer("ProxyServer");
282
283            URL url = new URL("http://remotehost:55555/requested.data");
284            HttpURLConnection connection =
285                (HttpURLConnection) url.openConnection(
286                        new Proxy(Proxy.Type.HTTP,
287                            new InetSocketAddress("localhost", proxy.port())));
288            connection.setConnectTimeout(1000);
289            connection.setReadTimeout(1000);
290
291            proxy.start();
292
293            connection.connect();
294            assertEquals("unexpected response code",
295                    200, connection.getResponseCode());
296            proxy.join();
297            assertTrue("Connection did not send proxy authorization request",
298                    proxy.acceptedAuthorizedRequest);
299        } finally {
300            // remove previously set authenticator
301            Authenticator.setDefault(null);
302        }
303    }
304
305}
306