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 }