1 /* 2 * Copyright (c) 2015, 2017, 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 jdk.incubator.http; 27 28 import jdk.incubator.http.internal.common.HttpHeadersImpl; 29 import jdk.incubator.http.internal.websocket.WebSocketRequest; 30 31 import java.io.IOException; 32 import java.net.InetSocketAddress; 33 import java.net.ProxySelector; 34 import java.net.URI; 35 import java.security.AccessControlContext; 36 import java.time.Duration; 37 import java.util.Locale; 38 import java.util.Optional; 39 40 import static jdk.incubator.http.internal.common.Utils.ALLOWED_HEADERS; 41 42 class HttpRequestImpl extends HttpRequest implements WebSocketRequest { 43 44 private final HttpHeaders userHeaders; 45 private final HttpHeadersImpl systemHeaders; 46 private final URI uri; 47 private InetSocketAddress authority; // only used when URI not specified 48 private final String method; 49 final BodyProcessor requestProcessor; 50 final boolean secure; 51 final boolean expectContinue; 52 private boolean isWebSocket; 53 private AccessControlContext acc; 54 private final Duration duration; 55 private final Optional<HttpClient.Version> version; 56 57 /** 58 * Creates an HttpRequestImpl from the given builder. 59 */ 60 public HttpRequestImpl(HttpRequestBuilderImpl builder) { 61 String method = builder.method(); 62 this.method = method == null? "GET" : method; 63 this.userHeaders = ImmutableHeaders.of(builder.headers().map(), ALLOWED_HEADERS); 64 this.systemHeaders = new HttpHeadersImpl(); 65 this.uri = builder.uri(); 66 this.expectContinue = builder.expectContinue(); 67 this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https"); 68 if (builder.body() == null) { 69 this.requestProcessor = HttpRequest.noBody(); 70 } else { 71 this.requestProcessor = builder.body(); 72 } 73 this.duration = builder.duration(); 74 this.version = builder.version(); 75 } 76 77 /** 78 * Creates an HttpRequestImpl from the given request. 79 */ 80 public HttpRequestImpl(HttpRequest request) { 81 String method = request.method(); 82 this.method = method == null? "GET" : method; 83 this.userHeaders = request.headers(); 84 if (request instanceof HttpRequestImpl) { 85 this.systemHeaders = ((HttpRequestImpl) request).systemHeaders; 86 this.isWebSocket = ((HttpRequestImpl) request).isWebSocket; 87 } else { 88 this.systemHeaders = new HttpHeadersImpl(); 89 } 90 this.uri = request.uri(); 91 this.expectContinue = request.expectContinue(); 92 this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https"); 93 if (!request.bodyProcessor().isPresent()) { 94 this.requestProcessor = HttpRequest.noBody(); 95 } else { 96 this.requestProcessor = request.bodyProcessor().get(); 97 } 98 this.duration = request.duration(); 99 this.version = request.version(); 100 } 101 102 /** Creates a HttpRequestImpl using fields of an existing request impl. */ 103 public HttpRequestImpl(URI uri, 104 String method, 105 HttpRequestImpl other) { 106 this.method = method == null? "GET" : method; 107 this.userHeaders = other.userHeaders; 108 this.isWebSocket = other.isWebSocket; 109 this.systemHeaders = other.systemHeaders; 110 this.uri = uri; 111 this.expectContinue = other.expectContinue; 112 this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https"); 113 this.requestProcessor = other.requestProcessor; 114 this.acc = other.acc; 115 this.duration = other.duration; 116 this.version = other.version(); 117 } 118 119 /* used for creating CONNECT requests */ 120 HttpRequestImpl(String method, HttpClientImpl client, 121 InetSocketAddress authority) { 122 // TODO: isWebSocket flag is not specified, but the assumption is that 123 // such a request will never be made on a connection that will be returned 124 // to the connection pool (we might need to revisit this constructor later) 125 this.method = method; 126 this.systemHeaders = new HttpHeadersImpl(); 127 this.userHeaders = ImmutableHeaders.empty(); 128 this.uri = URI.create("socket://" + authority.getHostString() + ":" + Integer.toString(authority.getPort()) + "/"); 129 this.requestProcessor = HttpRequest.noBody(); 130 this.authority = authority; 131 this.secure = false; 132 this.expectContinue = false; 133 this.duration = null; 134 this.version = Optional.of(client.version()); 135 } 136 137 /** 138 * Creates a HttpRequestImpl from the given set of Headers and the associated 139 * "parent" request. Fields not taken from the headers are taken from the 140 * parent. 141 */ 142 static HttpRequestImpl createPushRequest(HttpRequestImpl parent, 143 HttpHeadersImpl headers) 144 throws IOException 145 { 146 return new HttpRequestImpl(parent, headers); 147 } 148 149 // only used for push requests 150 private HttpRequestImpl(HttpRequestImpl parent, HttpHeadersImpl headers) 151 throws IOException 152 { 153 this.method = headers.firstValue(":method") 154 .orElseThrow(() -> new IOException("No method in Push Promise")); 155 String path = headers.firstValue(":path") 156 .orElseThrow(() -> new IOException("No path in Push Promise")); 157 String scheme = headers.firstValue(":scheme") 158 .orElseThrow(() -> new IOException("No scheme in Push Promise")); 159 String authority = headers.firstValue(":authority") 160 .orElseThrow(() -> new IOException("No authority in Push Promise")); 161 StringBuilder sb = new StringBuilder(); 162 sb.append(scheme).append("://").append(authority).append(path); 163 this.uri = URI.create(sb.toString()); 164 165 this.userHeaders = ImmutableHeaders.of(headers.map(), ALLOWED_HEADERS); 166 this.systemHeaders = parent.systemHeaders; 167 this.expectContinue = parent.expectContinue; 168 this.secure = parent.secure; 169 this.requestProcessor = parent.requestProcessor; 170 this.acc = parent.acc; 171 this.duration = parent.duration; 172 this.version = parent.version; 173 } 174 175 @Override 176 public String toString() { 177 return (uri == null ? "" : uri.toString()) + " " + method; 178 } 179 180 @Override 181 public HttpHeaders headers() { 182 return userHeaders; 183 } 184 185 InetSocketAddress authority() { return authority; } 186 187 void setH2Upgrade(Http2ClientImpl h2client) { 188 systemHeaders.setHeader("Connection", "Upgrade, HTTP2-Settings"); 189 systemHeaders.setHeader("Upgrade", "h2c"); 190 systemHeaders.setHeader("HTTP2-Settings", h2client.getSettingsString()); 191 } 192 193 @Override 194 public boolean expectContinue() { return expectContinue; } 195 196 InetSocketAddress proxy(HttpClientImpl client) { 197 ProxySelector ps = client.proxy().orElse(null); 198 if (ps == null) { 199 ps = client.proxy().orElse(null); 200 } 201 if (ps == null || method.equalsIgnoreCase("CONNECT")) { 202 return null; 203 } 204 return (InetSocketAddress)ps.select(uri).get(0).address(); 205 } 206 207 boolean secure() { return secure; } 208 209 @Override 210 public void isWebSocket(boolean is) { 211 isWebSocket = is; 212 } 213 214 boolean isWebSocket() { 215 return isWebSocket; 216 } 217 218 // /** Returns the follow-redirects setting for this request. */ 219 // @Override 220 // public jdk.incubator.http.HttpClient.Redirect followRedirects() { 221 // return followRedirects; 222 // } 223 224 @Override 225 public Optional<BodyProcessor> bodyProcessor() { 226 return Optional.of(requestProcessor); 227 } 228 229 /** 230 * Returns the request method for this request. If not set explicitly, 231 * the default method for any request is "GET". 232 */ 233 @Override 234 public String method() { return method; } 235 236 @Override 237 public URI uri() { return uri; } 238 239 @Override 240 public Duration duration() { 241 return duration; 242 } 243 244 // HttpClientImpl client() { 245 // return client; 246 // } 247 248 HttpHeaders getUserHeaders() { return userHeaders; } 249 250 HttpHeadersImpl getSystemHeaders() { return systemHeaders; } 251 252 @Override 253 public Optional<HttpClient.Version> version() { return version; } 254 255 void addSystemHeader(String name, String value) { 256 systemHeaders.addHeader(name, value); 257 } 258 259 @Override 260 public void setSystemHeader(String name, String value) { 261 systemHeaders.setHeader(name, value); 262 } 263 264 // @Override 265 // public <T> HttpResponse<T> 266 // response(HttpResponse.BodyHandler<T> responseHandler) 267 // throws IOException, InterruptedException 268 // { 269 // if (!sent.compareAndSet(false, true)) { 270 // throw new IllegalStateException("request already sent"); 271 // } 272 // MultiExchange<Void,T> mex = new MultiExchange<>(this, responseHandler); 273 // return mex.response(); 274 // } 275 // 276 // @Override 277 // public <T> CompletableFuture<HttpResponse<T>> 278 // responseAsync(HttpResponse.BodyHandler<T> responseHandler) 279 // { 280 // if (!sent.compareAndSet(false, true)) { 281 // throw new IllegalStateException("request already sent"); 282 // } 283 // MultiExchange<Void,T> mex = new MultiExchange<>(this, responseHandler); 284 // return mex.responseAsync(null) 285 // .thenApply((HttpResponseImpl<T> b) -> (HttpResponse<T>) b); 286 // } 287 // 288 // @Override 289 // public <U, T> CompletableFuture<U> 290 // multiResponseAsync(HttpResponse.MultiProcessor<U, T> responseHandler) 291 // { 292 // if (!sent.compareAndSet(false, true)) { 293 // throw new IllegalStateException("request already sent"); 294 // } 295 // MultiExchange<U,T> mex = new MultiExchange<>(this, responseHandler); 296 // return mex.multiResponseAsync(); 297 // } 298 299 public InetSocketAddress getAddress(HttpClientImpl client) { 300 URI uri = uri(); 301 if (uri == null) { 302 return authority(); 303 } 304 int port = uri.getPort(); 305 if (port == -1) { 306 if (uri.getScheme().equalsIgnoreCase("https")) { 307 port = 443; 308 } else { 309 port = 80; 310 } 311 } 312 String host = uri.getHost(); 313 if (proxy(client) == null) { 314 return new InetSocketAddress(host, port); 315 } else { 316 return InetSocketAddress.createUnresolved(host, port); 317 } 318 } 319 }