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