提交 b2c42206 编写于 作者: C chegar
上级 2bd60f0d
......@@ -89,6 +89,7 @@ PLATFORM_MODULES += \
#
PLATFORM_MODULES += \
java.net.http \
java.scripting \
java.security.jgss \
java.smartcardio \
......@@ -102,7 +103,6 @@ PLATFORM_MODULES += \
jdk.crypto.ec \
jdk.dynalink \
jdk.httpserver \
jdk.incubator.httpclient \
jdk.internal.vm.compiler.management \
jdk.jsobject \
jdk.localedata \
......@@ -145,7 +145,6 @@ DOCS_MODULES += \
jdk.editpad \
jdk.hotspot.agent \
jdk.httpserver \
jdk.incubator.httpclient \
jdk.jartool \
jdk.javadoc \
jdk.jcmd \
......
......@@ -36,9 +36,9 @@ import sun.security.util.SecurityConstants;
* handler. The HTTP state management mechanism specifies a way to
* create a stateful session with HTTP requests and responses.
*
* <p>A system-wide CookieHandler that to used by the HTTP protocol
* handler can be registered by doing a
* CookieHandler.setDefault(CookieHandler). The currently registered
* <p> A system-wide CookieHandler to be used by the {@linkplain
* HttpURLConnection HTTP URL stream protocol handler} can be registered by
* doing a CookieHandler.setDefault(CookieHandler). The currently registered
* CookieHandler can be retrieved by calling
* CookieHandler.getDefault().
*
......
......@@ -156,7 +156,7 @@ of proxies.</P>
checked only once at startup.</P>
</UL>
<a id="MiscHTTP"></a>
<H2>Misc HTTP properties</H2>
<H2>Misc HTTP URL stream protocol handler properties</H2>
<UL>
<LI><P><B>http.agent</B> (default: &ldquo;Java/&lt;version&gt;&rdquo;)<BR>
Defines the string sent in the User-Agent request header in http
......
......@@ -121,8 +121,8 @@
* underlying protocol handlers like http or https.</li>
* <li>{@link java.net.HttpURLConnection} is a subclass of URLConnection
* and provides some additional functionalities specific to the
* HTTP protocol. This API has been superceded by the newer
HTTP client API described in the previous section.</li>
* HTTP protocol. This API has been superseded by the newer
* {@linkplain java.net.http HTTP Client API}.</li>
* </ul>
* <p>The recommended usage is to use {@link java.net.URI} to identify
* resources, then convert it into a {@link java.net.URL} when it is time to
......
......@@ -171,7 +171,7 @@ module java.base {
jdk.attach,
jdk.charsets,
jdk.compiler,
jdk.incubator.httpclient,
java.net.http,
jdk.jdeps,
jdk.jlink,
jdk.jshell,
......@@ -204,12 +204,11 @@ module java.base {
jdk.internal.jvmstat;
exports jdk.internal.vm.annotation to
jdk.unsupported,
jdk.internal.vm.ci,
jdk.incubator.httpclient;
jdk.internal.vm.ci;
exports jdk.internal.util.jar to
jdk.jartool;
exports sun.net to
jdk.incubator.httpclient,
java.net.http,
jdk.naming.dns;
exports sun.net.ext to
jdk.net;
......@@ -219,10 +218,10 @@ module java.base {
exports sun.net.util to
java.desktop,
jdk.jconsole,
jdk.incubator.httpclient;
java.net.http;
exports sun.net.www to
java.desktop,
jdk.incubator.httpclient,
java.net.http,
jdk.jartool;
exports sun.net.www.protocol.http to
java.security.jgss;
......
......@@ -14,6 +14,23 @@ grant codeBase "jrt:/java.compiler" {
};
grant codeBase "jrt:/java.net.http" {
permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
permission java.net.SocketPermission "*","connect,resolve";
permission java.net.URLPermission "http:*","*:*";
permission java.net.URLPermission "https:*","*:*";
permission java.net.URLPermission "ws:*","*:*";
permission java.net.URLPermission "wss:*","*:*";
permission java.net.URLPermission "socket:*","CONNECT"; // proxy
// For request/response body processors, fromFile, asFile
permission java.io.FilePermission "<<ALL FILES>>","read,write,delete";
permission java.util.PropertyPermission "*","read";
permission java.net.NetPermission "getProxySelector";
};
grant codeBase "jrt:/java.scripting" {
permission java.security.AllPermission;
};
......
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, 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
......@@ -23,7 +23,7 @@
* questions.
*/
package jdk.incubator.http;
package java.net.http;
import java.util.List;
import java.util.Map;
......@@ -35,15 +35,19 @@ import static java.util.Objects.requireNonNull;
/**
* A read-only view of a set of HTTP headers.
* {@Incubating}
*
* <p> An {@code HttpHeaders} is not created directly, but rather returned from
* an {@link HttpResponse HttpResponse}. Specific HTTP headers can be set for
* {@linkplain HttpRequest requests} through the one of the request builder's
* {@link HttpRequest.Builder#header(String, String) headers} methods.
*
* <p> The methods of this class ( that accept a String header name ), and the
* Map returned by the {@linkplain #map() map} method, operate without regard to
* Map returned by the {@link #map() map} method, operate without regard to
* case when retrieving the header value.
*
* <p> HttpHeaders instances are immutable.
* <p> {@code HttpHeaders} instances are immutable.
*
* @since 9
* @since 11
*/
public abstract class HttpHeaders {
......@@ -139,7 +143,7 @@ public abstract class HttpHeaders {
* Computes a hash code for this HTTP headers instance.
*
* <p> The hash code is based upon the components of the HTTP headers
* {@linkplain #map() map}, and satisfies the general contract of the
* {@link #map() map}, and satisfies the general contract of the
* {@link Object#hashCode Object.hashCode} method.
*
* @return the hash-code value for this HTTP headers
......@@ -156,7 +160,7 @@ public abstract class HttpHeaders {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString()).append(" ");
sb.append(super.toString()).append(" { ");
sb.append(map());
sb.append(" }");
return sb.toString();
......
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, 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
......@@ -23,18 +23,25 @@
* questions.
*/
package jdk.incubator.http;
package java.net.http;
import java.io.IOException;
/**
* Thrown when a response is not received within a specified time period.
* {@Incubating}
*
* @since 11
*/
public class HttpTimeoutException extends IOException {
private static final long serialVersionUID = 981344271622632951L;
/**
* Constructs an {@code HttpTimeoutException} with the given detail message.
*
* @param message
* The detail message; can be {@code null}
*/
public HttpTimeoutException(String message) {
super(message);
}
......
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, 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
......@@ -23,15 +23,14 @@
* questions.
*/
package jdk.incubator.http;
package java.net.http;
import java.io.IOException;
/**
* An exception used to signal the opening handshake failed.
* {@Incubating}
*
* @since 9
* @since 11
*/
public final class WebSocketHandshakeException extends IOException {
......@@ -39,6 +38,13 @@ public final class WebSocketHandshakeException extends IOException {
private final transient HttpResponse<?> response;
/**
* Constructs a {@code WebSocketHandshakeException} with the given
* {@code HttpResponse}.
*
* @param response
* the {@code HttpResponse} that resulted in the handshake failure
*/
public WebSocketHandshakeException(HttpResponse<?> response) {
this.response = response;
}
......@@ -47,7 +53,7 @@ public final class WebSocketHandshakeException extends IOException {
* Returns the server's counterpart of the opening handshake.
*
* <p> The value may be unavailable ({@code null}) if this exception has
* been serialized and then read back in.
* been serialized and then deserialized.
*
* @return server response
*/
......
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, 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
......@@ -24,37 +24,42 @@
*/
/**
* <h2>High level HTTP and WebSocket API</h2>
* {@Incubating}
* <h2>HTTP Client and WebSocket APIs</h2>
*
* <p> Provides high-level client interfaces to HTTP (versions 1.1 and 2) and
* WebSocket. The main types defined are:
* low-level client interfaces to WebSocket. The main types defined are:
*
* <ul>
* <li>{@link jdk.incubator.http.HttpClient}</li>
* <li>{@link jdk.incubator.http.HttpRequest}</li>
* <li>{@link jdk.incubator.http.HttpResponse}</li>
* <li>{@link jdk.incubator.http.WebSocket}</li>
* <li>{@link java.net.http.HttpClient}</li>
* <li>{@link java.net.http.HttpRequest}</li>
* <li>{@link java.net.http.HttpResponse}</li>
* <li>{@link java.net.http.WebSocket}</li>
* </ul>
*
* <p> The API functions asynchronously (using {@link java.util.concurrent.CompletableFuture})
* and work is done on the threads supplied by the client's {@link java.util.concurrent.Executor}
* where practical.
* <p> The protocol-specific requirements are defined in the
* <a href="https://tools.ietf.org/html/rfc7540">Hypertext Transfer Protocol
* Version 2 (HTTP/2)</a>, the <a href="https://tools.ietf.org/html/rfc2616">
* Hypertext Transfer Protocol (HTTP/1.1)</a>, and
* <a href="https://tools.ietf.org/html/rfc6455">The WebSocket Protocol</a>.
*
* <p> {@code HttpClient} also provides a simple synchronous mode, where all
* work may be done on the calling thread.
* <p> Asynchronous tasks and dependent actions of returned {@link
* java.util.concurrent.CompletableFuture} instances are executed on the threads
* supplied by the client's {@link java.util.concurrent.Executor}, where
* practical.
*
* <p> {@code CompletableFuture}s returned by this API will throw {@link java.lang.UnsupportedOperationException}
* for their {@link java.util.concurrent.CompletableFuture#obtrudeValue(Object) obtrudeValue}
* and {@link java.util.concurrent.CompletableFuture#obtrudeException(Throwable) obtrudeException}
* methods. Invoking the {@link java.util.concurrent.CompletableFuture#cancel cancel}
* method on a {@code CompletableFuture} returned by this API will not interrupt
* the underlying operation, but may be useful to complete, exceptionally,
* dependent stages that have not already completed.
* <p> {@code CompletableFuture}s returned by this API will throw {@link
* java.lang.UnsupportedOperationException} for their {@link
* java.util.concurrent.CompletableFuture#obtrudeValue(Object) obtrudeValue}
* and {@link java.util.concurrent.CompletableFuture#obtrudeException(Throwable)
* obtrudeException} methods. Invoking the {@link
* java.util.concurrent.CompletableFuture#cancel cancel} method on a {@code
* CompletableFuture} returned by this API will not interrupt the underlying
* operation, but may be useful to complete, exceptionally, dependent stages
* that have not already completed.
*
* <p> Unless otherwise stated, {@code null} parameter values will cause methods
* of all classes in this package to throw {@code NullPointerException}.
*
* @since 9
* @since 11
*/
package jdk.incubator.http;
package java.net.http;
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, 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
......@@ -23,25 +23,21 @@
* questions.
*/
package jdk.incubator.http;
package jdk.internal.net.http;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLParameters;
import jdk.incubator.http.internal.common.SSLTube;
import jdk.incubator.http.internal.common.Log;
import jdk.incubator.http.internal.common.Utils;
import jdk.internal.net.http.common.SSLTube;
import jdk.internal.net.http.common.Log;
import jdk.internal.net.http.common.Utils;
import static jdk.internal.net.http.common.Utils.ServerName;
/**
* Asynchronous version of SSLConnection.
......@@ -67,16 +63,20 @@ abstract class AbstractAsyncSSLConnection extends HttpConnection
protected final String serverName;
protected final SSLParameters sslParameters;
// Setting this property disables HTTPS hostname verification. Use with care.
private static final boolean disableHostnameVerification
= Utils.isHostnameVerificationDisabled();
AbstractAsyncSSLConnection(InetSocketAddress addr,
HttpClientImpl client,
String serverName,
ServerName serverName, int port,
String[] alpn) {
super(addr, client);
this.serverName = serverName;
this.serverName = serverName.getName();
SSLContext context = client.theSSLContext();
sslParameters = createSSLParameters(client, serverName, alpn);
Log.logParams(sslParameters);
engine = createEngine(context, sslParameters);
engine = createEngine(context, serverName.getName(), port, sslParameters);
}
abstract HttpConnection plainConnection();
......@@ -90,10 +90,12 @@ abstract class AbstractAsyncSSLConnection extends HttpConnection
final SSLEngine getEngine() { return engine; }
private static SSLParameters createSSLParameters(HttpClientImpl client,
String serverName,
ServerName serverName,
String[] alpn) {
SSLParameters sslp = client.sslParameters();
SSLParameters sslParameters = Utils.copySSLParameters(sslp);
if (!disableHostnameVerification)
sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
if (alpn != null) {
Log.logSSL("AbstractAsyncSSLConnection: Setting application protocols: {0}",
Arrays.toString(alpn));
......@@ -101,15 +103,18 @@ abstract class AbstractAsyncSSLConnection extends HttpConnection
} else {
Log.logSSL("AbstractAsyncSSLConnection: no applications set!");
}
if (serverName != null) {
sslParameters.setServerNames(List.of(new SNIHostName(serverName)));
if (!serverName.isLiteral()) {
String name = serverName.getName();
if (name != null && name.length() > 0) {
sslParameters.setServerNames(List.of(new SNIHostName(name)));
}
}
return sslParameters;
}
private static SSLEngine createEngine(SSLContext context,
private static SSLEngine createEngine(SSLContext context, String serverName, int port,
SSLParameters sslParameters) {
SSLEngine engine = context.createSSLEngine();
SSLEngine engine = context.createSSLEngine(serverName, port);
engine.setUseClientMode(true);
engine.setSSLParameters(sslParameters);
return engine;
......@@ -120,69 +125,4 @@ abstract class AbstractAsyncSSLConnection extends HttpConnection
return true;
}
// Support for WebSocket/RawChannelImpl which unfortunately
// still depends on synchronous read/writes.
// It should be removed when RawChannelImpl moves to using asynchronous APIs.
static final class SSLConnectionChannel extends DetachedConnectionChannel {
final DetachedConnectionChannel delegate;
final SSLDelegate sslDelegate;
SSLConnectionChannel(DetachedConnectionChannel delegate, SSLDelegate sslDelegate) {
this.delegate = delegate;
this.sslDelegate = sslDelegate;
}
SocketChannel channel() {
return delegate.channel();
}
@Override
ByteBuffer read() throws IOException {
SSLDelegate.WrapperResult r = sslDelegate.recvData(ByteBuffer.allocate(8192));
// TODO: check for closure
int n = r.result.bytesProduced();
if (n > 0) {
return r.buf;
} else if (n == 0) {
return Utils.EMPTY_BYTEBUFFER;
} else {
return null;
}
}
@Override
long write(ByteBuffer[] buffers, int start, int number) throws IOException {
long l = SSLDelegate.countBytes(buffers, start, number);
SSLDelegate.WrapperResult r = sslDelegate.sendData(buffers, start, number);
if (r.result.getStatus() == SSLEngineResult.Status.CLOSED) {
if (l > 0) {
throw new IOException("SSLHttpConnection closed");
}
}
return l;
}
@Override
public void shutdownInput() throws IOException {
delegate.shutdownInput();
}
@Override
public void shutdownOutput() throws IOException {
delegate.shutdownOutput();
}
@Override
public void close() {
delegate.close();
}
}
// Support for WebSocket/RawChannelImpl which unfortunately
// still depends on synchronous read/writes.
// It should be removed when RawChannelImpl moves to using asynchronous APIs.
@Override
DetachedConnectionChannel detachChannel() {
assert client() != null;
DetachedConnectionChannel detachedChannel = plainConnection().detachChannel();
SSLDelegate sslDelegate = new SSLDelegate(engine,
detachedChannel.channel());
return new SSLConnectionChannel(detachedChannel, sslDelegate);
}
}
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2018, 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
......@@ -23,14 +23,13 @@
* questions.
*/
package jdk.incubator.http;
package jdk.internal.net.http;
import java.util.concurrent.Flow;
import jdk.incubator.http.internal.common.Demand;
import jdk.internal.net.http.common.Demand;
/**
* A {@link Flow.Subscription} wrapping a {@link Demand} instance.
*
*/
abstract class AbstractSubscription implements Flow.Subscription {
......@@ -41,5 +40,4 @@ abstract class AbstractSubscription implements Flow.Subscription {
* @return the subscription's demand.
*/
protected Demand demand() { return demand; }
}
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, 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
......@@ -23,7 +23,7 @@
* questions.
*/
package jdk.incubator.http;
package jdk.internal.net.http;
import java.io.IOException;
import java.nio.channels.SelectableChannel;
......
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, 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
......@@ -23,16 +23,13 @@
* questions.
*/
package jdk.incubator.http;
package jdk.internal.net.http;
import java.io.IOException;
import java.lang.System.Logger.Level;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.concurrent.CompletableFuture;
import jdk.incubator.http.internal.common.SSLTube;
import jdk.incubator.http.internal.common.Utils;
import jdk.internal.net.http.common.SSLTube;
import jdk.internal.net.http.common.Utils;
/**
* Asynchronous version of SSLConnection.
......@@ -46,7 +43,7 @@ class AsyncSSLConnection extends AbstractAsyncSSLConnection {
AsyncSSLConnection(InetSocketAddress addr,
HttpClientImpl client,
String[] alpn) {
super(addr, client, Utils.getServerName(addr), alpn);
super(addr, client, Utils.getServerName(addr), addr.getPort(), alpn);
plainConnection = new PlainHttpConnection(addr, client);
writePublisher = new PlainHttpPublisher();
}
......@@ -97,19 +94,7 @@ class AsyncSSLConnection extends AbstractAsyncSSLConnection {
}
@Override
void shutdownInput() throws IOException {
debug.log(Level.DEBUG, "plainConnection.channel().shutdownInput()");
plainConnection.channel().shutdownInput();
}
@Override
void shutdownOutput() throws IOException {
debug.log(Level.DEBUG, "plainConnection.channel().shutdownOutput()");
plainConnection.channel().shutdownOutput();
}
@Override
SSLTube getConnectionFlow() {
SSLTube getConnectionFlow() {
return flow;
}
}
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, 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
......@@ -23,15 +23,14 @@
* questions.
*/
package jdk.incubator.http;
package jdk.internal.net.http;
import java.io.IOException;
import java.lang.System.Logger.Level;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.concurrent.CompletableFuture;
import jdk.incubator.http.internal.common.SSLTube;
import jdk.incubator.http.internal.common.Utils;
import java.net.http.HttpHeaders;
import jdk.internal.net.http.common.SSLTube;
import jdk.internal.net.http.common.Utils;
/**
* An SSL tunnel built on a Plain (CONNECT) TCP tunnel.
......@@ -45,23 +44,24 @@ class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection {
AsyncSSLTunnelConnection(InetSocketAddress addr,
HttpClientImpl client,
String[] alpn,
InetSocketAddress proxy)
InetSocketAddress proxy,
HttpHeaders proxyHeaders)
{
super(addr, client, Utils.getServerName(addr), alpn);
this.plainConnection = new PlainTunnelingConnection(addr, proxy, client);
super(addr, client, Utils.getServerName(addr), addr.getPort(), alpn);
this.plainConnection = new PlainTunnelingConnection(addr, proxy, client, proxyHeaders);
this.writePublisher = new PlainHttpPublisher();
}
@Override
public CompletableFuture<Void> connectAsync() {
debug.log(Level.DEBUG, "Connecting plain tunnel connection");
if (debug.on()) debug.log("Connecting plain tunnel connection");
// This will connect the PlainHttpConnection flow, so that
// its HttpSubscriber and HttpPublisher are subscribed to the
// SocketTube
return plainConnection
.connectAsync()
.thenApply( unused -> {
debug.log(Level.DEBUG, "creating SSLTube");
if (debug.on()) debug.log("creating SSLTube");
// create the SSLTube wrapping the SocketTube, with the given engine
flow = new SSLTube(engine,
client().theExecutor(),
......@@ -69,6 +69,9 @@ class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection {
return null;} );
}
@Override
boolean isTunnel() { return true; }
@Override
boolean connected() {
return plainConnection.connected(); // && sslDelegate.connected();
......@@ -97,16 +100,6 @@ class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection {
plainConnection.close();
}
@Override
void shutdownInput() throws IOException {
plainConnection.channel().shutdownInput();
}
@Override
void shutdownOutput() throws IOException {
plainConnection.channel().shutdownOutput();
}
@Override
SocketChannel channel() {
return plainConnection.channel();
......
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2018, 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
......@@ -23,7 +23,7 @@
* questions.
*/
package jdk.incubator.http;
package jdk.internal.net.http;
import java.io.IOException;
import java.nio.channels.SelectableChannel;
......
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2018, 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
......@@ -23,18 +23,23 @@
* questions.
*/
package jdk.incubator.http;
package jdk.internal.net.http;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.InetSocketAddress;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Base64;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.WeakHashMap;
import jdk.incubator.http.internal.common.Utils;
import java.net.http.HttpHeaders;
import jdk.internal.net.http.common.Log;
import jdk.internal.net.http.common.Utils;
import static java.net.Authenticator.RequestorType.PROXY;
import static java.net.Authenticator.RequestorType.SERVER;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
......@@ -43,7 +48,7 @@ import static java.nio.charset.StandardCharsets.ISO_8859_1;
* Implementation of Http Basic authentication.
*/
class AuthenticationFilter implements HeaderFilter {
volatile MultiExchange<?,?> exchange;
volatile MultiExchange<?> exchange;
private static final Base64.Encoder encoder = Base64.getEncoder();
static final int DEFAULT_RETRY_LIMIT = 3;
......@@ -54,6 +59,10 @@ class AuthenticationFilter implements HeaderFilter {
static final int UNAUTHORIZED = 401;
static final int PROXY_UNAUTHORIZED = 407;
private static final List<String> BASIC_DUMMY =
List.of("Basic " + Base64.getEncoder()
.encodeToString("o:o".getBytes(ISO_8859_1)));
// A public no-arg constructor is required by FilterFactory
public AuthenticationFilter() {}
......@@ -72,19 +81,46 @@ class AuthenticationFilter implements HeaderFilter {
String realm = parser.findValue("realm");
java.net.Authenticator.RequestorType rtype = proxy ? PROXY : SERVER;
URL url = toURL(uri, req.method(), proxy);
String host;
int port;
String protocol;
InetSocketAddress proxyAddress;
if (proxy && (proxyAddress = req.proxy()) != null) {
// request sent to server through proxy
proxyAddress = req.proxy();
host = proxyAddress.getHostString();
port = proxyAddress.getPort();
protocol = "http"; // we don't support https connection to proxy
} else {
// direct connection to server or proxy
host = uri.getHost();
port = uri.getPort();
protocol = uri.getScheme();
}
// needs to be instance method in Authenticator
return auth.requestPasswordAuthenticationInstance(uri.getHost(),
return auth.requestPasswordAuthenticationInstance(host,
null,
uri.getPort(),
uri.getScheme(),
port,
protocol,
realm,
authscheme,
uri.toURL(),
url,
rtype
);
}
private URL toURL(URI uri, String method, boolean proxy)
throws MalformedURLException
{
if (proxy && "CONNECT".equalsIgnoreCase(method)
&& "socket".equalsIgnoreCase(uri.getScheme())) {
return null; // proxy tunneling
}
return uri.toURL();
}
private URI getProxyURI(HttpRequestImpl r) {
InetSocketAddress proxy = r.proxy();
if (proxy == null) {
......@@ -99,7 +135,7 @@ class AuthenticationFilter implements HeaderFilter {
null,
proxy.getHostString(),
proxy.getPort(),
null,
"/",
null,
null);
} catch (URISyntaxException e) {
......@@ -108,7 +144,7 @@ class AuthenticationFilter implements HeaderFilter {
}
@Override
public void request(HttpRequestImpl r, MultiExchange<?,?> e) throws IOException {
public void request(HttpRequestImpl r, MultiExchange<?> e) throws IOException {
// use preemptive authentication if an entry exists.
Cache cache = getCache(e);
this.exchange = e;
......@@ -144,6 +180,21 @@ class AuthenticationFilter implements HeaderFilter {
sb.append(pw.getUserName()).append(':').append(pw.getPassword());
String s = encoder.encodeToString(sb.toString().getBytes(ISO_8859_1));
String value = "Basic " + s;
if (proxy) {
if (r.isConnect()) {
if (!Utils.PROXY_TUNNEL_FILTER
.test(hdrname, List.of(value))) {
Log.logError("{0} disabled", hdrname);
return;
}
} else if (r.proxy() != null) {
if (!Utils.PROXY_FILTER
.test(hdrname, List.of(value))) {
Log.logError("{0} disabled", hdrname);
return;
}
}
}
r.setSystemHeader(hdrname, value);
}
......@@ -200,7 +251,10 @@ class AuthenticationFilter implements HeaderFilter {
}
if (exchange.proxyauth != null && !exchange.proxyauth.fromcache) {
AuthInfo au = exchange.proxyauth;
cache.store(au.scheme, req.uri(), false, au.credentials);
URI proxyURI = getProxyURI(req);
if (proxyURI != null) {
cache.store(au.scheme, proxyURI, true, au.credentials);
}
}
return null;
}
......@@ -219,8 +273,27 @@ class AuthenticationFilter implements HeaderFilter {
return null; // error gets returned to app
}
if (proxy) {
if (r.isConnectResponse) {
if (!Utils.PROXY_TUNNEL_FILTER
.test("Proxy-Authorization", BASIC_DUMMY)) {
Log.logError("{0} disabled", "Proxy-Authorization");
return null;
}
} else if (req.proxy() != null) {
if (!Utils.PROXY_FILTER
.test("Proxy-Authorization", BASIC_DUMMY)) {
Log.logError("{0} disabled", "Proxy-Authorization");
return null;
}
}
}
AuthInfo au = proxy ? exchange.proxyauth : exchange.serverauth;
if (au == null) {
// if no authenticator, let the user deal with 407/401
if (!exchange.client().authenticator().isPresent()) return null;
PasswordAuthentication pw = getCredentials(authval, proxy, req);
if (pw == null) {
throw new IOException("No credentials provided");
......@@ -232,6 +305,7 @@ class AuthenticationFilter implements HeaderFilter {
} else {
exchange.serverauth = au;
}
req = HttpRequestImpl.newInstanceForAuthentication(req);
addBasicCredentials(req, proxy, pw);
return req;
} else if (au.retries > retry_limit) {
......@@ -242,6 +316,10 @@ class AuthenticationFilter implements HeaderFilter {
if (au.fromcache) {
cache.remove(au.cacheEntry);
}
// if no authenticator, let the user deal with 407/401
if (!exchange.client().authenticator().isPresent()) return null;
// try again
PasswordAuthentication pw = getCredentials(authval, proxy, req);
if (pw == null) {
......@@ -253,6 +331,7 @@ class AuthenticationFilter implements HeaderFilter {
} else {
exchange.serverauth = au;
}
req = HttpRequestImpl.newInstanceForAuthentication(req);
addBasicCredentials(req, proxy, au.credentials);
au.retries++;
return req;
......@@ -260,10 +339,10 @@ class AuthenticationFilter implements HeaderFilter {
}
// Use a WeakHashMap to make it possible for the HttpClient to
// be garbaged collected when no longer referenced.
// be garbage collected when no longer referenced.
static final WeakHashMap<HttpClientImpl,Cache> caches = new WeakHashMap<>();
static synchronized Cache getCache(MultiExchange<?,?> exchange) {
static synchronized Cache getCache(MultiExchange<?> exchange) {
HttpClientImpl client = exchange.client();
Cache c = caches.get(client);
if (c == null) {
......@@ -276,9 +355,11 @@ class AuthenticationFilter implements HeaderFilter {
// Note: Make sure that Cache and CacheEntry do not keep any strong
// reference to the HttpClient: it would prevent the client being
// GC'ed when no longer referenced.
static class Cache {
static final class Cache {
final LinkedList<CacheEntry> entries = new LinkedList<>();
Cache() {}
synchronized CacheEntry get(URI uri, boolean proxy) {
for (CacheEntry entry : entries) {
if (entry.equalsKey(uri, proxy)) {
......@@ -309,7 +390,26 @@ class AuthenticationFilter implements HeaderFilter {
}
}
static class CacheEntry {
static URI normalize(URI uri, boolean isPrimaryKey) {
String path = uri.getPath();
if (path == null || path.isEmpty()) {
// make sure the URI has a path, ignore query and fragment
try {
return new URI(uri.getScheme(), uri.getAuthority(), "/", null, null);
} catch (URISyntaxException e) {
throw new InternalError(e);
}
} else if (isPrimaryKey || !"/".equals(path)) {
// remove extraneous components and normalize path
return uri.resolve(".");
} else {
// path == "/" and the URI is not used to store
// the primary key in the cache: nothing to do.
return uri;
}
}
static final class CacheEntry {
final String root;
final String scheme;
final boolean proxy;
......@@ -320,7 +420,7 @@ class AuthenticationFilter implements HeaderFilter {
boolean proxy,
PasswordAuthentication value) {
this.scheme = authscheme;
this.root = uri.resolve(".").toString(); // remove extraneous components
this.root = normalize(uri, true).toString(); // remove extraneous components
this.proxy = proxy;
this.value = value;
}
......@@ -333,7 +433,7 @@ class AuthenticationFilter implements HeaderFilter {
if (this.proxy != proxy) {
return false;
}
String other = uri.toString();
String other = String.valueOf(normalize(uri, false));
return other.startsWith(root);
}
}
......
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2018, 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
......@@ -23,7 +23,7 @@
* questions.
*/
package jdk.incubator.http;
package jdk.internal.net.http;
import java.nio.ByteBuffer;
import java.util.ArrayList;
......@@ -34,19 +34,20 @@ import java.util.Objects;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.incubator.http.internal.common.Demand;
import jdk.incubator.http.internal.common.SequentialScheduler;
import jdk.incubator.http.internal.common.Utils;
import java.net.http.HttpResponse.BodySubscriber;
import jdk.internal.net.http.common.Demand;
import jdk.internal.net.http.common.SequentialScheduler;
import jdk.internal.net.http.common.Utils;
/**
* A buffering BodySubscriber. When subscribed, accumulates ( buffers ) a given
* amount ( in bytes ) of a publisher's data before pushing it to a downstream
* subscriber.
*/
class BufferingSubscriber<T> implements HttpResponse.BodySubscriber<T>
public class BufferingSubscriber<T> implements BodySubscriber<T>
{
/** The downstream consumer of the data. */
private final HttpResponse.BodySubscriber<T> downstreamSubscriber;
private final BodySubscriber<T> downstreamSubscriber;
/** The amount of data to be accumulate before pushing downstream. */
private final int bufferSize;
......@@ -78,8 +79,8 @@ class BufferingSubscriber<T> implements HttpResponse.BodySubscriber<T>
private volatile int state;
BufferingSubscriber(HttpResponse.BodySubscriber<T> downstreamSubscriber,
int bufferSize) {
public BufferingSubscriber(BodySubscriber<T> downstreamSubscriber,
int bufferSize) {
this.downstreamSubscriber = Objects.requireNonNull(downstreamSubscriber);
this.bufferSize = bufferSize;
synchronized (buffersLock) {
......@@ -280,7 +281,8 @@ class BufferingSubscriber<T> implements HttpResponse.BodySubscriber<T>
throw new InternalError("onNext on inactive subscriber");
synchronized (buffersLock) {
accumulatedBytes += Utils.accumulateBuffers(internalBuffers, item);
internalBuffers.addAll(item);
accumulatedBytes += remaining(item);
}
downstreamSubscription.pushDemanded();
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册