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