Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/sun/net/www/http/HttpClient/B8025710.java
38868 views
1
/*
2
* Copyright (c) 2014, 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
import java.io.*;
25
import java.net.*;
26
import java.security.*;
27
import java.security.cert.X509Certificate;
28
import java.util.ArrayList;
29
import java.util.concurrent.atomic.AtomicBoolean;
30
import java.util.regex.Matcher;
31
import java.util.regex.Pattern;
32
import javax.net.ServerSocketFactory;
33
import javax.net.SocketFactory;
34
import javax.net.ssl.*;
35
36
/**
37
* @test
38
* @bug 8025710
39
* @summary Proxied https connection reuse by HttpClient can send CONNECT to the server
40
* @run main/othervm B8025710
41
*/
42
public class B8025710 {
43
44
private final static AtomicBoolean connectInServer = new AtomicBoolean();
45
private static final String keystorefile =
46
System.getProperty("test.src", "./")
47
+ "/../../../../../javax/net/ssl/etc/keystore";
48
private static final String passphrase = "passphrase";
49
50
public static void main(String[] args) throws Exception {
51
// test uses legacy MD5 based cert
52
Security.setProperty("jdk.certpath.disabledAlgorithms", "");
53
Security.setProperty("jdk.tls.disabledAlgorithms", "");
54
new B8025710().runTest();
55
56
if (connectInServer.get())
57
throw new RuntimeException("TEST FAILED: server got proxy header");
58
else
59
System.out.println("TEST PASSED");
60
}
61
62
private void runTest() throws Exception {
63
ProxyServer proxyServer = new ProxyServer();
64
HttpServer httpServer = new HttpServer();
65
httpServer.start();
66
proxyServer.start();
67
68
URL url = new URL("https", InetAddress.getLocalHost().getHostName(),
69
httpServer.getPort(), "/");
70
71
Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyServer.getAddress());
72
73
HttpsURLConnection.setDefaultSSLSocketFactory(createTestSSLSocketFactory());
74
75
// Make two connections. The bug occurs when the second request is made
76
for (int i = 0; i < 2; i++) {
77
System.out.println("Client: Requesting " + url.toExternalForm()
78
+ " via " + proxy.toString()
79
+ " (attempt " + (i + 1) + " of 2)");
80
81
HttpsURLConnection connection =
82
(HttpsURLConnection) url.openConnection(proxy);
83
84
connection.setRequestMethod("POST");
85
connection.setDoInput(true);
86
connection.setDoOutput(true);
87
connection.setRequestProperty("User-Agent", "Test/1.0");
88
connection.getOutputStream().write("Hello, world!".getBytes("UTF-8"));
89
90
if (connection.getResponseCode() != 200) {
91
System.err.println("Client: Unexpected response code "
92
+ connection.getResponseCode());
93
break;
94
}
95
96
String response = readLine(connection.getInputStream());
97
if (!"Hi!".equals(response)) {
98
System.err.println("Client: Unexpected response body: "
99
+ response);
100
}
101
}
102
httpServer.close();
103
proxyServer.close();
104
httpServer.join();
105
proxyServer.join();
106
}
107
108
class ProxyServer extends Thread implements Closeable {
109
110
private final ServerSocket proxySocket;
111
private final Pattern connectLinePattern =
112
Pattern.compile("^CONNECT ([^: ]+):([0-9]+) HTTP/[0-9.]+$");
113
private final String PROXY_RESPONSE =
114
"HTTP/1.0 200 Connection Established\r\n"
115
+ "Proxy-Agent: TestProxy/1.0\r\n"
116
+ "\r\n";
117
118
ProxyServer() throws Exception {
119
super("ProxyServer Thread");
120
121
// Create the http proxy server socket
122
proxySocket = ServerSocketFactory.getDefault().createServerSocket();
123
proxySocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0));
124
}
125
126
public SocketAddress getAddress() { return proxySocket.getLocalSocketAddress(); }
127
128
@Override
129
public void close() throws IOException {
130
proxySocket.close();
131
}
132
133
@Override
134
public void run() {
135
ArrayList<Thread> threads = new ArrayList<>();
136
int connectionCount = 0;
137
try {
138
while (connectionCount++ < 2) {
139
final Socket clientSocket = proxySocket.accept();
140
final int proxyConnectionCount = connectionCount;
141
System.out.println("Proxy: NEW CONNECTION "
142
+ proxyConnectionCount);
143
144
Thread t = new Thread("ProxySocket" + proxyConnectionCount) {
145
@Override
146
public void run() {
147
try {
148
String firstLine =
149
readHeader(clientSocket.getInputStream());
150
151
Matcher connectLineMatcher =
152
connectLinePattern.matcher(firstLine);
153
if (!connectLineMatcher.matches()) {
154
System.out.println("Proxy: Unexpected"
155
+ " request to the proxy: "
156
+ firstLine);
157
return;
158
}
159
160
String host = connectLineMatcher.group(1);
161
String portStr = connectLineMatcher.group(2);
162
int port = Integer.parseInt(portStr);
163
164
Socket serverSocket = SocketFactory.getDefault()
165
.createSocket(host, port);
166
167
clientSocket.getOutputStream()
168
.write(PROXY_RESPONSE.getBytes("UTF-8"));
169
170
ProxyTunnel copyToClient =
171
new ProxyTunnel(serverSocket, clientSocket);
172
ProxyTunnel copyToServer =
173
new ProxyTunnel(clientSocket, serverSocket);
174
175
copyToClient.start();
176
copyToServer.start();
177
178
copyToClient.join();
179
// here copyToClient.close() would not provoke the
180
// bug ( since it would trigger the retry logic in
181
// HttpURLConnction.writeRequests ), so close only
182
// the output to get the connection in this state.
183
clientSocket.shutdownOutput();
184
185
try {
186
Thread.sleep(3000);
187
} catch (InterruptedException ignored) { }
188
189
// now close all connections to finish the test
190
copyToServer.close();
191
copyToClient.close();
192
} catch (IOException | NumberFormatException
193
| InterruptedException e) {
194
e.printStackTrace();
195
}
196
}
197
};
198
threads.add(t);
199
t.start();
200
}
201
for (Thread t: threads)
202
t.join();
203
} catch (IOException | InterruptedException e) {
204
e.printStackTrace();
205
}
206
}
207
}
208
209
/**
210
* This inner class provides unidirectional data flow through the sockets
211
* by continuously copying bytes from the input socket onto the output
212
* socket, until both sockets are open and EOF has not been received.
213
*/
214
class ProxyTunnel extends Thread {
215
private final Socket sockIn;
216
private final Socket sockOut;
217
private final InputStream input;
218
private final OutputStream output;
219
220
public ProxyTunnel(Socket sockIn, Socket sockOut) throws IOException {
221
super("ProxyTunnel");
222
this.sockIn = sockIn;
223
this.sockOut = sockOut;
224
input = sockIn.getInputStream();
225
output = sockOut.getOutputStream();
226
}
227
228
public void run() {
229
byte[] buf = new byte[8192];
230
int bytesRead;
231
232
try {
233
while ((bytesRead = input.read(buf)) >= 0) {
234
output.write(buf, 0, bytesRead);
235
output.flush();
236
}
237
} catch (IOException ignored) {
238
close();
239
}
240
}
241
242
public void close() {
243
try {
244
if (!sockIn.isClosed())
245
sockIn.close();
246
if (!sockOut.isClosed())
247
sockOut.close();
248
} catch (IOException ignored) { }
249
}
250
}
251
252
/**
253
* the server thread
254
*/
255
class HttpServer extends Thread implements Closeable {
256
257
private final ServerSocket serverSocket;
258
private final SSLSocketFactory sslSocketFactory;
259
private final String serverResponse =
260
"HTTP/1.1 200 OK\r\n"
261
+ "Content-Type: text/plain\r\n"
262
+ "Content-Length: 3\r\n"
263
+ "\r\n"
264
+ "Hi!";
265
private int connectionCount = 0;
266
267
HttpServer() throws Exception {
268
super("HttpServer Thread");
269
270
KeyStore ks = KeyStore.getInstance("JKS");
271
ks.load(new FileInputStream(keystorefile), passphrase.toCharArray());
272
KeyManagerFactory factory = KeyManagerFactory.getInstance("SunX509");
273
factory.init(ks, passphrase.toCharArray());
274
SSLContext ctx = SSLContext.getInstance("TLS");
275
ctx.init(factory.getKeyManagers(), null, null);
276
277
sslSocketFactory = ctx.getSocketFactory();
278
279
// Create the server that the test wants to connect to via the proxy
280
serverSocket = ServerSocketFactory.getDefault().createServerSocket();
281
serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0));
282
}
283
284
public int getPort() { return serverSocket.getLocalPort(); }
285
286
@Override
287
public void close() throws IOException { serverSocket.close(); }
288
289
@Override
290
public void run() {
291
try {
292
while (connectionCount++ < 2) {
293
Socket socket = serverSocket.accept();
294
System.out.println("Server: NEW CONNECTION "
295
+ connectionCount);
296
297
SSLSocket sslSocket = (SSLSocket) sslSocketFactory
298
.createSocket(socket,null, getPort(), false);
299
sslSocket.setUseClientMode(false);
300
sslSocket.startHandshake();
301
302
String firstLine = readHeader(sslSocket.getInputStream());
303
if (firstLine != null && firstLine.contains("CONNECT")) {
304
System.out.println("Server: BUG! HTTP CONNECT"
305
+ " encountered: " + firstLine);
306
connectInServer.set(true);
307
}
308
309
// write the success response, the request body is not read.
310
// close only output and keep input open.
311
OutputStream out = sslSocket.getOutputStream();
312
out.write(serverResponse.getBytes("UTF-8"));
313
socket.shutdownOutput();
314
}
315
} catch (IOException e) {
316
e.printStackTrace();
317
}
318
}
319
}
320
321
/**
322
* read the header and return only the first line.
323
*
324
* @param inputStream the stream to read from
325
* @return the first line of the stream
326
* @throws IOException if reading failed
327
*/
328
private static String readHeader(InputStream inputStream)
329
throws IOException {
330
String line;
331
String firstLine = null;
332
while ((line = readLine(inputStream)) != null && line.length() > 0) {
333
if (firstLine == null) {
334
firstLine = line;
335
}
336
}
337
338
return firstLine;
339
}
340
341
/**
342
* read a line from stream.
343
*
344
* @param inputStream the stream to read from
345
* @return the line
346
* @throws IOException if reading failed
347
*/
348
private static String readLine(InputStream inputStream)
349
throws IOException {
350
final StringBuilder line = new StringBuilder();
351
int ch;
352
while ((ch = inputStream.read()) != -1) {
353
if (ch == '\r') {
354
continue;
355
}
356
357
if (ch == '\n') {
358
break;
359
}
360
361
line.append((char) ch);
362
}
363
364
return line.toString();
365
}
366
367
private SSLSocketFactory createTestSSLSocketFactory() {
368
369
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
370
@Override
371
public boolean verify(String hostname, SSLSession sslSession) {
372
// ignore the cert's CN; it's not important to this test
373
return true;
374
}
375
});
376
377
// Set up the socket factory to use a trust manager that trusts all
378
// certs, since trust validation isn't important to this test
379
final TrustManager[] trustAllCertChains = new TrustManager[] {
380
new X509TrustManager() {
381
@Override
382
public X509Certificate[] getAcceptedIssuers() {
383
return null;
384
}
385
386
@Override
387
public void checkClientTrusted(X509Certificate[] certs,
388
String authType) {
389
}
390
391
@Override
392
public void checkServerTrusted(X509Certificate[] certs,
393
String authType) {
394
}
395
}
396
};
397
398
final SSLContext sc;
399
try {
400
sc = SSLContext.getInstance("TLS");
401
} catch (NoSuchAlgorithmException e) {
402
throw new RuntimeException(e);
403
}
404
405
try {
406
sc.init(null, trustAllCertChains, new java.security.SecureRandom());
407
} catch (KeyManagementException e) {
408
throw new RuntimeException(e);
409
}
410
411
return sc.getSocketFactory();
412
}
413
}
414
415