undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
143
test/jdk/java/net/HttpURLConnection/HostHeaderTest.java
Normal file
143
test/jdk/java/net/HttpURLConnection/HostHeaderTest.java
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.List;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8359709
|
||||
* @summary verify that if the Host header is allowed to be set by the application
|
||||
* then the correct value gets set in a HTTP request issued through
|
||||
* java.net.HttpURLConnection
|
||||
* @library /test/lib
|
||||
* @run junit HostHeaderTest
|
||||
* @run junit/othervm -Dsun.net.http.allowRestrictedHeaders=true HostHeaderTest
|
||||
* @run junit/othervm -Dsun.net.http.allowRestrictedHeaders=false HostHeaderTest
|
||||
*/
|
||||
class HostHeaderTest {
|
||||
|
||||
private static final String SERVER_CTX_ROOT = "/8359709/";
|
||||
private static final boolean allowsHostHeader = Boolean.getBoolean("sun.net.http.allowRestrictedHeaders");
|
||||
|
||||
private static HttpServer server;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() throws Exception {
|
||||
final InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
||||
server = HttpServer.create(addr, 0);
|
||||
server.createContext(SERVER_CTX_ROOT, new Handler());
|
||||
server.start();
|
||||
System.err.println("started server at " + server.getAddress());
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void afterAll() throws Exception {
|
||||
if (server != null) {
|
||||
System.err.println("stopping server " + server.getAddress());
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHostHeader() throws Exception {
|
||||
final InetSocketAddress serverAddr = server.getAddress();
|
||||
final URL reqURL = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(serverAddr.getPort())
|
||||
.path(SERVER_CTX_ROOT)
|
||||
.build().toURL();
|
||||
final URLConnection conn = reqURL.openConnection(Proxy.NO_PROXY);
|
||||
|
||||
conn.setRequestProperty("Host", "foobar");
|
||||
if (!allowsHostHeader) {
|
||||
// if restricted headers aren't allowed to be set by the user, then
|
||||
// we expect the previous call to setRequestProperty to not set the Host
|
||||
// header
|
||||
assertNull(conn.getRequestProperty("Host"), "Host header unexpectedly set");
|
||||
}
|
||||
|
||||
assertInstanceOf(HttpURLConnection.class, conn);
|
||||
final HttpURLConnection httpURLConn = (HttpURLConnection) conn;
|
||||
|
||||
// send the HTTP request
|
||||
System.err.println("sending request " + reqURL);
|
||||
final int respCode = httpURLConn.getResponseCode();
|
||||
assertEquals(200, respCode, "unexpected response code");
|
||||
// verify that the server side handler received the expected
|
||||
// Host header value in the request
|
||||
try (final InputStream is = httpURLConn.getInputStream()) {
|
||||
final byte[] resp = is.readAllBytes();
|
||||
// if Host header wasn't explicitly set, then we expect it to be
|
||||
// derived from the request URL
|
||||
final String expected = allowsHostHeader
|
||||
? "foobar"
|
||||
: reqURL.getHost() + ":" + reqURL.getPort();
|
||||
final String actual = new String(resp, US_ASCII);
|
||||
assertEquals(expected, actual, "unexpected Host header received on server side");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Handler implements HttpHandler {
|
||||
private static final int NO_RESPONSE_BODY = -1;
|
||||
|
||||
@Override
|
||||
public void handle(final HttpExchange exchange) throws IOException {
|
||||
final List<String> headerVals = exchange.getRequestHeaders().get("Host");
|
||||
System.err.println("Host header has value(s): " + headerVals);
|
||||
// unexpected Host header value, respond with 400 status code
|
||||
if (headerVals == null || headerVals.size() != 1) {
|
||||
System.err.println("Unexpected header value(s) for Host header: " + headerVals);
|
||||
exchange.sendResponseHeaders(400, NO_RESPONSE_BODY);
|
||||
return;
|
||||
}
|
||||
// respond back with the Host header value that we found in the request
|
||||
final byte[] response = headerVals.getFirst().getBytes(US_ASCII);
|
||||
exchange.sendResponseHeaders(200, response.length);
|
||||
try (final OutputStream os = exchange.getResponseBody()) {
|
||||
os.write(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
test/jdk/java/net/HttpURLConnection/HttpResponseCode.java
Normal file
92
test/jdk/java/net/HttpURLConnection/HttpResponseCode.java
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 4473092
|
||||
* @library /test/lib
|
||||
* @summary Method throws IOException when object should be returned
|
||||
* @run main HttpResponseCode
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true HttpResponseCode
|
||||
*/
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class HttpResponseCode implements Runnable {
|
||||
ServerSocket ss;
|
||||
/*
|
||||
* Our "http" server
|
||||
*/
|
||||
public void run() {
|
||||
try {
|
||||
Socket s = ss.accept();
|
||||
|
||||
BufferedReader in = new BufferedReader(
|
||||
new InputStreamReader(s.getInputStream()) );
|
||||
String req = in.readLine();
|
||||
|
||||
PrintStream out = new PrintStream(
|
||||
new BufferedOutputStream(
|
||||
s.getOutputStream() ));
|
||||
|
||||
|
||||
/* send the header */
|
||||
out.print("HTTP/1.1 403 Forbidden\r\n");
|
||||
out.print("\r\n");
|
||||
out.flush();
|
||||
|
||||
s.close();
|
||||
ss.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
HttpResponseCode() throws Exception {
|
||||
/* start the server */
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
ss = new ServerSocket();
|
||||
ss.bind(new InetSocketAddress(loopback, 0));
|
||||
(new Thread(this)).start();
|
||||
|
||||
/* establish http connection to server */
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(ss.getLocalPort())
|
||||
.path("/missing.nothtml")
|
||||
.toURL();
|
||||
URLConnection uc = url.openConnection(Proxy.NO_PROXY);
|
||||
int respCode1 = ((HttpURLConnection)uc).getResponseCode();
|
||||
((HttpURLConnection)uc).disconnect();
|
||||
int respCode2 = ((HttpURLConnection)uc).getResponseCode();
|
||||
if (respCode1 != 403 || respCode2 != 403) {
|
||||
throw new RuntimeException("Testing Http response code failed");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
new HttpResponseCode();
|
||||
}
|
||||
}
|
||||
170
test/jdk/java/net/HttpURLConnection/HttpURLConWithProxy.java
Normal file
170
test/jdk/java/net/HttpURLConnection/HttpURLConWithProxy.java
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8161016 8183369
|
||||
* @library /test/lib
|
||||
* @summary When proxy is set HttpURLConnection should not use DIRECT connection.
|
||||
* @run main/othervm HttpURLConWithProxy
|
||||
*/
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
public class HttpURLConWithProxy {
|
||||
|
||||
private static Logger logger =
|
||||
Logger.getLogger("sun.net.www.protocol.http.HttpURLConnection");
|
||||
|
||||
public static void main(String... arg) throws Exception {
|
||||
// Remove the default nonProxyHosts to use localhost for testing
|
||||
System.setProperty("http.nonProxyHosts", "");
|
||||
|
||||
// 240.0.0.0/4 is unallocated and "reserved for future use" (RFC 1112, Section 4)
|
||||
System.setProperty("http.proxyHost", "240.0.0.1");
|
||||
System.setProperty("http.proxyPort", "1111");
|
||||
|
||||
// Use the logger to help verify the Proxy was used
|
||||
logger.setLevel(Level.ALL);
|
||||
Handler h = new ProxyHandler();
|
||||
h.setLevel(Level.ALL);
|
||||
logger.addHandler(h);
|
||||
|
||||
ServerSocket ss;
|
||||
URL url;
|
||||
HttpURLConnection con;
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress address = new InetSocketAddress(loopback, 0);
|
||||
|
||||
// Test1: using Proxy set by System Property:
|
||||
try {
|
||||
ss = new ServerSocket();
|
||||
ss.bind(address);
|
||||
url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(ss.getLocalPort())
|
||||
.toURL();
|
||||
con = (HttpURLConnection) url.openConnection();
|
||||
con.setConnectTimeout(10 * 1000);
|
||||
con.connect();
|
||||
if(con.usingProxy()){
|
||||
System.out.println("Test1 Passed with: Connection succeeded with proxy");
|
||||
} else {
|
||||
throw new RuntimeException("Shouldn't use DIRECT connection "
|
||||
+ "when proxy is invalid/down");
|
||||
}
|
||||
} catch (IOException ie) {
|
||||
if(!ProxyHandler.proxyRetried) {
|
||||
throw new RuntimeException("Connection not retried with proxy");
|
||||
}
|
||||
System.out.println("Test1 Passed with: " + ie.getMessage());
|
||||
}
|
||||
|
||||
// Test2: using custom ProxySelector implementation
|
||||
ProxyHandler.proxyRetried = false;
|
||||
MyProxySelector myProxySel = new MyProxySelector();
|
||||
ProxySelector.setDefault(myProxySel);
|
||||
try {
|
||||
ss = new ServerSocket();
|
||||
ss.bind(address);
|
||||
url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(ss.getLocalPort())
|
||||
.toURL();
|
||||
con = (HttpURLConnection) url.openConnection();
|
||||
con.setConnectTimeout(10 * 1000);
|
||||
con.connect();
|
||||
if(con.usingProxy()){
|
||||
System.out.println("Test2 Passed with: Connection succeeded with proxy");
|
||||
} else {
|
||||
throw new RuntimeException("Shouldn't use DIRECT connection "
|
||||
+ "when proxy is invalid/down");
|
||||
}
|
||||
} catch (IOException ie) {
|
||||
if(!ProxyHandler.proxyRetried) {
|
||||
throw new RuntimeException("Connection not retried with proxy");
|
||||
}
|
||||
System.out.println("Test2 Passed with: " + ie.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MyProxySelector extends ProxySelector {
|
||||
|
||||
List<Proxy> proxies = new ArrayList<>();
|
||||
|
||||
MyProxySelector() {
|
||||
// 240.0.0.0/4 is unallocated and "reserved for future use" (RFC 1112, Section 4)
|
||||
Proxy p1 = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("240.0.0.2", 2222));
|
||||
Proxy p2 = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("240.0.0.3", 3333));
|
||||
proxies.add(p1);
|
||||
proxies.add(p2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Proxy> select(URI uri) {
|
||||
return proxies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
|
||||
// System.out.println("MyProxySelector.connectFailed(): "+sa);
|
||||
}
|
||||
}
|
||||
|
||||
class ProxyHandler extends Handler {
|
||||
public static boolean proxyRetried = false;
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
if (record.getMessage().contains("Retrying with proxy")) {
|
||||
proxyRetried = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
335
test/jdk/java/net/HttpURLConnection/HttpURLConnUsingProxy.java
Normal file
335
test/jdk/java/net/HttpURLConnection/HttpURLConnUsingProxy.java
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 8231632
|
||||
* @summary HttpURLConnection::usingProxy could specify that it lazily evaluates the fact
|
||||
* @modules java.base/sun.net.www
|
||||
* @library /test/lib
|
||||
* @run main/othervm HttpURLConnUsingProxy
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class HttpURLConnUsingProxy {
|
||||
static HttpServer server;
|
||||
static Proxy proxy;
|
||||
static InetSocketAddress isa;
|
||||
|
||||
static class Handler implements HttpHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
byte[] response = "Hello World!".getBytes(StandardCharsets.UTF_8);
|
||||
try (InputStream req = exchange.getRequestBody()) {
|
||||
req.readAllBytes();
|
||||
}
|
||||
exchange.sendResponseHeaders(200, response.length);
|
||||
try (OutputStream resp = exchange.getResponseBody()) {
|
||||
resp.write(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress addr = new InetSocketAddress(loopbackAddress, 0);
|
||||
server = HttpServer.create(addr, 0);
|
||||
server.createContext("/HttpURLConnUsingProxy/http1/", new Handler());
|
||||
server.start();
|
||||
|
||||
ProxyServer pserver = new ProxyServer(loopbackAddress,
|
||||
server.getAddress().getPort());
|
||||
// Start proxy server
|
||||
new Thread(pserver).start();
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(server.getAddress().getPort())
|
||||
.path("/HttpURLConnUsingProxy/http1/x.html")
|
||||
.toURLUnchecked();
|
||||
|
||||
// NO_PROXY
|
||||
try {
|
||||
HttpURLConnection urlc =
|
||||
(HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
|
||||
assertEqual(urlc.usingProxy(), false);
|
||||
urlc.getResponseCode();
|
||||
assertEqual(urlc.usingProxy(), false);
|
||||
urlc.disconnect();
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Direct connection should succeed: "
|
||||
+ ioe.getMessage());
|
||||
}
|
||||
|
||||
// Non-existing proxy
|
||||
try {
|
||||
isa = InetSocketAddress.createUnresolved("inexistent", 8080);
|
||||
proxy = new Proxy(Proxy.Type.HTTP, isa);
|
||||
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
InputStream is = urlc.getInputStream();
|
||||
is.close();
|
||||
throw new RuntimeException("Non-existing proxy should cause IOException");
|
||||
} catch (IOException ioe) {
|
||||
// expected
|
||||
}
|
||||
|
||||
// Normal proxy settings
|
||||
try {
|
||||
isa = InetSocketAddress.createUnresolved(loopbackAddress.getHostAddress(),
|
||||
pserver.getPort());
|
||||
proxy = new Proxy(Proxy.Type.HTTP, isa);
|
||||
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
urlc.getResponseCode();
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
urlc.disconnect();
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Connection through local proxy should succeed: "
|
||||
+ ioe.getMessage());
|
||||
}
|
||||
|
||||
// Reuse proxy with new HttpURLConnection
|
||||
try {
|
||||
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
urlc.getResponseCode();
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
read(urlc.getInputStream());
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Connection through local proxy should succeed: "
|
||||
+ ioe.getMessage());
|
||||
}
|
||||
|
||||
// Reuse proxy with existing HttpURLConnection
|
||||
try {
|
||||
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(proxy);
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
urlc.getResponseCode();
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
read(urlc.getInputStream());
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
urlc.disconnect();
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Connection through local proxy should succeed: "
|
||||
+ ioe.getMessage());
|
||||
}
|
||||
|
||||
// ProxySelector with normal proxy settings
|
||||
try {
|
||||
ProxySelector.setDefault(ProxySelector.of(isa));
|
||||
HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
|
||||
assertEqual(urlc.usingProxy(), false);
|
||||
urlc.getResponseCode();
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
read(urlc.getInputStream());
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
urlc.disconnect();
|
||||
assertEqual(urlc.usingProxy(), true);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Connection through local proxy should succeed: "
|
||||
+ ioe.getMessage());
|
||||
}
|
||||
|
||||
// ProxySelector with proxying disabled
|
||||
try {
|
||||
ProxySelector.setDefault(ProxySelector.of(null));
|
||||
HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
|
||||
assertEqual(urlc.usingProxy(), false);
|
||||
urlc.getResponseCode();
|
||||
assertEqual(urlc.usingProxy(), false);
|
||||
read(urlc.getInputStream());
|
||||
assertEqual(urlc.usingProxy(), false);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Direct connection should succeed: "
|
||||
+ ioe.getMessage());
|
||||
}
|
||||
|
||||
// ProxySelector overwritten
|
||||
try {
|
||||
ProxySelector.setDefault(ProxySelector.of(isa));
|
||||
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
|
||||
assertEqual(urlc.usingProxy(), false);
|
||||
urlc.disconnect();
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Direct connection should succeed: "
|
||||
+ ioe.getMessage());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (server != null) {
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ProxyServer extends Thread {
|
||||
private static ServerSocket ss = null;
|
||||
|
||||
// Client requesting a tunnel
|
||||
private Socket clientSocket = null;
|
||||
|
||||
/*
|
||||
* Origin server's address and port that the client
|
||||
* wants to establish the tunnel for communication.
|
||||
*/
|
||||
private InetAddress serverInetAddr;
|
||||
private int serverPort;
|
||||
|
||||
public ProxyServer(InetAddress server, int port) throws IOException {
|
||||
serverInetAddr = server;
|
||||
serverPort = port;
|
||||
ss = new ServerSocket(0, 0, InetAddress.getLoopbackAddress());
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
clientSocket = ss.accept();
|
||||
processRequests();
|
||||
} catch (Exception e) {
|
||||
System.out.println("Proxy failed: " + e);
|
||||
e.printStackTrace();
|
||||
try {
|
||||
ss.close();
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("ProxyServer close error: " + ioe);
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processRequests() throws Exception {
|
||||
// Connection set to tunneling mode
|
||||
|
||||
Socket serverSocket = new Socket(serverInetAddr, serverPort);
|
||||
ProxyTunnel clientToServer = new ProxyTunnel(
|
||||
clientSocket, serverSocket);
|
||||
ProxyTunnel serverToClient = new ProxyTunnel(
|
||||
serverSocket, clientSocket);
|
||||
clientToServer.start();
|
||||
serverToClient.start();
|
||||
System.out.println("Proxy: Started tunneling...");
|
||||
|
||||
clientToServer.join();
|
||||
serverToClient.join();
|
||||
System.out.println("Proxy: Finished tunneling...");
|
||||
|
||||
clientToServer.close();
|
||||
serverToClient.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* **************************************************************
|
||||
* Helper methods follow
|
||||
* **************************************************************
|
||||
*/
|
||||
public int getPort() {
|
||||
return ss.getLocalPort();
|
||||
}
|
||||
|
||||
/*
|
||||
* This inner class provides unidirectional data flow through the sockets
|
||||
* by continuously copying bytes from input socket to output socket
|
||||
* while both sockets are open and EOF has not been received.
|
||||
*/
|
||||
static class ProxyTunnel extends Thread {
|
||||
Socket sockIn;
|
||||
Socket sockOut;
|
||||
InputStream input;
|
||||
OutputStream output;
|
||||
|
||||
public ProxyTunnel(Socket sockIn, Socket sockOut) throws Exception {
|
||||
this.sockIn = sockIn;
|
||||
this.sockOut = sockOut;
|
||||
input = sockIn.getInputStream();
|
||||
output = sockOut.getOutputStream();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
int BUFFER_SIZE = 400;
|
||||
byte[] buf = new byte[BUFFER_SIZE];
|
||||
int bytesRead = 0;
|
||||
int count = 0; // Keep track of amount of data transferred
|
||||
|
||||
try {
|
||||
while ((bytesRead = input.read(buf)) >= 0) {
|
||||
output.write(buf, 0, bytesRead);
|
||||
output.flush();
|
||||
count += bytesRead;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
/*
|
||||
* Peer end has closed connection
|
||||
* so we close tunnel
|
||||
*/
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
if (!sockIn.isClosed())
|
||||
sockIn.close();
|
||||
if (!sockOut.isClosed())
|
||||
sockOut.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertEqual(boolean usingProxy, boolean expected) {
|
||||
if (usingProxy != expected) {
|
||||
throw new RuntimeException("Expected: " + expected
|
||||
+ " but usingProxy returned: " + usingProxy);
|
||||
}
|
||||
}
|
||||
|
||||
private static String read(InputStream inputStream) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
BufferedReader bufferedReader = new BufferedReader(
|
||||
new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
||||
int i = bufferedReader.read();
|
||||
while (i != -1) {
|
||||
sb.append((char) i);
|
||||
i = bufferedReader.read();
|
||||
}
|
||||
bufferedReader.close();
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8314978
|
||||
* @summary Multiple server call from connection failing with expect100 in
|
||||
* getOutputStream
|
||||
* @library /test/lib
|
||||
* @run junit/othervm HttpURLConnectionExpect100Test
|
||||
*/
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class HttpURLConnectionExpect100Test {
|
||||
|
||||
private HttpServer server;
|
||||
private int port;
|
||||
static final String RESPONSE = "This is default response.";
|
||||
|
||||
@BeforeAll
|
||||
void setup() throws Exception {
|
||||
server = HttpServer.create();
|
||||
port = server.getPort();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
void teardown() throws Exception {
|
||||
server.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expect100ContinueHitCountTest() throws Exception {
|
||||
server.resetHitCount();
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.toURL();
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("PUT");
|
||||
//send expect continue
|
||||
conn.setRequestProperty("Expect", "100-continue");
|
||||
sendRequest(conn);
|
||||
getHeaderField(conn);
|
||||
// Server rejects the expect 100-continue request with 417 response
|
||||
assertEquals(417, conn.getResponseCode());
|
||||
assertEquals(1, server.getServerHitCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultRequestHitCountTest() throws Exception {
|
||||
server.resetHitCount();
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(port)
|
||||
.toURL();
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("PUT");
|
||||
sendRequest(conn);
|
||||
getHeaderField(conn);
|
||||
assertEquals(200, conn.getResponseCode());
|
||||
try ( InputStream in = conn.getInputStream()) {
|
||||
byte[] data = in.readAllBytes();
|
||||
assertEquals(RESPONSE.length(), data.length);
|
||||
}
|
||||
assertEquals(1, server.getServerHitCount());
|
||||
}
|
||||
|
||||
private void sendRequest(final HttpURLConnection conn) throws Exception {
|
||||
conn.setDoOutput(true);
|
||||
conn.setFixedLengthStreamingMode(10);
|
||||
byte[] payload = new byte[10];
|
||||
try ( OutputStream os = conn.getOutputStream()) {
|
||||
os.write(payload);
|
||||
os.flush();
|
||||
} catch (IOException e) {
|
||||
// intentional, server will reject the expect 100
|
||||
System.err.println("Got expected exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private void getHeaderField(final HttpURLConnection conn) {
|
||||
// Call getHeaderFiels in loop, this should not hit server.
|
||||
for (int i = 0; i < 5; i++) {
|
||||
System.out.println("Getting: field" + i);
|
||||
conn.getHeaderField("field" + i);
|
||||
}
|
||||
}
|
||||
|
||||
static class HttpServer extends Thread {
|
||||
|
||||
private final ServerSocket ss;
|
||||
private static HttpServer inst;
|
||||
private volatile int hitCount;
|
||||
private volatile boolean isRunning;
|
||||
private final int port;
|
||||
|
||||
private HttpServer() throws IOException {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
ss = new ServerSocket();
|
||||
ss.bind(new InetSocketAddress(loopback, 0));
|
||||
port = ss.getLocalPort();
|
||||
isRunning = true;
|
||||
}
|
||||
|
||||
static HttpServer create() throws IOException {
|
||||
if (inst != null) {
|
||||
return inst;
|
||||
} else {
|
||||
inst = new HttpServer();
|
||||
inst.setDaemon(true);
|
||||
inst.start();
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
|
||||
int getServerHitCount() {
|
||||
return hitCount;
|
||||
}
|
||||
|
||||
void resetHitCount() {
|
||||
hitCount = 0;
|
||||
}
|
||||
|
||||
int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
void close() {
|
||||
isRunning = false;
|
||||
if (ss != null && !ss.isClosed()) {
|
||||
try {
|
||||
ss.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Socket client;
|
||||
try (ss) {
|
||||
while (isRunning) {
|
||||
client = ss.accept();
|
||||
System.out.println(client.getRemoteSocketAddress().toString());
|
||||
hitCount++;
|
||||
handleConnection(client);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
// throw exception only if isRunning is true
|
||||
if (isRunning) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleConnection(Socket client) throws IOException {
|
||||
try (client; BufferedReader in = new BufferedReader(
|
||||
new InputStreamReader(client.getInputStream()));
|
||||
PrintStream out = new PrintStream(client.getOutputStream())) {
|
||||
handle_connection(in, out);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private void handle_connection(BufferedReader in, PrintStream out)
|
||||
throws IOException, InterruptedException {
|
||||
StringBuilder clientRequest = new StringBuilder();
|
||||
String line = null;
|
||||
do {
|
||||
line = in.readLine();
|
||||
clientRequest.append(line);
|
||||
} while (line != null && line.length() != 0);
|
||||
if (clientRequest.toString().contains("100-continue")) {
|
||||
rejectExpect100Continue(out);
|
||||
} else {
|
||||
defaultResponse(out);
|
||||
}
|
||||
// wait until the client closes the socket
|
||||
while (line != null) {
|
||||
line = in.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
private void rejectExpect100Continue(PrintStream out) {
|
||||
out.print("HTTP/1.1 417 Expectation Failed\r\n");
|
||||
out.print("Server: Test-Server\r\n");
|
||||
out.print("Connection: close\r\n");
|
||||
out.print("Content-Length: 0\r\n");
|
||||
out.print("\r\n");
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private void defaultResponse(PrintStream out) {
|
||||
// send the 200 OK
|
||||
out.print("HTTP/1.1 200 OK\r\n");
|
||||
out.print("Server: Test-Server\r\n");
|
||||
out.print("Connection: close\r\n");
|
||||
out.print("Content-Length: " + RESPONSE.length() + "\r\n\r\n");
|
||||
out.print(RESPONSE);
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8054022
|
||||
* @summary Verify that expect 100-continue doesn't hang
|
||||
* @library /test/lib
|
||||
* @run junit/othervm HttpURLConnectionExpectContinueTest
|
||||
* @run junit/othervm -Djava.net.preferIPv4Stack=true HttpURLConnectionExpectContinueTest
|
||||
* @run junit/othervm -Djava.net.preferIPv6Addresses=true HttpURLConnectionExpectContinueTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class HttpURLConnectionExpectContinueTest {
|
||||
|
||||
class Control {
|
||||
volatile ServerSocket serverSocket = null;
|
||||
volatile boolean stop = false;
|
||||
volatile boolean respondWith100Continue = false;
|
||||
volatile boolean write100ContinueTwice = false;
|
||||
volatile String response = null;
|
||||
}
|
||||
|
||||
private Thread serverThread = null;
|
||||
private volatile Control control;
|
||||
static final Logger logger;
|
||||
|
||||
static {
|
||||
logger = Logger.getLogger("sun.net.www.protocol.http.HttpURLConnection");
|
||||
logger.setLevel(Level.ALL);
|
||||
Logger.getLogger("").getHandlers()[0].setLevel(Level.ALL);
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public void startServerSocket() throws Exception {
|
||||
Control control = this.control = new Control();
|
||||
|
||||
control.serverSocket = new ServerSocket();
|
||||
control.serverSocket.setReuseAddress(true);
|
||||
control.serverSocket.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
|
||||
Runnable runnable = () -> {
|
||||
while (!control.stop) {
|
||||
try {
|
||||
Socket socket = control.serverSocket.accept();
|
||||
InputStream inputStream = socket.getInputStream();
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
// Read initial request
|
||||
byte b;
|
||||
while (true) {
|
||||
b = (byte) inputStreamReader.read();
|
||||
stringBuilder.append((char) b);
|
||||
|
||||
if (stringBuilder.length() >= 4) {
|
||||
char[] lastBytes = new char[4];
|
||||
stringBuilder.getChars(
|
||||
stringBuilder.length() - 4,
|
||||
stringBuilder.length(), lastBytes, 0);
|
||||
if (Arrays.equals(lastBytes, new char[]{'\r', '\n', '\r', '\n'})) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OutputStream outputStream = socket.getOutputStream();
|
||||
|
||||
String header = stringBuilder.toString();
|
||||
String contentLengthString = "Content-Length:";
|
||||
|
||||
// send 100 continue responses if set by test
|
||||
if (control.respondWith100Continue) {
|
||||
outputStream.write("HTTP/1.1 100 Continue\r\n\r\n".getBytes());
|
||||
outputStream.flush();
|
||||
if (control.write100ContinueTwice) {
|
||||
outputStream.write("HTTP/1.1 100 Continue\r\n\r\n".getBytes());
|
||||
outputStream.flush();
|
||||
}
|
||||
}
|
||||
|
||||
// expect main request to be received
|
||||
int idx = header.indexOf(contentLengthString);
|
||||
|
||||
if (idx >= 0) {
|
||||
String substr = header.substring(idx + contentLengthString.length());
|
||||
idx = substr.indexOf('\r');
|
||||
substr = substr.substring(0, idx);
|
||||
int contentLength = Integer.parseInt(substr.trim());
|
||||
|
||||
StringBuilder contentLengthBuilder = new StringBuilder();
|
||||
for (int i = 0; i < contentLength; i++) {
|
||||
b = (byte) inputStreamReader.read();
|
||||
contentLengthBuilder.append((char) b);
|
||||
}
|
||||
|
||||
} else {
|
||||
StringBuilder contentLengthBuilder = new StringBuilder();
|
||||
while (true) {
|
||||
b = (byte) inputStreamReader.read();
|
||||
contentLengthBuilder.append((char) b);
|
||||
|
||||
if (contentLengthBuilder.length() >= 2) {
|
||||
char[] lastBytes = new char[2];
|
||||
contentLengthBuilder.getChars(
|
||||
contentLengthBuilder.length() - 2,
|
||||
contentLengthBuilder.length(), lastBytes, 0);
|
||||
if (Arrays.equals(lastBytes, new char[]{'\r', '\n'})) {
|
||||
String lengthInHex =
|
||||
contentLengthBuilder.substring(0, contentLengthBuilder.length() - 2);
|
||||
|
||||
int contentLength = Integer.parseInt(lengthInHex, 16);
|
||||
char[] body = new char[contentLength];
|
||||
inputStreamReader.read(body);
|
||||
break;
|
||||
// normally we have to parse more data,
|
||||
// but for simplicity we expect no more chunks...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send response
|
||||
outputStream.write(control.response.getBytes());
|
||||
outputStream.flush();
|
||||
} catch (SocketException e) {
|
||||
// ignore
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread = new Thread(runnable);
|
||||
serverThread.start();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public void stopServerSocket() throws Exception {
|
||||
Control control = this.control;
|
||||
control.stop = true;
|
||||
control.serverSocket.close();
|
||||
serverThread.join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonChunkedRequestAndNoExpect100ContinueResponse() throws Exception {
|
||||
String body = "testNonChunkedRequestAndNoExpect100ContinueResponse";
|
||||
Control control = this.control;
|
||||
control.response = "HTTP/1.1 200 OK\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-Length: " + body.length() + "\r\n" +
|
||||
"\r\n" +
|
||||
body + "\r\n";
|
||||
control.respondWith100Continue = false;
|
||||
control.write100ContinueTwice = false;
|
||||
|
||||
HttpURLConnection connection = createConnection();
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(body.getBytes());
|
||||
outputStream.close();
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
String responseBody = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8).strip();
|
||||
System.err.println("response body: " + responseBody);
|
||||
assertTrue(responseCode == 200,
|
||||
String.format("Expected 200 response, instead received %s", responseCode));
|
||||
assertTrue(body.equals(responseBody),
|
||||
String.format("Expected response %s, instead received %s", body, responseBody));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonChunkedRequestWithExpect100ContinueResponse() throws Exception {
|
||||
String body = "testNonChunkedRequestWithExpect100ContinueResponse";
|
||||
Control control = this.control;
|
||||
control.response = "HTTP/1.1 200 OK\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-Length: " + body.length() + "\r\n" +
|
||||
"\r\n" +
|
||||
body + "\r\n";
|
||||
control.respondWith100Continue = true;
|
||||
control.write100ContinueTwice = false;
|
||||
|
||||
HttpURLConnection connection = createConnection();
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(body.getBytes());
|
||||
outputStream.close();
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
String responseBody = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8).strip();
|
||||
System.err.println("response body: " + responseBody);
|
||||
assertTrue(responseCode == 200,
|
||||
String.format("Expected 200 response, instead received %s", responseCode));
|
||||
assertTrue(body.equals(responseBody),
|
||||
String.format("Expected response %s, instead received %s", body, responseBody));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonChunkedRequestWithDoubleExpect100ContinueResponse() throws Exception {
|
||||
String body = "testNonChunkedRequestWithDoubleExpect100ContinueResponse";
|
||||
Control control = this.control;
|
||||
control.response = "HTTP/1.1 200 OK\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-Length: " + body.length() + "\r\n" +
|
||||
"\r\n" +
|
||||
body + "\r\n";
|
||||
control.respondWith100Continue = true;
|
||||
control.write100ContinueTwice = true;
|
||||
|
||||
HttpURLConnection connection = createConnection();
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(body.getBytes());
|
||||
outputStream.close();
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
String responseBody = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8).strip();
|
||||
System.err.println("response body: " + responseBody);
|
||||
assertTrue(responseCode == 200,
|
||||
String.format("Expected 200 response, instead received %s", responseCode));
|
||||
assertTrue(body.equals(responseBody),
|
||||
String.format("Expected response %s, instead received %s", body, responseBody));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChunkedRequestAndNoExpect100ContinueResponse() throws Exception {
|
||||
String body = "testChunkedRequestAndNoExpect100ContinueResponse";
|
||||
Control control = this.control;
|
||||
control.response = "HTTP/1.1 200 OK\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-Length: " + body.length() + "\r\n" +
|
||||
"\r\n" +
|
||||
body + "\r\n";
|
||||
control.respondWith100Continue = false;
|
||||
control.write100ContinueTwice = false;
|
||||
|
||||
HttpURLConnection connection = createConnection();
|
||||
connection.setChunkedStreamingMode(body.length() / 2);
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(body.getBytes());
|
||||
outputStream.close();
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
String responseBody = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8).strip();
|
||||
System.err.println("response body: " + responseBody);
|
||||
assertTrue(responseCode == 200,
|
||||
String.format("Expected 200 response, instead received %s", responseCode));
|
||||
assertTrue(body.equals(responseBody),
|
||||
String.format("Expected response %s, instead received %s", body, responseBody));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChunkedRequestWithExpect100ContinueResponse() throws Exception {
|
||||
String body = "testChunkedRequestWithExpect100ContinueResponse";
|
||||
Control control = this.control;
|
||||
control.response = "HTTP/1.1 200 OK\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-Length: " + body.length() + "\r\n" +
|
||||
"\r\n" +
|
||||
body + "\r\n";
|
||||
control.respondWith100Continue = true;
|
||||
control.write100ContinueTwice = false;
|
||||
|
||||
HttpURLConnection connection = createConnection();
|
||||
connection.setChunkedStreamingMode(body.length() / 2);
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(body.getBytes());
|
||||
outputStream.close();
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
String responseBody = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8).strip();
|
||||
System.err.println("response body: " + responseBody);
|
||||
assertTrue(responseCode == 200,
|
||||
String.format("Expected 200 response, instead received %s", responseCode));
|
||||
assertTrue(body.equals(responseBody),
|
||||
String.format("Expected response %s, instead received %s", body, responseBody));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChunkedRequestWithDoubleExpect100ContinueResponse() throws Exception {
|
||||
String body = "testChunkedRequestWithDoubleExpect100ContinueResponse";
|
||||
Control control = this.control;
|
||||
control.response = "HTTP/1.1 200 OK\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-Length: " + body.length() + "\r\n" +
|
||||
"\r\n" +
|
||||
body + "\r\n";
|
||||
control.respondWith100Continue = true;
|
||||
control.write100ContinueTwice = true;
|
||||
|
||||
HttpURLConnection connection = createConnection();
|
||||
connection.setChunkedStreamingMode(body.length() / 2);
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(body.getBytes());
|
||||
outputStream.close();
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
String responseBody = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8).strip();
|
||||
System.err.println("response body: " + responseBody);
|
||||
assertTrue(responseCode == 200,
|
||||
String.format("Expected 200 response, instead received %s", responseCode));
|
||||
assertTrue(body.equals(responseBody),
|
||||
String.format("Expected response %s, instead received %s", body, responseBody));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixedLengthRequestAndNoExpect100ContinueResponse() throws Exception {
|
||||
String body = "testFixedLengthRequestAndNoExpect100ContinueResponse";
|
||||
Control control = this.control;
|
||||
control.response = "HTTP/1.1 200 OK\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-Length: " + body.length() + "\r\n" +
|
||||
"\r\n" +
|
||||
body + "\r\n";
|
||||
control.respondWith100Continue = false;
|
||||
control.write100ContinueTwice = false;
|
||||
|
||||
HttpURLConnection connection = createConnection();
|
||||
connection.setFixedLengthStreamingMode(body.length());
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(body.getBytes());
|
||||
outputStream.close();
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
String responseBody = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8).strip();
|
||||
System.err.println("response body: " + responseBody);
|
||||
assertTrue(responseCode == 200,
|
||||
String.format("Expected 200 response, instead received %s", responseCode));
|
||||
assertTrue(body.equals(responseBody),
|
||||
String.format("Expected response %s, instead received %s", body, responseBody));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixedLengthRequestWithExpect100ContinueResponse() throws Exception {
|
||||
String body = "testFixedLengthRequestWithExpect100ContinueResponse";
|
||||
Control control = this.control;
|
||||
control.response = "HTTP/1.1 200 OK\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-Length: " + body.length() + "\r\n" +
|
||||
"\r\n" +
|
||||
body + "\r\n";
|
||||
control.respondWith100Continue = true;
|
||||
control.write100ContinueTwice = false;
|
||||
|
||||
HttpURLConnection connection = createConnection();
|
||||
connection.setFixedLengthStreamingMode(body.getBytes().length);
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(body.getBytes());
|
||||
outputStream.close();
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
String responseBody = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8).strip();
|
||||
System.err.println("response body: " + responseBody);
|
||||
assertTrue(responseCode == 200,
|
||||
String.format("Expected 200 response, instead received %s", responseCode));
|
||||
assertTrue(body.equals(responseBody),
|
||||
String.format("Expected response %s, instead received %s", body, responseBody));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixedLengthRequestWithDoubleExpect100ContinueResponse() throws Exception {
|
||||
String body = "testFixedLengthRequestWithDoubleExpect100ContinueResponse";
|
||||
Control control = this.control;
|
||||
control.response = "HTTP/1.1 200 OK\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"Content-Length: " + body.length() + "\r\n" +
|
||||
"\r\n" +
|
||||
body + "\r\n";
|
||||
control.respondWith100Continue = true;
|
||||
control.write100ContinueTwice = true;
|
||||
|
||||
HttpURLConnection connection = createConnection();
|
||||
connection.setFixedLengthStreamingMode(body.getBytes().length);
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(body.getBytes());
|
||||
outputStream.close();
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
String responseBody = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8).strip();
|
||||
System.err.println("response body: " + responseBody);
|
||||
assertTrue(responseCode == 200,
|
||||
String.format("Expected 200 response, instead received %s", responseCode));
|
||||
assertTrue(body.equals(responseBody),
|
||||
String.format("Expected response %s, instead received %s", body, responseBody));
|
||||
}
|
||||
|
||||
// Creates a connection with all the common settings used in each test
|
||||
private HttpURLConnection createConnection() throws Exception {
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(control.serverSocket.getLocalPort())
|
||||
.toURL();
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
|
||||
connection.setDoOutput(true);
|
||||
connection.setReadTimeout(5000);
|
||||
connection.setUseCaches(false);
|
||||
connection.setInstanceFollowRedirects(false);
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Connection", "Close");
|
||||
connection.setRequestProperty("Expect", "100-Continue");
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8133686
|
||||
* @summary Ensuring that multiple header values for a given field-name are returned in
|
||||
* the order they were added for HttpURLConnection.getRequestProperties
|
||||
* and HttpURLConnection.getHeaderFields
|
||||
* @library /test/lib
|
||||
* @run testng HttpURLConnectionHeadersOrder
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.AfterTest;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class HttpURLConnectionHeadersOrder {
|
||||
private static final String LOCAL_TEST_ENDPOINT = "/headertest";
|
||||
private static final String ERROR_MESSAGE_TEMPLATE = "Expected Request Properties = %s, Actual Request Properties = %s";
|
||||
private static final List<String> EXPECTED_HEADER_VALUES = Arrays.asList("a", "b", "c");
|
||||
private static HttpServer server;
|
||||
private static URL serverUrl;
|
||||
|
||||
|
||||
@BeforeTest
|
||||
public void beforeTest() throws Exception {
|
||||
SimpleHandler handler = new SimpleHandler();
|
||||
server = createSimpleHttpServer(handler);
|
||||
serverUrl = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.host(server.getAddress().getAddress())
|
||||
.port(server.getAddress().getPort())
|
||||
.path(LOCAL_TEST_ENDPOINT)
|
||||
.toURL();
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
public void afterTest() {
|
||||
if (server != null)
|
||||
server.stop(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* - This tests requestProperty insertion-order
|
||||
* - on the client side by sending a HTTP GET
|
||||
* - request to a "dummy" server with additional
|
||||
* - custom request properties
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test (priority = 1)
|
||||
public void testRequestPropertiesOrder() throws Exception {
|
||||
final var conn = (HttpURLConnection) serverUrl.openConnection();
|
||||
|
||||
conn.addRequestProperty("test_req_prop", "a");
|
||||
conn.addRequestProperty("test_req_prop", "b");
|
||||
conn.addRequestProperty("test_req_prop", "c");
|
||||
|
||||
conn.setRequestMethod("GET");
|
||||
|
||||
var requestProperties = conn.getRequestProperties();
|
||||
var customRequestProps = requestProperties.get("test_req_prop");
|
||||
|
||||
conn.disconnect();
|
||||
Assert.assertNotNull(customRequestProps);
|
||||
Assert.assertEquals(customRequestProps, EXPECTED_HEADER_VALUES, String.format(ERROR_MESSAGE_TEMPLATE, EXPECTED_HEADER_VALUES.toString(), customRequestProps.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* - This tests whether or not the insertion order is preserved for custom headers
|
||||
* - on the server's side.
|
||||
* - The server will return a custom status code (999) if the expected headers
|
||||
* - are not equal to the actual headers
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test (priority = 2)
|
||||
public void testServerSideRequestHeadersOrder() throws Exception {
|
||||
final var conn = (HttpURLConnection) serverUrl.openConnection();
|
||||
conn.addRequestProperty("test_server_handling", "a");
|
||||
conn.addRequestProperty("test_server_handling", "b");
|
||||
conn.addRequestProperty("test_server_handling", "c");
|
||||
|
||||
int statusCode = conn.getResponseCode();
|
||||
conn.disconnect();
|
||||
Assert.assertEquals(statusCode, 999, "The insertion-order was not preserved on the server-side response headers handling");
|
||||
}
|
||||
|
||||
@Test (priority = 3)
|
||||
public void testClientSideResponseHeadersOrder() throws Exception {
|
||||
final var conn = (HttpURLConnection) serverUrl.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
|
||||
var actualCustomResponseHeaders = conn.getHeaderFields().get("Test_response");
|
||||
Assert.assertNotNull(actualCustomResponseHeaders, "Error in reading custom response headers");
|
||||
Assert.assertEquals(EXPECTED_HEADER_VALUES, actualCustomResponseHeaders, String.format(ERROR_MESSAGE_TEMPLATE, EXPECTED_HEADER_VALUES.toString(), actualCustomResponseHeaders.toString()));
|
||||
}
|
||||
|
||||
private static HttpServer createSimpleHttpServer(SimpleHandler handler) throws IOException {
|
||||
var serverAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
||||
var server = HttpServer.create(serverAddress, 0);
|
||||
server.createContext(LOCAL_TEST_ENDPOINT, handler);
|
||||
server.start();
|
||||
System.out.println("Server started on " + server.getAddress());
|
||||
return server;
|
||||
}
|
||||
|
||||
private static class SimpleHandler implements HttpHandler {
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
int statusCode = testRequestHeadersOrder(exchange);
|
||||
sendCustomResponse(exchange, statusCode);
|
||||
}
|
||||
|
||||
private int testRequestHeadersOrder(HttpExchange exchange) {
|
||||
var requestHeaders = exchange.getRequestHeaders();
|
||||
var actualTestRequestHeaders = requestHeaders.get("test_server_handling");
|
||||
|
||||
if (actualTestRequestHeaders == null) {
|
||||
System.out.println("Error: requestHeaders.get(\"test_server_handling\") returned null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!actualTestRequestHeaders.equals(EXPECTED_HEADER_VALUES)) {
|
||||
System.out.println("Error: " + String.format(ERROR_MESSAGE_TEMPLATE, EXPECTED_HEADER_VALUES.toString(), actualTestRequestHeaders.toString()));
|
||||
return -1;
|
||||
}
|
||||
return 999;
|
||||
}
|
||||
|
||||
private void sendCustomResponse(HttpExchange exchange, int statusCode) throws IOException {
|
||||
var responseHeaders = exchange.getResponseHeaders();
|
||||
responseHeaders.add("test_response", "a");
|
||||
responseHeaders.add("test_response", "b");
|
||||
responseHeaders.add("test_response", "c");
|
||||
|
||||
var outputStream = exchange.getResponseBody();
|
||||
var response = "Testing headers";
|
||||
exchange.sendResponseHeaders(statusCode, response.length());
|
||||
|
||||
outputStream.write(response.getBytes());
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.AfterTest;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.Test;
|
||||
import sun.net.spi.DefaultProxySelector;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 6563286 6797318 8177648 8230220
|
||||
* @summary Tests that sun.net.www.protocol.http.HttpURLConnection when dealing with
|
||||
* sun.net.spi.DefaultProxySelector#select() handles any IllegalArgumentException
|
||||
* correctly
|
||||
* @library /test/lib
|
||||
* @run testng HttpURLProxySelectionTest
|
||||
* @modules java.base/sun.net.spi:+open
|
||||
*/
|
||||
public class HttpURLProxySelectionTest {
|
||||
|
||||
private static final String WEB_APP_CONTEXT = "/httpurlproxytest";
|
||||
|
||||
private HttpServer server;
|
||||
private SimpleHandler handler;
|
||||
private ProxySelector previousDefault;
|
||||
private CustomProxySelector ourProxySelector = new CustomProxySelector();
|
||||
|
||||
@BeforeTest
|
||||
public void beforeTest() throws Exception {
|
||||
previousDefault = ProxySelector.getDefault();
|
||||
ProxySelector.setDefault(ourProxySelector);
|
||||
handler = new SimpleHandler();
|
||||
server = createServer(handler);
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
public void afterTest() {
|
||||
try {
|
||||
if (server != null) {
|
||||
final int delaySeconds = 0;
|
||||
server.stop(delaySeconds);
|
||||
}
|
||||
} finally {
|
||||
ProxySelector.setDefault(previousDefault);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - Test initiates a HTTP request to server
|
||||
* - Server receives request and sends a 301 redirect to an URI which doesn't have a "host"
|
||||
* - Redirect is expected to fail with IOException (caused by IllegalArgumentException from DefaultProxySelector)
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
final URL targetURL = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.host(server.getAddress().getAddress())
|
||||
.port(server.getAddress().getPort())
|
||||
.path(WEB_APP_CONTEXT)
|
||||
.toURL();
|
||||
System.out.println("Sending request to " + targetURL);
|
||||
final HttpURLConnection conn = (HttpURLConnection) targetURL.openConnection();
|
||||
try {
|
||||
conn.getResponseCode();
|
||||
Assert.fail("Request to " + targetURL + " was expected to fail during redirect");
|
||||
} catch (IOException ioe) {
|
||||
// expected because of the redirect to an invalid URL, for which a proxy can't be selected
|
||||
|
||||
// make sure the it was indeed a redirect
|
||||
Assert.assertTrue(handler.redirectSent, "Server was expected to send a redirect, but didn't");
|
||||
Assert.assertTrue(ourProxySelector.selectorUsedForRedirect, "Proxy selector wasn't used for redirect");
|
||||
|
||||
// make sure the IOException was caused by an IllegalArgumentException
|
||||
Assert.assertTrue(ioe.getCause() instanceof IllegalArgumentException, "Unexpected cause in the IOException");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static HttpServer createServer(final HttpHandler handler) throws IOException {
|
||||
final InetSocketAddress serverAddr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
||||
final int backlog = -1;
|
||||
final HttpServer server = HttpServer.create(serverAddr, backlog);
|
||||
// setup the handler
|
||||
server.createContext(WEB_APP_CONTEXT, handler);
|
||||
// start the server
|
||||
server.start();
|
||||
System.out.println("Server started on " + server.getAddress());
|
||||
return server;
|
||||
}
|
||||
|
||||
private static class SimpleHandler implements HttpHandler {
|
||||
private volatile boolean redirectSent = false;
|
||||
|
||||
@Override
|
||||
public void handle(final HttpExchange httpExchange) throws IOException {
|
||||
final String redirectURL;
|
||||
try {
|
||||
redirectURL = new URI("http", "/irrelevant", null).toString();
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
httpExchange.getResponseHeaders().add("Location", redirectURL);
|
||||
final URI requestURI = httpExchange.getRequestURI();
|
||||
System.out.println("Handling " + httpExchange.getRequestMethod() + " request "
|
||||
+ requestURI + " responding with redirect to " + redirectURL);
|
||||
this.redirectSent = true;
|
||||
httpExchange.sendResponseHeaders(301, -1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class CustomProxySelector extends DefaultProxySelector {
|
||||
|
||||
private volatile boolean selectorUsedForRedirect = false;
|
||||
|
||||
@Override
|
||||
public List<Proxy> select(final URI uri) {
|
||||
if (uri.toString().contains("/irrelevant")) {
|
||||
this.selectorUsedForRedirect = true;
|
||||
}
|
||||
return super.select(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test that Response Message gets set even if a non 100 response
|
||||
* gets return when Expect Continue is set
|
||||
* @bug 8352502
|
||||
* @library /test/lib
|
||||
* @run junit/othervm -Djdk.internal.httpclient.debug=true
|
||||
* -Djdk.httpclient.HttpClient.log=all
|
||||
* HttpUrlConnectionExpectContinueResponseMessageTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.Socket;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URL;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class HttpUrlConnectionExpectContinueResponseMessageTest {
|
||||
class Control {
|
||||
volatile ServerSocket serverSocket = null;
|
||||
volatile boolean stop = false;
|
||||
volatile String response = null;
|
||||
volatile Socket acceptingSocket = null;
|
||||
volatile String testPath = null;
|
||||
}
|
||||
|
||||
private Thread serverThread = null;
|
||||
private volatile Control control;
|
||||
static final Logger logger;
|
||||
|
||||
static {
|
||||
logger = Logger.getLogger("sun.net.www.protocol.http.HttpURLConnection");
|
||||
logger.setLevel(Level.ALL);
|
||||
Logger.getLogger("").getHandlers()[0].setLevel(Level.ALL);
|
||||
}
|
||||
|
||||
public Object[][] args() {
|
||||
return new Object[][]{
|
||||
// Expected Status Code, Status Line, Expected responseMessage
|
||||
{ 404, "HTTP/1.1 404 Not Found", "Not Found" },
|
||||
{ 405, "HTTP/1.1 405 Method Not Allowed", "Method Not Allowed" },
|
||||
{ 401, "HTTP/1.1 401 Unauthorized", "Unauthorized"}
|
||||
};
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public void startServerSocket() throws Exception {
|
||||
Control control = this.control = new Control();
|
||||
|
||||
control.serverSocket = new ServerSocket();
|
||||
control.serverSocket.setReuseAddress(true);
|
||||
control.serverSocket.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
|
||||
Runnable runnable = () -> {
|
||||
while (!control.stop) {
|
||||
try {
|
||||
Socket socket = control.serverSocket.accept();
|
||||
String path = getPath(socket);
|
||||
|
||||
OutputStream outputStream;
|
||||
if (path.equals(control.testPath)) {
|
||||
control.acceptingSocket = socket;
|
||||
outputStream = control.acceptingSocket.getOutputStream();
|
||||
|
||||
// send a wrong response and then shutdown
|
||||
outputStream.write(control.response.getBytes());
|
||||
outputStream.flush();
|
||||
control.acceptingSocket.shutdownOutput();
|
||||
} else {
|
||||
// stray request showed up, return 500 and close socket
|
||||
outputStream = socket.getOutputStream();
|
||||
outputStream.write("HTTP/1.1 500 Internal Server Error\r\n".getBytes());
|
||||
outputStream.write("Connection: close\r\n".getBytes());
|
||||
outputStream.write("Content-Length: 0\r\n".getBytes());
|
||||
outputStream.write("\r\n".getBytes());
|
||||
outputStream.flush();
|
||||
socket.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Any exceptions will be ignored
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread = new Thread(runnable);
|
||||
serverThread.start();
|
||||
}
|
||||
|
||||
private static String getPath(Socket socket) throws IOException {
|
||||
InputStream inputStream = socket.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
StringBuilder reqBuilder = new StringBuilder();
|
||||
String line = null;
|
||||
while (!(line = reader.readLine()).isEmpty()) {
|
||||
reqBuilder.append(line + "\r\n");
|
||||
}
|
||||
String req = reqBuilder.toString();
|
||||
StringTokenizer tokenizer = new StringTokenizer(req);
|
||||
String method = tokenizer.nextToken();
|
||||
String path = tokenizer.nextToken();
|
||||
return path;
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public void stopServerSocket() throws Exception {
|
||||
Control control = this.control;
|
||||
control.stop = true;
|
||||
control.serverSocket.close();
|
||||
serverThread.join();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("args")
|
||||
public void test(int expectedCode, String statusLine, String expectedMessage) throws Exception {
|
||||
String body = "Testing: " + expectedCode;
|
||||
Control control = this.control;
|
||||
control.response = statusLine + "\r\n"
|
||||
+ "Content-Length: 0\r\n"
|
||||
+ "\r\n";
|
||||
control.testPath = "/ContinueResponseMessageTest/" + expectedCode;
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.port(control.serverSocket.getLocalPort())
|
||||
.path(control.testPath)
|
||||
.toURL();
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Connection", "Close");
|
||||
connection.setRequestProperty("Expect", "100-Continue");
|
||||
|
||||
try {
|
||||
connection.setFixedLengthStreamingMode(body.getBytes().length);
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
outputStream.write(body.getBytes());
|
||||
outputStream.close();
|
||||
} catch (Exception ex) {
|
||||
// server returning 4xx responses can result in exceptions
|
||||
// but we can just swallow them
|
||||
}
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
String responseMessage = connection.getResponseMessage();
|
||||
assertTrue(responseCode == expectedCode,
|
||||
String.format("Expected %s response, instead received %s", expectedCode, responseCode));
|
||||
assertTrue(expectedMessage.equals(responseMessage),
|
||||
String.format("Expected Response Message %s, instead received %s",
|
||||
expectedMessage, responseMessage));
|
||||
control.acceptingSocket.close();
|
||||
}
|
||||
}
|
||||
72
test/jdk/java/net/HttpURLConnection/NoProxyTest.java
Normal file
72
test/jdk/java/net/HttpURLConnection/NoProxyTest.java
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8144008
|
||||
* @library /test/lib
|
||||
* @summary Setting NO_PROXY on HTTP URL connections does not stop proxying
|
||||
* @run main/othervm NoProxyTest
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Proxy;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.List;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class NoProxyTest {
|
||||
|
||||
static class NoProxyTestSelector extends ProxySelector {
|
||||
@Override
|
||||
public List<Proxy> select(URI uri) {
|
||||
throw new RuntimeException("Should not reach here as proxy==Proxy.NO_PROXY");
|
||||
}
|
||||
@Override
|
||||
public void connectFailed(URI u, SocketAddress s, IOException e) { }
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws MalformedURLException {
|
||||
ProxySelector.setDefault(new NoProxyTestSelector());
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.loopback()
|
||||
.path("/")
|
||||
.toURLUnchecked();
|
||||
System.out.println("URL: " + url);
|
||||
URLConnection connection;
|
||||
try {
|
||||
connection = url.openConnection(Proxy.NO_PROXY);
|
||||
connection.connect();
|
||||
} catch (IOException ignore) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
228
test/jdk/java/net/HttpURLConnection/Response1xxTest.java
Normal file
228
test/jdk/java/net/HttpURLConnection/Response1xxTest.java
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8170305
|
||||
* @summary Tests behaviour of HttpURLConnection when server responds with 1xx interim response status codes
|
||||
* @library /test/lib
|
||||
* @run testng Response1xxTest
|
||||
*/
|
||||
public class Response1xxTest {
|
||||
private static final String EXPECTED_RSP_BODY = "Hello World";
|
||||
|
||||
private ServerSocket serverSocket;
|
||||
private Http11Server server;
|
||||
private String requestURIBase;
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public void setup() throws Exception {
|
||||
serverSocket = new ServerSocket(0, 0, InetAddress.getLoopbackAddress());
|
||||
server = new Http11Server(serverSocket);
|
||||
new Thread(server).start();
|
||||
requestURIBase = URIBuilder.newBuilder().scheme("http").loopback()
|
||||
.port(serverSocket.getLocalPort()).build().toString();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void teardown() throws Exception {
|
||||
if (server != null) {
|
||||
server.stop = true;
|
||||
System.out.println("(HTTP 1.1) Server stop requested");
|
||||
}
|
||||
if (serverSocket != null) {
|
||||
serverSocket.close();
|
||||
System.out.println("Closed (HTTP 1.1) server socket");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Http11Server implements Runnable {
|
||||
private static final int CONTENT_LENGTH = EXPECTED_RSP_BODY.getBytes(StandardCharsets.UTF_8).length;
|
||||
|
||||
private static final String HTTP_1_1_RSP_200 = "HTTP/1.1 200 OK\r\n" +
|
||||
"Content-Length: " + CONTENT_LENGTH + "\r\n\r\n" +
|
||||
EXPECTED_RSP_BODY;
|
||||
|
||||
private static final String REQ_LINE_FOO = "GET /test/foo HTTP/1.1\r\n";
|
||||
private static final String REQ_LINE_BAR = "GET /test/bar HTTP/1.1\r\n";
|
||||
private static final String REQ_LINE_HELLO = "GET /test/hello HTTP/1.1\r\n";
|
||||
private static final String REQ_LINE_BYE = "GET /test/bye HTTP/1.1\r\n";
|
||||
|
||||
|
||||
private final ServerSocket serverSocket;
|
||||
private volatile boolean stop;
|
||||
|
||||
private Http11Server(final ServerSocket serverSocket) {
|
||||
this.serverSocket = serverSocket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("Server running at " + serverSocket);
|
||||
while (!stop) {
|
||||
Socket socket = null;
|
||||
try {
|
||||
// accept a connection
|
||||
socket = serverSocket.accept();
|
||||
System.out.println("Accepted connection from client " + socket);
|
||||
// read request
|
||||
final String requestLine;
|
||||
try {
|
||||
requestLine = readRequestLine(socket);
|
||||
} catch (Throwable t) {
|
||||
// ignore connections from potential rogue client
|
||||
System.err.println("Ignoring connection/request from client " + socket
|
||||
+ " due to exception:");
|
||||
t.printStackTrace();
|
||||
// close the socket
|
||||
safeClose(socket);
|
||||
continue;
|
||||
}
|
||||
System.out.println("Received following request line from client " + socket
|
||||
+ " :\n" + requestLine);
|
||||
final int informationalResponseCode;
|
||||
if (requestLine.startsWith(REQ_LINE_FOO)) {
|
||||
// we will send intermediate/informational 102 response
|
||||
informationalResponseCode = 102;
|
||||
} else if (requestLine.startsWith(REQ_LINE_BAR)) {
|
||||
// we will send intermediate/informational 103 response
|
||||
informationalResponseCode = 103;
|
||||
} else if (requestLine.startsWith(REQ_LINE_HELLO)) {
|
||||
// we will send intermediate/informational 100 response
|
||||
informationalResponseCode = 100;
|
||||
} else if (requestLine.startsWith(REQ_LINE_BYE)) {
|
||||
// we will send intermediate/informational 101 response
|
||||
informationalResponseCode = 101;
|
||||
} else {
|
||||
// unexpected client. ignore and close the client
|
||||
System.err.println("Ignoring unexpected request from client " + socket);
|
||||
safeClose(socket);
|
||||
continue;
|
||||
}
|
||||
try (final OutputStream os = socket.getOutputStream()) {
|
||||
// send informational response headers a few times (spec allows them to
|
||||
// be sent multiple times)
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// send 1xx response header
|
||||
os.write(("HTTP/1.1 " + informationalResponseCode + "\r\n\r\n")
|
||||
.getBytes(StandardCharsets.UTF_8));
|
||||
os.flush();
|
||||
System.out.println("Sent response code " + informationalResponseCode
|
||||
+ " to client " + socket);
|
||||
}
|
||||
// now send a final response
|
||||
System.out.println("Now sending 200 response code to client " + socket);
|
||||
os.write(HTTP_1_1_RSP_200.getBytes(StandardCharsets.UTF_8));
|
||||
os.flush();
|
||||
System.out.println("Sent 200 response code to client " + socket);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// close the client connection
|
||||
safeClose(socket);
|
||||
// continue accepting any other client connections until we are asked to stop
|
||||
System.err.println("Ignoring exception in server:");
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String readRequestLine(final Socket sock) throws IOException {
|
||||
final InputStream is = sock.getInputStream();
|
||||
final StringBuilder sb = new StringBuilder("");
|
||||
byte[] buf = new byte[1024];
|
||||
while (!sb.toString().endsWith("\r\n\r\n")) {
|
||||
final int numRead = is.read(buf);
|
||||
if (numRead == -1) {
|
||||
return sb.toString();
|
||||
}
|
||||
final String part = new String(buf, 0, numRead, StandardCharsets.ISO_8859_1);
|
||||
sb.append(part);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void safeClose(final Socket socket) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (Throwable t) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that when a HTTP/1.1 server sends intermediate 1xx response codes and then the final
|
||||
* response, the client (internally) will ignore those intermediate informational response codes
|
||||
* and only return the final response to the application
|
||||
*/
|
||||
@Test
|
||||
public void test1xx() throws Exception {
|
||||
final URI[] requestURIs = new URI[]{
|
||||
new URI(requestURIBase + "/test/foo"),
|
||||
new URI(requestURIBase + "/test/bar"),
|
||||
new URI(requestURIBase + "/test/hello")};
|
||||
for (final URI requestURI : requestURIs) {
|
||||
System.out.println("Issuing request to " + requestURI);
|
||||
final HttpURLConnection urlConnection = (HttpURLConnection) requestURI.toURL().openConnection();
|
||||
final int responseCode = urlConnection.getResponseCode();
|
||||
Assert.assertEquals(responseCode, 200, "Unexpected response code");
|
||||
final String body;
|
||||
try (final InputStream is = urlConnection.getInputStream()) {
|
||||
final byte[] bytes = is.readAllBytes();
|
||||
body = new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
Assert.assertEquals(body, EXPECTED_RSP_BODY, "Unexpected response body");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that when a HTTP/1.1 server sends 101 response code, when the client
|
||||
* didn't ask for a connection upgrade, then the request fails with an exception.
|
||||
*/
|
||||
@Test
|
||||
public void test101CausesRequestFailure() throws Exception {
|
||||
final URI requestURI = new URI(requestURIBase + "/test/bye");
|
||||
System.out.println("Issuing request to " + requestURI);
|
||||
final HttpURLConnection urlConnection = (HttpURLConnection) requestURI.toURL().openConnection();
|
||||
// we expect the request to fail because the server unexpectedly sends a 101 response
|
||||
Assert.assertThrows(ProtocolException.class, () -> urlConnection.getResponseCode());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Authenticator;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8169415
|
||||
* @library /test/lib
|
||||
* @modules java.logging
|
||||
* java.base/sun.net.www
|
||||
* java.base/sun.net.www.protocol.http
|
||||
* jdk.httpserver/sun.net.httpserver
|
||||
* @build jdk.test.lib.net.SimpleSSLContext HTTPTest HTTPTestServer HTTPTestClient HTTPSetAuthenticatorTest
|
||||
* @summary A simple HTTP test that starts an echo server supporting the given
|
||||
* authentication scheme, then starts a regular HTTP client to invoke it.
|
||||
* The client first does a GET request on "/", then follows on
|
||||
* with a POST request that sends "Hello World!" to the server.
|
||||
* The client expects to receive "Hello World!" in return.
|
||||
* The test supports several execution modes:
|
||||
* SERVER: The server performs Server authentication;
|
||||
* PROXY: The server pretends to be a proxy and performs
|
||||
* Proxy authentication;
|
||||
* SERVER307: The server redirects the client (307) to another
|
||||
* server that perform Server authentication;
|
||||
* PROXY305: The server attempts to redirect
|
||||
* the client to a proxy using 305 code;
|
||||
* This test runs the client several times, providing different
|
||||
* authenticators to the HttpURLConnection and verifies that
|
||||
* the authenticator is invoked as expected - validating that
|
||||
* connections with different authenticators do not share each
|
||||
* other's socket channel and authentication info.
|
||||
* Note: BASICSERVER means that the server will let the underlying
|
||||
* com.sun.net.httpserver.HttpServer perform BASIC
|
||||
* authentication when in Server mode. There should be
|
||||
* no real difference between BASICSERVER and BASIC - it should
|
||||
* be transparent on the client side.
|
||||
* @run main/othervm HTTPSetAuthenticatorTest NONE SERVER PROXY SERVER307 PROXY305
|
||||
* @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST SERVER
|
||||
* @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST PROXY
|
||||
* @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST PROXY305
|
||||
* @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST SERVER307
|
||||
* @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER
|
||||
* @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY
|
||||
* @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY305
|
||||
* @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER307
|
||||
* @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER
|
||||
* @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER307
|
||||
*
|
||||
* @author danielfuchs
|
||||
*/
|
||||
public class HTTPSetAuthenticatorTest extends HTTPTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String[] schemes;
|
||||
String[] params;
|
||||
if (args == null || args.length == 0) {
|
||||
schemes = Stream.of(HttpSchemeType.values())
|
||||
.map(HttpSchemeType::name)
|
||||
.collect(Collectors.toList())
|
||||
.toArray(new String[0]);
|
||||
params = new String[0];
|
||||
} else {
|
||||
schemes = new String[] { args[0] };
|
||||
params = Arrays.copyOfRange(args, 1, args.length);
|
||||
}
|
||||
for (String scheme : schemes) {
|
||||
System.out.println("==== Testing with scheme=" + scheme + " ====\n");
|
||||
new HTTPSetAuthenticatorTest(HttpSchemeType.valueOf(scheme))
|
||||
.execute(params);
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
final HttpSchemeType scheme;
|
||||
public HTTPSetAuthenticatorTest(HttpSchemeType scheme) {
|
||||
this.scheme = scheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpSchemeType getHttpSchemeType() {
|
||||
return scheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int run(HTTPTestServer server,
|
||||
HttpProtocolType protocol,
|
||||
HttpAuthType mode)
|
||||
throws IOException
|
||||
{
|
||||
HttpTestAuthenticator authOne = new HttpTestAuthenticator("authOne", "dublin", "foox");
|
||||
HttpTestAuthenticator authTwo = new HttpTestAuthenticator("authTwo", "dublin", "foox");
|
||||
int expectedIncrement = scheme == HttpSchemeType.NONE
|
||||
? 0 : EXPECTED_AUTH_CALLS_PER_TEST;
|
||||
int count;
|
||||
int defaultCount = AUTHENTICATOR.count.get();
|
||||
// Connect to the server with a GET request, then with a
|
||||
// POST that contains "Hello World!"
|
||||
// Uses authenticator #1
|
||||
System.out.println("\nClient: Using authenticator #1: "
|
||||
+ toString(authOne));
|
||||
HTTPTestClient.connect(protocol, server, mode, authOne);
|
||||
count = authOne.count.get();
|
||||
if (count != expectedIncrement) {
|
||||
throw new AssertionError("Authenticator #1 called " + count(count)
|
||||
+ " expected it to be called " + expected(expectedIncrement));
|
||||
}
|
||||
|
||||
// Connect to the server with a GET request, then with a
|
||||
// POST that contains "Hello World!"
|
||||
// Uses authenticator #2
|
||||
System.out.println("\nClient: Using authenticator #2: "
|
||||
+ toString(authTwo));
|
||||
HTTPTestClient.connect(protocol, server, mode, authTwo);
|
||||
count = authTwo.count.get();
|
||||
if (count != expectedIncrement) {
|
||||
throw new AssertionError("Authenticator #2 called " + count(count)
|
||||
+ " expected it to be called " + expected(expectedIncrement));
|
||||
}
|
||||
|
||||
// Connect to the server with a GET request, then with a
|
||||
// POST that contains "Hello World!"
|
||||
// Uses authenticator #1
|
||||
System.out.println("\nClient: Using authenticator #1 again: "
|
||||
+ toString(authOne));
|
||||
HTTPTestClient.connect(protocol, server, mode, authOne);
|
||||
count = authOne.count.get();
|
||||
if (count != expectedIncrement) {
|
||||
throw new AssertionError("Authenticator #1 called " + count(count)
|
||||
+ " expected it to be called " + expected(expectedIncrement));
|
||||
}
|
||||
count = authTwo.count.get();
|
||||
if (count != expectedIncrement) {
|
||||
throw new AssertionError("Authenticator #2 called " + count(count)
|
||||
+ " expected it to be called " + expected(expectedIncrement));
|
||||
}
|
||||
count = AUTHENTICATOR.count.get();
|
||||
if (count != defaultCount) {
|
||||
throw new AssertionError("Default Authenticator called " + count(count)
|
||||
+ " expected it to be called " + expected(defaultCount));
|
||||
}
|
||||
|
||||
// Now tries with the default authenticator: it should be invoked.
|
||||
System.out.println("\nClient: Using the default authenticator: "
|
||||
+ toString(null));
|
||||
HTTPTestClient.connect(protocol, server, mode, null);
|
||||
count = authOne.count.get();
|
||||
if (count != expectedIncrement) {
|
||||
throw new AssertionError("Authenticator #1 called " + count(count)
|
||||
+ " expected it to be called " + expected(expectedIncrement));
|
||||
}
|
||||
count = authTwo.count.get();
|
||||
if (count != expectedIncrement) {
|
||||
throw new AssertionError("Authenticator #2 called " + count(count)
|
||||
+ " expected it to be called " + expected(expectedIncrement));
|
||||
}
|
||||
count = AUTHENTICATOR.count.get();
|
||||
if (count != defaultCount + expectedIncrement) {
|
||||
throw new AssertionError("Default Authenticator called " + count(count)
|
||||
+ " expected it to be called " + expected(defaultCount + expectedIncrement));
|
||||
}
|
||||
|
||||
// Now tries with explicitly setting the default authenticator: it should
|
||||
// be invoked again.
|
||||
// Uncomment the code below when 8169068 is available.
|
||||
System.out.println("\nClient: Explicitly setting the default authenticator: "
|
||||
+ toString(Authenticator.getDefault()));
|
||||
HTTPTestClient.connect(protocol, server, mode, Authenticator.getDefault());
|
||||
count = authOne.count.get();
|
||||
if (count != expectedIncrement) {
|
||||
throw new AssertionError("Authenticator #1 called " + count(count)
|
||||
+ " expected it to be called " + expected(expectedIncrement));
|
||||
}
|
||||
count = authTwo.count.get();
|
||||
if (count != expectedIncrement) {
|
||||
throw new AssertionError("Authenticator #2 called " + count(count)
|
||||
+ " expected it to be called " + expected(expectedIncrement));
|
||||
}
|
||||
count = AUTHENTICATOR.count.get();
|
||||
if (count != defaultCount + 2 * expectedIncrement) {
|
||||
throw new AssertionError("Default Authenticator called " + count(count)
|
||||
+ " expected it to be called "
|
||||
+ expected(defaultCount + 2 * expectedIncrement));
|
||||
}
|
||||
|
||||
// Now tries to set an authenticator on a connected connection.
|
||||
URL url = url(protocol, server.getAddress(), "/");
|
||||
Proxy proxy = proxy(server, mode);
|
||||
HttpURLConnection conn = openConnection(url, mode, proxy);
|
||||
try {
|
||||
conn.setAuthenticator(null);
|
||||
throw new RuntimeException("Expected NullPointerException"
|
||||
+ " trying to set a null authenticator"
|
||||
+ " not raised.");
|
||||
} catch (NullPointerException npe) {
|
||||
System.out.println("Client: caught expected NPE"
|
||||
+ " trying to set a null authenticator: "
|
||||
+ npe);
|
||||
}
|
||||
conn.connect();
|
||||
try {
|
||||
try {
|
||||
conn.setAuthenticator(authOne);
|
||||
throw new RuntimeException("Expected IllegalStateException"
|
||||
+ " trying to set an authenticator after connect"
|
||||
+ " not raised.");
|
||||
} catch (IllegalStateException ise) {
|
||||
System.out.println("Client: caught expected ISE"
|
||||
+ " trying to set an authenticator after connect: "
|
||||
+ ise);
|
||||
}
|
||||
// Uncomment the code below when 8169068 is available.
|
||||
try {
|
||||
conn.setAuthenticator(Authenticator.getDefault());
|
||||
throw new RuntimeException("Expected IllegalStateException"
|
||||
+ " trying to set an authenticator after connect"
|
||||
+ " not raised.");
|
||||
} catch (IllegalStateException ise) {
|
||||
System.out.println("Client: caught expected ISE"
|
||||
+ " trying to set an authenticator after connect: "
|
||||
+ ise);
|
||||
}
|
||||
try {
|
||||
conn.setAuthenticator(null);
|
||||
throw new RuntimeException("Expected"
|
||||
+ " IllegalStateException or NullPointerException"
|
||||
+ " trying to set a null authenticator after connect"
|
||||
+ " not raised.");
|
||||
} catch (IllegalStateException | NullPointerException xxe) {
|
||||
System.out.println("Client: caught expected "
|
||||
+ xxe.getClass().getSimpleName()
|
||||
+ " trying to set a null authenticator after connect: "
|
||||
+ xxe);
|
||||
}
|
||||
} finally {
|
||||
conn.disconnect();
|
||||
}
|
||||
|
||||
// double check that authOne and authTwo haven't been invoked.
|
||||
count = authOne.count.get();
|
||||
if (count != expectedIncrement) {
|
||||
throw new AssertionError("Authenticator #1 called " + count(count)
|
||||
+ " expected it to be called " + expected(expectedIncrement));
|
||||
}
|
||||
count = authTwo.count.get();
|
||||
if (count != expectedIncrement) {
|
||||
throw new AssertionError("Authenticator #2 called " + count(count)
|
||||
+ " expected it to be called " + expected(expectedIncrement));
|
||||
}
|
||||
|
||||
// All good!
|
||||
// return the number of times the default authenticator is supposed
|
||||
// to have been called.
|
||||
return scheme == HttpSchemeType.NONE ? 0 : 2 * EXPECTED_AUTH_CALLS_PER_TEST;
|
||||
}
|
||||
|
||||
static String toString(Authenticator a) {
|
||||
return a == null ? "null" : a.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.Authenticator;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Stream;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import jdk.test.lib.net.SimpleSSLContext;
|
||||
import static java.net.Proxy.NO_PROXY;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8169415
|
||||
* @library /test/lib
|
||||
* @modules java.logging
|
||||
* java.base/sun.net.www
|
||||
* jdk.httpserver/sun.net.httpserver
|
||||
* @build jdk.test.lib.net.SimpleSSLContext HTTPTest HTTPTestServer HTTPTestClient
|
||||
* @summary A simple HTTP test that starts an echo server supporting Digest
|
||||
* authentication, then starts a regular HTTP client to invoke it.
|
||||
* The client first does a GET request on "/", then follows on
|
||||
* with a POST request that sends "Hello World!" to the server.
|
||||
* The client expects to receive "Hello World!" in return.
|
||||
* The test supports several execution modes:
|
||||
* SERVER: The server performs Digest Server authentication;
|
||||
* PROXY: The server pretends to be a proxy and performs
|
||||
* Digest Proxy authentication;
|
||||
* SERVER307: The server redirects the client (307) to another
|
||||
* server that perform Digest authentication;
|
||||
* PROXY305: The server attempts to redirect
|
||||
* the client to a proxy using 305 code;
|
||||
* @run main/othervm -Dtest.debug=true -Dtest.digest.algorithm=SHA-512 HTTPTest SERVER
|
||||
* @run main/othervm -Dtest.debug=true -Dtest.digest.algorithm=SHA-256 HTTPTest SERVER
|
||||
* @run main/othervm -Dtest.debug=true -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPTest SERVER
|
||||
* @run main/othervm -Dtest.debug=true -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPTest PROXY
|
||||
* @run main/othervm -Dtest.debug=true -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPTest SERVER307
|
||||
* @run main/othervm -Dtest.debug=true -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPTest PROXY305
|
||||
*
|
||||
* @author danielfuchs
|
||||
*/
|
||||
public class HTTPTest {
|
||||
public static final boolean DEBUG =
|
||||
Boolean.parseBoolean(System.getProperty("test.debug", "false"));
|
||||
public static enum HttpAuthType { SERVER, PROXY, SERVER307, PROXY305 };
|
||||
public static enum HttpProtocolType { HTTP, HTTPS };
|
||||
public static enum HttpSchemeType { NONE, BASICSERVER, BASIC, DIGEST };
|
||||
public static final HttpAuthType DEFAULT_HTTP_AUTH_TYPE = HttpAuthType.SERVER;
|
||||
public static final HttpProtocolType DEFAULT_PROTOCOL_TYPE = HttpProtocolType.HTTP;
|
||||
public static final HttpSchemeType DEFAULT_SCHEME_TYPE = HttpSchemeType.DIGEST;
|
||||
|
||||
public static class HttpTestAuthenticator extends Authenticator {
|
||||
private final String realm;
|
||||
private final String username;
|
||||
// Used to prevent incrementation of 'count' when calling the
|
||||
// authenticator from the server side.
|
||||
private final ThreadLocal<Boolean> skipCount = new ThreadLocal<>();
|
||||
// count will be incremented every time getPasswordAuthentication()
|
||||
// is called from the client side.
|
||||
final AtomicInteger count = new AtomicInteger();
|
||||
private final String name;
|
||||
|
||||
public HttpTestAuthenticator(String name, String realm, String username) {
|
||||
this.name = name;
|
||||
this.realm = realm;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
if (skipCount.get() == null || skipCount.get().booleanValue() == false) {
|
||||
System.out.println("Authenticator " + name + " called: " + count.incrementAndGet());
|
||||
}
|
||||
return new PasswordAuthentication(getUserName(),
|
||||
new char[] {'b','a','r'});
|
||||
}
|
||||
|
||||
// Called by the server side to get the password of the user
|
||||
// being authentified.
|
||||
public final char[] getPassword(String user) {
|
||||
if (user.equals(username)) {
|
||||
skipCount.set(Boolean.TRUE);
|
||||
try {
|
||||
return getPasswordAuthentication().getPassword();
|
||||
} finally {
|
||||
skipCount.set(Boolean.FALSE);
|
||||
}
|
||||
}
|
||||
throw new SecurityException("User unknown: " + user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "[name=\"" + name + "\"]";
|
||||
}
|
||||
|
||||
public final String getUserName() {
|
||||
return username;
|
||||
}
|
||||
public final String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
}
|
||||
public static final HttpTestAuthenticator AUTHENTICATOR;
|
||||
static {
|
||||
AUTHENTICATOR = new HttpTestAuthenticator("AUTHENTICATOR","dublin", "foox");
|
||||
Authenticator.setDefault(AUTHENTICATOR);
|
||||
}
|
||||
|
||||
static {
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
SSLContext.setDefault(SimpleSSLContext.findSSLContext());
|
||||
}
|
||||
|
||||
static final Logger logger = Logger.getLogger ("com.sun.net.httpserver");
|
||||
static {
|
||||
if (DEBUG) logger.setLevel(Level.ALL);
|
||||
Stream.of(Logger.getLogger("").getHandlers())
|
||||
.forEach(h -> h.setLevel(Level.ALL));
|
||||
}
|
||||
|
||||
static final int EXPECTED_AUTH_CALLS_PER_TEST = 1;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// new HTTPTest().execute(HttpAuthType.SERVER.name());
|
||||
new HTTPTest().execute(args);
|
||||
}
|
||||
|
||||
public void execute(String... args) throws Exception {
|
||||
Stream<HttpAuthType> modes;
|
||||
if (args == null || args.length == 0) {
|
||||
modes = Stream.of(HttpAuthType.values());
|
||||
} else {
|
||||
modes = Stream.of(args).map(HttpAuthType::valueOf);
|
||||
}
|
||||
modes.forEach(this::test);
|
||||
System.out.println("Test PASSED - Authenticator called: "
|
||||
+ expected(AUTHENTICATOR.count.get()));
|
||||
}
|
||||
|
||||
public void test(HttpAuthType mode) {
|
||||
for (HttpProtocolType type: HttpProtocolType.values()) {
|
||||
test(type, mode);
|
||||
}
|
||||
}
|
||||
|
||||
public HttpSchemeType getHttpSchemeType() {
|
||||
return DEFAULT_SCHEME_TYPE;
|
||||
}
|
||||
|
||||
public void test(HttpProtocolType protocol, HttpAuthType mode) {
|
||||
if (mode == HttpAuthType.PROXY305 && protocol == HttpProtocolType.HTTPS ) {
|
||||
// silently skip unsupported test combination
|
||||
return;
|
||||
}
|
||||
String digestalg = System.getProperty("test.digest.algorithm");
|
||||
if (digestalg == null || "".equals(digestalg))
|
||||
digestalg = "MD5";
|
||||
|
||||
System.out.println("\n**** Testing " + protocol + " "
|
||||
+ mode + " mode ****\n");
|
||||
int authCount = AUTHENTICATOR.count.get();
|
||||
int expectedIncrement = 0;
|
||||
try {
|
||||
// Creates an HTTP server that echoes back whatever is in the
|
||||
// request body.
|
||||
HTTPTestServer server =
|
||||
HTTPTestServer.create(protocol,
|
||||
mode,
|
||||
AUTHENTICATOR,
|
||||
getHttpSchemeType(),
|
||||
null,
|
||||
digestalg);
|
||||
try {
|
||||
expectedIncrement += run(server, protocol, mode);
|
||||
} finally {
|
||||
server.stop();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace(System.err);
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
int count = AUTHENTICATOR.count.get();
|
||||
if (count != authCount + expectedIncrement) {
|
||||
throw new AssertionError("Authenticator called " + count(count)
|
||||
+ " expected it to be called "
|
||||
+ expected(authCount + expectedIncrement));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the test with the given parameters.
|
||||
* @param server The server
|
||||
* @param protocol The protocol (HTTP/HTTPS)
|
||||
* @param mode The mode (PROXY, SERVER, SERVER307...)
|
||||
* @return The number of times the default authenticator should have been
|
||||
* called.
|
||||
* @throws IOException in case of connection or protocol issues
|
||||
*/
|
||||
public int run(HTTPTestServer server,
|
||||
HttpProtocolType protocol,
|
||||
HttpAuthType mode)
|
||||
throws IOException
|
||||
{
|
||||
// Connect to the server with a GET request, then with a
|
||||
// POST that contains "Hello World!"
|
||||
HTTPTestClient.connect(protocol, server, mode, null);
|
||||
// return the number of times the default authenticator is supposed
|
||||
// to have been called.
|
||||
return EXPECTED_AUTH_CALLS_PER_TEST;
|
||||
}
|
||||
|
||||
public static String count(int count) {
|
||||
switch(count) {
|
||||
case 0: return "not even once";
|
||||
case 1: return "once";
|
||||
case 2: return "twice";
|
||||
default: return String.valueOf(count) + " times";
|
||||
}
|
||||
}
|
||||
|
||||
public static String expected(int count) {
|
||||
switch(count) {
|
||||
default: return count(count);
|
||||
}
|
||||
}
|
||||
public static String protocol(HttpProtocolType type) {
|
||||
return type.name().toLowerCase(Locale.US);
|
||||
}
|
||||
|
||||
public static URL url(HttpProtocolType protocol, InetSocketAddress address,
|
||||
String path) throws MalformedURLException {
|
||||
return new URL(protocol(protocol),
|
||||
address.getAddress().getHostAddress(),
|
||||
address.getPort(), path);
|
||||
}
|
||||
|
||||
public static Proxy proxy(HTTPTestServer server, HttpAuthType authType) {
|
||||
if (authType != HttpAuthType.PROXY) return null;
|
||||
|
||||
InetSocketAddress proxyAddress = server.getProxyAddress();
|
||||
if (!proxyAddress.isUnresolved()) {
|
||||
// Forces the proxy to use an unresolved address created
|
||||
// from the actual IP address to avoid using the proxy
|
||||
// address hostname which would result in resolving to
|
||||
// a posibly different address. For instance we want to
|
||||
// avoid cases such as:
|
||||
// ::1 => "localhost" => 127.0.0.1
|
||||
proxyAddress = InetSocketAddress.
|
||||
createUnresolved(proxyAddress.getAddress().getHostAddress(),
|
||||
proxyAddress.getPort());
|
||||
}
|
||||
|
||||
return new Proxy(Proxy.Type.HTTP, proxyAddress);
|
||||
}
|
||||
|
||||
public static HttpURLConnection openConnection(URL url,
|
||||
HttpAuthType authType,
|
||||
Proxy proxy)
|
||||
throws IOException {
|
||||
|
||||
HttpURLConnection conn = (HttpURLConnection)
|
||||
(authType == HttpAuthType.PROXY
|
||||
? url.openConnection(proxy)
|
||||
: url.openConnection(NO_PROXY));
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Authenticator;
|
||||
import java.net.BindException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
/**
|
||||
* A simple Http client that connects to the HTTPTestServer.
|
||||
* @author danielfuchs
|
||||
*/
|
||||
public class HTTPTestClient extends HTTPTest {
|
||||
|
||||
public static final long DELAY_BEFORE_RETRY = 2500; // milliseconds
|
||||
|
||||
public static void connect(HttpProtocolType protocol,
|
||||
HTTPTestServer server,
|
||||
HttpAuthType authType,
|
||||
Authenticator auth)
|
||||
throws IOException {
|
||||
try {
|
||||
doConnect(protocol, server, authType, auth);
|
||||
} catch (BindException ex) {
|
||||
// sleep a bit then try again once
|
||||
System.out.println("WARNING: Unexpected BindException: " + ex);
|
||||
System.out.println("\tSleeping a bit and try again...");
|
||||
long start = System.nanoTime();
|
||||
System.gc();
|
||||
try {
|
||||
Thread.sleep(DELAY_BEFORE_RETRY);
|
||||
} catch (InterruptedException iex) {
|
||||
// ignore
|
||||
}
|
||||
System.gc();
|
||||
System.out.println("\tRetrying after "
|
||||
+ Duration.ofNanos(System.nanoTime() - start).toMillis()
|
||||
+ " milliseconds");
|
||||
doConnect(protocol, server, authType, auth);
|
||||
}
|
||||
}
|
||||
|
||||
public static void doConnect(HttpProtocolType protocol,
|
||||
HTTPTestServer server,
|
||||
HttpAuthType authType,
|
||||
Authenticator auth)
|
||||
throws IOException {
|
||||
|
||||
InetSocketAddress address = server.getAddress();
|
||||
final URL url = url(protocol, address, "/");
|
||||
final Proxy proxy = proxy(server, authType);
|
||||
|
||||
System.out.println("Client: FIRST request: " + url + " GET");
|
||||
HttpURLConnection conn = openConnection(url, authType, proxy);
|
||||
configure(conn, auth);
|
||||
System.out.println("Response code: " + conn.getResponseCode());
|
||||
String result = new String(conn.getInputStream().readAllBytes(), "UTF-8");
|
||||
System.out.println("Response body: " + result);
|
||||
if (!result.isEmpty()) {
|
||||
throw new RuntimeException("Unexpected response to GET: " + result);
|
||||
}
|
||||
System.out.println("\nClient: NEXT request: " + url + " POST");
|
||||
conn = openConnection(url, authType, proxy);
|
||||
configure(conn, auth);
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
conn.getOutputStream().write("Hello World!".getBytes("UTF-8"));
|
||||
System.out.println("Response code: " + conn.getResponseCode());
|
||||
result = new String(conn.getInputStream().readAllBytes(), "UTF-8");
|
||||
System.out.println("Response body: " + result);
|
||||
if ("Hello World!".equals(result)) {
|
||||
System.out.println("Test passed!");
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected response to POST: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
private static void configure(HttpURLConnection conn, Authenticator auth)
|
||||
throws IOException {
|
||||
if (auth != null) {
|
||||
conn.setAuthenticator(auth);
|
||||
}
|
||||
if (conn instanceof HttpsURLConnection) {
|
||||
System.out.println("Client: configuring SSL connection");
|
||||
// We have set a default SSLContext so we don't need to do
|
||||
// anything here. Otherwise it could look like:
|
||||
// HttpsURLConnection httpsConn = (HttpsURLConnection)conn;
|
||||
// httpsConn.setSSLSocketFactory(
|
||||
// SimpleSSLContext.findSSLContext().getSocketFactory());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
157
test/jdk/java/net/HttpURLConnection/UnmodifiableMaps.java
Normal file
157
test/jdk/java/net/HttpURLConnection/UnmodifiableMaps.java
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 7128648
|
||||
* @library /test/lib
|
||||
* @modules jdk.httpserver
|
||||
* @summary HttpURLConnection.getHeaderFields should return an unmodifiable Map
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import com.sun.net.httpserver.Headers;
|
||||
import static java.net.Proxy.NO_PROXY;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class UnmodifiableMaps {
|
||||
|
||||
void test(String[] args) throws Exception {
|
||||
HttpServer server = startHttpServer();
|
||||
try {
|
||||
InetSocketAddress address = server.getAddress();
|
||||
URI uri = URIBuilder.newBuilder()
|
||||
.scheme("http")
|
||||
.host(address.getAddress())
|
||||
.port(address.getPort())
|
||||
.path("/foo")
|
||||
.build();
|
||||
doClient(uri);
|
||||
} finally {
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
|
||||
void doClient(URI uri) throws Exception {
|
||||
HttpURLConnection uc = (HttpURLConnection) uri.toURL().openConnection(NO_PROXY);
|
||||
|
||||
// Test1: getRequestProperties is unmodifiable
|
||||
System.out.println("Check getRequestProperties");
|
||||
checkUnmodifiable(uc.getRequestProperties());
|
||||
uc.addRequestProperty("X", "V");
|
||||
uc.addRequestProperty("X1", "V1");
|
||||
checkUnmodifiable(uc.getRequestProperties());
|
||||
|
||||
int resp = uc.getResponseCode();
|
||||
check(resp == 200,
|
||||
"Unexpected response code. Expected 200, got " + resp);
|
||||
|
||||
// Test2: getHeaderFields is unmodifiable
|
||||
System.out.println("Check getHeaderFields");
|
||||
checkUnmodifiable(uc.getHeaderFields());
|
||||
// If the implementation does caching, check again.
|
||||
checkUnmodifiable(uc.getHeaderFields());
|
||||
}
|
||||
|
||||
// HTTP Server
|
||||
HttpServer startHttpServer() throws IOException {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
|
||||
httpServer.createContext("/foo", new SimpleHandler());
|
||||
httpServer.start();
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
class SimpleHandler implements HttpHandler {
|
||||
@Override
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
Headers respHeaders = t.getResponseHeaders();
|
||||
// ensure some response headers, over the usual ones
|
||||
respHeaders.add("RespHdr1", "Value1");
|
||||
respHeaders.add("RespHdr2", "Value2");
|
||||
respHeaders.add("RespHdr3", "Value3");
|
||||
t.sendResponseHeaders(200, -1);
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
|
||||
void checkUnmodifiable(Map<String,List<String>> map) {
|
||||
checkUnmodifiableMap(map);
|
||||
|
||||
// Now check the individual values
|
||||
Collection<List<String>> values = map.values();
|
||||
for (List<String> value : values) {
|
||||
checkUnmodifiableList(value);
|
||||
}
|
||||
}
|
||||
|
||||
void checkUnmodifiableMap(final Map<String,List<String>> map) {
|
||||
expectThrow( new Runnable() {
|
||||
public void run() { map.clear(); }});
|
||||
expectThrow( new Runnable() {
|
||||
public void run() { map.put("X", new ArrayList<String>()); }});
|
||||
expectThrow( new Runnable() {
|
||||
public void run() { map.remove("X"); }});
|
||||
}
|
||||
|
||||
void checkUnmodifiableList(final List<String> list) {
|
||||
expectThrow( new Runnable() {
|
||||
public void run() { list.clear(); }});
|
||||
expectThrow( new Runnable() {
|
||||
public void run() { list.add("X"); }});
|
||||
expectThrow( new Runnable() {
|
||||
public void run() { list.remove("X"); }});
|
||||
}
|
||||
|
||||
void expectThrow(Runnable r) {
|
||||
try { r.run(); fail("Excepted UOE to be thrown."); Thread.dumpStack(); }
|
||||
catch (UnsupportedOperationException e) { pass(); }
|
||||
}
|
||||
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++;}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
Class<?> k = new Object(){}.getClass().getEnclosingClass();
|
||||
try {k.getMethod("instanceMain",String[].class)
|
||||
.invoke( k.newInstance(), (Object) args);}
|
||||
catch (Throwable e) {throw e.getCause();}}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
64
test/jdk/java/net/HttpURLConnection/getResponseCode.java
Normal file
64
test/jdk/java/net/HttpURLConnection/getResponseCode.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4666195
|
||||
* @build getResponseCode
|
||||
* @run main getResponseCode
|
||||
* @summary REGRESSION: HttpURLConnection.getResponseCode() returns always -1
|
||||
*/
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
|
||||
public class getResponseCode {
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
MyHttpURLConnectionImpl myCon = new MyHttpURLConnectionImpl(null);
|
||||
int responseCode = myCon.getResponseCode();
|
||||
if (responseCode == -1) {
|
||||
throw new RuntimeException("java.net.HttpURLConnection "
|
||||
+"should provide implementation "
|
||||
+"for getResponseCode()");
|
||||
}
|
||||
} catch (java.net.UnknownServiceException e) {
|
||||
System.out.println("PASS");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MyHttpURLConnectionImpl extends java.net.HttpURLConnection {
|
||||
MyHttpURLConnectionImpl(URL url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
public boolean usingProxy(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public void connect(){
|
||||
}
|
||||
|
||||
public void disconnect(){
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue