1 /*
   2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.net.http;
  27 
  28 import java.io.IOException;
  29 import java.net.InetSocketAddress;
  30 import java.net.ProxySelector;
  31 import java.net.URI;
  32 import java.net.http.HttpClient.Version;
  33 import java.net.http.HttpResponse.MultiProcessor;
  34 import java.util.concurrent.CompletableFuture;
  35 import java.security.AccessControlContext;
  36 import java.security.AccessController;
  37 import static java.net.http.HttpRedirectImpl.getRedirects;
  38 import java.util.Locale;
  39 
  40 class HttpRequestImpl extends HttpRequest {
  41 
  42     private final ImmutableHeaders userHeaders;
  43     private final HttpHeadersImpl systemHeaders;
  44     private final URI uri;
  45     private InetSocketAddress authority; // only used when URI not specified
  46     private final String method;
  47     private final HttpClientImpl client;
  48     private final HttpRedirectImpl followRedirects;
  49     private final ProxySelector proxy;
  50     final BodyProcessor requestProcessor;
  51     final boolean secure;
  52     final boolean expectContinue;
  53     private final java.net.http.HttpClient.Version version;
  54     private boolean isWebSocket;
  55     final MultiExchange exchange;
  56     private boolean receiving;
  57     private AccessControlContext acc;
  58     private final long timeval;
  59     private Stream.PushGroup<?> pushGroup;
  60 
  61     public HttpRequestImpl(HttpClientImpl client,
  62                            String method,
  63                            HttpRequestBuilderImpl builder) {
  64         this.client = client;
  65         this.method = method == null? "GET" : method;
  66         this.userHeaders = builder.headers() == null ?
  67                 new ImmutableHeaders() :
  68                 new ImmutableHeaders(builder.headers(), Utils.ALLOWED_HEADERS);
  69         this.followRedirects = getRedirects(builder.followRedirects() == null ?
  70                 client.followRedirects() : builder.followRedirects());
  71         this.systemHeaders = new HttpHeadersImpl();
  72         this.uri = builder.uri();
  73         this.proxy = builder.proxy();
  74         this.expectContinue = builder.expectContinue();
  75         this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
  76         this.version = builder.version();
  77         if (builder.body() == null) {
  78             this.requestProcessor = HttpRequest.noBody();
  79         } else {
  80             this.requestProcessor = builder.body();
  81         }
  82         this.exchange = new MultiExchange(this);
  83         this.timeval = builder.timeval();
  84     }
  85 
  86     /** Creates a HttpRequestImpl using fields of an existing request impl. */
  87     public HttpRequestImpl(URI uri,
  88                            HttpRequest request,
  89                            HttpClientImpl client,
  90                            String method,
  91                            HttpRequestImpl other) {
  92         this.client = client;
  93         this.method = method == null? "GET" : method;
  94         this.userHeaders = other.userHeaders;
  95         this.followRedirects = getRedirects(other.followRedirects() == null ?
  96                 client.followRedirects() : other.followRedirects());
  97         this.systemHeaders = other.systemHeaders;
  98         this.uri = uri;
  99         this.expectContinue = other.expectContinue;
 100         this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
 101         this.requestProcessor = other.requestProcessor;
 102         this.proxy = other.proxy;
 103         this.version = other.version;
 104         this.acc = other.acc;
 105         this.exchange = new MultiExchange(this);
 106         this.timeval = other.timeval;
 107     }
 108 
 109     /* used for creating CONNECT requests  */
 110     HttpRequestImpl(HttpClientImpl client,
 111                     String method,
 112                     InetSocketAddress authority) {
 113         this.client = client;
 114         this.method = method;
 115         this.followRedirects = getRedirects(client.followRedirects());
 116         this.systemHeaders = new HttpHeadersImpl();
 117         this.userHeaders = new ImmutableHeaders();
 118         this.uri = null;
 119         this.proxy = null;
 120         this.requestProcessor = HttpRequest.noBody();
 121         this.version = java.net.http.HttpClient.Version.HTTP_1_1;
 122         this.authority = authority;
 123         this.secure = false;
 124         this.expectContinue = false;
 125         this.exchange = new MultiExchange(this);
 126         this.timeval = 0; // block TODO: fix
 127     }
 128 
 129     @Override
 130     public HttpClientImpl client() {
 131         return client;
 132     }
 133 
 134     /**
 135      * Creates a HttpRequestImpl from the given set of Headers and the associated
 136      * "parent" request. Fields not taken from the headers are taken from the
 137      * parent.
 138      */
 139     static HttpRequestImpl createPushRequest(HttpRequestImpl parent,
 140             HttpHeadersImpl headers) throws IOException {
 141 
 142         return new HttpRequestImpl(parent, headers);
 143     }
 144 
 145     // only used for push requests
 146     private HttpRequestImpl(HttpRequestImpl parent, HttpHeadersImpl headers) throws IOException {
 147         this.method = headers.firstValue(":method")
 148                 .orElseThrow(() -> new IOException("No method in Push Promise"));
 149         String path = headers.firstValue(":path")
 150                 .orElseThrow(() -> new IOException("No path in Push Promise"));
 151         String scheme = headers.firstValue(":scheme")
 152                 .orElseThrow(() -> new IOException("No scheme in Push Promise"));
 153         String authority = headers.firstValue(":authority")
 154                 .orElseThrow(() -> new IOException("No authority in Push Promise"));
 155         StringBuilder sb = new StringBuilder();
 156         sb.append(scheme).append("://").append(authority).append(path);
 157         this.uri = URI.create(sb.toString());
 158 
 159         this.client = parent.client;
 160         this.userHeaders = new ImmutableHeaders(headers, Utils.ALLOWED_HEADERS);
 161         this.followRedirects = parent.followRedirects;
 162         this.systemHeaders = parent.systemHeaders;
 163         this.expectContinue = parent.expectContinue;
 164         this.secure = parent.secure;
 165         this.requestProcessor = parent.requestProcessor;
 166         this.proxy = parent.proxy;
 167         this.version = parent.version;
 168         this.acc = parent.acc;
 169         this.exchange = parent.exchange;
 170         this.timeval = parent.timeval;
 171     }
 172 
 173     @Override
 174     public String toString() {
 175         return (uri == null ? "" : uri.toString()) + " " + method;
 176     }
 177 
 178     @Override
 179     public HttpHeaders headers() {
 180         return userHeaders;
 181     }
 182 
 183     InetSocketAddress authority() { return authority; }
 184 
 185     void setH2Upgrade() {
 186         Http2ClientImpl h2client = client.client2();
 187         systemHeaders.setHeader("Connection", "Upgrade, HTTP2-Settings");
 188         systemHeaders.setHeader("Upgrade", "h2c");
 189         systemHeaders.setHeader("HTTP2-Settings", h2client.getSettingsString());
 190     }
 191 
 192     private synchronized void receiving() {
 193         if (receiving) {
 194             throw new IllegalStateException("already receiving response");
 195         }
 196         receiving = true;
 197     }
 198 
 199     synchronized Stream.PushGroup<?> pushGroup() {
 200         return pushGroup;
 201     }
 202 
 203     /*
 204      * Response filters may result in a new HttpRequestImpl being created
 205      * (but still associated with the same API HttpRequest) and the process
 206      * is repeated.
 207      */
 208     @Override
 209     public HttpResponse response() throws IOException, InterruptedException {
 210         receiving(); // TODO: update docs
 211         if (System.getSecurityManager() != null) {
 212             acc = AccessController.getContext();
 213         }
 214         return exchange.response();
 215     }
 216 
 217     @Override
 218     public synchronized CompletableFuture<HttpResponse> responseAsync() {
 219         receiving(); // TODO: update docs
 220         if (System.getSecurityManager() != null) {
 221             acc = AccessController.getContext();
 222         }
 223         return exchange.responseAsync(null)
 224             .thenApply((r) -> (HttpResponse)r);
 225     }
 226 
 227 
 228     @SuppressWarnings("unchecked")
 229     @Override
 230     public synchronized <U> CompletableFuture<U>
 231     multiResponseAsync(MultiProcessor<U> rspproc) {
 232         if (System.getSecurityManager() != null) {
 233             acc = AccessController.getContext();
 234         }
 235         this.pushGroup = new Stream.PushGroup<>(rspproc, this);
 236         CompletableFuture<HttpResponse> cf = pushGroup.mainResponse();
 237         responseAsync()
 238             .whenComplete((HttpResponse r, Throwable t) -> {
 239                 if (r != null)
 240                     cf.complete(r);
 241                 else
 242                     cf.completeExceptionally(t);
 243                 pushGroup.pushError(t);
 244             });
 245         return (CompletableFuture<U>)pushGroup.groupResult();
 246     }
 247 
 248     @Override
 249     public boolean expectContinue() { return expectContinue; }
 250 
 251     public boolean requestHttp2() {
 252         return version.equals(HttpClient.Version.HTTP_2);
 253         //return client.getHttp2Allowed();
 254     }
 255 
 256     AccessControlContext getAccessControlContext() { return acc; }
 257 
 258     InetSocketAddress proxy() {
 259         ProxySelector ps = this.proxy;
 260         if (ps == null) {
 261             ps = client.proxy().orElse(null);
 262         }
 263         if (ps == null || method.equalsIgnoreCase("CONNECT")) {
 264             return null;
 265         }
 266         return (InetSocketAddress)ps.select(uri).get(0).address();
 267     }
 268 
 269     boolean secure() { return secure; }
 270 
 271     void isWebSocket(boolean is) {
 272         isWebSocket = is;
 273     }
 274 
 275     boolean isWebSocket() {
 276         return isWebSocket;
 277     }
 278 
 279     /** Returns the follow-redirects setting for this request. */
 280     @Override
 281     public java.net.http.HttpClient.Redirect followRedirects() {
 282         return getRedirects(followRedirects);
 283     }
 284 
 285     HttpRedirectImpl followRedirectsImpl() { return followRedirects; }
 286 
 287     /**
 288      * Returns the request method for this request. If not set explicitly,
 289      * the default method for any request is "GET".
 290      */
 291     @Override
 292     public String method() { return method; }
 293 
 294     @Override
 295     public URI uri() { return uri; }
 296 
 297     HttpHeaders getUserHeaders() { return userHeaders; }
 298 
 299     HttpHeadersImpl getSystemHeaders() { return systemHeaders; }
 300 
 301     HttpClientImpl getClient() { return client; }
 302 
 303     BodyProcessor requestProcessor() { return requestProcessor; }
 304 
 305     @Override
 306     public Version version() { return version; }
 307 
 308     void addSystemHeader(String name, String value) {
 309         systemHeaders.addHeader(name, value);
 310     }
 311 
 312     void setSystemHeader(String name, String value) {
 313         systemHeaders.setHeader(name, value);
 314     }
 315 
 316     long timeval() { return timeval; }
 317 }