1 /* 2 * Copyright (c) 2015, 2018, 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.internal.net.http; 27 28 import java.net.URI; 29 import java.time.Duration; 30 import java.util.Locale; 31 import java.util.Optional; 32 import java.net.http.HttpClient; 33 import java.net.http.HttpRequest; 34 import java.net.http.HttpRequest.BodyPublisher; 35 36 import jdk.internal.net.http.common.HttpHeadersBuilder; 37 import jdk.internal.net.http.common.Utils; 38 import static java.util.Objects.requireNonNull; 39 import static jdk.internal.net.http.common.Utils.isValidName; 40 import static jdk.internal.net.http.common.Utils.isValidValue; 41 import static jdk.internal.net.http.common.Utils.newIAE; 42 43 public class HttpRequestBuilderImpl implements HttpRequest.Builder { 44 45 private HttpHeadersBuilder headersBuilder; 46 private URI uri; 47 private String method; 48 private boolean expectContinue; 49 private BodyPublisher bodyPublisher; 50 private volatile Optional<HttpClient.Version> version; 51 private Duration duration; 52 53 public HttpRequestBuilderImpl(URI uri) { 54 requireNonNull(uri, "uri must be non-null"); 55 checkURI(uri); 56 this.uri = uri; 57 this.headersBuilder = new HttpHeadersBuilder(); 58 this.method = "GET"; // default, as per spec 59 this.version = Optional.empty(); 60 } 61 62 public HttpRequestBuilderImpl() { 63 this.headersBuilder = new HttpHeadersBuilder(); 64 this.method = "GET"; // default, as per spec 65 this.version = Optional.empty(); 66 } 67 68 @Override 69 public HttpRequestBuilderImpl uri(URI uri) { 70 requireNonNull(uri, "uri must be non-null"); 71 checkURI(uri); 72 this.uri = uri; 73 return this; 74 } 75 76 static void checkURI(URI uri) { 77 String scheme = uri.getScheme(); 78 if (scheme == null) 79 throw newIAE("URI with undefined scheme"); 80 scheme = scheme.toLowerCase(Locale.US); 81 if (!(scheme.equals("https") || scheme.equals("http"))) { 82 throw newIAE("invalid URI scheme %s", scheme); 83 } 84 if (uri.getHost() == null) { 85 throw newIAE("unsupported URI %s", uri); 86 } 87 } 88 89 @Override 90 public HttpRequestBuilderImpl copy() { 91 HttpRequestBuilderImpl b = new HttpRequestBuilderImpl(); 92 b.uri = this.uri; 93 b.headersBuilder = this.headersBuilder.structuralCopy(); 94 b.method = this.method; 95 b.expectContinue = this.expectContinue; 96 b.bodyPublisher = bodyPublisher; 97 b.uri = uri; 98 b.duration = duration; 99 b.version = version; 100 return b; 101 } 102 103 private void checkNameAndValue(String name, String value) { 104 requireNonNull(name, "name"); 105 requireNonNull(value, "value"); 106 if (!isValidName(name)) { 107 throw newIAE("invalid header name: \"%s\"", name); 108 } 109 if (!Utils.ALLOWED_HEADERS.test(name, null)) { 110 throw newIAE("restricted header name: \"%s\"", name); 111 } 112 if (!isValidValue(value)) { 113 throw newIAE("invalid header value: \"%s\"", value); 114 } 115 } 116 117 @Override 118 public HttpRequestBuilderImpl setHeader(String name, String value) { 119 checkNameAndValue(name, value); 120 headersBuilder.setHeader(name, value); 121 return this; 122 } 123 124 @Override 125 public HttpRequestBuilderImpl header(String name, String value) { 126 checkNameAndValue(name, value); 127 headersBuilder.addHeader(name, value); 128 return this; 129 } 130 131 @Override 132 public HttpRequestBuilderImpl headers(String... params) { 133 requireNonNull(params); 134 if (params.length == 0 || params.length % 2 != 0) { 135 throw newIAE("wrong number, %d, of parameters", params.length); 136 } 137 for (int i = 0; i < params.length; i += 2) { 138 String name = params[i]; 139 String value = params[i + 1]; 140 header(name, value); 141 } 142 return this; 143 } 144 145 @Override 146 public HttpRequestBuilderImpl expectContinue(boolean enable) { 147 expectContinue = enable; 148 return this; 149 } 150 151 @Override 152 public HttpRequestBuilderImpl version(HttpClient.Version version) { 153 requireNonNull(version); 154 this.version = Optional.of(version); 155 return this; 156 } 157 158 HttpHeadersBuilder headersBuilder() { return headersBuilder; } 159 160 URI uri() { return uri; } 161 162 String method() { return method; } 163 164 boolean expectContinue() { return expectContinue; } 165 166 BodyPublisher bodyPublisher() { return bodyPublisher; } 167 168 Optional<HttpClient.Version> version() { return version; } 169 170 @Override 171 public HttpRequest.Builder GET() { 172 return method0("GET", null); 173 } 174 175 @Override 176 public HttpRequest.Builder POST(BodyPublisher body) { 177 return method0("POST", requireNonNull(body)); 178 } 179 180 @Override 181 public HttpRequest.Builder DELETE() { 182 return method0("DELETE", null); 183 } 184 185 @Override 186 public HttpRequest.Builder PUT(BodyPublisher body) { 187 return method0("PUT", requireNonNull(body)); 188 } 189 190 @Override 191 public HttpRequest.Builder method(String method, BodyPublisher body) { 192 requireNonNull(method); 193 if (method.equals("")) 194 throw newIAE("illegal method <empty string>"); 195 if (method.equals("CONNECT")) 196 throw newIAE("method CONNECT is not supported"); 197 if (!Utils.isValidName(method)) 198 throw newIAE("illegal method \"" 199 + method.replace("\n","\\n") 200 .replace("\r", "\\r") 201 .replace("\t", "\\t") 202 + "\""); 203 return method0(method, requireNonNull(body)); 204 } 205 206 private HttpRequest.Builder method0(String method, BodyPublisher body) { 207 assert method != null; 208 assert !method.equals(""); 209 this.method = method; 210 this.bodyPublisher = body; 211 return this; 212 } 213 214 @Override 215 public HttpRequest build() { 216 if (uri == null) 217 throw new IllegalStateException("uri is null"); 218 assert method != null; 219 return new ImmutableHttpRequest(this); 220 } 221 222 public HttpRequestImpl buildForWebSocket() { 223 if (uri == null) 224 throw new IllegalStateException("uri is null"); 225 assert method != null; 226 return new HttpRequestImpl(this); 227 } 228 229 @Override 230 public HttpRequest.Builder timeout(Duration duration) { 231 requireNonNull(duration); 232 if (duration.isNegative() || Duration.ZERO.equals(duration)) 233 throw new IllegalArgumentException("Invalid duration: " + duration); 234 this.duration = duration; 235 return this; 236 } 237 238 Duration timeout() { return duration; } 239 240 }