1/*
2 * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/* @test
25 * @bug 4920526
26 * @summary Needs per connection proxy support for URLs
27 * @modules java.base/sun.net.www
28 * @library ../../../sun/net/www/httptest/
29 * @build ClosedChannelList TestHttpServer HttpTransaction HttpCallback
30 * @compile PerConnectionProxy.java
31 * @run main/othervm -Dhttp.proxyHost=inexistant -Dhttp.proxyPort=8080 PerConnectionProxy
32 */
33
34import java.net.*;
35import java.io.*;
36import sun.net.www.*;
37
38public class PerConnectionProxy implements HttpCallback {
39    static TestHttpServer server;
40
41    public void request (HttpTransaction req) {
42        req.setResponseEntityBody ("Hello .");
43        try {
44            req.sendResponse (200, "Ok");
45            req.orderlyClose();
46        } catch (IOException e) {
47        }
48    }
49
50    public static void main(String[] args) {
51        try {
52            server = new TestHttpServer (new PerConnectionProxy(), 1, 10, 0);
53            ProxyServer pserver = new ProxyServer(InetAddress.getByName("localhost"), server.getLocalPort());
54            // start proxy server
55            new Thread(pserver).start();
56
57            URL url = new URL("http://localhost:"+server.getLocalPort());
58
59            // for non existing proxy expect an IOException
60            try {
61                InetSocketAddress isa = InetSocketAddress.createUnresolved("inexistent", 8080);
62                Proxy proxy = new Proxy(Proxy.Type.HTTP, isa);
63                HttpURLConnection urlc = (HttpURLConnection)url.openConnection (proxy);
64                InputStream is = urlc.getInputStream ();
65                is.close();
66                throw new RuntimeException("non existing per connection proxy should lead to IOException");
67            } catch (IOException ioex) {
68                // expected
69            }
70
71            // for NO_PROXY, expect direct connection
72            try {
73                HttpURLConnection urlc = (HttpURLConnection)url.openConnection (Proxy.NO_PROXY);
74                int respCode = urlc.getResponseCode();
75                urlc.disconnect();
76            } catch (IOException ioex) {
77                throw new RuntimeException("direct connection should succeed :"+ioex.getMessage());
78            }
79
80            // for a normal proxy setting expect to see connection
81            // goes through that proxy
82            try {
83                InetSocketAddress isa = InetSocketAddress.createUnresolved("localhost", pserver.getPort());
84                Proxy p = new Proxy(Proxy.Type.HTTP, isa);
85                HttpURLConnection urlc = (HttpURLConnection)url.openConnection (p);
86                int respCode = urlc.getResponseCode();
87                urlc.disconnect();
88            } catch (IOException ioex) {
89                throw new RuntimeException("connection through a local proxy should succeed :"+ioex.getMessage());
90            }
91
92        } catch (Exception e) {
93            throw new RuntimeException(e);
94        } finally {
95            if (server != null) {
96                server.terminate();
97            }
98        }
99
100    }
101
102    static class ProxyServer extends Thread {
103        private static ServerSocket ss = null;
104
105        // client requesting for a tunnel
106        private Socket clientSocket = null;
107
108        /*
109         * Origin server's address and port that the client
110         * wants to establish the tunnel for communication.
111         */
112        private InetAddress serverInetAddr;
113        private int     serverPort;
114
115        public ProxyServer(InetAddress server, int port) throws IOException {
116            serverInetAddr = server;
117            serverPort = port;
118            ss = new ServerSocket(0);
119        }
120
121        public void run() {
122            try {
123                clientSocket = ss.accept();
124                processRequests();
125            } catch (Exception e) {
126                System.out.println("Proxy Failed: " + e);
127                e.printStackTrace();
128                try {
129                    ss.close();
130                }
131                catch (IOException excep) {
132                    System.out.println("ProxyServer close error: " + excep);
133                    excep.printStackTrace();
134                }
135            }
136        }
137
138        private void processRequests() throws Exception {
139            // connection set to the tunneling mode
140
141            Socket serverSocket = new Socket(serverInetAddr, serverPort);
142            ProxyTunnel clientToServer = new ProxyTunnel(
143                                                         clientSocket, serverSocket);
144            ProxyTunnel serverToClient = new ProxyTunnel(
145                                                         serverSocket, clientSocket);
146            clientToServer.start();
147            serverToClient.start();
148            System.out.println("Proxy: Started tunneling.......");
149
150            clientToServer.join();
151            serverToClient.join();
152            System.out.println("Proxy: Finished tunneling........");
153
154            clientToServer.close();
155            serverToClient.close();
156
157        }
158
159        /**
160***************************************************************
161*                       helper methods follow
162***************************************************************
163*/
164        public int getPort() {
165            return ss.getLocalPort();
166        }
167        /*
168         * This inner class provides unidirectional data flow through the sockets
169         * by continuously copying bytes from the input socket onto the output
170         * socket, until both sockets are open and EOF has not been received.
171         */
172        static class ProxyTunnel extends Thread {
173            Socket sockIn;
174            Socket sockOut;
175            InputStream input;
176            OutputStream output;
177
178            public ProxyTunnel(Socket sockIn, Socket sockOut)
179                throws Exception {
180                this.sockIn = sockIn;
181                this.sockOut = sockOut;
182                input = sockIn.getInputStream();
183                output = sockOut.getOutputStream();
184            }
185
186            public void run() {
187                int BUFFER_SIZE = 400;
188                byte[] buf = new byte[BUFFER_SIZE];
189                int bytesRead = 0;
190                int count = 0;  // keep track of the amount of data transfer
191
192                try {
193                    while ((bytesRead = input.read(buf)) >= 0) {
194                        output.write(buf, 0, bytesRead);
195                        output.flush();
196                        count += bytesRead;
197                    }
198                } catch (IOException e) {
199                    /*
200                     * The peer end has closed the connection
201                     * we will close the tunnel
202                     */
203                    close();
204                }
205            }
206
207            public void close() {
208                try {
209                    if (!sockIn.isClosed())
210                        sockIn.close();
211                    if (!sockOut.isClosed())
212                        sockOut.close();
213                } catch (IOException ignored) { }
214            }
215        }
216
217    }
218}
219