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
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// SunJSSE does not support dynamic system properties, no way to re-use
|
||||
// system properties in samevm/agentvm mode.
|
||||
//
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6216082
|
||||
* @summary Redirect problem with HttpsURLConnection using a proxy
|
||||
* @modules java.base/sun.net.www
|
||||
* @library .. /test/lib
|
||||
* @build jdk.test.lib.NetworkConfiguration
|
||||
* jdk.test.lib.Platform
|
||||
* ClosedChannelList
|
||||
* TunnelProxy
|
||||
* @key intermittent
|
||||
* @run main/othervm B6216082
|
||||
*/
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.URL;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpsConfigurator;
|
||||
import com.sun.net.httpserver.HttpsServer;
|
||||
import jdk.test.lib.NetworkConfiguration;
|
||||
|
||||
public class B6216082 {
|
||||
static SimpleHttpTransaction httpTrans;
|
||||
static HttpsServer server;
|
||||
static TunnelProxy proxy;
|
||||
|
||||
// it seems there's no proxy ever if a url points to 'localhost',
|
||||
// even if proxy related properties are set. so we need to bind
|
||||
// our simple http proxy and http server to a non-loopback address
|
||||
static InetAddress firstNonLoAddress = null;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
HostnameVerifier reservedHV =
|
||||
HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
try {
|
||||
// XXX workaround for CNFE
|
||||
Class.forName("java.nio.channels.ClosedByInterruptException");
|
||||
if (!setupEnv()) {
|
||||
return;
|
||||
}
|
||||
startHttpServer();
|
||||
// https.proxyPort can only be set after the TunnelProxy has been
|
||||
// created as it will use an ephemeral port.
|
||||
ProxySelector.setDefault(ProxySelector.of(new InetSocketAddress(firstNonLoAddress, proxy.getLocalPort())));
|
||||
makeHttpCall();
|
||||
} finally {
|
||||
if (proxy != null) {
|
||||
proxy.terminate();
|
||||
}
|
||||
if (server != null) {
|
||||
server.stop(1);
|
||||
}
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(reservedHV);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Where do we find the keystores for ssl?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
public static boolean setupEnv() throws Exception {
|
||||
firstNonLoAddress = getNonLoAddress();
|
||||
if (firstNonLoAddress == null) {
|
||||
System.err.println("The test needs at least one non-loopback address to run. Quit now.");
|
||||
return false;
|
||||
}
|
||||
System.out.println(firstNonLoAddress.getHostAddress());
|
||||
// will use proxy
|
||||
System.setProperty( "https.proxyHost", firstNonLoAddress.getHostAddress());
|
||||
|
||||
// setup properties to do ssl
|
||||
String keyFilename = System.getProperty("test.src", "./") + "/" +
|
||||
pathToStores + "/" + keyStoreFile;
|
||||
String trustFilename = System.getProperty("test.src", "./") + "/" +
|
||||
pathToStores + "/" + trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(new NameVerifier());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static InetAddress getNonLoAddress() throws Exception {
|
||||
InetAddress lh = InetAddress.getByName("localhost");
|
||||
NetworkInterface loNIC = NetworkInterface.getByInetAddress(lh);
|
||||
|
||||
NetworkConfiguration nc = NetworkConfiguration.probe();
|
||||
Optional<InetAddress> oaddr = nc.interfaces()
|
||||
.filter(nif -> !nif.getName().equalsIgnoreCase(loNIC.getName()))
|
||||
.flatMap(nif -> nc.addresses(nif))
|
||||
.filter(a -> !a.isLoopbackAddress())
|
||||
.findFirst();
|
||||
|
||||
return oaddr.orElseGet(() -> null);
|
||||
}
|
||||
|
||||
public static void startHttpServer() throws Exception {
|
||||
// Both the https server and the proxy let the
|
||||
// system pick up an ephemeral port.
|
||||
httpTrans = new SimpleHttpTransaction();
|
||||
// create and initialize a SSLContext
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
KeyStore ts = KeyStore.getInstance("JKS");
|
||||
char[] passphrase = "passphrase".toCharArray();
|
||||
|
||||
ks.load(new FileInputStream(System.getProperty("javax.net.ssl.keyStore")), passphrase);
|
||||
ts.load(new FileInputStream(System.getProperty("javax.net.ssl.trustStore")), passphrase);
|
||||
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||
kmf.init(ks, passphrase);
|
||||
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
||||
tmf.init(ts);
|
||||
|
||||
SSLContext sslCtx = SSLContext.getInstance("TLS");
|
||||
|
||||
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||
|
||||
server = HttpsServer.create(new InetSocketAddress(firstNonLoAddress, 0), 10);
|
||||
server.setHttpsConfigurator(new HttpsConfigurator(sslCtx));
|
||||
server.createContext("/", httpTrans);
|
||||
server.setExecutor(Executors.newSingleThreadExecutor());
|
||||
server.start();
|
||||
proxy = new TunnelProxy(1, 10, firstNonLoAddress, 0);
|
||||
}
|
||||
|
||||
public static void makeHttpCall() throws Exception {
|
||||
System.out.println("https server listen on: " + server.getAddress().getPort());
|
||||
System.out.println("https proxy listen on: " + proxy.getLocalPort());
|
||||
URL url = new URL("https" , firstNonLoAddress.getHostAddress(),
|
||||
server.getAddress().getPort(), "/");
|
||||
HttpURLConnection uc = (HttpURLConnection)url.openConnection();
|
||||
System.out.println(uc.getResponseCode());
|
||||
if(uc.getResponseCode() != 200) {
|
||||
uc.disconnect();
|
||||
throw new RuntimeException("Test failed : bad http request with response code : "+ uc.getResponseCode());
|
||||
}
|
||||
uc.disconnect();
|
||||
}
|
||||
|
||||
static class NameVerifier implements HostnameVerifier {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleHttpTransaction implements HttpHandler {
|
||||
|
||||
/*
|
||||
* Our http server which simply redirect first call
|
||||
*/
|
||||
public void handle(HttpExchange trans) {
|
||||
try {
|
||||
String path = trans.getRequestURI().getPath();
|
||||
if (path.equals("/")) {
|
||||
// the first call, redirect it
|
||||
String location = "/redirect";
|
||||
trans.getResponseHeaders().set("Location", location);
|
||||
trans.sendResponseHeaders(302, -1);
|
||||
} else {
|
||||
trans.sendResponseHeaders(200, -1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6226610 6973030
|
||||
* @summary HTTP tunnel connections send user headers to proxy
|
||||
* @library /test/lib
|
||||
* @run main/othervm B6226610
|
||||
*/
|
||||
|
||||
/* This class includes a proxy server that processes the HTTP CONNECT request,
|
||||
* and validates that the request does not have the user defined header in it.
|
||||
* The proxy server always returns 400 Bad Request so that the Http client
|
||||
* will not try to proceed with the connection as there is no back end http server.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import jdk.test.lib.net.HttpHeaderParser;
|
||||
|
||||
|
||||
public class B6226610 {
|
||||
static HeaderCheckerProxyTunnelServer proxy;
|
||||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
proxy = new HeaderCheckerProxyTunnelServer();
|
||||
proxy.start();
|
||||
|
||||
InetAddress localHost = InetAddress.getLocalHost();
|
||||
String hostname = localHost.getHostName();
|
||||
String hostAddress = localHost.getHostAddress();
|
||||
|
||||
try {
|
||||
URL u = new URL("https://" + hostname + "/");
|
||||
System.out.println("Connecting to " + u);
|
||||
InetSocketAddress proxyAddr = InetSocketAddress.createUnresolved(hostAddress, proxy.getLocalPort());
|
||||
java.net.URLConnection c = u.openConnection(new Proxy(Proxy.Type.HTTP, proxyAddr));
|
||||
|
||||
/* I want this header to go to the destination server only, protected
|
||||
* by SSL
|
||||
*/
|
||||
c.setRequestProperty("X-TestHeader", "value");
|
||||
c.connect();
|
||||
|
||||
} catch (IOException e) {
|
||||
if ( e.getMessage().equals("Unable to tunnel through proxy. Proxy returns \"HTTP/1.1 400 Bad Request\"") )
|
||||
{
|
||||
// OK. Proxy will always return 400 so that the main thread can terminate correctly.
|
||||
}
|
||||
else
|
||||
System.out.println(e);
|
||||
} finally {
|
||||
if (proxy != null) proxy.shutdown();
|
||||
}
|
||||
|
||||
if (HeaderCheckerProxyTunnelServer.failed)
|
||||
throw new RuntimeException("Test failed; see output");
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderCheckerProxyTunnelServer extends Thread
|
||||
{
|
||||
public static boolean failed = false;
|
||||
|
||||
private static ServerSocket ss = null;
|
||||
|
||||
// client requesting for 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 HeaderCheckerProxyTunnelServer() throws IOException
|
||||
{
|
||||
if (ss == null) {
|
||||
ss = new ServerSocket();
|
||||
ss.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
try { ss.close(); } catch (IOException e) {}
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
clientSocket = ss.accept();
|
||||
processRequests();
|
||||
} catch (IOException e) {
|
||||
System.out.println("Proxy Failed: " + e);
|
||||
e.printStackTrace();
|
||||
try {
|
||||
ss.close();
|
||||
}
|
||||
catch (IOException excep) {
|
||||
System.out.println("ProxyServer close error: " + excep);
|
||||
excep.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port on which the proxy is accepting connections.
|
||||
*/
|
||||
public int getLocalPort() {
|
||||
return ss.getLocalPort();
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes the CONNECT request
|
||||
*/
|
||||
private void processRequests() throws IOException
|
||||
{
|
||||
InputStream in = clientSocket.getInputStream();
|
||||
HttpHeaderParser mheader = new HttpHeaderParser(in);
|
||||
String statusLine = mheader.getRequestDetails();
|
||||
|
||||
if (statusLine.startsWith("CONNECT")) {
|
||||
// retrieve the host and port info from the status-line
|
||||
retrieveConnectInfo(statusLine);
|
||||
|
||||
if (mheader.getHeaderValue("X-TestHeader") != null) {
|
||||
System.out.println("Proxy should not receive user defined headers for tunneled requests");
|
||||
failed = true;
|
||||
}
|
||||
|
||||
// 6973030
|
||||
String value;
|
||||
if ((value = mheader.getHeaderValue("Proxy-Connection").get(0)) == null ||
|
||||
!value.equals("keep-alive")) {
|
||||
System.out.println("Proxy-Connection:keep-alive not being sent");
|
||||
failed = true;
|
||||
}
|
||||
|
||||
//This will allow the main thread to terminate without trying to perform the SSL handshake.
|
||||
send400();
|
||||
|
||||
in.close();
|
||||
clientSocket.close();
|
||||
ss.close();
|
||||
}
|
||||
else {
|
||||
System.out.println("proxy server: processes only "
|
||||
+ "CONNECT method requests, recieved: "
|
||||
+ statusLine);
|
||||
}
|
||||
}
|
||||
|
||||
private void send400() throws IOException
|
||||
{
|
||||
OutputStream out = clientSocket.getOutputStream();
|
||||
PrintWriter pout = new PrintWriter(out);
|
||||
|
||||
pout.println("HTTP/1.1 400 Bad Request");
|
||||
pout.println();
|
||||
pout.flush();
|
||||
}
|
||||
|
||||
private void restart() throws IOException {
|
||||
(new Thread(this)).start();
|
||||
}
|
||||
|
||||
/*
|
||||
* This method retrieves the hostname and port of the destination
|
||||
* that the connect request wants to establish a tunnel for
|
||||
* communication.
|
||||
* The input, connectStr is of the form:
|
||||
* CONNECT server-name:server-port HTTP/1.x
|
||||
*/
|
||||
private void retrieveConnectInfo(String connectStr) throws IOException {
|
||||
|
||||
int starti;
|
||||
int endi;
|
||||
String connectInfo;
|
||||
String serverName = null;
|
||||
try {
|
||||
starti = connectStr.indexOf(' ');
|
||||
endi = connectStr.lastIndexOf(' ');
|
||||
connectInfo = connectStr.substring(starti+1, endi).trim();
|
||||
// retrieve server name and port
|
||||
endi = connectInfo.indexOf(':');
|
||||
serverName = connectInfo.substring(0, endi);
|
||||
serverPort = Integer.parseInt(connectInfo.substring(endi+1));
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Proxy recieved a request: "
|
||||
+ connectStr, e);
|
||||
}
|
||||
serverInetAddr = InetAddress.getByName(serverName);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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 4423074
|
||||
* @summary Need to rebase all the duplicated classes from Merlin.
|
||||
* This test will check out http POST
|
||||
* @modules java.base/sun.net.www.protocol.https
|
||||
*/
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.lang.reflect.*;
|
||||
import sun.net.www.protocol.https.HttpsURLConnectionImpl;
|
||||
|
||||
public class CheckMethods {
|
||||
static boolean debug = false;
|
||||
static class MethodSignature {
|
||||
String name;
|
||||
Class[] paramTypes;
|
||||
MethodSignature(String name, Class[] paramTypes) {
|
||||
this.name = name;
|
||||
this.paramTypes = paramTypes;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (debug)
|
||||
System.out.println("comparing "+this +" against: "+obj);
|
||||
if (!(obj instanceof MethodSignature)) {
|
||||
if (debug)
|
||||
System.out.println(false);
|
||||
return false;
|
||||
}
|
||||
MethodSignature ms = (MethodSignature) obj;
|
||||
Class[] types = ms.paramTypes;
|
||||
try {
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (!types[i].equals(paramTypes[i])) {
|
||||
if (debug)
|
||||
System.out.println(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (debug)
|
||||
System.out.println(false);
|
||||
return false;
|
||||
}
|
||||
boolean result = this.name.equals(ms.name);
|
||||
if (debug)
|
||||
System.out.println(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(name+"(");
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
sb.append(paramTypes[i].getName()+",");
|
||||
if (i == (paramTypes.length -1))
|
||||
sb.deleteCharAt(sb.length()-1);
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// check HttpsURLConnectionImpl contain all public and protected methods
|
||||
// defined in HttpURLConnection and URLConnection
|
||||
public static void main(String[] args) {
|
||||
ArrayList allMethods = new ArrayList(
|
||||
Arrays.asList(HttpURLConnection.class.getDeclaredMethods()));
|
||||
allMethods.addAll(Arrays.asList(URLConnection.class.getDeclaredMethods()));
|
||||
ArrayList allMethodSignatures = new ArrayList();
|
||||
for (Iterator itr = allMethods.iterator(); itr.hasNext(); ) {
|
||||
Method m = (Method)itr.next();
|
||||
// don't include static and private methods
|
||||
if (!Modifier.isStatic(m.getModifiers()) &&
|
||||
(Modifier.isPublic(m.getModifiers()) ||
|
||||
Modifier.isProtected(m.getModifiers()))) {
|
||||
allMethodSignatures.add(
|
||||
new MethodSignature(m.getName(), m.getParameterTypes()));
|
||||
}
|
||||
}
|
||||
|
||||
// testing HttpsURLConnectionImpl
|
||||
List httpsMethods =
|
||||
Arrays.asList(HttpsURLConnectionImpl.class.getDeclaredMethods());
|
||||
|
||||
ArrayList httpsMethodSignatures = new ArrayList();
|
||||
for (Iterator itr = httpsMethods.iterator(); itr.hasNext(); ) {
|
||||
Method m = (Method)itr.next();
|
||||
if (!Modifier.isStatic(m.getModifiers())) {
|
||||
httpsMethodSignatures.add(
|
||||
new MethodSignature(m.getName(), m.getParameterTypes()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!httpsMethodSignatures.containsAll(allMethodSignatures)) {
|
||||
throw new RuntimeException("Method definition test failed on HttpsURLConnectionImpl");
|
||||
}
|
||||
|
||||
// testing for non static public field
|
||||
ArrayList allFields = new ArrayList(
|
||||
Arrays.asList(URLConnection.class.getFields()));
|
||||
allFields.addAll(Arrays.asList(HttpURLConnection.class.getFields()));
|
||||
|
||||
for (Iterator itr = allFields.iterator(); itr.hasNext(); ) {
|
||||
Field f = (Field) itr.next();
|
||||
if (!Modifier.isStatic(f.getModifiers())) {
|
||||
throw new RuntimeException("Non static Public fields" +
|
||||
" declared in superclasses");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2011, 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 6618387
|
||||
* @summary SSL client sessions do not close cleanly. A TCP reset occurs
|
||||
* instead of a close_notify alert.
|
||||
* @run main/othervm CloseKeepAliveCached
|
||||
*
|
||||
* SunJSSE does not support dynamic system properties, no way to re-use
|
||||
* system properties in samevm/agentvm mode.
|
||||
*
|
||||
* @ignore
|
||||
* After run the test manually, at the end of the debug output,
|
||||
* if "MainThread, called close()" found, the test passed. Otherwise,
|
||||
* if "Keep-Alive-Timer: called close()", the test failed.
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import javax.net.ssl.*;
|
||||
|
||||
public class CloseKeepAliveCached {
|
||||
static Map cookies;
|
||||
ServerSocket ss;
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* Set the various variables needed for the tests, then
|
||||
* specify what tests to run on each side.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
static boolean separateServerThread = true;
|
||||
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
volatile static boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Turn on SSL debugging?
|
||||
*/
|
||||
static boolean debug = false;
|
||||
|
||||
private SSLServerSocket sslServerSocket = null;
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
SSLServerSocketFactory sslssf =
|
||||
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
||||
sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket(serverPort);
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
SSLSocket sslSocket = null;
|
||||
try {
|
||||
sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
for (int i = 0; i < 3 && !sslSocket.isClosed(); i++) {
|
||||
// read request
|
||||
InputStream is = sslSocket.getInputStream ();
|
||||
|
||||
BufferedReader r = new BufferedReader(
|
||||
new InputStreamReader(is));
|
||||
String x;
|
||||
while ((x=r.readLine()) != null) {
|
||||
if (x.length() ==0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PrintStream out = new PrintStream(
|
||||
new BufferedOutputStream(
|
||||
sslSocket.getOutputStream() ));
|
||||
|
||||
/* send the header */
|
||||
out.print("HTTP/1.1 200 OK\r\n");
|
||||
out.print("Keep-Alive: timeout=15, max=100\r\n");
|
||||
out.print("Connection: Keep-Alive\r\n");
|
||||
out.print("Content-Type: text/html; charset=iso-8859-1\r\n");
|
||||
out.print("Content-Length: 9\r\n");
|
||||
out.print("\r\n");
|
||||
out.print("Testing\r\n");
|
||||
out.flush();
|
||||
|
||||
Thread.sleep(50);
|
||||
}
|
||||
sslSocket.close();
|
||||
sslServerSocket.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
HostnameVerifier reservedHV =
|
||||
HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
try {
|
||||
HttpsURLConnection http = null;
|
||||
|
||||
/* establish http connection to server */
|
||||
URL url = new URL("https://localhost:" + serverPort+"/");
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(new NameVerifier());
|
||||
http = (HttpsURLConnection)url.openConnection();
|
||||
InputStream is = http.getInputStream ();
|
||||
while (is.read() != -1);
|
||||
is.close();
|
||||
|
||||
url = new URL("https://localhost:" + serverPort+"/");
|
||||
http = (HttpsURLConnection)url.openConnection();
|
||||
is = http.getInputStream ();
|
||||
while (is.read() != -1);
|
||||
|
||||
// if inputstream.close() called, the http.disconnect() will
|
||||
// not able to close the cached connection. If application
|
||||
// wanna close the keep-alive cached connection immediately
|
||||
// with httpURLConnection.disconnect(), they should not call
|
||||
// inputstream.close() explicitly, the
|
||||
// httpURLConnection.disconnect() will do it internally.
|
||||
// is.close();
|
||||
|
||||
// close the connection, sending close_notify to peer.
|
||||
// otherwise, the connection will be closed by Finalizer or
|
||||
// Keep-Alive-Timer if timeout.
|
||||
http.disconnect();
|
||||
// Thread.sleep(5000);
|
||||
} catch (IOException ioex) {
|
||||
if (sslServerSocket != null)
|
||||
sslServerSocket.close();
|
||||
throw ioex;
|
||||
} finally {
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(reservedHV);
|
||||
}
|
||||
}
|
||||
|
||||
static class NameVerifier implements HostnameVerifier {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
volatile int serverPort = 0;
|
||||
|
||||
volatile Exception serverException = null;
|
||||
volatile Exception clientException = null;
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
String keyFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + keyStoreFile;
|
||||
String trustFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
if (debug)
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
new CloseKeepAliveCached();
|
||||
}
|
||||
|
||||
Thread clientThread = null;
|
||||
Thread serverThread = null;
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
CloseKeepAliveCached() throws Exception {
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
serverThread.join();
|
||||
} else {
|
||||
clientThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
*
|
||||
* If the main thread excepted, that propagates back
|
||||
* immediately. If the other thread threw an exception, we
|
||||
* should report back.
|
||||
*/
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
if (clientException != null)
|
||||
throw clientException;
|
||||
}
|
||||
|
||||
void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
doServerSide();
|
||||
}
|
||||
}
|
||||
|
||||
void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died...");
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
doClientSide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 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 4696506 4942650
|
||||
* @summary Unit test for java.net.CookieHandler
|
||||
* @library /test/lib
|
||||
* @run main/othervm CookieHandlerTest
|
||||
*
|
||||
* SunJSSE does not support dynamic system properties, no way to re-use
|
||||
* system properties in samevm/agentvm mode.
|
||||
* @author Yingxian Wang
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import javax.net.ssl.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class CookieHandlerTest {
|
||||
static Map<String,String> cookies;
|
||||
ServerSocket ss;
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* Set the various variables needed for the tests, then
|
||||
* specify what tests to run on each side.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
static boolean separateServerThread = true;
|
||||
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
volatile static boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Turn on SSL debugging?
|
||||
*/
|
||||
static boolean debug = false;
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
SSLServerSocketFactory sslssf =
|
||||
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
||||
SSLServerSocket sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket();
|
||||
sslServerSocket.bind(new InetSocketAddress(loopback, serverPort));
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
SSLSocket sslSocket = null;
|
||||
try {
|
||||
sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
|
||||
// check request contains "Cookie"
|
||||
InputStream is = sslSocket.getInputStream ();
|
||||
BufferedReader r = new BufferedReader(new InputStreamReader(is));
|
||||
boolean flag = false;
|
||||
String x;
|
||||
while ((x=r.readLine()) != null) {
|
||||
if (x.length() ==0) {
|
||||
break;
|
||||
}
|
||||
String header = "Cookie: ";
|
||||
if (x.startsWith(header)) {
|
||||
if (x.equals("Cookie: "+((String)cookies.get("Cookie")))) {
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
throw new RuntimeException("server should see cookie in request");
|
||||
}
|
||||
|
||||
PrintStream out = new PrintStream(
|
||||
new BufferedOutputStream(
|
||||
sslSocket.getOutputStream() ));
|
||||
|
||||
/* send the header */
|
||||
out.print("HTTP/1.1 200 OK\r\n");
|
||||
out.print("Set-Cookie2: "+((String)cookies.get("Set-Cookie2")+"\r\n"));
|
||||
out.print("Content-Type: text/html; charset=iso-8859-1\r\n");
|
||||
out.print("Connection: close\r\n");
|
||||
out.print("\r\n");
|
||||
out.print("<HTML>");
|
||||
out.print("<HEAD><TITLE>Testing cookie</TITLE></HEAD>");
|
||||
out.print("<BODY>OK.</BODY>");
|
||||
out.print("</HTML>");
|
||||
out.flush();
|
||||
|
||||
sslSocket.close();
|
||||
sslServerSocket.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
HttpsURLConnection http = null;
|
||||
/* establish http connection to server */
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(serverPort)
|
||||
.toURL();
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(new NameVerifier());
|
||||
http = (HttpsURLConnection)url.openConnection();
|
||||
|
||||
int respCode = http.getResponseCode();
|
||||
http.disconnect();
|
||||
|
||||
}
|
||||
|
||||
static class NameVerifier implements HostnameVerifier {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
volatile int serverPort = 0;
|
||||
|
||||
volatile Exception serverException = null;
|
||||
volatile Exception clientException = null;
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
String keyFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + keyStoreFile;
|
||||
String trustFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + trustStoreFile;
|
||||
|
||||
CookieHandler reservedCookieHandler = CookieHandler.getDefault();
|
||||
HostnameVerifier reservedHV =
|
||||
HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
try {
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
if (debug)
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
cookies = new HashMap<String, String>();
|
||||
cookies.put("Cookie",
|
||||
"$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"");
|
||||
cookies.put("Set-Cookie2",
|
||||
"$Version=\"1\"; Part_Number=\"Riding_Rocket_0023\"; " +
|
||||
"$Path=\"/acme/ammo\"; Part_Number=\"Rocket_Launcher_0001\"; "+
|
||||
"$Path=\"/acme\"");
|
||||
CookieHandler.setDefault(new MyCookieHandler());
|
||||
new CookieHandlerTest();
|
||||
} finally {
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(reservedHV);
|
||||
CookieHandler.setDefault(reservedCookieHandler);
|
||||
}
|
||||
}
|
||||
|
||||
Thread clientThread = null;
|
||||
Thread serverThread = null;
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
CookieHandlerTest() throws Exception {
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
serverThread.join();
|
||||
} else {
|
||||
clientThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
*
|
||||
* If the main thread excepted, that propagates back
|
||||
* immediately. If the other thread threw an exception, we
|
||||
* should report back.
|
||||
*/
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
if (clientException != null)
|
||||
throw clientException;
|
||||
|
||||
if (!getCalled || !putCalled) {
|
||||
throw new RuntimeException ("Either get or put method is not called");
|
||||
}
|
||||
}
|
||||
|
||||
void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
doServerSide();
|
||||
}
|
||||
}
|
||||
|
||||
void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died...");
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
doClientSide();
|
||||
}
|
||||
}
|
||||
|
||||
static boolean getCalled = false, putCalled = false;
|
||||
|
||||
static class MyCookieHandler extends CookieHandler {
|
||||
public Map<String,List<String>>
|
||||
get(URI uri, Map<String,List<String>> requestHeaders)
|
||||
throws IOException {
|
||||
getCalled = true;
|
||||
// returns cookies[0]
|
||||
// they will be include in request
|
||||
Map<String,List<String>> map = new HashMap<>();
|
||||
List<String> l = new ArrayList<>();
|
||||
l.add(cookies.get("Cookie"));
|
||||
map.put("Cookie",l);
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
public void
|
||||
put(URI uri, Map<String,List<String>> responseHeaders)
|
||||
throws IOException {
|
||||
putCalled = true;
|
||||
// check response has cookies[1]
|
||||
List<String> l = responseHeaders.get("Set-Cookie2");
|
||||
String value = l.get(0);
|
||||
if (!value.equals((String)cookies.get("Set-Cookie2"))) {
|
||||
throw new RuntimeException("cookie should be available for handle to put into cache");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// SunJSSE does not support dynamic system properties, no way to re-use
|
||||
// system properties in samevm/agentvm mode.
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 7129083
|
||||
* @summary Cookiemanager does not store cookies if url is read
|
||||
* before setting cookiemanager
|
||||
* @library /test/lib
|
||||
* @run main/othervm CookieHttpsClientTest
|
||||
*/
|
||||
|
||||
import java.net.CookieHandler;
|
||||
import java.net.CookieManager;
|
||||
import java.net.CookiePolicy;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URL;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class CookieHttpsClientTest {
|
||||
static final int TIMEOUT = 10 * 1000;
|
||||
|
||||
static final String replyString = "HTTP/1.1 200 OK\r\n" +
|
||||
"Set-Cookie: name=test\r\n" +
|
||||
"Content-Length: 10\r\n\r\n" +
|
||||
"1234567890";
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* Set the various variables needed for the tests, then
|
||||
* specify what tests to run on each side.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
static boolean separateServerThread = true;
|
||||
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
volatile static boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Turn on SSL debugging?
|
||||
*/
|
||||
static boolean debug = false;
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
SSLServerSocketFactory sslssf =
|
||||
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
||||
SSLServerSocket sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket(serverPort, 0, loopback);
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
SSLSocket sslSocket = null;
|
||||
try {
|
||||
sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
sslSocket.setSoTimeout(TIMEOUT);
|
||||
readOneRequest(sslSocket.getInputStream());
|
||||
sslSocket.getOutputStream().write(replyString.getBytes());
|
||||
|
||||
readOneRequest(sslSocket.getInputStream());
|
||||
sslSocket.getOutputStream().write(replyString.getBytes());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (sslSocket != null) { sslSocket.close(); }
|
||||
sslServerSocket.close();
|
||||
} catch (IOException unused) { /* gulp!burp! */ }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
// Wait for server to get started.
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}});
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(serverPort)
|
||||
.path("/")
|
||||
.toURL();
|
||||
|
||||
System.out.println("Client ready to connect to: " + url);
|
||||
|
||||
// Run without a CookieHandler first
|
||||
InputStream in = url.openConnection(java.net.Proxy.NO_PROXY).getInputStream();
|
||||
while (in.read() != -1); // read response body so connection can be reused
|
||||
|
||||
// Set a CookeHandler and retest using the HttpClient from the KAC
|
||||
CookieManager manager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
|
||||
CookieHandler.setDefault(manager);
|
||||
|
||||
in = url.openConnection().getInputStream();
|
||||
while (in.read() != -1);
|
||||
|
||||
if (manager.getCookieStore().getCookies().isEmpty()) {
|
||||
throw new RuntimeException("Failed: No cookies in the cookie Handler.");
|
||||
}
|
||||
}
|
||||
|
||||
static final byte[] requestEnd = new byte[] {'\r', '\n', '\r', '\n' };
|
||||
|
||||
// Read until the end of a HTTP request
|
||||
static void readOneRequest(InputStream is) throws IOException {
|
||||
int requestEndCount = 0, r;
|
||||
while ((r = is.read()) != -1) {
|
||||
if (r == requestEnd[requestEndCount]) {
|
||||
requestEndCount++;
|
||||
if (requestEndCount == 4) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
requestEndCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
volatile int serverPort = 0;
|
||||
|
||||
volatile Exception serverException = null;
|
||||
volatile Exception clientException = null;
|
||||
|
||||
private boolean sslConnectionFailed() {
|
||||
return clientException instanceof SSLHandshakeException;
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
String keyFilename =
|
||||
System.getProperty("test.src", ".") + "/" + pathToStores +
|
||||
"/" + keyStoreFile;
|
||||
String trustFilename =
|
||||
System.getProperty("test.src", ".") + "/" + pathToStores +
|
||||
"/" + trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
if (debug)
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
|
||||
new CookieHttpsClientTest();
|
||||
}
|
||||
|
||||
Thread clientThread = null;
|
||||
Thread serverThread = null;
|
||||
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
CookieHttpsClientTest() throws Exception {
|
||||
Exception startException = null;
|
||||
try {
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
startException = e;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
if (serverThread != null) {
|
||||
// don't join the server thread if the
|
||||
// client failed to connect
|
||||
if (!sslConnectionFailed()) {
|
||||
serverThread.join();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (clientThread != null) {
|
||||
clientThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
* Which side threw the error?
|
||||
*/
|
||||
Exception local;
|
||||
Exception remote;
|
||||
|
||||
if (separateServerThread) {
|
||||
remote = serverException;
|
||||
local = clientException;
|
||||
} else {
|
||||
remote = clientException;
|
||||
local = serverException;
|
||||
}
|
||||
|
||||
Exception exception = null;
|
||||
|
||||
/*
|
||||
* Check various exception conditions.
|
||||
*/
|
||||
if ((local != null) && (remote != null)) {
|
||||
// If both failed, return the curthread's exception.
|
||||
local.addSuppressed(remote);
|
||||
exception = local;
|
||||
} else if (local != null) {
|
||||
exception = local;
|
||||
} else if (remote != null) {
|
||||
exception = remote;
|
||||
} else if (startException != null) {
|
||||
exception = startException;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there was an exception *AND* a startException,
|
||||
* output it.
|
||||
*/
|
||||
if (exception != null) {
|
||||
if (exception != startException && startException != null) {
|
||||
exception.addSuppressed(startException);
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
|
||||
// Fall-through: no exception to throw!
|
||||
}
|
||||
|
||||
void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
serverException = e;
|
||||
} finally {
|
||||
serverReady = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died: " + e);
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Client died: " + e);
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Copyright (c) 2010, 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// SunJSSE does not support dynamic system properties, no way to re-use
|
||||
// system properties in samevm/agentvm mode.
|
||||
//
|
||||
|
||||
/* @test id=tls12
|
||||
* @bug 6766775
|
||||
* @summary X509 certificate hostname checking is broken in JDK1.6.0_10
|
||||
* @library /test/lib
|
||||
* @run main/othervm DNSIdentities TLSv1.2 MD5withRSA
|
||||
* @author Xuelei Fan
|
||||
*/
|
||||
|
||||
/* @test id=tls13
|
||||
* @bug 6766775
|
||||
* @summary X509 certificate hostname checking is broken in JDK1.6.0_10
|
||||
* @library /test/lib
|
||||
* @run main/othervm DNSIdentities TLSv1.3 SHA256withRSA
|
||||
* @author Xuelei Fan
|
||||
*/
|
||||
|
||||
import jdk.test.lib.security.CertificateBuilder;
|
||||
import jdk.test.lib.security.SecurityUtils;
|
||||
import sun.security.x509.DNSName;
|
||||
import sun.security.x509.GeneralName;
|
||||
import sun.security.x509.GeneralNames;
|
||||
import sun.security.x509.SubjectAlternativeNameExtension;
|
||||
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import java.io.PrintStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class DNSIdentities extends IdentitiesBase {
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
static boolean separateServerThread = true;
|
||||
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
volatile static boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Is the connection ready to close?
|
||||
*/
|
||||
volatile static boolean closeReady = false;
|
||||
|
||||
/*
|
||||
* Turn on SSL debugging?
|
||||
*/
|
||||
static boolean debug = false;
|
||||
|
||||
private SSLServerSocket sslServerSocket = null;
|
||||
|
||||
@Override
|
||||
protected CertificateBuilder customizeServerCert(CertificateBuilder builder) throws Exception {
|
||||
GeneralNames gns = new GeneralNames();
|
||||
gns.add(new GeneralName(new DNSName("localhost")));
|
||||
return builder.addExtension(new SubjectAlternativeNameExtension(true, gns));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CertificateBuilder customizeClientCert(CertificateBuilder builder) throws Exception {
|
||||
GeneralNames gns = new GeneralNames();
|
||||
gns.add(new GeneralName(new DNSName("localhost")));
|
||||
return builder.addExtension(new SubjectAlternativeNameExtension(true, gns));
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
SSLContext context = getServerSSLContext();
|
||||
SSLServerSocketFactory sslssf = context.getServerSocketFactory();
|
||||
|
||||
// doClientSide() connects to "localhost"
|
||||
InetAddress localHost = InetAddress.getByName("localhost");
|
||||
InetSocketAddress address = new InetSocketAddress(localHost, serverPort);
|
||||
|
||||
sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket();
|
||||
sslServerSocket.bind(address);
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
|
||||
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
sslSocket.setNeedClientAuth(true);
|
||||
|
||||
PrintStream out =
|
||||
new PrintStream(sslSocket.getOutputStream());
|
||||
|
||||
try {
|
||||
// ignore request data
|
||||
|
||||
// send the response
|
||||
out.print("HTTP/1.1 200 OK\r\n");
|
||||
out.print("Content-Type: text/html; charset=iso-8859-1\r\n");
|
||||
out.print("Content-Length: "+ 9 +"\r\n");
|
||||
out.print("\r\n");
|
||||
out.print("Testing\r\n");
|
||||
out.flush();
|
||||
} finally {
|
||||
// close the socket
|
||||
while (!closeReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
System.out.println("Server closing socket");
|
||||
sslSocket.close();
|
||||
serverReady = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
SSLContext reservedSSLContext = SSLContext.getDefault();
|
||||
try {
|
||||
SSLContext context = getClientSSLContext();
|
||||
|
||||
SSLContext.setDefault(context);
|
||||
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
HttpsURLConnection http = null;
|
||||
|
||||
/* establish http connection to server */
|
||||
URL url = new URL("https://localhost:" + serverPort+"/");
|
||||
System.out.println("url is "+url.toString());
|
||||
|
||||
try {
|
||||
http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
|
||||
int respCode = http.getResponseCode();
|
||||
System.out.println("respCode = "+respCode);
|
||||
} finally {
|
||||
if (http != null) {
|
||||
http.disconnect();
|
||||
}
|
||||
closeReady = true;
|
||||
}
|
||||
} finally {
|
||||
SSLContext.setDefault(reservedSSLContext);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
volatile int serverPort = 0;
|
||||
|
||||
volatile Exception serverException = null;
|
||||
volatile Exception clientException = null;
|
||||
|
||||
public static void main(String [] args) throws Exception {
|
||||
if (args[1].contains("MD5")) {
|
||||
SecurityUtils.removeFromDisabledAlgs(
|
||||
"jdk.certpath.disabledAlgorithms", List.of("MD5"));
|
||||
SecurityUtils.removeFromDisabledTlsAlgs("MD5");
|
||||
}
|
||||
|
||||
if (debug)
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
new DNSIdentities(args[0], args[1]);
|
||||
}
|
||||
|
||||
Thread clientThread = null;
|
||||
Thread serverThread = null;
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
DNSIdentities(String protocol, String signatureAlg) throws Exception {
|
||||
super(protocol, signatureAlg);
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
serverThread.join();
|
||||
} else {
|
||||
clientThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
*
|
||||
* If the main thread excepted, that propagates back
|
||||
* immediately. If the other thread threw an exception, we
|
||||
* should report back.
|
||||
*/
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
if (clientException != null)
|
||||
throw clientException;
|
||||
}
|
||||
|
||||
void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
doServerSide();
|
||||
}
|
||||
}
|
||||
|
||||
void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died...");
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
doClientSide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
/*
|
||||
* @test
|
||||
* @bug 8376031
|
||||
* @modules jdk.httpserver
|
||||
* @library /test/lib
|
||||
* @summary Ensure HttpsURLConnection::getServerCertificates does not
|
||||
* throw after calling getResponseCode() if the response doesn't have
|
||||
* a body.
|
||||
* @run main/othervm ${test.main.class}
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class}
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
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 com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
|
||||
import com.sun.net.httpserver.HttpsConfigurator;
|
||||
import com.sun.net.httpserver.HttpsServer;
|
||||
import jdk.test.lib.net.SimpleSSLContext;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
|
||||
|
||||
|
||||
public class GetServerCertificates {
|
||||
|
||||
static final String URI_PATH = "/GetServerCertificates/";
|
||||
static final String BODY = "Go raibh maith agat";
|
||||
enum TESTS {
|
||||
HEAD("head", 200, "HEAD"),
|
||||
NOBODY("nobody", 200, "GET", "POST"),
|
||||
S204("204", 204, "GET", "POST"),
|
||||
S304("304", 304, "GET", "POST"),
|
||||
S200("200", 200, "GET", "POST");
|
||||
final String test;
|
||||
final int code;
|
||||
final List<String> methods;
|
||||
private TESTS(String test, int code, String... methods) {
|
||||
this.test = test;
|
||||
this.code = code;
|
||||
this.methods = List.of(methods);
|
||||
}
|
||||
boolean isFor(String path) {
|
||||
return path != null && path.endsWith("/" + test);
|
||||
}
|
||||
|
||||
String test() { return test; }
|
||||
int code() { return code; }
|
||||
List<String> methods() { return methods; }
|
||||
static Optional<TESTS> fromPath(String path) {
|
||||
return Stream.of(values())
|
||||
.filter(test -> test.isFor(path))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
void test(String[] args) throws Exception {
|
||||
SSLContext.setDefault(SimpleSSLContext.findSSLContext());
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
|
||||
@Override
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
HttpServer server = startHttpServer();
|
||||
try {
|
||||
InetSocketAddress address = server.getAddress();
|
||||
URI uri = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.host(address.getAddress())
|
||||
.port(address.getPort())
|
||||
.path(URI_PATH)
|
||||
.build();
|
||||
for (var test : TESTS.values()) {
|
||||
for (String method : test.methods()) {
|
||||
doClient(method, uri, test);
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
server.stop(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void doClient(String method, URI baseUri, TESTS test) throws Exception {
|
||||
assert baseUri.getRawQuery() == null;
|
||||
assert baseUri.getRawFragment() == null;
|
||||
assert test.methods().contains(method);
|
||||
|
||||
String uriStr = baseUri.toString();
|
||||
if (!uriStr.endsWith("/")) uriStr = uriStr + "/";
|
||||
|
||||
URI uri = new URI(uriStr + test.test());
|
||||
assert uri.toString().endsWith("/" + test.test());
|
||||
int code = test.code();
|
||||
System.out.println("doClient(%s, %s, %s)"
|
||||
.formatted(method, test.test(), test.code));
|
||||
|
||||
// first request - should create a TCP connection
|
||||
HttpsURLConnection uc = (HttpsURLConnection)
|
||||
uri.toURL().openConnection(Proxy.NO_PROXY);
|
||||
if (!"GET".equals(method)) {
|
||||
uc.setRequestMethod(method);
|
||||
}
|
||||
try {
|
||||
uc.getServerCertificates();
|
||||
throw new AssertionError("Expected IllegalStateException not thrown");
|
||||
} catch (IllegalStateException ise) {
|
||||
System.out.println("Got expected ISE: " + ise);
|
||||
}
|
||||
int resp = uc.getResponseCode();
|
||||
check(resp == code, "Unexpected response code. Expected %s, got %s"
|
||||
.formatted(code, resp));
|
||||
|
||||
check(uc.getServerCertificates());
|
||||
if (test == TESTS.S200) {
|
||||
byte[] bytes = uc.getInputStream().readAllBytes();
|
||||
String body = new String(bytes, StandardCharsets.UTF_8);
|
||||
System.out.println("body: " + body);
|
||||
check(BODY.equals(body), "Unexpected response body. Expected \"%s\", got \"%s\""
|
||||
.formatted(BODY, body));
|
||||
}
|
||||
|
||||
// second request - should go on the same TCP connection.
|
||||
// We don't have a reliable way to test that, and it could
|
||||
// go on a new TCP connection if the previous connection
|
||||
// was already closed. It is not an issue either way.
|
||||
uc = (HttpsURLConnection)
|
||||
uri.toURL().openConnection(Proxy.NO_PROXY);
|
||||
if (!"GET".equals(method)) {
|
||||
uc.setRequestMethod(method);
|
||||
}
|
||||
try {
|
||||
uc.getServerCertificates();
|
||||
throw new AssertionError("Expected IllegalStateException not thrown");
|
||||
} catch (IllegalStateException ise) {
|
||||
System.out.println("Got expected ISE: " + ise);
|
||||
}
|
||||
resp = uc.getResponseCode();
|
||||
check(resp == code, "Unexpected response code. Expected %s, got %s"
|
||||
.formatted(code, resp));
|
||||
|
||||
check(uc.getServerCertificates());
|
||||
if (test == TESTS.S200) {
|
||||
byte[] bytes = uc.getInputStream().readAllBytes();
|
||||
String body = new String(bytes, StandardCharsets.UTF_8);
|
||||
System.out.println("body: " + body);
|
||||
check(BODY.equals(body), "Unexpected response body. Expected \"%s\", got \"%s\""
|
||||
.formatted(BODY, body));
|
||||
}
|
||||
|
||||
uc.disconnect();
|
||||
try {
|
||||
uc.getServerCertificates();
|
||||
throw new AssertionError("Expected IllegalStateException not thrown");
|
||||
} catch (IllegalStateException ise) {
|
||||
System.out.println("Got expected ISE: " + ise);
|
||||
}
|
||||
|
||||
// third request - forces the connection to close
|
||||
// after use so that we don't find any connection in the pool
|
||||
// for the next test case, assuming there was only
|
||||
// one connection in the first place.
|
||||
// Again there's no easy way to verify that the pool
|
||||
// is empty (and it's not really necessary to bother)
|
||||
uc = (HttpsURLConnection)
|
||||
uri.toURL().openConnection(Proxy.NO_PROXY);
|
||||
if (!"GET".equals(method)) {
|
||||
uc.setRequestMethod(method);
|
||||
}
|
||||
uc.setRequestProperty("Connection", "close");
|
||||
try {
|
||||
uc.getServerCertificates();
|
||||
throw new AssertionError("Expected IllegalStateException not thrown");
|
||||
} catch (IllegalStateException ise) {
|
||||
System.out.println("Got expected ISE: " + ise);
|
||||
}
|
||||
resp = uc.getResponseCode();
|
||||
check(resp == code, "Unexpected response code. Expected %s, got %s"
|
||||
.formatted(code, resp));
|
||||
check(uc.getServerCertificates());
|
||||
uc.disconnect();
|
||||
try {
|
||||
uc.getServerCertificates();
|
||||
throw new AssertionError("Expected IllegalStateException not thrown");
|
||||
} catch (IllegalStateException ise) {
|
||||
System.out.println("Got expected ISE: " + ise);
|
||||
}
|
||||
}
|
||||
|
||||
// HTTP Server
|
||||
HttpServer startHttpServer() throws IOException {
|
||||
InetAddress localhost = InetAddress.getLoopbackAddress();
|
||||
HttpsServer httpServer = HttpsServer
|
||||
.create(new InetSocketAddress(localhost, 0), 0);
|
||||
var configurator = new HttpsConfigurator(SimpleSSLContext.findSSLContext());
|
||||
httpServer.setHttpsConfigurator(configurator);
|
||||
httpServer.createContext(URI_PATH, new SimpleHandler());
|
||||
httpServer.start();
|
||||
return httpServer;
|
||||
}
|
||||
|
||||
static class SimpleHandler implements HttpHandler {
|
||||
@Override
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
try {
|
||||
String path = t.getRequestURI().getRawPath();
|
||||
var test = TESTS.fromPath(path);
|
||||
if (!path.startsWith(URI_PATH) || !test.isPresent()) {
|
||||
t.getRequestBody().close();
|
||||
t.getResponseHeaders().add("Connection", "close");
|
||||
t.sendResponseHeaders(421, RSPBODY_EMPTY);
|
||||
t.close();
|
||||
return;
|
||||
}
|
||||
try (var is = t.getRequestBody()) {
|
||||
is.readAllBytes();
|
||||
}
|
||||
switch (test.get()) {
|
||||
case S204, S304, NOBODY ->
|
||||
t.sendResponseHeaders(test.get().code(), RSPBODY_EMPTY);
|
||||
case S200 -> {
|
||||
byte[] bytes = BODY.getBytes(StandardCharsets.UTF_8);
|
||||
t.sendResponseHeaders(test.get().code(), bytes.length);
|
||||
try (var os = t.getResponseBody()) {
|
||||
os.write(bytes);
|
||||
}
|
||||
}
|
||||
case HEAD -> {
|
||||
assert t.getRequestMethod().equals("HEAD");
|
||||
byte[] bytes = BODY.getBytes(StandardCharsets.UTF_8);
|
||||
t.sendResponseHeaders(test.get().code(), bytes.length);
|
||||
}
|
||||
}
|
||||
t.close();
|
||||
} catch (Throwable error) {
|
||||
error.printStackTrace();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
volatile int passed = 0, failed = 0;
|
||||
boolean debug = false;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++;}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void debug(String message) { if (debug) System.out.println(message); }
|
||||
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
|
||||
void check(java.security.cert.Certificate[] certs) {
|
||||
// Use List.of to check that certs is not null and does not
|
||||
// contain null. NullPointerException will be thrown here
|
||||
// if that happens, which will make the test fail.
|
||||
check(!List.of(certs).isEmpty(), "no certificates returned");
|
||||
}
|
||||
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");}
|
||||
}
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* Copyright (c) 2010, 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 6771432
|
||||
* @summary createSocket() - smpatch fails using 1.6.0_10 because of
|
||||
* "Unconnected sockets not implemented"
|
||||
* @modules jdk.httpserver
|
||||
* @library /test/lib
|
||||
* @run main/othervm HttpsCreateSockTest
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true HttpsCreateSockTest
|
||||
*
|
||||
* SunJSSE does not support dynamic system properties, no way to re-use
|
||||
* system properties in samevm/agentvm mode.
|
||||
*/
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpsConfigurator;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
/*
|
||||
* This class tests that the HTTPS protocol handler is using its socket factory for
|
||||
* creating new Sockets. It does this by wrapping the default SSLSocketFactory with
|
||||
* its own socket factory, SimpleSSLSocketFactory, and verifying that when a https
|
||||
* connection is made one of the socket factories createSocket methods, that
|
||||
* actually creates a Socket, is being invoked by the protocol handler.
|
||||
*/
|
||||
|
||||
public class HttpsCreateSockTest
|
||||
{
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
|
||||
com.sun.net.httpserver.HttpsServer httpsServer;
|
||||
MyHandler httpHandler;
|
||||
|
||||
public static void main(String[] args) {
|
||||
String keyFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + keyStoreFile;
|
||||
String trustFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
new HttpsCreateSockTest();
|
||||
}
|
||||
|
||||
public HttpsCreateSockTest() {
|
||||
try {
|
||||
startHttpsServer();
|
||||
doClient();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
} finally {
|
||||
httpsServer.stop(1);
|
||||
}
|
||||
}
|
||||
|
||||
void doClient() throws IOException {
|
||||
InetSocketAddress address = httpsServer.getAddress();
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.host(address.getAddress())
|
||||
.port(address.getPort())
|
||||
.path("/")
|
||||
.toURLUnchecked();
|
||||
System.out.println("trying to connect to " + url + "...");
|
||||
|
||||
HttpsURLConnection uc = (HttpsURLConnection) url.openConnection(Proxy.NO_PROXY);
|
||||
uc.setHostnameVerifier(new AllHostnameVerifier());
|
||||
if (uc instanceof javax.net.ssl.HttpsURLConnection) {
|
||||
((javax.net.ssl.HttpsURLConnection) uc).setSSLSocketFactory(new SimpleSSLSocketFactory());
|
||||
System.out.println("Using TestSocketFactory");
|
||||
}
|
||||
uc.connect();
|
||||
System.out.println("CONNECTED " + uc);
|
||||
System.out.println(uc.getResponseMessage());
|
||||
uc.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Https Server
|
||||
*/
|
||||
public void startHttpsServer() throws IOException, NoSuchAlgorithmException {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress address = new InetSocketAddress(loopback, 0);
|
||||
httpsServer = com.sun.net.httpserver.HttpsServer.create(address, 0);
|
||||
httpsServer.createContext("/", new MyHandler());
|
||||
httpsServer.setHttpsConfigurator(new HttpsConfigurator(SSLContext.getDefault()));
|
||||
httpsServer.start();
|
||||
}
|
||||
|
||||
class MyHandler implements HttpHandler {
|
||||
private String message = "This is a message!";
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
t.sendResponseHeaders(200, message.length());
|
||||
BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(t.getResponseBody(), "ISO8859-1"));
|
||||
writer.write(message, 0, message.length());
|
||||
writer.close();
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple wrapper on default SSLSocketFactory
|
||||
*/
|
||||
class SimpleSSLSocketFactory extends SSLSocketFactory
|
||||
{
|
||||
/*
|
||||
* true if this factory has been used to create a new Socket, i.e.
|
||||
* one of the SocketFactory methods has been called.
|
||||
*/
|
||||
boolean socketCreated = false;
|
||||
|
||||
/*
|
||||
* true if this factory has been used to wrap a Socket, i.e.
|
||||
* the SSLSocketFactory method,
|
||||
* createSocket(Socket, String, int, boolean), has been called.
|
||||
*/
|
||||
boolean socketWrapped = false;
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||
socketCreated = true;
|
||||
return SocketFactory.getDefault().createSocket(host, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
|
||||
int localPort) throws IOException {
|
||||
socketCreated = true;
|
||||
return SocketFactory.getDefault().createSocket(address, port, localAddress, localPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
socketCreated = true;
|
||||
return SocketFactory.getDefault().createSocket(host, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost,
|
||||
int localPort) throws IOException {
|
||||
socketCreated = true;
|
||||
return SocketFactory.getDefault().createSocket(host, port, localHost, localPort);
|
||||
}
|
||||
|
||||
// methods from SSLSocketFactory
|
||||
@Override
|
||||
public Socket createSocket(Socket s, String host, int port,
|
||||
boolean autoClose) throws IOException {
|
||||
socketWrapped = true;
|
||||
return ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket
|
||||
(s, host, port, autoClose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return ((SSLSocketFactory) SSLSocketFactory.getDefault()).getDefaultCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return ((SSLSocketFactory) SSLSocketFactory.getDefault()).getSupportedCipherSuites();
|
||||
}
|
||||
}
|
||||
|
||||
class AllHostnameVerifier implements HostnameVerifier
|
||||
{
|
||||
@Override
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* 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 4423074
|
||||
* @summary Need to rebase all the duplicated classes from Merlin.
|
||||
* This test will check out http POST
|
||||
* @library /test/lib
|
||||
* @run main/othervm HttpsPost
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true HttpsPost
|
||||
*
|
||||
* SunJSSE does not support dynamic system properties, no way to re-use
|
||||
* system properties in samevm/agentvm mode.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import javax.net.ssl.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class HttpsPost {
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* Set the various variables needed for the tests, then
|
||||
* specify what tests to run on each side.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
static boolean separateServerThread = true;
|
||||
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
volatile static boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Is the connection ready to close?
|
||||
*/
|
||||
volatile static boolean closeReady = false;
|
||||
|
||||
/*
|
||||
* Turn on SSL debugging?
|
||||
*/
|
||||
static boolean debug = false;
|
||||
|
||||
/*
|
||||
* Message posted
|
||||
*/
|
||||
static String postMsg = "Testing HTTP post on a https server";
|
||||
|
||||
/*
|
||||
* If the client or server is doing some kind of object creation
|
||||
* that the other side depends on, and that thread prematurely
|
||||
* exits, you may experience a hang. The test harness will
|
||||
* terminate all hung threads after its timeout has expired,
|
||||
* currently 3 minutes by default, but you might try to be
|
||||
* smart about it....
|
||||
*/
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
SSLServerSocketFactory sslssf =
|
||||
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
||||
SSLServerSocket sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket(serverPort, 0, loopback);
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
System.out.println("Starting server at: "
|
||||
+ sslServerSocket.getInetAddress()
|
||||
+ ":" + serverPort);
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
|
||||
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
try {
|
||||
InputStream sslIS = sslSocket.getInputStream();
|
||||
OutputStream sslOS = sslSocket.getOutputStream();
|
||||
BufferedReader br =
|
||||
new BufferedReader(new InputStreamReader(sslIS));
|
||||
PrintStream ps = new PrintStream(sslOS);
|
||||
|
||||
// process HTTP POST request from client
|
||||
System.out.println("status line: "+br.readLine());
|
||||
String msg = null;
|
||||
while ((msg = br.readLine()) != null && msg.length() > 0);
|
||||
|
||||
msg = br.readLine();
|
||||
if (msg.equals(postMsg)) {
|
||||
ps.println("HTTP/1.1 200 OK\n\n");
|
||||
} else {
|
||||
ps.println("HTTP/1.1 500 Not OK\n\n");
|
||||
}
|
||||
ps.flush();
|
||||
|
||||
// close the socket
|
||||
while (!closeReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
} finally {
|
||||
sslSocket.close();
|
||||
sslServerSocket.close();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
HostnameVerifier reservedHV =
|
||||
HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
try {
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
// Send HTTP POST request to server
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(serverPort)
|
||||
.toURL();
|
||||
|
||||
System.out.println("Client connecting to: " + url);
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(new NameVerifier());
|
||||
HttpsURLConnection http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
http.setDoOutput(true);
|
||||
|
||||
http.setRequestMethod("POST");
|
||||
PrintStream ps = new PrintStream(http.getOutputStream());
|
||||
try {
|
||||
ps.println(postMsg);
|
||||
ps.flush();
|
||||
if (http.getResponseCode() != 200) {
|
||||
throw new RuntimeException("test Failed");
|
||||
}
|
||||
} finally {
|
||||
ps.close();
|
||||
http.disconnect();
|
||||
closeReady = true;
|
||||
}
|
||||
} finally {
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(reservedHV);
|
||||
}
|
||||
}
|
||||
|
||||
static class NameVerifier implements HostnameVerifier {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
volatile int serverPort = 0;
|
||||
|
||||
volatile Exception serverException = null;
|
||||
volatile Exception clientException = null;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String keyFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + keyStoreFile;
|
||||
String trustFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
if (debug)
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
new HttpsPost();
|
||||
}
|
||||
|
||||
Thread clientThread = null;
|
||||
Thread serverThread = null;
|
||||
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
HttpsPost() throws Exception {
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
serverThread.join();
|
||||
} else {
|
||||
clientThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
*
|
||||
* If the main thread excepted, that propagates back
|
||||
* immediately. If the other thread threw an exception, we
|
||||
* should report back.
|
||||
*/
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
if (clientException != null)
|
||||
throw clientException;
|
||||
}
|
||||
|
||||
void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
doServerSide();
|
||||
}
|
||||
}
|
||||
|
||||
void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died...");
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
doClientSide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2011, 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 6670868
|
||||
* @summary StackOverFlow with bad authenticated Proxy tunnels
|
||||
* @run main/othervm HttpsProxyStackOverflow
|
||||
*
|
||||
* No way to reserve default Authenticator, need to run in othervm mode.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Authenticator;
|
||||
import java.net.Proxy;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
public class HttpsProxyStackOverflow {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
BadAuthProxyServer server = startServer();
|
||||
doClient(server);
|
||||
}
|
||||
|
||||
static void doClient(BadAuthProxyServer server) throws IOException {
|
||||
// url doesn't matter since we will never make the connection
|
||||
URL url = new URL("https://anythingwilldo/");
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
String loopbackAddress = loopback.getHostAddress();
|
||||
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(
|
||||
new Proxy(Proxy.Type.HTTP,
|
||||
InetSocketAddress.createUnresolved(loopbackAddress, server.getPort())));
|
||||
try (InputStream is = conn.getInputStream()) {
|
||||
} catch(IOException unused) {
|
||||
// no real server, IOException is expected.
|
||||
// failure if StackOverflowError
|
||||
} finally {
|
||||
server.done();
|
||||
}
|
||||
}
|
||||
|
||||
static BadAuthProxyServer startServer() throws IOException {
|
||||
Authenticator.setDefault(new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication("xyz", "xyz".toCharArray());
|
||||
}
|
||||
});
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress address = new InetSocketAddress(loopback, 0);
|
||||
ServerSocket ss = new ServerSocket();
|
||||
ss.bind(address);
|
||||
BadAuthProxyServer server = new BadAuthProxyServer(ss);
|
||||
Thread serverThread = new Thread(server);
|
||||
serverThread.start();
|
||||
return server;
|
||||
}
|
||||
|
||||
static class BadAuthProxyServer implements Runnable {
|
||||
private ServerSocket ss;
|
||||
private boolean done;
|
||||
|
||||
BadAuthProxyServer(ServerSocket ss) { this.ss = ss; }
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
while (!done) {
|
||||
Socket s = ss.accept();
|
||||
s.getOutputStream().write(
|
||||
("HTTP/1.1 407\nProxy-Authenticate:Basic " +
|
||||
"realm=\"WallyWorld\"\n\n").getBytes("US-ASCII"));
|
||||
|
||||
s.close();
|
||||
|
||||
s = ss.accept();
|
||||
s.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore IOException when the main thread calls done
|
||||
} finally {
|
||||
try { ss.close(); } catch (IOException e) {}
|
||||
}
|
||||
}
|
||||
|
||||
int getPort() {
|
||||
return ss.getLocalPort();
|
||||
}
|
||||
|
||||
void done() {
|
||||
try { ss.close(); } catch (IOException e) {}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright (c) 2010, 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 6614957
|
||||
* @summary HttpsURLConnection not using the set SSLSocketFactory for creating all its Sockets
|
||||
* @modules jdk.httpserver
|
||||
* @library /test/lib
|
||||
* @run main/othervm HttpsSocketFacTest
|
||||
*
|
||||
* SunJSSE does not support dynamic system properties, no way to re-use
|
||||
* system properties in samevm/agentvm mode.
|
||||
*/
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.net.Proxy;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpsConfigurator;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
/*
|
||||
* This class tests that the HTTPS protocol handler is using its socket factory for
|
||||
* creating new Sockets. It does this by wrapping the default SSLSocketFactory with
|
||||
* its own socket factory, SimpleSSLSocketFactory, and verifying that when a https
|
||||
* connection is made one of the socket factories createSocket methods, that
|
||||
* actually creates a Socket, is being invoked by the protocol handler.
|
||||
*/
|
||||
|
||||
public class HttpsSocketFacTest
|
||||
{
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
|
||||
com.sun.net.httpserver.HttpsServer httpsServer;
|
||||
MyHandler httpHandler;
|
||||
|
||||
public static void main(String[] args) {
|
||||
String keyFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + keyStoreFile;
|
||||
String trustFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
new HttpsSocketFacTest();
|
||||
}
|
||||
|
||||
public HttpsSocketFacTest() {
|
||||
try {
|
||||
startHttpsServer();
|
||||
doClient();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
} finally {
|
||||
httpsServer.stop(1);
|
||||
}
|
||||
}
|
||||
|
||||
void doClient() throws IOException {
|
||||
InetSocketAddress address = httpsServer.getAddress();
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(address.getPort())
|
||||
.path("/test6614957/")
|
||||
.toURLUnchecked();
|
||||
System.out.println("trying to connect to " + url + "...");
|
||||
|
||||
HttpsURLConnection uc = (HttpsURLConnection) url.openConnection(Proxy.NO_PROXY);
|
||||
SimpleSSLSocketFactory sssf = new SimpleSSLSocketFactory();
|
||||
uc.setSSLSocketFactory(sssf);
|
||||
uc.setHostnameVerifier(new AllHostnameVerifier());
|
||||
InputStream is = uc.getInputStream();
|
||||
|
||||
byte[] ba = new byte[1024];
|
||||
int read = 0;
|
||||
while ((read = is.read(ba)) != -1) {
|
||||
System.out.println(new String(ba, 0, read));
|
||||
}
|
||||
|
||||
System.out.println("SimpleSSLSocketFactory.socketCreated = " + sssf.socketCreated);
|
||||
System.out.println("SimpleSSLSocketFactory.socketWrapped = " + sssf.socketWrapped);
|
||||
|
||||
if (!sssf.socketCreated)
|
||||
throw new RuntimeException("Failed: Socket Factory not being called to create Socket");
|
||||
}
|
||||
|
||||
/**
|
||||
* Https Server
|
||||
*/
|
||||
public void startHttpsServer() throws IOException, NoSuchAlgorithmException {
|
||||
httpsServer = com.sun.net.httpserver.HttpsServer
|
||||
.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
|
||||
httpsServer.createContext("/test6614957/", new MyHandler());
|
||||
httpsServer.setHttpsConfigurator(new HttpsConfigurator(SSLContext.getDefault()));
|
||||
httpsServer.start();
|
||||
}
|
||||
|
||||
class MyHandler implements HttpHandler {
|
||||
private String message = "This is a message!";
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange t) throws IOException {
|
||||
t.sendResponseHeaders(200, message.length());
|
||||
BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(t.getResponseBody(), "ISO8859-1"));
|
||||
writer.write(message, 0, message.length());
|
||||
writer.close();
|
||||
t.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple wrapper on default SSLSocketFactory
|
||||
*/
|
||||
class SimpleSSLSocketFactory extends SSLSocketFactory
|
||||
{
|
||||
/*
|
||||
* true if this factory has been used to create a new Socket, i.e.
|
||||
* one of the SocketFactory methods has been called.
|
||||
*/
|
||||
boolean socketCreated = false;
|
||||
|
||||
/*
|
||||
* true if this factory has been used to wrap a Socket, i.e.
|
||||
* the SSLSocketFactory method,
|
||||
* createSocket(Socket, String, int, boolean), has been called.
|
||||
*/
|
||||
boolean socketWrapped = false;
|
||||
|
||||
// methods for SocketFactory
|
||||
@Override
|
||||
public Socket createSocket() throws IOException {
|
||||
socketCreated = true;
|
||||
return SocketFactory.getDefault().createSocket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||
socketCreated = true;
|
||||
return SocketFactory.getDefault().createSocket(host, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
|
||||
int localPort) throws IOException {
|
||||
socketCreated = true;
|
||||
return SocketFactory.getDefault().createSocket(address, port, localAddress, localPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
socketCreated = true;
|
||||
return SocketFactory.getDefault().createSocket(host, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost,
|
||||
int localPort) throws IOException {
|
||||
socketCreated = true;
|
||||
return SocketFactory.getDefault().createSocket(host, port, localHost, localPort);
|
||||
}
|
||||
|
||||
// methods from SSLSocketFactory
|
||||
@Override
|
||||
public Socket createSocket(Socket s, String host, int port,
|
||||
boolean autoClose) throws IOException {
|
||||
socketWrapped = true;
|
||||
return ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket
|
||||
(s, host, port, autoClose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return ((SSLSocketFactory) SSLSocketFactory.getDefault()).getDefaultCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return ((SSLSocketFactory) SSLSocketFactory.getDefault()).getSupportedCipherSuites();
|
||||
}
|
||||
}
|
||||
|
||||
class AllHostnameVerifier implements HostnameVerifier
|
||||
{
|
||||
@Override
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* Copyright (c) 2010, 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 id=tls12
|
||||
* @bug 6766775
|
||||
* @library /test/lib
|
||||
* @summary X509 certificate hostname checking is broken in JDK1.6.0_10
|
||||
*
|
||||
* @author Xuelei Fan
|
||||
* @run main/othervm IPAddressDNSIdentities TLSv1.2 MD5withRSA
|
||||
*/
|
||||
|
||||
|
||||
/* @test id=tls13
|
||||
* @bug 6766775
|
||||
* @library /test/lib
|
||||
* @summary X509 certificate hostname checking is broken in JDK1.6.0_10
|
||||
*
|
||||
* @author Xuelei Fan
|
||||
* @run main/othervm IPAddressDNSIdentities TLSv1.3 SHA256withRSA
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import javax.net.ssl.*;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.spec.*;
|
||||
import java.security.interfaces.*;
|
||||
import java.math.BigInteger;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import jdk.test.lib.security.CertificateBuilder;
|
||||
import jdk.test.lib.security.SecurityUtils;
|
||||
|
||||
|
||||
|
||||
public class IPAddressDNSIdentities extends IdentitiesBase {
|
||||
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
static boolean separateServerThread = true;
|
||||
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
volatile static boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Is the connection ready to close?
|
||||
*/
|
||||
volatile static boolean closeReady = false;
|
||||
|
||||
|
||||
private SSLServerSocket sslServerSocket = null;
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
SSLContext context = getServerSSLContext();
|
||||
SSLServerSocketFactory sslssf = context.getServerSocketFactory();
|
||||
|
||||
// doClientSide() connects to the loopback address
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress address = new InetSocketAddress(loopback, serverPort);
|
||||
|
||||
sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket();
|
||||
sslServerSocket.bind(address);
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
|
||||
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
sslSocket.setNeedClientAuth(true);
|
||||
|
||||
PrintStream out =
|
||||
new PrintStream(sslSocket.getOutputStream());
|
||||
|
||||
try {
|
||||
// ignore request data
|
||||
|
||||
// send the response
|
||||
out.print("HTTP/1.1 200 OK\r\n");
|
||||
out.print("Content-Type: text/html; charset=iso-8859-1\r\n");
|
||||
out.print("Content-Length: "+ 9 +"\r\n");
|
||||
out.print("\r\n");
|
||||
out.print("Testing\r\n");
|
||||
out.flush();
|
||||
} finally {
|
||||
// close the socket
|
||||
while (!closeReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
System.out.println("Server closing socket");
|
||||
sslSocket.close();
|
||||
serverReady = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
SSLContext reservedSSLContext = SSLContext.getDefault();
|
||||
try {
|
||||
SSLContext context = getClientSSLContext();
|
||||
|
||||
SSLContext.setDefault(context);
|
||||
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
HttpsURLConnection http = null;
|
||||
|
||||
/* establish http connection to server */
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(serverPort)
|
||||
.path("/")
|
||||
.toURL();
|
||||
System.out.println("url is "+url.toString());
|
||||
|
||||
try {
|
||||
http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
|
||||
int respCode = http.getResponseCode();
|
||||
System.out.println("respCode = " + respCode);
|
||||
|
||||
throw new Exception("Unexpectly found " +
|
||||
"subject alternative name matching IP address");
|
||||
} catch (SSLHandshakeException sslhe) {
|
||||
// no subject alternative names matching IP address 127.0.0.1
|
||||
// found that's the expected exception, ignore it.
|
||||
} catch (IOException ioe) {
|
||||
// HttpsClient may throw IOE during checking URL spoofing,
|
||||
// that's the expected exception, ignore it.
|
||||
} finally {
|
||||
if (http != null) {
|
||||
http.disconnect();
|
||||
}
|
||||
closeReady = true;
|
||||
}
|
||||
} finally {
|
||||
SSLContext.setDefault(reservedSSLContext);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
volatile int serverPort = 0;
|
||||
|
||||
volatile Exception serverException = null;
|
||||
volatile Exception clientException = null;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args[1].contains("MD5")) {
|
||||
System.err.println("RE-ENABLING MD5");
|
||||
SecurityUtils.removeFromDisabledAlgs(
|
||||
"jdk.certpath.disabledAlgorithms", List.of("MD5"));
|
||||
SecurityUtils.removeFromDisabledTlsAlgs("MD5");
|
||||
}
|
||||
|
||||
if (debug)
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
new IPAddressDNSIdentities(args[0], args[1]);
|
||||
}
|
||||
|
||||
Thread clientThread = null;
|
||||
Thread serverThread = null;
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
IPAddressDNSIdentities(String protocol, String signatureAlgo) throws Exception {
|
||||
super(protocol, signatureAlgo);
|
||||
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
serverThread.join();
|
||||
} else {
|
||||
clientThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
*
|
||||
* If the main thread excepted, that propagates back
|
||||
* immediately. If the other thread threw an exception, we
|
||||
* should report back.
|
||||
*/
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
if (clientException != null)
|
||||
throw clientException;
|
||||
}
|
||||
|
||||
void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
doServerSide();
|
||||
}
|
||||
}
|
||||
|
||||
void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died...");
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
doClientSide();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CertificateBuilder customizeServerCert(CertificateBuilder builder)
|
||||
throws Exception {
|
||||
return builder.addExtension(CertificateBuilder.createDNSSubjectAltNameExt(
|
||||
true, "localhost"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CertificateBuilder customizeClientCert(CertificateBuilder builder)
|
||||
throws Exception {
|
||||
return builder.addExtension(CertificateBuilder.createDNSSubjectAltNameExt(
|
||||
true, "localhost"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* Copyright (c) 2010, 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// SunJSSE does not support dynamic system properties, no way to re-use
|
||||
// system properties in samevm/agentvm mode.
|
||||
//
|
||||
|
||||
/* @test id=tls12
|
||||
* @summary X509 certificate hostname checking is broken in JDK1.6.0_10
|
||||
* @library /test/lib
|
||||
* @bug 6766775
|
||||
* @run main/othervm IPAddressIPIdentities TLSv1.2 MD5withRSA
|
||||
* @author Xuelei Fan
|
||||
*/
|
||||
|
||||
/* @test id=tls13
|
||||
* @summary X509 certificate hostname checking is broken in JDK1.6.0_10
|
||||
* @library /test/lib
|
||||
* @bug 6766775
|
||||
* @run main/othervm IPAddressIPIdentities TLSv1.3 SHA256withRSA
|
||||
* @author Xuelei Fan
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
import jdk.test.lib.security.SecurityUtils;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
|
||||
|
||||
public class IPAddressIPIdentities extends IdentitiesBase {
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* Set the various variables needed for the tests, then
|
||||
* specify what tests to run on each side.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
static boolean separateServerThread = true;
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
volatile static boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Is the connection ready to close?
|
||||
*/
|
||||
volatile static boolean closeReady = false;
|
||||
|
||||
|
||||
private SSLServerSocket sslServerSocket = null;
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
SSLContext context = getServerSSLContext();
|
||||
SSLServerSocketFactory sslssf = context.getServerSocketFactory();
|
||||
|
||||
// doClientSide() connects to the loopback address
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
InetSocketAddress address = new InetSocketAddress(loopback, serverPort);
|
||||
|
||||
sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket();
|
||||
sslServerSocket.bind(address);
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
|
||||
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
sslSocket.setNeedClientAuth(true);
|
||||
|
||||
PrintStream out =
|
||||
new PrintStream(sslSocket.getOutputStream());
|
||||
|
||||
try {
|
||||
// ignore request data
|
||||
|
||||
// send the response
|
||||
out.print("HTTP/1.1 200 OK\r\n");
|
||||
out.print("Content-Type: text/html; charset=iso-8859-1\r\n");
|
||||
out.print("Content-Length: "+ 9 +"\r\n");
|
||||
out.print("\r\n");
|
||||
out.print("Testing\r\n");
|
||||
out.flush();
|
||||
} finally {
|
||||
// close the socket
|
||||
while (!closeReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
System.out.println("Server closing socket");
|
||||
sslSocket.close();
|
||||
serverReady = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
SSLContext reservedSSLContext = SSLContext.getDefault();
|
||||
try {
|
||||
SSLContext context = getClientSSLContext();
|
||||
|
||||
SSLContext.setDefault(context);
|
||||
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
HttpsURLConnection http = null;
|
||||
|
||||
/* establish http connection to server */
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(serverPort)
|
||||
.path("/")
|
||||
.toURL();
|
||||
System.out.println("url is "+url.toString());
|
||||
|
||||
try {
|
||||
http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
|
||||
int respCode = http.getResponseCode();
|
||||
System.out.println("respCode = "+respCode);
|
||||
} finally {
|
||||
if (http != null) {
|
||||
http.disconnect();
|
||||
}
|
||||
closeReady = true;
|
||||
}
|
||||
} finally {
|
||||
SSLContext.setDefault(reservedSSLContext);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
volatile int serverPort = 0;
|
||||
|
||||
volatile Exception serverException = null;
|
||||
volatile Exception clientException = null;
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
if (args[1].contains("MD5")) {
|
||||
SecurityUtils.removeFromDisabledAlgs(
|
||||
"jdk.certpath.disabledAlgorithms", List.of("MD5"));
|
||||
SecurityUtils.removeFromDisabledTlsAlgs("MD5");
|
||||
}
|
||||
|
||||
if (debug)
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
new IPAddressIPIdentities(args[0], args[1]);
|
||||
}
|
||||
|
||||
Thread clientThread = null;
|
||||
Thread serverThread = null;
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
IPAddressIPIdentities(String protocol, String signatureAlg) throws Exception {
|
||||
super(protocol, signatureAlg);
|
||||
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
serverThread.join();
|
||||
} else {
|
||||
clientThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
*
|
||||
* If the main thread excepted, that propagates back
|
||||
* immediately. If the other thread threw an exception, we
|
||||
* should report back.
|
||||
*/
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
if (clientException != null)
|
||||
throw clientException;
|
||||
}
|
||||
|
||||
void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
doServerSide();
|
||||
}
|
||||
}
|
||||
|
||||
void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died...");
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
doClientSide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* Copyright (c) 2010, 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// SunJSSE does not support dynamic system properties, no way to re-use
|
||||
// system properties in samevm/agentvm mode.
|
||||
//
|
||||
|
||||
/* @test id=tls12
|
||||
* @bug 6766775
|
||||
* @summary X509 certificate hostname checking is broken in JDK1.6.0_10
|
||||
* @library /test/lib
|
||||
* @run main/othervm Identities TLSv1.2 MD5withRSA
|
||||
* @author Xuelei Fan
|
||||
*/
|
||||
|
||||
|
||||
/* @test id=tls13
|
||||
* @bug 6766775
|
||||
* @summary X509 certificate hostname checking is broken in JDK1.6.0_10
|
||||
* @library /test/lib
|
||||
* @run main/othervm Identities TLSv1.3 SHA256withRSA
|
||||
* @author Xuelei Fan
|
||||
*/
|
||||
|
||||
import jdk.test.lib.security.CertificateBuilder;
|
||||
import jdk.test.lib.security.SecurityUtils;
|
||||
import sun.security.x509.DNSName;
|
||||
import sun.security.x509.GeneralName;
|
||||
import sun.security.x509.GeneralNames;
|
||||
import sun.security.x509.IPAddressName;
|
||||
import sun.security.x509.SubjectAlternativeNameExtension;
|
||||
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import java.io.PrintStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
public class Identities extends IdentitiesBase {
|
||||
/*
|
||||
* =============================================================
|
||||
* Set the various variables needed for the tests, then
|
||||
* specify what tests to run on each side.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
static boolean separateServerThread = true;
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
volatile static boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Is the connection ready to close?
|
||||
*/
|
||||
volatile static boolean closeReady = false;
|
||||
|
||||
|
||||
private SSLServerSocket sslServerSocket = null;
|
||||
|
||||
|
||||
@Override
|
||||
protected CertificateBuilder customizeServerCert(CertificateBuilder builder) throws Exception {
|
||||
GeneralNames gns = new GeneralNames();
|
||||
gns.add(new GeneralName(new IPAddressName("127.0.0.1")));
|
||||
gns.add(new GeneralName(new DNSName("localhost")));
|
||||
return builder.addExtension(new SubjectAlternativeNameExtension(true, gns));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CertificateBuilder customizeClientCert(CertificateBuilder builder) throws Exception {
|
||||
GeneralNames gns = new GeneralNames();
|
||||
gns.add(new GeneralName(new IPAddressName("127.0.0.1")));
|
||||
gns.add(new GeneralName(new DNSName("localhost")));
|
||||
return builder.addExtension(new SubjectAlternativeNameExtension(true, gns));
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
SSLContext context = getServerSSLContext();
|
||||
SSLServerSocketFactory sslssf = context.getServerSocketFactory();
|
||||
|
||||
// doClientSide() connects to "localhost"
|
||||
InetAddress localHost = InetAddress.getByName("localhost");
|
||||
InetSocketAddress address = new InetSocketAddress(localHost, serverPort);
|
||||
|
||||
sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket();
|
||||
sslServerSocket.bind(address);
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
|
||||
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
sslSocket.setNeedClientAuth(true);
|
||||
|
||||
PrintStream out =
|
||||
new PrintStream(sslSocket.getOutputStream());
|
||||
|
||||
try {
|
||||
// ignore request data
|
||||
|
||||
// send the response
|
||||
out.print("HTTP/1.1 200 OK\r\n");
|
||||
out.print("Content-Type: text/html; charset=iso-8859-1\r\n");
|
||||
out.print("Content-Length: "+ 9 +"\r\n");
|
||||
out.print("\r\n");
|
||||
out.print("Testing\r\n");
|
||||
out.flush();
|
||||
} finally {
|
||||
// close the socket
|
||||
while (!closeReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
System.out.println("Server closing socket");
|
||||
sslSocket.close();
|
||||
serverReady = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
SSLContext reservedSSLContext = SSLContext.getDefault();
|
||||
try {
|
||||
SSLContext context = getClientSSLContext();
|
||||
|
||||
SSLContext.setDefault(context);
|
||||
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
HttpsURLConnection http = null;
|
||||
|
||||
/* establish http connection to server */
|
||||
URL url = new URL("https://localhost:" + serverPort+"/");
|
||||
System.out.println("url is "+url.toString());
|
||||
|
||||
try {
|
||||
http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
|
||||
int respCode = http.getResponseCode();
|
||||
System.out.println("respCode = "+respCode);
|
||||
} finally {
|
||||
if (http != null) {
|
||||
http.disconnect();
|
||||
}
|
||||
closeReady = true;
|
||||
}
|
||||
} finally {
|
||||
SSLContext.setDefault(reservedSSLContext);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
volatile int serverPort = 0;
|
||||
|
||||
volatile Exception serverException = null;
|
||||
volatile Exception clientException = null;
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
if (args[1].contains("MD5")) {
|
||||
SecurityUtils.removeFromDisabledAlgs(
|
||||
"jdk.certpath.disabledAlgorithms", List.of("MD5"));
|
||||
SecurityUtils.removeFromDisabledTlsAlgs("MD5");
|
||||
}
|
||||
if (debug)
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
new Identities(args[0], args[1]);
|
||||
}
|
||||
|
||||
Thread clientThread = null;
|
||||
Thread serverThread = null;
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
Identities(String protocol, String signatureAlg) throws Exception {
|
||||
super(protocol, signatureAlg);
|
||||
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
serverThread.join();
|
||||
} else {
|
||||
clientThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
*
|
||||
* If the main thread excepted, that propagates back
|
||||
* immediately. If the other thread threw an exception, we
|
||||
* should report back.
|
||||
*/
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
if (clientException != null)
|
||||
throw clientException;
|
||||
}
|
||||
|
||||
void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
doServerSide();
|
||||
}
|
||||
}
|
||||
|
||||
void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died...");
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
doClientSide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* 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 jdk.test.lib.security.CertificateBuilder;
|
||||
import sun.security.x509.AuthorityKeyIdentifierExtension;
|
||||
import sun.security.x509.GeneralName;
|
||||
import sun.security.x509.GeneralNames;
|
||||
import sun.security.x509.KeyIdentifier;
|
||||
import sun.security.x509.SerialNumber;
|
||||
import sun.security.x509.X500Name;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public abstract class IdentitiesBase {
|
||||
static X509Certificate trustedCert;
|
||||
static X509Certificate serverCert;
|
||||
static X509Certificate clientCert;
|
||||
static KeyPair serverKeys;
|
||||
static KeyPair clientKeys;
|
||||
static char[] passphrase = "passphrase".toCharArray();
|
||||
|
||||
protected final String protocol;
|
||||
protected final String signatureAlg;
|
||||
|
||||
/*
|
||||
* Turn on SSL debugging?
|
||||
*/
|
||||
static boolean debug = Boolean.getBoolean("test.debug");
|
||||
|
||||
public IdentitiesBase(String protocol, String signatureAlg) throws Exception {
|
||||
this.protocol = protocol;
|
||||
this.signatureAlg = signatureAlg;
|
||||
|
||||
setupCertificates();
|
||||
}
|
||||
|
||||
protected String getTrustedDname() {
|
||||
return "O=Some-Org, L=Some-City, ST=Some-State, C=US";
|
||||
}
|
||||
|
||||
protected String getServerDname() {
|
||||
return "CN=localhost, OU=SSL-Server, O=Some-Org, L=Some-City, ST=Some-State, C=US";
|
||||
}
|
||||
|
||||
protected String getClientDname() {
|
||||
return "CN=localhost, OU=SSL-Client, O=Some-Org, L=Some-City, ST=Some-State, C=US";
|
||||
}
|
||||
|
||||
protected CertificateBuilder customizeServerCert(CertificateBuilder builder) throws Exception {
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected CertificateBuilder customizeClientCert(CertificateBuilder builder) throws Exception {
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void setupCertificates() throws Exception {
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
|
||||
KeyPair caKeys = kpg.generateKeyPair();
|
||||
serverKeys = kpg.generateKeyPair();
|
||||
clientKeys = kpg.generateKeyPair();
|
||||
|
||||
trustedCert = createTrustedCert(getTrustedDname(), caKeys, signatureAlg);
|
||||
if (debug) {
|
||||
System.out.println("----------- Trusted Cert -----------");
|
||||
CertificateBuilder.printCertificate(trustedCert, System.out);
|
||||
}
|
||||
|
||||
CertificateBuilder builder = CertificateBuilder.newCertificateBuilder(
|
||||
getServerDname(),
|
||||
serverKeys.getPublic(), caKeys.getPublic(),
|
||||
CertificateBuilder.KeyUsage.DIGITAL_SIGNATURE,
|
||||
CertificateBuilder.KeyUsage.NONREPUDIATION,
|
||||
CertificateBuilder.KeyUsage.KEY_ENCIPHERMENT)
|
||||
.addBasicConstraintsExt(false, false, -1)
|
||||
.addExtension(CertificateBuilder.createIPSubjectAltNameExt(true, "127.0.0.1", "::1"))
|
||||
.setOneHourValidity();
|
||||
serverCert = customizeServerCert(builder)
|
||||
.build(trustedCert, caKeys.getPrivate(), signatureAlg);
|
||||
if (debug) {
|
||||
System.out.println("----------- Server Cert -----------");
|
||||
CertificateBuilder.printCertificate(serverCert, System.out);
|
||||
}
|
||||
|
||||
builder = CertificateBuilder.newCertificateBuilder(
|
||||
getClientDname(),
|
||||
clientKeys.getPublic(), caKeys.getPublic(),
|
||||
CertificateBuilder.KeyUsage.DIGITAL_SIGNATURE,
|
||||
CertificateBuilder.KeyUsage.NONREPUDIATION,
|
||||
CertificateBuilder.KeyUsage.KEY_ENCIPHERMENT)
|
||||
.addExtension(CertificateBuilder.createIPSubjectAltNameExt(true, "127.0.0.1", "::1"))
|
||||
.addBasicConstraintsExt(false, false, -1)
|
||||
.setOneHourValidity();
|
||||
builder = customizeClientCert(builder);
|
||||
clientCert = builder.build(trustedCert, caKeys.getPrivate(), signatureAlg);
|
||||
if (debug) {
|
||||
System.out.println("----------- Client Cert -----------");
|
||||
CertificateBuilder.printCertificate(clientCert, System.out);
|
||||
}
|
||||
}
|
||||
|
||||
protected static X509Certificate createTrustedCert(String dname, KeyPair caKeys,
|
||||
String signatureAlgo) throws Exception {
|
||||
SecureRandom random = new SecureRandom();
|
||||
|
||||
KeyIdentifier kid = new KeyIdentifier(caKeys.getPublic());
|
||||
GeneralNames gns = new GeneralNames();
|
||||
GeneralName name = new GeneralName(new X500Name(dname));
|
||||
gns.add(name);
|
||||
BigInteger serialNumber = BigInteger.valueOf(random.nextLong(1000000) + 1);
|
||||
return CertificateBuilder.newCertificateBuilder(dname,
|
||||
caKeys.getPublic(), caKeys.getPublic())
|
||||
.setSerialNumber(serialNumber)
|
||||
.addExtension(new AuthorityKeyIdentifierExtension(kid, gns,
|
||||
new SerialNumber(serialNumber)))
|
||||
.addBasicConstraintsExt(true, true, -1)
|
||||
.setOneHourValidity()
|
||||
.build(null, caKeys.getPrivate(), signatureAlgo);
|
||||
}
|
||||
|
||||
protected SSLContext getClientSSLContext() throws Exception {
|
||||
return getSSLContext(trustedCert, clientCert, clientKeys, passphrase);
|
||||
}
|
||||
|
||||
protected SSLContext getServerSSLContext() throws Exception {
|
||||
return getSSLContext(trustedCert, serverCert, serverKeys, passphrase);
|
||||
}
|
||||
|
||||
// get the ssl context
|
||||
private SSLContext getSSLContext(X509Certificate trustedCert,
|
||||
X509Certificate keyCert, KeyPair key, char[] passphrase)
|
||||
throws Exception {
|
||||
|
||||
// create a key store
|
||||
KeyStore ks = KeyStore.getInstance("PKCS12");
|
||||
ks.load(null, null);
|
||||
|
||||
// import the trused cert
|
||||
ks.setCertificateEntry("RSA Export Signer", trustedCert);
|
||||
|
||||
Certificate[] chain = new Certificate[2];
|
||||
chain[0] = keyCert;
|
||||
chain[1] = trustedCert;
|
||||
|
||||
// import the key entry.
|
||||
ks.setKeyEntry("Whatever", key.getPrivate(), passphrase, chain);
|
||||
|
||||
// create SSL context
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
|
||||
tmf.init(ks);
|
||||
|
||||
SSLContext ctx = SSLContext.getInstance(protocol);
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||
kmf.init(ks, passphrase);
|
||||
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,400 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// SunJSSE does not support dynamic system properties, no way to re-use
|
||||
// system properties in samevm/agentvm mode.
|
||||
//
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8144566
|
||||
* @summary Custom HostnameVerifier disables SNI extension
|
||||
* @run main/othervm ImpactOnSNI
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import javax.net.ssl.*;
|
||||
|
||||
public class ImpactOnSNI {
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* Set the various variables needed for the tests, then
|
||||
* specify what tests to run on each side.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
private static final boolean separateServerThread = true;
|
||||
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
private static final String pathToStores =
|
||||
"../../../../../../javax/net/ssl/etc";
|
||||
private static final String keyStoreFile = "keystore";
|
||||
private static final String trustStoreFile = "truststore";
|
||||
private static final String passwd = "passphrase";
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
private static volatile boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Is the connection ready to close?
|
||||
*/
|
||||
private static volatile boolean closeReady = false;
|
||||
|
||||
/*
|
||||
* Turn on SSL debugging?
|
||||
*/
|
||||
private static final boolean debug = false;
|
||||
|
||||
/*
|
||||
* Message posted
|
||||
*/
|
||||
private static final String postMsg = "HTTP post on a https server";
|
||||
|
||||
/*
|
||||
* the fully qualified domain name of localhost
|
||||
*/
|
||||
private static String hostname = null;
|
||||
|
||||
/*
|
||||
* If the client or server is doing some kind of object creation
|
||||
* that the other side depends on, and that thread prematurely
|
||||
* exits, you may experience a hang. The test harness will
|
||||
* terminate all hung threads after its timeout has expired,
|
||||
* currently 3 minutes by default, but you might try to be
|
||||
* smart about it....
|
||||
*/
|
||||
|
||||
private SSLServerSocket createServerSocket(SSLServerSocketFactory sslssf)
|
||||
throws Exception {
|
||||
SSLServerSocket sslServerSocket =
|
||||
(SSLServerSocket)sslssf.createServerSocket();
|
||||
InetAddress localHost = InetAddress.getLocalHost();
|
||||
InetSocketAddress address = new InetSocketAddress(localHost, serverPort);
|
||||
sslServerSocket.bind(address);
|
||||
return sslServerSocket;
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
private void doServerSide() throws Exception {
|
||||
SSLServerSocketFactory sslssf =
|
||||
(SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
|
||||
try (SSLServerSocket sslServerSocket = createServerSocket(sslssf)) {
|
||||
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
|
||||
/*
|
||||
* Accept connections
|
||||
*/
|
||||
try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) {
|
||||
InputStream sslIS = sslSocket.getInputStream();
|
||||
OutputStream sslOS = sslSocket.getOutputStream();
|
||||
BufferedReader br =
|
||||
new BufferedReader(new InputStreamReader(sslIS));
|
||||
PrintStream ps = new PrintStream(sslOS);
|
||||
|
||||
// process HTTP POST request from client
|
||||
System.out.println("status line: " + br.readLine());
|
||||
String msg = null;
|
||||
while ((msg = br.readLine()) != null && msg.length() > 0);
|
||||
|
||||
msg = br.readLine();
|
||||
if (msg.equals(postMsg)) {
|
||||
ps.println("HTTP/1.1 200 OK\n\n");
|
||||
} else {
|
||||
ps.println("HTTP/1.1 500 Not OK\n\n");
|
||||
}
|
||||
ps.flush();
|
||||
|
||||
ExtendedSSLSession session =
|
||||
(ExtendedSSLSession)sslSocket.getSession();
|
||||
if (session.getRequestedServerNames().isEmpty()) {
|
||||
throw new Exception("No expected Server Name Indication");
|
||||
}
|
||||
|
||||
// close the socket
|
||||
while (!closeReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
private void doClientSide() throws Exception {
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
// Send HTTP POST request to server
|
||||
URL url = new URL("https://" + hostname + ":" + serverPort);
|
||||
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(new NameVerifier());
|
||||
HttpsURLConnection http = (HttpsURLConnection)url.openConnection();
|
||||
http.setDoOutput(true);
|
||||
|
||||
http.setRequestMethod("POST");
|
||||
PrintStream ps = new PrintStream(http.getOutputStream());
|
||||
try {
|
||||
ps.println(postMsg);
|
||||
ps.flush();
|
||||
if (http.getResponseCode() != 200) {
|
||||
throw new RuntimeException("test Failed");
|
||||
}
|
||||
} finally {
|
||||
ps.close();
|
||||
http.disconnect();
|
||||
closeReady = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class NameVerifier implements HostnameVerifier {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
private volatile int serverPort = 0;
|
||||
|
||||
private volatile Exception serverException = null;
|
||||
private volatile Exception clientException = null;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String keyFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + keyStoreFile;
|
||||
String trustFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
if (debug) {
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
}
|
||||
|
||||
try {
|
||||
hostname = InetAddress.getLocalHost().getCanonicalHostName();
|
||||
} catch (UnknownHostException uhe) {
|
||||
System.out.println(
|
||||
"Ignore the test as the local hostname cannot be determined");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println(
|
||||
"The fully qualified domain name of the local host is " +
|
||||
hostname);
|
||||
// Ignore the test if the hostname does not sound like a domain name.
|
||||
if ((hostname == null) || hostname.isEmpty() ||
|
||||
!hostname.contains(".") || hostname.endsWith(".") ||
|
||||
hostname.startsWith("localhost") ||
|
||||
Character.isDigit(hostname.charAt(hostname.length() - 1))) {
|
||||
|
||||
System.out.println("Ignore the test as the local hostname " +
|
||||
"cannot be determined as fully qualified domain name");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
new ImpactOnSNI();
|
||||
}
|
||||
|
||||
private Thread clientThread = null;
|
||||
private Thread serverThread = null;
|
||||
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
ImpactOnSNI() throws Exception {
|
||||
Exception startException = null;
|
||||
try {
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
startException = e;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
if (serverThread != null) {
|
||||
serverThread.join();
|
||||
}
|
||||
} else {
|
||||
if (clientThread != null) {
|
||||
clientThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
* Which side threw the error?
|
||||
*/
|
||||
Exception local;
|
||||
Exception remote;
|
||||
|
||||
if (separateServerThread) {
|
||||
remote = serverException;
|
||||
local = clientException;
|
||||
} else {
|
||||
remote = clientException;
|
||||
local = serverException;
|
||||
}
|
||||
|
||||
Exception exception = null;
|
||||
|
||||
/*
|
||||
* Check various exception conditions.
|
||||
*/
|
||||
if ((local != null) && (remote != null)) {
|
||||
// If both failed, return the curthread's exception.
|
||||
local.initCause(remote);
|
||||
exception = local;
|
||||
} else if (local != null) {
|
||||
exception = local;
|
||||
} else if (remote != null) {
|
||||
exception = remote;
|
||||
} else if (startException != null) {
|
||||
exception = startException;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there was an exception *AND* a startException,
|
||||
* output it.
|
||||
*/
|
||||
if (exception != null) {
|
||||
if (exception != startException && startException != null) {
|
||||
exception.addSuppressed(startException);
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
|
||||
// Fall-through: no exception to throw!
|
||||
}
|
||||
|
||||
private void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
serverException = e;
|
||||
} finally {
|
||||
serverReady = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died...");
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2003, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* This is a HTTP test server.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import javax.net.*;
|
||||
|
||||
/*
|
||||
* OriginServer.java -- a simple server that can serve
|
||||
* http requests in both clear and secure channel.
|
||||
*/
|
||||
|
||||
public abstract class OriginServer implements Runnable {
|
||||
|
||||
private ServerSocket server = null;
|
||||
Exception serverException = null;
|
||||
|
||||
/**
|
||||
* Constructs a OriginServer based on ss and
|
||||
* obtains a response data's bytecodes using the method
|
||||
* getBytes.
|
||||
*/
|
||||
protected OriginServer(ServerSocket ss) throws Exception
|
||||
{
|
||||
server = ss;
|
||||
newListener();
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of bytes containing the bytes for
|
||||
* data sent in the response.
|
||||
*
|
||||
* @return the bytes for the information that is being sent
|
||||
*/
|
||||
public abstract byte[] getBytes();
|
||||
|
||||
/**
|
||||
* The "listen" thread that accepts a connection to the
|
||||
* server, parses header and sends back the response
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
Socket socket;
|
||||
|
||||
// accept a connection
|
||||
try {
|
||||
socket = server.accept();
|
||||
} catch (IOException e) {
|
||||
System.out.println("Class Server died: " + e.getMessage());
|
||||
serverException = e;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
DataOutputStream out =
|
||||
new DataOutputStream(socket.getOutputStream());
|
||||
try {
|
||||
BufferedReader in =
|
||||
new BufferedReader(new InputStreamReader(
|
||||
socket.getInputStream()));
|
||||
// read the request
|
||||
readRequest(in);
|
||||
// retrieve bytecodes
|
||||
byte[] bytecodes = getBytes();
|
||||
// send bytecodes in response (assumes HTTP/1.0 or later)
|
||||
try {
|
||||
out.writeBytes("HTTP/1.0 200 OK\r\n");
|
||||
out.writeBytes("Content-Length: " + bytecodes.length +
|
||||
"\r\n");
|
||||
out.writeBytes("Content-Type: text/html\r\n\r\n");
|
||||
out.write(bytecodes);
|
||||
out.flush();
|
||||
} catch (IOException ie) {
|
||||
serverException = ie;
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// write out error response
|
||||
out.writeBytes("HTTP/1.0 400 " + e.getMessage() + "\r\n");
|
||||
out.writeBytes("Content-Type: text/html\r\n\r\n");
|
||||
out.flush();
|
||||
}
|
||||
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Server: Error writing response: "
|
||||
+ ex.getMessage());
|
||||
serverException = ex;
|
||||
|
||||
} finally {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new thread to listen.
|
||||
*/
|
||||
private void newListener()
|
||||
{
|
||||
(new Thread(this)).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* read the response, don't care for the syntax of the request-line
|
||||
*/
|
||||
private static void readRequest(BufferedReader in)
|
||||
throws IOException
|
||||
{
|
||||
String line = null;
|
||||
do {
|
||||
line = in.readLine();
|
||||
System.out.println("Server recieved: " + line);
|
||||
} while ((line.length() != 0) &&
|
||||
(line.charAt(0) != '\r') && (line.charAt(0) != '\n'));
|
||||
|
||||
// read the last line to clear the POST input buffer
|
||||
// would be best to make sure all bytes are read, maybe later.
|
||||
line = in.readLine();
|
||||
System.out.println("Command received: " + line);
|
||||
|
||||
System.out.println("");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.security.KeyStore;
|
||||
import javax.net.*;
|
||||
import javax.net.ssl.*;
|
||||
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4423074
|
||||
* @modules java.base/sun.net.www
|
||||
* @summary This test case is written to test the https POST through a proxy.
|
||||
* There is no proxy authentication done. It includes a simple server
|
||||
* that serves http POST method requests in secure channel, and a client
|
||||
* that makes https POST request through a proxy.
|
||||
* @library /test/lib
|
||||
* @compile OriginServer.java ProxyTunnelServer.java
|
||||
* @run main/othervm PostThruProxy
|
||||
*/
|
||||
public class PostThruProxy {
|
||||
|
||||
private static final String TEST_SRC = System.getProperty("test.src", ".");
|
||||
private static final int TIMEOUT = 30000;
|
||||
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
|
||||
private static int serverPort = 0;
|
||||
private static ProxyTunnelServer pserver;
|
||||
private static TestServer server;
|
||||
static final String RESPONSE_MSG = "Https POST thru proxy is successful";
|
||||
|
||||
/*
|
||||
* The TestServer implements a OriginServer that
|
||||
* processes HTTP requests and responses.
|
||||
*/
|
||||
static class TestServer extends OriginServer {
|
||||
public TestServer(ServerSocket ss) throws Exception {
|
||||
super(ss);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an array of bytes containing the bytes for
|
||||
* the data sent in the response.
|
||||
*
|
||||
* @return bytes for the data in the response
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
return RESPONSE_MSG.getBytes();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Main method to create the server and client
|
||||
*/
|
||||
public static void main(String args[]) throws Exception {
|
||||
|
||||
String keyFilename = TEST_SRC + "/" + pathToStores + "/" + keyStoreFile;
|
||||
String trustFilename = TEST_SRC + "/" + pathToStores + "/"
|
||||
+ trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
boolean useSSL = true;
|
||||
/*
|
||||
* setup the server
|
||||
*/
|
||||
try {
|
||||
ServerSocketFactory ssf = getServerSocketFactory(useSSL);
|
||||
ServerSocket ss = ssf.createServerSocket(serverPort, 0, loopback);
|
||||
ss.setSoTimeout(TIMEOUT); // 30 seconds
|
||||
serverPort = ss.getLocalPort();
|
||||
server = new TestServer(ss);
|
||||
System.out.println("Server started at: " + ss);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Server side failed:" +
|
||||
e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
// trigger the client
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
System.out.println("Client side failed: " +
|
||||
e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
long connectCount = pserver.getConnectCount();
|
||||
if (connectCount == 0) {
|
||||
throw new AssertionError("Proxy was not used!");
|
||||
} else {
|
||||
System.out.println("Proxy CONNECT count: " + connectCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static ServerSocketFactory getServerSocketFactory
|
||||
(boolean useSSL) throws Exception {
|
||||
if (useSSL) {
|
||||
// set up key manager to do server authentication
|
||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
char[] passphrase = passwd.toCharArray();
|
||||
|
||||
ks.load(new FileInputStream(System.getProperty(
|
||||
"javax.net.ssl.keyStore")), passphrase);
|
||||
kmf.init(ks, passphrase);
|
||||
ctx.init(kmf.getKeyManagers(), null, null);
|
||||
|
||||
return ctx.getServerSocketFactory();
|
||||
} else {
|
||||
return ServerSocketFactory.getDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Message to be posted
|
||||
*/
|
||||
static String postMsg = "Testing HTTP post on a https server";
|
||||
|
||||
static void doClientSide() throws Exception {
|
||||
HostnameVerifier reservedHV =
|
||||
HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
try {
|
||||
/*
|
||||
* setup up a proxy
|
||||
*/
|
||||
SocketAddress pAddr = setupProxy();
|
||||
|
||||
/*
|
||||
* we want to avoid URLspoofCheck failures in cases where the cert
|
||||
* DN name does not match the hostname in the URL.
|
||||
*/
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(
|
||||
new NameVerifier());
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(serverPort)
|
||||
.toURL();
|
||||
|
||||
Proxy p = new Proxy(Proxy.Type.HTTP, pAddr);
|
||||
System.out.println("Client connecting to: " + url);
|
||||
System.out.println("Through proxy: " + pAddr);
|
||||
HttpsURLConnection https = (HttpsURLConnection)url.openConnection(p);
|
||||
https.setConnectTimeout(TIMEOUT);
|
||||
https.setReadTimeout(TIMEOUT);
|
||||
https.setDoOutput(true);
|
||||
https.setRequestMethod("POST");
|
||||
PrintStream ps = null;
|
||||
try {
|
||||
ps = new PrintStream(https.getOutputStream());
|
||||
ps.println(postMsg);
|
||||
ps.flush();
|
||||
if (https.getResponseCode() != 200) {
|
||||
throw new RuntimeException("test Failed");
|
||||
}
|
||||
ps.close();
|
||||
|
||||
// clear the pipe
|
||||
BufferedReader in = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
https.getInputStream()));
|
||||
String inputLine;
|
||||
boolean msgFound = false;
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
System.out.println("Client received: " + inputLine);
|
||||
if (inputLine.contains(RESPONSE_MSG)) msgFound = true;
|
||||
}
|
||||
in.close();
|
||||
if (!msgFound) {
|
||||
throw new RuntimeException("POST message not found.");
|
||||
}
|
||||
} catch (SSLException e) {
|
||||
if (ps != null)
|
||||
ps.close();
|
||||
throw e;
|
||||
} catch (SocketTimeoutException e) {
|
||||
System.out.println("Client can not get response in time: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
} finally {
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(reservedHV);
|
||||
}
|
||||
}
|
||||
|
||||
static class NameVerifier implements HostnameVerifier {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static SocketAddress setupProxy() throws IOException {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
pserver = new ProxyTunnelServer(loopback);
|
||||
|
||||
// disable proxy authentication
|
||||
pserver.needUserAuth(false);
|
||||
pserver.start();
|
||||
return new InetSocketAddress(loopback, pserver.getPort());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.security.KeyStore;
|
||||
import javax.net.*;
|
||||
import javax.net.ssl.*;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4423074
|
||||
* @modules java.base/sun.net.www
|
||||
* @summary This test case is written to test the https POST through a proxy
|
||||
* with proxy authentication. It includes a simple server that serves
|
||||
* http POST method requests in secure channel, and a client that
|
||||
* makes https POST request through a proxy.
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.Utils
|
||||
* jdk.test.lib.Asserts
|
||||
* jdk.test.lib.JDKToolFinder
|
||||
* jdk.test.lib.JDKToolLauncher
|
||||
* jdk.test.lib.Platform
|
||||
* jdk.test.lib.process.*
|
||||
* @compile OriginServer.java ProxyTunnelServer.java
|
||||
* @run main/othervm -Djdk.http.auth.tunneling.disabledSchemes= PostThruProxyWithAuth
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true
|
||||
-Djdk.http.auth.tunneling.disabledSchemes= PostThruProxyWithAuth
|
||||
*/
|
||||
public class PostThruProxyWithAuth {
|
||||
|
||||
private static final String TEST_SRC = System.getProperty("test.src", ".");
|
||||
private static final int TIMEOUT = 30000;
|
||||
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
|
||||
volatile private static int serverPort = 0;
|
||||
private static ProxyTunnelServer pserver;
|
||||
private static TestServer server;
|
||||
|
||||
static final String RESPONSE_MSG =
|
||||
"Https POST thru proxy is successful with proxy authentication";
|
||||
|
||||
/*
|
||||
* The TestServer implements a OriginServer that
|
||||
* processes HTTP requests and responses.
|
||||
*/
|
||||
static class TestServer extends OriginServer {
|
||||
public TestServer(ServerSocket ss) throws Exception {
|
||||
super(ss);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an array of bytes containing the bytes for
|
||||
* the data sent in the response.
|
||||
*
|
||||
* @return bytes for the data in the response
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
return RESPONSE_MSG.getBytes();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Main method to create the server and client
|
||||
*/
|
||||
public static void main(String args[]) throws Exception {
|
||||
String keyFilename = TEST_SRC + "/" + pathToStores + "/" + keyStoreFile;
|
||||
String trustFilename = TEST_SRC + "/" + pathToStores + "/"
|
||||
+ trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
boolean useSSL = true;
|
||||
/*
|
||||
* setup the server
|
||||
*/
|
||||
try {
|
||||
InetAddress localhost = InetAddress.getLocalHost();
|
||||
ServerSocketFactory ssf = getServerSocketFactory(useSSL);
|
||||
ServerSocket ss = ssf.createServerSocket(serverPort, 0, localhost);
|
||||
ss.setSoTimeout(TIMEOUT); // 30 seconds
|
||||
serverPort = ss.getLocalPort();
|
||||
server = new TestServer(ss);
|
||||
System.out.println("Server started at: " + ss);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Server side failed:" +
|
||||
e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
// trigger the client
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
System.out.println("Client side failed: " +
|
||||
e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
long connectCount = pserver.getConnectCount();
|
||||
if (connectCount == 0) {
|
||||
throw new AssertionError("Proxy was not used!");
|
||||
} else {
|
||||
System.out.println("Proxy CONNECT count: " + connectCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static ServerSocketFactory getServerSocketFactory
|
||||
(boolean useSSL) throws Exception {
|
||||
if (useSSL) {
|
||||
// set up key manager to do server authentication
|
||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
char[] passphrase = passwd.toCharArray();
|
||||
|
||||
ks.load(new FileInputStream(System.getProperty(
|
||||
"javax.net.ssl.keyStore")), passphrase);
|
||||
kmf.init(ks, passphrase);
|
||||
ctx.init(kmf.getKeyManagers(), null, null);
|
||||
|
||||
return ctx.getServerSocketFactory();
|
||||
} else {
|
||||
return ServerSocketFactory.getDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Message to be posted
|
||||
*/
|
||||
static String postMsg = "Testing HTTP post on a https server";
|
||||
|
||||
static void doClientSide() throws Exception {
|
||||
/*
|
||||
* setup up a proxy
|
||||
*/
|
||||
SocketAddress pAddr = setupProxy();
|
||||
|
||||
/*
|
||||
* we want to avoid URLspoofCheck failures in cases where the cert
|
||||
* DN name does not match the hostname in the URL.
|
||||
*/
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(
|
||||
new NameVerifier());
|
||||
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.host(getHostname())
|
||||
.port(serverPort)
|
||||
.toURL();
|
||||
|
||||
Proxy p = new Proxy(Proxy.Type.HTTP, pAddr);
|
||||
System.out.println("Client connecting to: " + url);
|
||||
System.out.println("Through proxy: " + pAddr);
|
||||
HttpsURLConnection https = (HttpsURLConnection)url.openConnection(p);
|
||||
https.setConnectTimeout(TIMEOUT);
|
||||
https.setReadTimeout(TIMEOUT);
|
||||
https.setDoOutput(true);
|
||||
https.setRequestMethod("POST");
|
||||
PrintStream ps = null;
|
||||
try {
|
||||
ps = new PrintStream(https.getOutputStream());
|
||||
ps.println(postMsg);
|
||||
ps.flush();
|
||||
if (https.getResponseCode() != 200) {
|
||||
throw new RuntimeException("test Failed");
|
||||
}
|
||||
ps.close();
|
||||
// clear the pipe
|
||||
BufferedReader in = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
https.getInputStream()));
|
||||
String inputLine;
|
||||
boolean msgFound = false;
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
System.out.println("Client received: " + inputLine);
|
||||
if (inputLine.contains(RESPONSE_MSG)) msgFound = true;
|
||||
}
|
||||
in.close();
|
||||
if (!msgFound) {
|
||||
throw new RuntimeException("POST message not found.");
|
||||
}
|
||||
} catch (SSLException e) {
|
||||
if (ps != null)
|
||||
ps.close();
|
||||
throw e;
|
||||
} catch (SocketTimeoutException e) {
|
||||
System.out.println("Client can not get response in time: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
static class NameVerifier implements HostnameVerifier {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static SocketAddress setupProxy() throws IOException {
|
||||
|
||||
InetAddress localhost = InetAddress.getLocalHost();
|
||||
pserver = new ProxyTunnelServer(localhost);
|
||||
|
||||
/*
|
||||
* register a system wide authenticator and setup the proxy for
|
||||
* authentication
|
||||
*/
|
||||
Authenticator.setDefault(new TestAuthenticator());
|
||||
|
||||
// register with the username and password
|
||||
pserver.needUserAuth(true);
|
||||
pserver.setUserAuth("Test", "test123");
|
||||
|
||||
pserver.start();
|
||||
|
||||
return new InetSocketAddress(localhost, pserver.getPort());
|
||||
}
|
||||
|
||||
public static class TestAuthenticator extends Authenticator {
|
||||
public PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication("Test",
|
||||
"test123".toCharArray());
|
||||
}
|
||||
}
|
||||
|
||||
private static String getHostname() {
|
||||
try {
|
||||
OutputAnalyzer oa = ProcessTools.executeCommand("hostname");
|
||||
return oa.getOutput().trim();
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Get hostname failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* This class includes a proxy server that processes HTTP CONNECT requests,
|
||||
* and tunnels the data from the client to the server, once the CONNECT
|
||||
* request is accepted.
|
||||
* The proxy server processes only one transaction, i.e a CONNECT request
|
||||
* followed by the corresponding tunnel data.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.net.ServerSocketFactory;
|
||||
|
||||
import jdk.test.lib.net.HttpHeaderParser;
|
||||
|
||||
public class ProxyTunnelServer extends Thread {
|
||||
|
||||
private static final int TIMEOUT = 30000;
|
||||
|
||||
private static ServerSocket ss = null;
|
||||
/*
|
||||
* holds the registered user's username and password
|
||||
* only one such entry is maintained
|
||||
*/
|
||||
private String userPlusPass;
|
||||
|
||||
// client requesting for 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;
|
||||
|
||||
/*
|
||||
* denote whether the proxy needs to authorize
|
||||
* CONNECT requests.
|
||||
*/
|
||||
static boolean needAuth = false;
|
||||
|
||||
volatile long connectCount;
|
||||
|
||||
public ProxyTunnelServer() throws IOException {
|
||||
if (ss == null) {
|
||||
ss = (ServerSocket) ServerSocketFactory.getDefault()
|
||||
.createServerSocket(0);
|
||||
ss.setSoTimeout(TIMEOUT);
|
||||
System.out.println("Proxy server created: " + ss);
|
||||
}
|
||||
}
|
||||
|
||||
public ProxyTunnelServer(InetAddress address) throws IOException {
|
||||
if (ss == null) {
|
||||
ss = (ServerSocket) ServerSocketFactory.getDefault()
|
||||
.createServerSocket(0, 0, address);
|
||||
ss.setSoTimeout(TIMEOUT);
|
||||
System.out.println("Proxy server created: " + ss);
|
||||
}
|
||||
}
|
||||
|
||||
public void needUserAuth(boolean auth) {
|
||||
needAuth = auth;
|
||||
}
|
||||
|
||||
/*
|
||||
* register users with the proxy, by providing username and
|
||||
* password. The username and password are used for authorizing the
|
||||
* user when a CONNECT request is made and needAuth is set to true.
|
||||
*/
|
||||
public void setUserAuth(String uname, String passwd) {
|
||||
userPlusPass = uname + ":" + passwd;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
System.out.println("Proxy server listening at: " + ss);
|
||||
clientSocket = ss.accept();
|
||||
System.out.println("Proxy server accepted connection: " + clientSocket);
|
||||
processRequests();
|
||||
} catch (SocketTimeoutException e) {
|
||||
System.out.println(
|
||||
"Proxy can not get response in time: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
System.out.println("Proxy Failed: " + e);
|
||||
e.printStackTrace();
|
||||
try {
|
||||
ss.close();
|
||||
}
|
||||
catch (IOException excep) {
|
||||
System.out.println("ProxyServer close error: " + excep);
|
||||
excep.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
System.out.println("Proxy server: request served");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes the CONNECT requests, if needAuth is set to true, then
|
||||
* the name and password are extracted from the Proxy-Authorization header
|
||||
* of the request. They are checked against the one that is registered,
|
||||
* if there is a match, connection is set in tunneling mode. If
|
||||
* needAuth is set to false, Proxy-Authorization checks are not made
|
||||
*/
|
||||
private void processRequests() throws Exception {
|
||||
|
||||
InputStream in = clientSocket.getInputStream();
|
||||
HttpHeaderParser mheader = new HttpHeaderParser(in);
|
||||
String statusLine = mheader.getRequestDetails();
|
||||
|
||||
if (statusLine.startsWith("CONNECT")) {
|
||||
synchronized(this) { connectCount++; }
|
||||
// retrieve the host and port info from the status-line
|
||||
retrieveConnectInfo(statusLine);
|
||||
if (needAuth) {
|
||||
String authInfo;
|
||||
if ((authInfo = (mheader.getHeaderValue("Proxy-Authorization") != null ?
|
||||
mheader.getHeaderValue("Proxy-Authorization").get(0) : null))
|
||||
!= null) {
|
||||
if (authenticate(authInfo)) {
|
||||
needAuth = false;
|
||||
System.out.println(
|
||||
"Proxy: client authentication successful");
|
||||
}
|
||||
}
|
||||
}
|
||||
respondForConnect(needAuth);
|
||||
|
||||
// connection set to the tunneling mode
|
||||
if (!needAuth) {
|
||||
doTunnel();
|
||||
/*
|
||||
* done with tunneling, we process only one successful
|
||||
* tunneling request
|
||||
*/
|
||||
ss.close();
|
||||
} else {
|
||||
// we may get another request with Proxy-Authorization set
|
||||
in.close();
|
||||
clientSocket.close();
|
||||
restart();
|
||||
}
|
||||
} else {
|
||||
System.out.println("proxy server: processes only "
|
||||
+ "CONNECT method requests, recieved: "
|
||||
+ statusLine);
|
||||
}
|
||||
}
|
||||
|
||||
public long getConnectCount() {
|
||||
return connectCount;
|
||||
}
|
||||
|
||||
private void respondForConnect(boolean needAuth) throws Exception {
|
||||
|
||||
OutputStream out = clientSocket.getOutputStream();
|
||||
PrintWriter pout = new PrintWriter(out);
|
||||
|
||||
if (needAuth) {
|
||||
pout.println("HTTP/1.1 407 Proxy Auth Required");
|
||||
pout.println("Proxy-Authenticate: Basic realm=\"WallyWorld\"");
|
||||
pout.println();
|
||||
pout.flush();
|
||||
out.close();
|
||||
} else {
|
||||
pout.println("HTTP/1.1 200 OK");
|
||||
pout.println();
|
||||
pout.flush();
|
||||
}
|
||||
}
|
||||
|
||||
private void restart() throws IOException {
|
||||
(new Thread(this)).start();
|
||||
}
|
||||
|
||||
/*
|
||||
* note: Tunneling has to be provided in both directions, i.e
|
||||
* from client->server and server->client, even if the application
|
||||
* data may be unidirectional, SSL handshaking data flows in either
|
||||
* direction.
|
||||
*/
|
||||
private void doTunnel() throws Exception {
|
||||
|
||||
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(TIMEOUT);
|
||||
serverToClient.join(TIMEOUT);
|
||||
System.out.println("Proxy: Finished tunneling........");
|
||||
|
||||
clientToServer.close();
|
||||
serverToClient.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* This inner class provides unidirectional data flow through the sockets
|
||||
* by continuously copying bytes from the input socket onto the output
|
||||
* socket, until both sockets are open and EOF has not been received.
|
||||
*/
|
||||
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;
|
||||
|
||||
try {
|
||||
while ((bytesRead = input.read(buf)) >= 0) {
|
||||
output.write(buf, 0, bytesRead);
|
||||
output.flush();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
/*
|
||||
* The peer end has closed the connection
|
||||
* we will close the tunnel
|
||||
*/
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
private void close() {
|
||||
try {
|
||||
if (!sockIn.isClosed())
|
||||
sockIn.close();
|
||||
if (!sockOut.isClosed())
|
||||
sockOut.close();
|
||||
} catch (IOException ignored) { }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
***************************************************************
|
||||
* helper methods follow
|
||||
***************************************************************
|
||||
*/
|
||||
|
||||
/*
|
||||
* This method retrieves the hostname and port of the destination
|
||||
* that the connect request wants to establish a tunnel for
|
||||
* communication.
|
||||
* The input, connectStr is of the form:
|
||||
* CONNECT server-name:server-port HTTP/1.x
|
||||
*/
|
||||
private void retrieveConnectInfo(String connectStr) throws Exception {
|
||||
|
||||
int starti;
|
||||
int endi;
|
||||
String connectInfo;
|
||||
String serverName = null;
|
||||
try {
|
||||
starti = connectStr.indexOf(' ');
|
||||
endi = connectStr.lastIndexOf(' ');
|
||||
connectInfo = connectStr.substring(starti+1, endi).trim();
|
||||
// retrieve server name and port
|
||||
if (connectInfo.charAt(0) == '[') {
|
||||
endi = connectInfo.indexOf(']');
|
||||
serverName = connectInfo.substring(1, endi++);
|
||||
assert connectInfo.charAt(endi) == ':' : "Expected [IPv6]:port";
|
||||
} else {
|
||||
endi = connectInfo.indexOf(':');
|
||||
serverName = connectInfo.substring(0, endi);
|
||||
}
|
||||
serverPort = Integer.parseInt(connectInfo.substring(endi+1));
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Proxy recieved a request: "
|
||||
+ connectStr, e);
|
||||
}
|
||||
serverInetAddr = InetAddress.getByName(serverName);
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return ss.getLocalPort();
|
||||
}
|
||||
|
||||
/*
|
||||
* do "basic" authentication, authInfo is of the form:
|
||||
* Basic <encoded username":"password>
|
||||
* reference RFC 2617
|
||||
*/
|
||||
private boolean authenticate(String authInfo) throws IOException {
|
||||
boolean matched = false;
|
||||
try {
|
||||
authInfo.trim();
|
||||
int ind = authInfo.indexOf(' ');
|
||||
String recvdUserPlusPass = authInfo.substring(ind + 1).trim();
|
||||
// extract encoded (username:passwd
|
||||
if (userPlusPass.equals(
|
||||
new String( Base64.getMimeDecoder()
|
||||
.decode(recvdUserPlusPass))))
|
||||
{
|
||||
matched = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new IOException(
|
||||
"Proxy received invalid Proxy-Authorization value: "
|
||||
+ authInfo);
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// SunJSSE does not support dynamic system properties, no way to re-use
|
||||
// system properties in samevm/agentvm mode.
|
||||
//
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4811482 4700777 4905410
|
||||
* @summary sun.net.client.defaultConnectTimeout should work with
|
||||
* HttpsURLConnection; HTTP client: Connect and read timeouts;
|
||||
* Https needs to support new tiger features that went into http
|
||||
* @library /test/lib
|
||||
* @run main/othervm ReadTimeout
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import javax.net.ssl.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class ReadTimeout {
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* Set the various variables needed for the tests, then
|
||||
* specify what tests to run on each side.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
static boolean separateServerThread = true;
|
||||
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
volatile static boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Turn on SSL debugging?
|
||||
*/
|
||||
static boolean debug = false;
|
||||
|
||||
/*
|
||||
* Message posted
|
||||
*/
|
||||
static String postMsg = "Testing HTTP post on a https server";
|
||||
|
||||
/*
|
||||
* If the client or server is doing some kind of object creation
|
||||
* that the other side depends on, and that thread prematurely
|
||||
* exits, you may experience a hang. The test harness will
|
||||
* terminate all hung threads after its timeout has expired,
|
||||
* currently 3 minutes by default, but you might try to be
|
||||
* smart about it....
|
||||
*/
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
SSLServerSocketFactory sslssf =
|
||||
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
||||
SSLServerSocket sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket(serverPort, 0, loopback);
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
try {
|
||||
try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) {
|
||||
InputStream sslIS = sslSocket.getInputStream();
|
||||
BufferedReader br =
|
||||
new BufferedReader(new InputStreamReader(sslIS));
|
||||
br.readLine();
|
||||
while (!finished()) {
|
||||
Thread.sleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
reset();
|
||||
// doing second test
|
||||
try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) {
|
||||
InputStream sslIS = sslSocket.getInputStream();
|
||||
BufferedReader br =
|
||||
new BufferedReader(new InputStreamReader(sslIS));
|
||||
br.readLine();
|
||||
while (!finished()) {
|
||||
Thread.sleep(2000);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Should be an expected exception: " + e);
|
||||
} finally {
|
||||
sslServerSocket.close();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isFinished = false;
|
||||
|
||||
synchronized boolean finished () {
|
||||
return (isFinished);
|
||||
}
|
||||
synchronized void done () {
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
synchronized void reset() {
|
||||
isFinished = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
HostnameVerifier reservedHV =
|
||||
HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
try {
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
HttpsURLConnection http = null;
|
||||
try {
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(serverPort)
|
||||
.toURL();
|
||||
|
||||
// set read timeout through system property
|
||||
System.setProperty("sun.net.client.defaultReadTimeout", "2000");
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(
|
||||
new NameVerifier());
|
||||
http = (HttpsURLConnection)url.openConnection();
|
||||
|
||||
InputStream is = http.getInputStream();
|
||||
|
||||
throw new Exception(
|
||||
"system property timeout configuration does not work");
|
||||
} catch (SSLException | SocketTimeoutException ex) {
|
||||
System.out.println("Got expected timeout exception for " +
|
||||
"system property timeout configuration: " + getCause(ex));
|
||||
} finally {
|
||||
done();
|
||||
http.disconnect();
|
||||
}
|
||||
|
||||
try {
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(serverPort)
|
||||
.toURL();
|
||||
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(
|
||||
new NameVerifier());
|
||||
http = (HttpsURLConnection)url.openConnection();
|
||||
// set read timeout through API
|
||||
http.setReadTimeout(2000);
|
||||
|
||||
InputStream is = http.getInputStream();
|
||||
|
||||
throw new Exception(
|
||||
"HttpsURLConnection.setReadTimeout() does not work");
|
||||
} catch (SSLException | SocketTimeoutException ex) {
|
||||
System.out.println("Got expected timeout exception for " +
|
||||
"HttpsURLConnection.setReadTimeout(): " + getCause(ex));
|
||||
} finally {
|
||||
done();
|
||||
http.disconnect();
|
||||
}
|
||||
} finally {
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(reservedHV);
|
||||
}
|
||||
}
|
||||
|
||||
private Exception getCause(Exception ex) {
|
||||
Exception cause = null;
|
||||
if (ex instanceof SSLException) {
|
||||
cause = (Exception) ex.getCause();
|
||||
if (!(cause instanceof SocketTimeoutException)) {
|
||||
throw new RuntimeException("Unexpected cause", cause);
|
||||
}
|
||||
} else {
|
||||
cause = ex;
|
||||
}
|
||||
|
||||
return cause;
|
||||
}
|
||||
|
||||
static class NameVerifier implements HostnameVerifier {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
volatile int serverPort = 0;
|
||||
|
||||
volatile Exception serverException = null;
|
||||
volatile Exception clientException = null;
|
||||
|
||||
private boolean sslConnectionFailed() {
|
||||
return clientException instanceof SSLHandshakeException;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String keyFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + keyStoreFile;
|
||||
String trustFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
if (debug)
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
new ReadTimeout();
|
||||
}
|
||||
|
||||
Thread clientThread = null;
|
||||
Thread serverThread = null;
|
||||
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
ReadTimeout() throws Exception {
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
if (!sslConnectionFailed()) {
|
||||
serverThread.join();
|
||||
}
|
||||
} else {
|
||||
clientThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
*
|
||||
* If the main thread excepted, that propagates back
|
||||
* immediately. If the other thread threw an exception, we
|
||||
* should report back.
|
||||
*/
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
if (clientException != null)
|
||||
throw clientException;
|
||||
}
|
||||
|
||||
void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
doServerSide();
|
||||
}
|
||||
}
|
||||
|
||||
void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died...");
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
doClientSide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* 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 4423074
|
||||
* @summary Need to rebase all the duplicated classes from Merlin.
|
||||
* This test will check out http POST
|
||||
* @library /test/lib
|
||||
* @run main/othervm Redirect
|
||||
*
|
||||
* SunJSSE does not support dynamic system properties, no way to re-use
|
||||
* system properties in samevm/agentvm mode.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import javax.net.ssl.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class Redirect {
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* Set the various variables needed for the tests, then
|
||||
* specify what tests to run on each side.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
static boolean separateServerThread = true;
|
||||
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
volatile static boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Is the connection ready to close?
|
||||
*/
|
||||
volatile static boolean closeReady = false;
|
||||
|
||||
/*
|
||||
* Turn on SSL debugging?
|
||||
*/
|
||||
static boolean debug = false;
|
||||
|
||||
/*
|
||||
* Message posted
|
||||
*/
|
||||
static String postMsg = "Testing HTTP post on a https server";
|
||||
|
||||
/*
|
||||
* If the client or server is doing some kind of object creation
|
||||
* that the other side depends on, and that thread prematurely
|
||||
* exits, you may experience a hang. The test harness will
|
||||
* terminate all hung threads after its timeout has expired,
|
||||
* currently 3 minutes by default, but you might try to be
|
||||
* smart about it....
|
||||
*/
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
SSLServerSocketFactory sslssf =
|
||||
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
||||
SSLServerSocket sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket(serverPort, 0, loopback);
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
|
||||
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
try {
|
||||
InputStream sslIS = sslSocket.getInputStream();
|
||||
OutputStream sslOS = sslSocket.getOutputStream();
|
||||
BufferedReader br =
|
||||
new BufferedReader(new InputStreamReader(sslIS));
|
||||
PrintStream ps = new PrintStream(sslOS);
|
||||
|
||||
// process HTTP POST request from client
|
||||
System.out.println("status line: "+br.readLine());
|
||||
|
||||
ps.println("HTTP/1.1 307 Redirect");
|
||||
ps.println("Location: https://localhost:" + serverPort +
|
||||
"/index.html\n\n");
|
||||
ps.flush();
|
||||
sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
sslOS = sslSocket.getOutputStream();
|
||||
ps = new PrintStream(sslOS);
|
||||
ps.println("HTTP/1.1 200 Redirect succeeded\n\n");
|
||||
ps.flush();
|
||||
} finally {
|
||||
// close the socket
|
||||
while (!closeReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
sslSocket.close();
|
||||
sslServerSocket.close();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
HostnameVerifier reservedHV =
|
||||
HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
try {
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
// Send HTTP POST request to server
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(serverPort)
|
||||
.toURL();
|
||||
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(
|
||||
new NameVerifier());
|
||||
HttpsURLConnection http = (HttpsURLConnection)url.openConnection();
|
||||
try {
|
||||
System.out.println("response header: "+http.getHeaderField(0));
|
||||
if (http.getResponseCode() != 200) {
|
||||
throw new RuntimeException("test Failed");
|
||||
}
|
||||
} finally {
|
||||
http.disconnect();
|
||||
closeReady = true;
|
||||
}
|
||||
} finally {
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(reservedHV);
|
||||
}
|
||||
}
|
||||
|
||||
static class NameVerifier implements HostnameVerifier {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
volatile int serverPort = 0;
|
||||
|
||||
volatile Exception serverException = null;
|
||||
volatile Exception clientException = null;
|
||||
|
||||
private boolean sslConnectionFailed() {
|
||||
return clientException instanceof SSLHandshakeException;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String keyFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + keyStoreFile;
|
||||
String trustFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
if (debug)
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
new Redirect();
|
||||
}
|
||||
|
||||
Thread clientThread = null;
|
||||
Thread serverThread = null;
|
||||
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
Redirect() throws Exception {
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
if (!sslConnectionFailed()) {
|
||||
serverThread.join();
|
||||
}
|
||||
} else {
|
||||
clientThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
*
|
||||
* If the main thread excepted, that propagates back
|
||||
* immediately. If the other thread threw an exception, we
|
||||
* should report back.
|
||||
*/
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
if (clientException != null)
|
||||
throw clientException;
|
||||
}
|
||||
|
||||
void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
doServerSide();
|
||||
}
|
||||
}
|
||||
|
||||
void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died...");
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
doClientSide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* Copyright (c) 2003, 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 4799427
|
||||
* @summary Https can not retry request
|
||||
* @library /test/lib
|
||||
* @run main/othervm RetryHttps
|
||||
* @run main/othervm -Djava.net.preferIPv6Addresses=true RetryHttps
|
||||
*
|
||||
* SunJSSE does not support dynamic system properties, no way to re-use
|
||||
* system properties in samevm/agentvm mode.
|
||||
* @author Yingxian Wang
|
||||
*/
|
||||
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import javax.net.ssl.*;
|
||||
import jdk.test.lib.net.URIBuilder;
|
||||
|
||||
public class RetryHttps {
|
||||
static Map cookies;
|
||||
ServerSocket ss;
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* Set the various variables needed for the tests, then
|
||||
* specify what tests to run on each side.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Should we run the client or server in a separate thread?
|
||||
* Both sides can throw exceptions, but do you have a preference
|
||||
* as to which side should be the main thread.
|
||||
*/
|
||||
static boolean separateServerThread = true;
|
||||
|
||||
/*
|
||||
* Where do we find the keystores?
|
||||
*/
|
||||
static String pathToStores = "../../../../../../javax/net/ssl/etc";
|
||||
static String keyStoreFile = "keystore";
|
||||
static String trustStoreFile = "truststore";
|
||||
static String passwd = "passphrase";
|
||||
|
||||
/*
|
||||
* Is the server ready to serve?
|
||||
*/
|
||||
volatile static boolean serverReady = false;
|
||||
|
||||
/*
|
||||
* Turn on SSL debugging?
|
||||
*/
|
||||
static boolean debug = true;
|
||||
|
||||
private SSLServerSocket sslServerSocket = null;
|
||||
|
||||
/*
|
||||
* Define the server side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doServerSide() throws Exception {
|
||||
InetAddress loopback = InetAddress.getLoopbackAddress();
|
||||
SSLServerSocketFactory sslssf =
|
||||
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
||||
sslServerSocket =
|
||||
(SSLServerSocket) sslssf.createServerSocket(serverPort, 0, loopback);
|
||||
serverPort = sslServerSocket.getLocalPort();
|
||||
|
||||
System.out.println("Starting server at: "
|
||||
+ sslServerSocket.getInetAddress()
|
||||
+ ":" + serverPort);
|
||||
|
||||
/*
|
||||
* Signal Client, we're ready for his connect.
|
||||
*/
|
||||
serverReady = true;
|
||||
SSLSocket sslSocket = null;
|
||||
try {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
sslSocket = (SSLSocket) sslServerSocket.accept();
|
||||
// read request
|
||||
InputStream is = sslSocket.getInputStream ();
|
||||
BufferedReader r = new BufferedReader(new InputStreamReader(is));
|
||||
boolean flag = false;
|
||||
String x;
|
||||
while ((x=r.readLine()) != null) {
|
||||
if (x.length() ==0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PrintStream out = new PrintStream(
|
||||
new BufferedOutputStream(
|
||||
sslSocket.getOutputStream() ));
|
||||
|
||||
/* send the header */
|
||||
out.print("HTTP/1.1 200 OK\r\n");
|
||||
out.print("Content-Type: text/html; charset=iso-8859-1\r\n");
|
||||
out.print("Content-Length: "+10+"\r\n");
|
||||
out.print("\r\n");
|
||||
out.print("Testing"+i+"\r\n");
|
||||
out.flush();
|
||||
sslSocket.close();
|
||||
}
|
||||
|
||||
|
||||
sslServerSocket.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Define the client side of the test.
|
||||
*
|
||||
* If the server prematurely exits, serverReady will be set to true
|
||||
* to avoid infinite hangs.
|
||||
*/
|
||||
void doClientSide() throws Exception {
|
||||
HostnameVerifier reservedHV =
|
||||
HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
try {
|
||||
/*
|
||||
* Wait for server to get started.
|
||||
*/
|
||||
while (!serverReady) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
try {
|
||||
HttpsURLConnection http = null;
|
||||
/* establish http connection to server */
|
||||
URL url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(serverPort)
|
||||
.path("/file1")
|
||||
.toURL();
|
||||
System.out.println("url is "+url.toString());
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(
|
||||
new NameVerifier());
|
||||
http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
int respCode = http.getResponseCode();
|
||||
int cl = http.getContentLength();
|
||||
InputStream is = http.getInputStream ();
|
||||
int count = 0;
|
||||
while (is.read() != -1 && count++ < cl);
|
||||
System.out.println("respCode1 = "+respCode);
|
||||
Thread.sleep(2000);
|
||||
url = URIBuilder.newBuilder()
|
||||
.scheme("https")
|
||||
.loopback()
|
||||
.port(serverPort)
|
||||
.path("/file2")
|
||||
.toURL();
|
||||
http = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
|
||||
respCode = http.getResponseCode();
|
||||
System.out.println("respCode2 = "+respCode);
|
||||
} catch (IOException ioex) {
|
||||
if (sslServerSocket != null)
|
||||
sslServerSocket.close();
|
||||
throw ioex;
|
||||
}
|
||||
} finally {
|
||||
HttpsURLConnection.setDefaultHostnameVerifier(reservedHV);
|
||||
}
|
||||
}
|
||||
|
||||
static class NameVerifier implements HostnameVerifier {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* =============================================================
|
||||
* The remainder is just support stuff
|
||||
*/
|
||||
|
||||
// use any free port by default
|
||||
volatile int serverPort = 0;
|
||||
|
||||
volatile Exception serverException = null;
|
||||
volatile Exception clientException = null;
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
String keyFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + keyStoreFile;
|
||||
String trustFilename =
|
||||
System.getProperty("test.src", "./") + "/" + pathToStores +
|
||||
"/" + trustStoreFile;
|
||||
|
||||
System.setProperty("javax.net.ssl.keyStore", keyFilename);
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
|
||||
System.setProperty("javax.net.ssl.trustStore", trustFilename);
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", passwd);
|
||||
|
||||
if (debug)
|
||||
System.setProperty("javax.net.debug", "all");
|
||||
|
||||
/*
|
||||
* Start the tests.
|
||||
*/
|
||||
new RetryHttps();
|
||||
}
|
||||
|
||||
Thread clientThread = null;
|
||||
Thread serverThread = null;
|
||||
/*
|
||||
* Primary constructor, used to drive remainder of the test.
|
||||
*
|
||||
* Fork off the other side, then do your work.
|
||||
*/
|
||||
RetryHttps() throws Exception {
|
||||
if (separateServerThread) {
|
||||
startServer(true);
|
||||
startClient(false);
|
||||
} else {
|
||||
startClient(true);
|
||||
startServer(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for other side to close down.
|
||||
*/
|
||||
if (separateServerThread) {
|
||||
serverThread.join();
|
||||
} else {
|
||||
clientThread.join();
|
||||
}
|
||||
|
||||
/*
|
||||
* When we get here, the test is pretty much over.
|
||||
*
|
||||
* If the main thread excepted, that propagates back
|
||||
* immediately. If the other thread threw an exception, we
|
||||
* should report back.
|
||||
*/
|
||||
if (serverException != null)
|
||||
throw serverException;
|
||||
if (clientException != null)
|
||||
throw clientException;
|
||||
}
|
||||
|
||||
void startServer(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
serverThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doServerSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our server thread just died.
|
||||
*
|
||||
* Release the client, if not active already...
|
||||
*/
|
||||
System.err.println("Server died...");
|
||||
serverReady = true;
|
||||
serverException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
serverThread.start();
|
||||
} else {
|
||||
doServerSide();
|
||||
}
|
||||
}
|
||||
|
||||
void startClient(boolean newThread) throws Exception {
|
||||
if (newThread) {
|
||||
clientThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
doClientSide();
|
||||
} catch (Exception e) {
|
||||
/*
|
||||
* Our client thread just died.
|
||||
*/
|
||||
System.err.println("Client died...");
|
||||
clientException = e;
|
||||
}
|
||||
}
|
||||
};
|
||||
clientThread.start();
|
||||
} else {
|
||||
doClientSide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
modules = \
|
||||
java.base/sun.security.util \
|
||||
java.base/sun.security.x509
|
||||
|
|
@ -0,0 +1,737 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.test.lib.net.HttpHeaderParser;
|
||||
|
||||
|
||||
public class TunnelProxy {
|
||||
|
||||
ServerSocketChannel schan;
|
||||
int threads;
|
||||
int cperthread;
|
||||
Server[] servers;
|
||||
|
||||
/**
|
||||
* Create a <code>TunnelProxy<code> instance with the specified callback object
|
||||
* for handling requests. One thread is created to handle requests,
|
||||
* and up to ten TCP connections will be handled simultaneously.
|
||||
* incoming request
|
||||
*/
|
||||
|
||||
public TunnelProxy () throws IOException {
|
||||
this (1, 10, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <code>TunnelProxy<code> instance with the specified number of
|
||||
* threads and maximum number of connections per thread. This functions
|
||||
* the same as the 4 arg constructor, where the port argument is set to zero.
|
||||
* @param threads the number of threads to create to handle requests
|
||||
* in parallel
|
||||
* @param cperthread the number of simultaneous TCP connections to
|
||||
* handle per thread
|
||||
*/
|
||||
|
||||
public TunnelProxy (int threads, int cperthread)
|
||||
throws IOException {
|
||||
this (threads, cperthread, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <code>TunnelProxy<code> instance with the specified number
|
||||
* of threads and maximum number of connections per thread and running on
|
||||
* the specified port. The specified number of threads are created to
|
||||
* handle incoming requests, and each thread is allowed
|
||||
* to handle a number of simultaneous TCP connections.
|
||||
* @param threads the number of threads to create to handle
|
||||
* requests in parallel
|
||||
* @param cperthread the number of simultaneous TCP connections
|
||||
* to handle per thread
|
||||
* @param port the port number to bind the server to. <code>Zero</code>
|
||||
* means choose any free port.
|
||||
*/
|
||||
|
||||
public TunnelProxy (int threads, int cperthread, int port)
|
||||
throws IOException {
|
||||
this(threads, cperthread, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <code>TunnelProxy<code> instance with the specified number
|
||||
* of threads and maximum number of connections per thread and running on
|
||||
* the specified port. The specified number of threads are created to
|
||||
* handle incoming requests, and each thread is allowed
|
||||
* to handle a number of simultaneous TCP connections.
|
||||
* @param threads the number of threads to create to handle
|
||||
* requests in parallel
|
||||
* @param cperthread the number of simultaneous TCP connections
|
||||
* to handle per thread
|
||||
* @param address the address to bind to. null means all addresses.
|
||||
* @param port the port number to bind the server to. <code>Zero</code>
|
||||
* means choose any free port.
|
||||
*/
|
||||
public TunnelProxy (int threads, int cperthread, InetAddress address, int port)
|
||||
throws IOException {
|
||||
schan = ServerSocketChannel.open ();
|
||||
InetSocketAddress addr = new InetSocketAddress (address, port);
|
||||
schan.socket().bind (addr);
|
||||
this.threads = threads;
|
||||
this.cperthread = cperthread;
|
||||
servers = new Server [threads];
|
||||
for (int i=0; i<threads; i++) {
|
||||
servers[i] = new Server (schan, cperthread);
|
||||
servers[i].start();
|
||||
}
|
||||
}
|
||||
|
||||
/** Tell all threads in the server to exit within 5 seconds.
|
||||
* This is an abortive termination. Just prior to the thread exiting
|
||||
* all channels in that thread waiting to be closed are forceably closed.
|
||||
*/
|
||||
|
||||
public void terminate () {
|
||||
for (int i=0; i<threads; i++) {
|
||||
servers[i].terminate ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return the local port number to which the server is bound.
|
||||
* @return the local port number
|
||||
*/
|
||||
|
||||
public int getLocalPort () {
|
||||
return schan.socket().getLocalPort ();
|
||||
}
|
||||
|
||||
static class Server extends Thread {
|
||||
|
||||
ServerSocketChannel schan;
|
||||
Selector selector;
|
||||
SelectionKey listenerKey;
|
||||
SelectionKey key; /* the current key being processed */
|
||||
ByteBuffer consumeBuffer;
|
||||
int maxconn;
|
||||
int nconn;
|
||||
ClosedChannelList clist;
|
||||
boolean shutdown;
|
||||
Pipeline pipe1 = null;
|
||||
Pipeline pipe2 = null;
|
||||
|
||||
Server (ServerSocketChannel schan, int maxconn) {
|
||||
this.schan = schan;
|
||||
this.maxconn = maxconn;
|
||||
nconn = 0;
|
||||
consumeBuffer = ByteBuffer.allocate (512);
|
||||
clist = new ClosedChannelList ();
|
||||
try {
|
||||
selector = Selector.open ();
|
||||
schan.configureBlocking (false);
|
||||
listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT);
|
||||
} catch (IOException e) {
|
||||
System.err.println ("Server could not start: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
/* Stop the thread as soon as possible */
|
||||
public synchronized void terminate () {
|
||||
shutdown = true;
|
||||
if (pipe1 != null) pipe1.terminate();
|
||||
if (pipe2 != null) pipe2.terminate();
|
||||
}
|
||||
|
||||
public void run () {
|
||||
try {
|
||||
while (true) {
|
||||
selector.select (1000);
|
||||
Set selected = selector.selectedKeys();
|
||||
Iterator iter = selected.iterator();
|
||||
while (iter.hasNext()) {
|
||||
key = (SelectionKey)iter.next();
|
||||
if (key.equals (listenerKey)) {
|
||||
SocketChannel sock = schan.accept ();
|
||||
if (sock == null) {
|
||||
/* false notification */
|
||||
iter.remove();
|
||||
continue;
|
||||
}
|
||||
sock.configureBlocking (false);
|
||||
sock.register (selector, SelectionKey.OP_READ);
|
||||
nconn ++;
|
||||
if (nconn == maxconn) {
|
||||
/* deregister */
|
||||
listenerKey.cancel ();
|
||||
listenerKey = null;
|
||||
}
|
||||
} else {
|
||||
if (key.isReadable()) {
|
||||
boolean closed;
|
||||
SocketChannel chan = (SocketChannel) key.channel();
|
||||
if (key.attachment() != null) {
|
||||
closed = consume (chan);
|
||||
} else {
|
||||
closed = read (chan, key);
|
||||
}
|
||||
if (closed) {
|
||||
chan.close ();
|
||||
key.cancel ();
|
||||
if (nconn == maxconn) {
|
||||
listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT);
|
||||
}
|
||||
nconn --;
|
||||
}
|
||||
}
|
||||
}
|
||||
iter.remove();
|
||||
}
|
||||
clist.check();
|
||||
if (shutdown) {
|
||||
clist.terminate ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println ("Server exception: " + e);
|
||||
// TODO finish
|
||||
}
|
||||
}
|
||||
|
||||
/* read all the data off the channel without looking at it
|
||||
* return true if connection closed
|
||||
*/
|
||||
boolean consume (SocketChannel chan) {
|
||||
try {
|
||||
consumeBuffer.clear ();
|
||||
int c = chan.read (consumeBuffer);
|
||||
if (c == -1)
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* return true if the connection is closed, false otherwise */
|
||||
|
||||
private boolean read (SocketChannel chan, SelectionKey key) {
|
||||
boolean res;
|
||||
try {
|
||||
InputStream is = new BufferedInputStream (new NioInputStream (chan));
|
||||
HttpHeaderParser mHead = new HttpHeaderParser (is);
|
||||
String requestLine = mHead.getRequestDetails();
|
||||
String[] req = requestLine.split (" ");
|
||||
if (req.length < 2) {
|
||||
/* invalid request line */
|
||||
return false;
|
||||
}
|
||||
String cmd = req[0];
|
||||
URI uri = null;
|
||||
if (!("CONNECT".equalsIgnoreCase(cmd))) {
|
||||
// we expect CONNECT command
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
uri = new URI("http://" + req[1]);
|
||||
} catch (URISyntaxException e) {
|
||||
System.err.println ("Invalid URI: " + e);
|
||||
res = true;
|
||||
}
|
||||
|
||||
// CONNECT ack
|
||||
OutputStream os = new BufferedOutputStream(new NioOutputStream(chan));
|
||||
byte[] ack = "HTTP/1.1 200 Connection established\r\n\r\n".getBytes();
|
||||
os.write(ack, 0, ack.length);
|
||||
os.flush();
|
||||
|
||||
// tunnel anything else
|
||||
tunnel(is, os, uri);
|
||||
|
||||
res = false;
|
||||
} catch (IOException e) {
|
||||
res = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private void tunnel(InputStream fromClient, OutputStream toClient, URI serverURI) throws IOException {
|
||||
Socket sockToServer = new Socket(serverURI.getHost(), serverURI.getPort());
|
||||
OutputStream toServer = sockToServer.getOutputStream();
|
||||
InputStream fromServer = sockToServer.getInputStream();
|
||||
|
||||
pipe1 = new Pipeline(fromClient, toServer);
|
||||
pipe2 = new Pipeline(fromServer, toClient);
|
||||
// start pump
|
||||
pipe1.start();
|
||||
pipe2.start();
|
||||
// wait them to end
|
||||
try {
|
||||
pipe1.join();
|
||||
} catch (InterruptedException e) {
|
||||
// No-op
|
||||
} finally {
|
||||
sockToServer.close();
|
||||
}
|
||||
}
|
||||
|
||||
private String readLine (InputStream is) throws IOException {
|
||||
boolean done=false, readCR=false;
|
||||
byte[] b = new byte [512];
|
||||
int c, l = 0;
|
||||
|
||||
while (!done) {
|
||||
c = is.read ();
|
||||
if (c == '\n' && readCR) {
|
||||
done = true;
|
||||
} else {
|
||||
if (c == '\r' && !readCR) {
|
||||
readCR = true;
|
||||
} else {
|
||||
b[l++] = (byte)c;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new String (b);
|
||||
}
|
||||
|
||||
/** close the channel associated with the current key by:
|
||||
* 1. shutdownOutput (send a FIN)
|
||||
* 2. mark the key so that incoming data is to be consumed and discarded
|
||||
* 3. After a period, close the socket
|
||||
*/
|
||||
|
||||
synchronized void orderlyCloseChannel (SelectionKey key) throws IOException {
|
||||
SocketChannel ch = (SocketChannel)key.channel ();
|
||||
ch.socket().shutdownOutput();
|
||||
key.attach (this);
|
||||
clist.add (key);
|
||||
}
|
||||
|
||||
synchronized void abortiveCloseChannel (SelectionKey key) throws IOException {
|
||||
SocketChannel ch = (SocketChannel)key.channel ();
|
||||
Socket s = ch.socket ();
|
||||
s.setSoLinger (true, 0);
|
||||
ch.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements blocking reading semantics on top of a non-blocking channel
|
||||
*/
|
||||
|
||||
static class NioInputStream extends InputStream {
|
||||
SocketChannel channel;
|
||||
Selector selector;
|
||||
ByteBuffer chanbuf;
|
||||
SelectionKey key;
|
||||
int available;
|
||||
byte[] one;
|
||||
boolean closed;
|
||||
ByteBuffer markBuf; /* reads may be satisifed from this buffer */
|
||||
boolean marked;
|
||||
boolean reset;
|
||||
int readlimit;
|
||||
|
||||
public NioInputStream (SocketChannel chan) throws IOException {
|
||||
this.channel = chan;
|
||||
selector = Selector.open();
|
||||
chanbuf = ByteBuffer.allocate (1024);
|
||||
key = chan.register (selector, SelectionKey.OP_READ);
|
||||
available = 0;
|
||||
one = new byte[1];
|
||||
closed = marked = reset = false;
|
||||
}
|
||||
|
||||
public synchronized int read (byte[] b) throws IOException {
|
||||
return read (b, 0, b.length);
|
||||
}
|
||||
|
||||
public synchronized int read () throws IOException {
|
||||
return read (one, 0, 1);
|
||||
}
|
||||
|
||||
public synchronized int read (byte[] b, int off, int srclen) throws IOException {
|
||||
|
||||
int canreturn, willreturn;
|
||||
|
||||
if (closed)
|
||||
return -1;
|
||||
|
||||
if (reset) { /* satisfy from markBuf */
|
||||
canreturn = markBuf.remaining ();
|
||||
willreturn = canreturn>srclen ? srclen : canreturn;
|
||||
markBuf.get(b, off, willreturn);
|
||||
if (canreturn == willreturn) {
|
||||
reset = false;
|
||||
}
|
||||
} else { /* satisfy from channel */
|
||||
canreturn = available();
|
||||
if (canreturn == 0) {
|
||||
block ();
|
||||
canreturn = available();
|
||||
}
|
||||
willreturn = canreturn>srclen ? srclen : canreturn;
|
||||
chanbuf.get(b, off, willreturn);
|
||||
available -= willreturn;
|
||||
|
||||
if (marked) { /* copy into markBuf */
|
||||
try {
|
||||
markBuf.put (b, off, willreturn);
|
||||
} catch (BufferOverflowException e) {
|
||||
marked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return willreturn;
|
||||
}
|
||||
|
||||
public synchronized int available () throws IOException {
|
||||
if (closed)
|
||||
throw new IOException ("Stream is closed");
|
||||
|
||||
if (reset)
|
||||
return markBuf.remaining();
|
||||
|
||||
if (available > 0)
|
||||
return available;
|
||||
|
||||
chanbuf.clear ();
|
||||
available = channel.read (chanbuf);
|
||||
if (available > 0)
|
||||
chanbuf.flip();
|
||||
else if (available == -1)
|
||||
throw new IOException ("Stream is closed");
|
||||
return available;
|
||||
}
|
||||
|
||||
/**
|
||||
* block() only called when available==0 and buf is empty
|
||||
*/
|
||||
private synchronized void block () throws IOException {
|
||||
//assert available == 0;
|
||||
int n = selector.select ();
|
||||
//assert n == 1;
|
||||
selector.selectedKeys().clear();
|
||||
available ();
|
||||
}
|
||||
|
||||
public void close () throws IOException {
|
||||
if (closed)
|
||||
return;
|
||||
channel.close ();
|
||||
closed = true;
|
||||
}
|
||||
|
||||
public synchronized void mark (int readlimit) {
|
||||
if (closed)
|
||||
return;
|
||||
this.readlimit = readlimit;
|
||||
markBuf = ByteBuffer.allocate (readlimit);
|
||||
marked = true;
|
||||
reset = false;
|
||||
}
|
||||
|
||||
public synchronized void reset () throws IOException {
|
||||
if (closed )
|
||||
return;
|
||||
if (!marked)
|
||||
throw new IOException ("Stream not marked");
|
||||
marked = false;
|
||||
reset = true;
|
||||
markBuf.flip ();
|
||||
}
|
||||
}
|
||||
|
||||
static class NioOutputStream extends OutputStream {
|
||||
SocketChannel channel;
|
||||
ByteBuffer buf;
|
||||
SelectionKey key;
|
||||
Selector selector;
|
||||
boolean closed;
|
||||
byte[] one;
|
||||
|
||||
public NioOutputStream (SocketChannel channel) throws IOException {
|
||||
this.channel = channel;
|
||||
selector = Selector.open ();
|
||||
key = channel.register (selector, SelectionKey.OP_WRITE);
|
||||
closed = false;
|
||||
one = new byte [1];
|
||||
}
|
||||
|
||||
public synchronized void write (int b) throws IOException {
|
||||
one[0] = (byte)b;
|
||||
write (one, 0, 1);
|
||||
}
|
||||
|
||||
public synchronized void write (byte[] b) throws IOException {
|
||||
write (b, 0, b.length);
|
||||
}
|
||||
|
||||
public synchronized void write (byte[] b, int off, int len) throws IOException {
|
||||
if (closed)
|
||||
throw new IOException ("stream is closed");
|
||||
|
||||
buf = ByteBuffer.allocate (len);
|
||||
buf.put (b, off, len);
|
||||
buf.flip ();
|
||||
int n;
|
||||
while ((n = channel.write (buf)) < len) {
|
||||
len -= n;
|
||||
if (len == 0)
|
||||
return;
|
||||
selector.select ();
|
||||
selector.selectedKeys().clear ();
|
||||
}
|
||||
}
|
||||
|
||||
public void close () throws IOException {
|
||||
if (closed)
|
||||
return;
|
||||
channel.close ();
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Pipeline object :-
|
||||
* 1) Will pump every byte from its input stream to output stream
|
||||
* 2) Is an 'active object'
|
||||
*/
|
||||
static class Pipeline implements Runnable {
|
||||
InputStream in;
|
||||
OutputStream out;
|
||||
Thread t;
|
||||
|
||||
public Pipeline(InputStream is, OutputStream os) {
|
||||
in = is;
|
||||
out = os;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
t = new Thread(this);
|
||||
t.start();
|
||||
}
|
||||
|
||||
public void join() throws InterruptedException {
|
||||
t.join();
|
||||
}
|
||||
|
||||
public void terminate() {
|
||||
t.interrupt();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
byte[] buffer = new byte[10000];
|
||||
try {
|
||||
while (!Thread.interrupted()) {
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
} catch(IOException e) {
|
||||
// No-op
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilities for synchronization. A condition is
|
||||
* identified by a string name, and is initialized
|
||||
* upon first use (ie. setCondition() or waitForCondition()). Threads
|
||||
* are blocked until some thread calls (or has called) setCondition() for the same
|
||||
* condition.
|
||||
* <P>
|
||||
* A rendezvous built on a condition is also provided for synchronizing
|
||||
* N threads.
|
||||
*/
|
||||
|
||||
private static HashMap conditions = new HashMap();
|
||||
|
||||
/*
|
||||
* Modifiable boolean object
|
||||
*/
|
||||
private static class BValue {
|
||||
boolean v;
|
||||
}
|
||||
|
||||
/*
|
||||
* Modifiable int object
|
||||
*/
|
||||
private static class IValue {
|
||||
int v;
|
||||
IValue (int i) {
|
||||
v =i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static BValue getCond (String condition) {
|
||||
synchronized (conditions) {
|
||||
BValue cond = (BValue) conditions.get (condition);
|
||||
if (cond == null) {
|
||||
cond = new BValue();
|
||||
conditions.put (condition, cond);
|
||||
}
|
||||
return cond;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the condition to true. Any threads that are currently blocked
|
||||
* waiting on the condition, will be unblocked and allowed to continue.
|
||||
* Threads that subsequently call waitForCondition() will not block.
|
||||
* If the named condition did not exist prior to the call, then it is created
|
||||
* first.
|
||||
*/
|
||||
|
||||
public static void setCondition (String condition) {
|
||||
BValue cond = getCond (condition);
|
||||
synchronized (cond) {
|
||||
if (cond.v) {
|
||||
return;
|
||||
}
|
||||
cond.v = true;
|
||||
cond.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the named condition does not exist, then it is created and initialized
|
||||
* to false. If the condition exists or has just been created and its value
|
||||
* is false, then the thread blocks until another thread sets the condition.
|
||||
* If the condition exists and is already set to true, then this call returns
|
||||
* immediately without blocking.
|
||||
*/
|
||||
|
||||
public static void waitForCondition (String condition) {
|
||||
BValue cond = getCond (condition);
|
||||
synchronized (cond) {
|
||||
if (!cond.v) {
|
||||
try {
|
||||
cond.wait();
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* conditions must be locked when accessing this */
|
||||
static HashMap rv = new HashMap();
|
||||
|
||||
/**
|
||||
* Force N threads to rendezvous (ie. wait for each other) before proceeding.
|
||||
* The first thread(s) to call are blocked until the last
|
||||
* thread makes the call. Then all threads continue.
|
||||
* <p>
|
||||
* All threads that call with the same condition name, must use the same value
|
||||
* for N (or the results may be not be as expected).
|
||||
* <P>
|
||||
* Obviously, if fewer than N threads make the rendezvous then the result
|
||||
* will be a hang.
|
||||
*/
|
||||
|
||||
public static void rendezvous (String condition, int N) {
|
||||
BValue cond;
|
||||
IValue iv;
|
||||
String name = "RV_"+condition;
|
||||
|
||||
/* get the condition */
|
||||
|
||||
synchronized (conditions) {
|
||||
cond = (BValue)conditions.get (name);
|
||||
if (cond == null) {
|
||||
/* we are first caller */
|
||||
if (N < 2) {
|
||||
throw new RuntimeException ("rendezvous must be called with N >= 2");
|
||||
}
|
||||
cond = new BValue ();
|
||||
conditions.put (name, cond);
|
||||
iv = new IValue (N-1);
|
||||
rv.put (name, iv);
|
||||
} else {
|
||||
/* already initialised, just decrement the counter */
|
||||
iv = (IValue) rv.get (name);
|
||||
iv.v --;
|
||||
}
|
||||
}
|
||||
|
||||
if (iv.v > 0) {
|
||||
waitForCondition (name);
|
||||
} else {
|
||||
setCondition (name);
|
||||
synchronized (conditions) {
|
||||
clearCondition (name);
|
||||
rv.remove (name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the named condition exists and is set then remove it, so it can
|
||||
* be re-initialized and used again. If the condition does not exist, or
|
||||
* exists but is not set, then the call returns without doing anything.
|
||||
* Note, some higher level synchronization
|
||||
* may be needed between clear and the other operations.
|
||||
*/
|
||||
|
||||
public static void clearCondition(String condition) {
|
||||
BValue cond;
|
||||
synchronized (conditions) {
|
||||
cond = (BValue) conditions.get (condition);
|
||||
if (cond == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (cond) {
|
||||
if (cond.v) {
|
||||
conditions.remove (condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue