ProxyAuthTest.java revision 17113:d17577d4839b
1/*
2 * Copyright (c) 2016, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * @test
28 * @bug 8163561
29 * @modules java.base/sun.net.www
30 *          jdk.incubator.httpclient
31 * @summary Verify that Proxy-Authenticate header is correctly handled
32 *
33 * @run main/othervm ProxyAuthTest
34 */
35
36import java.io.BufferedWriter;
37import java.io.IOException;
38import java.io.InputStream;
39import java.io.OutputStream;
40import java.io.OutputStreamWriter;
41import java.io.PrintWriter;
42import java.net.Authenticator;
43import java.net.InetSocketAddress;
44import java.net.PasswordAuthentication;
45import java.net.ProxySelector;
46import java.net.ServerSocket;
47import java.net.Socket;
48import java.net.URI;
49import jdk.incubator.http.HttpClient;
50import jdk.incubator.http.HttpRequest;
51import jdk.incubator.http.HttpResponse;
52import java.util.Base64;
53import sun.net.www.MessageHeader;
54import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
55
56public class ProxyAuthTest {
57    private static final String AUTH_USER = "user";
58    private static final String AUTH_PASSWORD = "password";
59
60    public static void main(String[] args) throws Exception {
61        try (ServerSocket ss = new ServerSocket(0)) {
62            int port = ss.getLocalPort();
63            MyProxy proxy = new MyProxy(ss);
64            (new Thread(proxy)).start();
65            System.out.println("Proxy listening port " + port);
66
67            Auth auth = new Auth();
68            InetSocketAddress paddr = new InetSocketAddress("localhost", port);
69
70            URI uri = new URI("http://www.google.ie/");
71            HttpClient client = HttpClient.newBuilder()
72                                          .proxy(ProxySelector.of(paddr))
73                                          .authenticator(auth)
74                                          .build();
75            HttpRequest req = HttpRequest.newBuilder(uri).GET().build();
76            HttpResponse<?> resp = client.sendAsync(req, discard(null)).get();
77            if (resp.statusCode() != 404) {
78                throw new RuntimeException("Unexpected status code: " + resp.statusCode());
79            }
80
81            if (auth.isCalled) {
82                System.out.println("Authenticator is called");
83            } else {
84                throw new RuntimeException("Authenticator is not called");
85            }
86
87            if (!proxy.matched) {
88                throw new RuntimeException("Proxy authentication failed");
89            }
90        }
91    }
92
93    static class Auth extends Authenticator {
94        private volatile boolean isCalled;
95
96        @Override
97        protected PasswordAuthentication getPasswordAuthentication() {
98            System.out.println("scheme: " + this.getRequestingScheme());
99            isCalled = true;
100            return new PasswordAuthentication(AUTH_USER,
101                    AUTH_PASSWORD.toCharArray());
102        }
103    }
104
105    static class MyProxy implements Runnable {
106        final ServerSocket ss;
107        private volatile boolean matched;
108
109        MyProxy(ServerSocket ss) {
110            this.ss = ss;
111        }
112
113        public void run() {
114            for (int i = 0; i < 2; i++) {
115                try (Socket s = ss.accept();
116                     InputStream in = s.getInputStream();
117                     OutputStream os = s.getOutputStream();
118                     BufferedWriter writer = new BufferedWriter(
119                             new OutputStreamWriter(os));
120                     PrintWriter out = new PrintWriter(writer);) {
121                    MessageHeader headers = new MessageHeader(in);
122                    System.out.println("Proxy: received " + headers);
123
124                    String authInfo = headers.findValue("Proxy-Authorization");
125                    if (authInfo != null) {
126                        authenticate(authInfo);
127                        out.print("HTTP/1.1 404 Not found\r\n");
128                        out.print("\r\n");
129                        System.out.println("Proxy: 404");
130                        out.flush();
131                    } else {
132                        out.print("HTTP/1.1 407 Proxy Authorization Required\r\n");
133                        out.print(
134                                "Proxy-Authenticate: Basic realm=\"a fake realm\"\r\n");
135                        out.print("\r\n");
136                        System.out.println("Proxy: Authorization required");
137                        out.flush();
138                    }
139                } catch (IOException x) {
140                    System.err.println("Unexpected IOException from proxy.");
141                    x.printStackTrace();
142                    break;
143                }
144            }
145        }
146
147        private void authenticate(String authInfo) throws IOException {
148            try {
149                authInfo.trim();
150                int ind = authInfo.indexOf(' ');
151                String recvdUserPlusPass = authInfo.substring(ind + 1).trim();
152                // extract encoded username:passwd
153                String value = new String(
154                        Base64.getMimeDecoder().decode(recvdUserPlusPass));
155                String userPlusPassword = AUTH_USER + ":" + AUTH_PASSWORD;
156                if (userPlusPassword.equals(value)) {
157                    matched = true;
158                    System.out.println("Proxy: client authentication successful");
159                } else {
160                    System.err.println(
161                            "Proxy: client authentication failed, expected ["
162                                    + userPlusPassword + "], actual [" + value
163                                    + "]");
164                }
165            } catch (Exception e) {
166                throw new IOException(
167                        "Proxy received invalid Proxy-Authorization value: "
168                                + authInfo);
169            }
170        }
171    }
172
173}
174