src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestImpl.java
changeset 48083 b1c1b4ef4be2
parent 47216 71c04702a3d5
child 55973 4d9b002587db
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestImpl.java	Fri Nov 03 10:01:08 2017 -0700
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestImpl.java	Wed Dec 06 11:11:59 2017 -0800
@@ -30,10 +30,14 @@
 
 import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.net.Proxy;
 import java.net.ProxySelector;
 import java.net.URI;
 import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.time.Duration;
+import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
 
@@ -44,42 +48,50 @@
     private final HttpHeaders userHeaders;
     private final HttpHeadersImpl systemHeaders;
     private final URI uri;
+    private Proxy proxy;
     private InetSocketAddress authority; // only used when URI not specified
     private final String method;
-    final BodyProcessor requestProcessor;
+    final BodyPublisher requestPublisher;
     final boolean secure;
     final boolean expectContinue;
     private boolean isWebSocket;
     private AccessControlContext acc;
-    private final Duration duration;
+    private final Duration timeout;  // may be null
     private final Optional<HttpClient.Version> version;
 
+    private static String userAgent() {
+        PrivilegedAction<String> pa = () -> System.getProperty("java.version");
+        String version = AccessController.doPrivileged(pa);
+        return "Java-http-client/" + version;
+    }
+
+    /** The value of the User-Agent header for all requests sent by the client. */
+    public static final String USER_AGENT = userAgent();
+
     /**
      * Creates an HttpRequestImpl from the given builder.
      */
     public HttpRequestImpl(HttpRequestBuilderImpl builder) {
         String method = builder.method();
-        this.method = method == null? "GET" : method;
+        this.method = method == null ? "GET" : method;
         this.userHeaders = ImmutableHeaders.of(builder.headers().map(), ALLOWED_HEADERS);
         this.systemHeaders = new HttpHeadersImpl();
         this.uri = builder.uri();
+        assert uri != null;
+        this.proxy = null;
         this.expectContinue = builder.expectContinue();
         this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
-        if (builder.body() == null) {
-            this.requestProcessor = HttpRequest.noBody();
-        } else {
-            this.requestProcessor = builder.body();
-        }
-        this.duration = builder.duration();
+        this.requestPublisher = builder.bodyPublisher();  // may be null
+        this.timeout = builder.timeout();
         this.version = builder.version();
     }
 
     /**
      * Creates an HttpRequestImpl from the given request.
      */
-    public HttpRequestImpl(HttpRequest request) {
+    public HttpRequestImpl(HttpRequest request, ProxySelector ps, AccessControlContext acc) {
         String method = request.method();
-        this.method = method == null? "GET" : method;
+        this.method = method == null ? "GET" : method;
         this.userHeaders = request.headers();
         if (request instanceof HttpRequestImpl) {
             this.systemHeaders = ((HttpRequestImpl) request).systemHeaders;
@@ -87,15 +99,25 @@
         } else {
             this.systemHeaders = new HttpHeadersImpl();
         }
+        this.systemHeaders.setHeader("User-Agent", USER_AGENT);
         this.uri = request.uri();
+        if (isWebSocket) {
+            // WebSocket determines and sets the proxy itself
+            this.proxy = ((HttpRequestImpl) request).proxy;
+        } else {
+            if (ps != null)
+                this.proxy = retrieveProxy(ps, uri);
+            else
+                this.proxy = null;
+        }
         this.expectContinue = request.expectContinue();
         this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
-        if (!request.bodyProcessor().isPresent()) {
-            this.requestProcessor = HttpRequest.noBody();
-        } else {
-            this.requestProcessor = request.bodyProcessor().get();
+        this.requestPublisher = request.bodyPublisher().orElse(null);
+        if (acc != null && requestPublisher instanceof RequestPublishers.FilePublisher) {
+            // Restricts the file publisher with the senders ACC, if any
+            ((RequestPublishers.FilePublisher)requestPublisher).setAccessControlContext(acc);
         }
-        this.duration = request.duration();
+        this.timeout = request.timeout().orElse(null);
         this.version = request.version();
     }
 
@@ -108,30 +130,38 @@
         this.isWebSocket = other.isWebSocket;
         this.systemHeaders = other.systemHeaders;
         this.uri = uri;
+        this.proxy = other.proxy;
         this.expectContinue = other.expectContinue;
         this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
-        this.requestProcessor = other.requestProcessor;
+        this.requestPublisher = other.requestPublisher;  // may be null
         this.acc = other.acc;
-        this.duration = other.duration;
+        this.timeout = other.timeout;
         this.version = other.version();
     }
 
     /* used for creating CONNECT requests  */
-    HttpRequestImpl(String method, HttpClientImpl client,
-                    InetSocketAddress authority) {
+    HttpRequestImpl(String method, InetSocketAddress authority) {
         // TODO: isWebSocket flag is not specified, but the assumption is that
         // such a request will never be made on a connection that will be returned
         // to the connection pool (we might need to revisit this constructor later)
         this.method = method;
         this.systemHeaders = new HttpHeadersImpl();
         this.userHeaders = ImmutableHeaders.empty();
-        this.uri = URI.create("socket://" + authority.getHostString() + ":" + Integer.toString(authority.getPort()) + "/");
-        this.requestProcessor = HttpRequest.noBody();
+        this.uri = URI.create("socket://" + authority.getHostString() + ":"
+                              + Integer.toString(authority.getPort()) + "/");
+        this.proxy = null;
+        this.requestPublisher = null;
         this.authority = authority;
         this.secure = false;
         this.expectContinue = false;
-        this.duration = null;
-        this.version = Optional.of(client.version());
+        this.timeout = null;
+        // The CONNECT request sent for tunneling is only used in two cases:
+        //   1. websocket, which only supports HTTP/1.1
+        //   2. SSL tunneling through a HTTP/1.1 proxy
+        // In either case we do not want to upgrade the connection to the proxy.
+        // What we want to possibly upgrade is the tunneled connection to the
+        // target server (so not the CONNECT request itself)
+        this.version = Optional.of(HttpClient.Version.HTTP_1_1);
     }
 
     /**
@@ -161,14 +191,14 @@
         StringBuilder sb = new StringBuilder();
         sb.append(scheme).append("://").append(authority).append(path);
         this.uri = URI.create(sb.toString());
-
+        this.proxy = null;
         this.userHeaders = ImmutableHeaders.of(headers.map(), ALLOWED_HEADERS);
         this.systemHeaders = parent.systemHeaders;
         this.expectContinue = parent.expectContinue;
         this.secure = parent.secure;
-        this.requestProcessor = parent.requestProcessor;
+        this.requestPublisher = parent.requestPublisher;
         this.acc = parent.acc;
-        this.duration = parent.duration;
+        this.timeout = parent.timeout;
         this.version = parent.version;
     }
 
@@ -193,20 +223,35 @@
     @Override
     public boolean expectContinue() { return expectContinue; }
 
-    InetSocketAddress proxy(HttpClientImpl client) {
-        ProxySelector ps = client.proxy().orElse(null);
-        if (ps == null) {
-            ps = client.proxy().orElse(null);
+    /** Retrieves the proxy, from the given ProxySelector, if there is one. */
+    private static Proxy retrieveProxy(ProxySelector ps, URI uri) {
+        Proxy proxy = null;
+        List<Proxy> pl = ps.select(uri);
+        if (!pl.isEmpty()) {
+            Proxy p = pl.get(0);
+            if (p.type() == Proxy.Type.HTTP)
+                proxy = p;
         }
-        if (ps == null || method.equalsIgnoreCase("CONNECT")) {
+        return proxy;
+    }
+
+    InetSocketAddress proxy() {
+        if (proxy == null || proxy.type() != Proxy.Type.HTTP
+                || method.equalsIgnoreCase("CONNECT")) {
             return null;
         }
-        return (InetSocketAddress)ps.select(uri).get(0).address();
+        return (InetSocketAddress)proxy.address();
     }
 
     boolean secure() { return secure; }
 
     @Override
+    public void setProxy(Proxy proxy) {
+        assert isWebSocket;
+        this.proxy = proxy;
+    }
+
+    @Override
     public void isWebSocket(boolean is) {
         isWebSocket = is;
     }
@@ -215,15 +260,10 @@
         return isWebSocket;
     }
 
-//    /** Returns the follow-redirects setting for this request. */
-//    @Override
-//    public jdk.incubator.http.HttpClient.Redirect followRedirects() {
-//        return followRedirects;
-//    }
-
     @Override
-    public Optional<BodyProcessor> bodyProcessor() {
-        return Optional.of(requestProcessor);
+    public Optional<BodyPublisher> bodyPublisher() {
+        return requestPublisher == null ? Optional.empty()
+                                        : Optional.of(requestPublisher);
     }
 
     /**
@@ -237,14 +277,10 @@
     public URI uri() { return uri; }
 
     @Override
-    public Duration duration() {
-        return duration;
+    public Optional<Duration> timeout() {
+        return timeout == null ? Optional.empty() : Optional.of(timeout);
     }
 
-//    HttpClientImpl client() {
-//        return client;
-//    }
-
     HttpHeaders getUserHeaders() { return userHeaders; }
 
     HttpHeadersImpl getSystemHeaders() { return systemHeaders; }
@@ -261,57 +297,24 @@
         systemHeaders.setHeader(name, value);
     }
 
-//    @Override
-//    public <T> HttpResponse<T>
-//    response(HttpResponse.BodyHandler<T> responseHandler)
-//        throws IOException, InterruptedException
-//    {
-//        if (!sent.compareAndSet(false, true)) {
-//            throw new IllegalStateException("request already sent");
-//        }
-//        MultiExchange<Void,T> mex = new MultiExchange<>(this, responseHandler);
-//        return mex.response();
-//    }
-//
-//    @Override
-//    public <T> CompletableFuture<HttpResponse<T>>
-//    responseAsync(HttpResponse.BodyHandler<T> responseHandler)
-//    {
-//        if (!sent.compareAndSet(false, true)) {
-//            throw new IllegalStateException("request already sent");
-//        }
-//        MultiExchange<Void,T> mex = new MultiExchange<>(this, responseHandler);
-//        return mex.responseAsync(null)
-//                  .thenApply((HttpResponseImpl<T> b) -> (HttpResponse<T>) b);
-//    }
-//
-//    @Override
-//    public <U, T> CompletableFuture<U>
-//    multiResponseAsync(HttpResponse.MultiProcessor<U, T> responseHandler)
-//    {
-//        if (!sent.compareAndSet(false, true)) {
-//            throw new IllegalStateException("request already sent");
-//        }
-//        MultiExchange<U,T> mex = new MultiExchange<>(this, responseHandler);
-//        return mex.multiResponseAsync();
-//    }
-
-    public InetSocketAddress getAddress(HttpClientImpl client) {
+    InetSocketAddress getAddress() {
         URI uri = uri();
         if (uri == null) {
             return authority();
         }
-        int port = uri.getPort();
-        if (port == -1) {
+        int p = uri.getPort();
+        if (p == -1) {
             if (uri.getScheme().equalsIgnoreCase("https")) {
-                port = 443;
+                p = 443;
             } else {
-                port = 80;
+                p = 80;
             }
         }
-        String host = uri.getHost();
-        if (proxy(client) == null) {
-            return new InetSocketAddress(host, port);
+        final String host = uri.getHost();
+        final int port = p;
+        if (proxy() == null) {
+            PrivilegedAction<InetSocketAddress> pa = () -> new InetSocketAddress(host, port);
+            return AccessController.doPrivileged(pa);
         } else {
             return InetSocketAddress.createUnresolved(host, port);
         }