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 */
24 package java.net.http;
25
26 import java.io.IOException;
27 import java.net.InetSocketAddress;
28 import java.net.ProxySelector;
29 import java.net.URI;
30 import java.net.http.HttpClient.Version;
31 import java.net.http.HttpResponse.MultiProcessor;
32 import java.util.concurrent.CompletableFuture;
33 import java.net.SocketPermission;
34 import java.security.AccessControlContext;
35 import java.security.AccessController;
36 import java.util.Set;
37 import static java.net.http.HttpRedirectImpl.getRedirects;
38 import java.util.Locale;
39
40 class HttpRequestImpl extends HttpRequest {
41
42 private final HttpHeadersImpl 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
60 public HttpRequestImpl(HttpClientImpl client,
61 String method,
62 HttpRequestBuilderImpl builder) {
63 this.client = client;
64 this.method = method == null? "GET" : method;
65 this.userHeaders = builder.headers() == null ?
66 new HttpHeadersImpl() : builder.headers();
67 dropDisallowedHeaders();
68 this.followRedirects = getRedirects(builder.followRedirects() == null ?
69 client.followRedirects() : builder.followRedirects());
70 this.systemHeaders = new HttpHeadersImpl();
71 this.uri = builder.uri();
72 this.proxy = builder.proxy();
73 this.expectContinue = builder.expectContinue();
74 this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
75 this.version = builder.version();
76 if (builder.body() == null) {
77 this.requestProcessor = HttpRequest.noBody();
78 } else {
79 this.requestProcessor = builder.body();
80 }
81 this.exchange = new MultiExchange(this);
82 this.timeval = builder.timeval();
83 }
84
85 /** Creates a HttpRequestImpl using fields of an existing request impl. */
86 public HttpRequestImpl(URI uri,
87 HttpRequest request,
88 HttpClientImpl client,
89 String method,
90 HttpRequestImpl other) {
91 this.client = client;
92 this.method = method == null? "GET" : method;
93 this.userHeaders = other.userHeaders == null ?
94 new HttpHeadersImpl() : other.userHeaders;
95 dropDisallowedHeaders();
96 this.followRedirects = getRedirects(other.followRedirects() == null ?
97 client.followRedirects() : other.followRedirects());
98 this.systemHeaders = other.systemHeaders;
99 this.uri = uri;
100 this.expectContinue = other.expectContinue;
101 this.secure = other.secure;
102 this.requestProcessor = other.requestProcessor;
103 this.proxy = other.proxy;
104 this.version = other.version;
105 this.acc = other.acc;
106 this.exchange = new MultiExchange(this);
107 this.timeval = other.timeval;
108 }
109
110 /* used for creating CONNECT requests */
111 HttpRequestImpl(HttpClientImpl client,
112 String method,
113 InetSocketAddress authority) {
114 this.client = client;
115 this.method = method;
116 this.followRedirects = getRedirects(client.followRedirects());
117 this.systemHeaders = new HttpHeadersImpl();
118 this.userHeaders = new HttpHeadersImpl();
119 this.uri = null;
120 this.proxy = null;
121 this.requestProcessor = HttpRequest.noBody();
122 this.version = java.net.http.HttpClient.Version.HTTP_1_1;
123 this.authority = authority;
124 this.secure = false;
125 this.expectContinue = false;
126 this.exchange = new MultiExchange(this);
127 this.timeval = 0; // block TODO: fix
128 }
129
130 @Override
131 public HttpClientImpl client() {
132 return client;
133 }
134
135
136 @Override
137 public String toString() {
138 return (uri == null ? "" : uri.toString()) + "/" + method + "("
139 + hashCode() + ")";
140 }
141
142 @Override
143 public HttpHeaders headers() {
144 userHeaders.makeUnmodifiable();
145 return userHeaders;
146 }
147
148 InetSocketAddress authority() { return authority; }
149
150 void setH2Upgrade() {
151 Http2ClientImpl h2client = client.client2();
152 systemHeaders.setHeader("Connection", "Upgrade, HTTP2-Settings");
153 systemHeaders.setHeader("Upgrade", "h2c");
154 systemHeaders.setHeader("HTTP2-Settings", h2client.getSettingsString());
155 }
156
157 private static final Set<String> DISALLOWED_HEADERS_SET = Set.of(
158 "authorization", "connection", "cookie", "content-length",
159 "date", "expect", "from", "host", "origin", "proxy-authorization",
160 "referer", "user-agent", "upgrade", "via", "warning");
161
162
163 // we silently drop headers that are disallowed
164 private void dropDisallowedHeaders() {
165 Set<String> hdrnames = userHeaders.directMap().keySet();
166
167 hdrnames.removeIf((s) ->
168 DISALLOWED_HEADERS_SET.contains(s.toLowerCase())
169 );
170 }
171
172 private synchronized void receiving() {
173 if (receiving) {
174 throw new IllegalStateException("already receiving response");
175 }
176 receiving = true;
177 }
178
179 /*
180 * Response filters may result in a new HttpRequestImpl being created
181 * (but still associated with the same API HttpRequest) and the process
182 * is repeated.
183 */
184 @Override
185 public HttpResponse response() throws IOException, InterruptedException {
186 receiving(); // TODO: update docs
187 if (System.getSecurityManager() != null) {
188 acc = AccessController.getContext();
189 }
190 return exchange.response();
191 }
192
193 @Override
194 public synchronized CompletableFuture<HttpResponse> responseAsync() {
195 receiving(); // TODO: update docs
196 if (System.getSecurityManager() != null) {
197 acc = AccessController.getContext();
198 }
199 return exchange.responseAsync(null)
200 .thenApply((r) -> (HttpResponse)r);
201 }
202
203 public <U> CompletableFuture<U>
204 sendAsyncMulti(HttpResponse.MultiProcessor<U> rspproc) {
205 // To change body of generated methods, choose Tools | Templates.
206 throw new UnsupportedOperationException("Not supported yet.");
207 }
208
209 @Override
210 public boolean expectContinue() { return expectContinue; }
211
212 public boolean requestHttp2() {
213 return version.equals(HttpClient.Version.HTTP_2);
214 //return client.getHttp2Allowed();
215 }
216
217 AccessControlContext getAccessControlContext() { return acc; }
218
219 InetSocketAddress proxy() {
220 ProxySelector ps = this.proxy;
221 if (ps == null) {
222 ps = client.proxy().orElse(null);
223 }
224 if (ps == null || method.equalsIgnoreCase("CONNECT")) {
225 return null;
226 }
238 }
239
240 /** Returns the follow-redirects setting for this request. */
241 @Override
242 public java.net.http.HttpClient.Redirect followRedirects() {
243 return getRedirects(followRedirects);
244 }
245
246 HttpRedirectImpl followRedirectsImpl() { return followRedirects; }
247
248 /**
249 * Returns the request method for this request. If not set explicitly,
250 * the default method for any request is "GET".
251 */
252 @Override
253 public String method() { return method; }
254
255 @Override
256 public URI uri() { return uri; }
257
258 HttpHeadersImpl getUserHeaders() { return userHeaders; }
259
260 HttpHeadersImpl getSystemHeaders() { return systemHeaders; }
261
262 HttpClientImpl getClient() { return client; }
263
264 BodyProcessor requestProcessor() { return requestProcessor; }
265
266 @Override
267 public Version version() { return version; }
268
269 void addSystemHeader(String name, String value) {
270 systemHeaders.addHeader(name, value);
271 }
272
273 void setSystemHeader(String name, String value) {
274 systemHeaders.setHeader(name, value);
275 }
276
277 long timeval() { return timeval; }
278
279 @Override
280 public <U> CompletableFuture<U>
281 multiResponseAsync(MultiProcessor<U> rspproc) {
282 //To change body of generated methods, choose Tools | Templates.
283 throw new UnsupportedOperationException("Not supported yet.");
284 }
285 }
|
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 */
24 package java.net.http;
25
26 import java.io.IOException;
27 import java.net.InetSocketAddress;
28 import java.net.ProxySelector;
29 import java.net.URI;
30 import java.net.http.HttpClient.Version;
31 import java.net.http.HttpResponse.MultiProcessor;
32 import java.util.concurrent.CompletableFuture;
33 import java.security.AccessControlContext;
34 import java.security.AccessController;
35 import static java.net.http.HttpRedirectImpl.getRedirects;
36 import java.util.Locale;
37
38 class HttpRequestImpl extends HttpRequest {
39
40 private final ImmutableHeaders userHeaders;
41 private final HttpHeadersImpl systemHeaders;
42 private final URI uri;
43 private InetSocketAddress authority; // only used when URI not specified
44 private final String method;
45 private final HttpClientImpl client;
46 private final HttpRedirectImpl followRedirects;
47 private final ProxySelector proxy;
48 final BodyProcessor requestProcessor;
49 final boolean secure;
50 final boolean expectContinue;
51 private final java.net.http.HttpClient.Version version;
52 private boolean isWebSocket;
53 final MultiExchange exchange;
54 private boolean receiving;
55 private AccessControlContext acc;
56 private final long timeval;
57 private Stream.PushGroup<?> pushGroup;
58
59 public HttpRequestImpl(HttpClientImpl client,
60 String method,
61 HttpRequestBuilderImpl builder) {
62 this.client = client;
63 this.method = method == null? "GET" : method;
64 this.userHeaders = builder.headers() == null ?
65 new ImmutableHeaders() :
66 new ImmutableHeaders(builder.headers(), Utils.ALLOWED_HEADERS);
67 this.followRedirects = getRedirects(builder.followRedirects() == null ?
68 client.followRedirects() : builder.followRedirects());
69 this.systemHeaders = new HttpHeadersImpl();
70 this.uri = builder.uri();
71 this.proxy = builder.proxy();
72 this.expectContinue = builder.expectContinue();
73 this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
74 this.version = builder.version();
75 if (builder.body() == null) {
76 this.requestProcessor = HttpRequest.noBody();
77 } else {
78 this.requestProcessor = builder.body();
79 }
80 this.exchange = new MultiExchange(this);
81 this.timeval = builder.timeval();
82 }
83
84 /** Creates a HttpRequestImpl using fields of an existing request impl. */
85 public HttpRequestImpl(URI uri,
86 HttpRequest request,
87 HttpClientImpl client,
88 String method,
89 HttpRequestImpl other) {
90 this.client = client;
91 this.method = method == null? "GET" : method;
92 this.userHeaders = other.userHeaders;
93 this.followRedirects = getRedirects(other.followRedirects() == null ?
94 client.followRedirects() : other.followRedirects());
95 this.systemHeaders = other.systemHeaders;
96 this.uri = uri;
97 this.expectContinue = other.expectContinue;
98 this.secure = other.secure;
99 this.requestProcessor = other.requestProcessor;
100 this.proxy = other.proxy;
101 this.version = other.version;
102 this.acc = other.acc;
103 this.exchange = new MultiExchange(this);
104 this.timeval = other.timeval;
105 }
106
107 /* used for creating CONNECT requests */
108 HttpRequestImpl(HttpClientImpl client,
109 String method,
110 InetSocketAddress authority) {
111 this.client = client;
112 this.method = method;
113 this.followRedirects = getRedirects(client.followRedirects());
114 this.systemHeaders = new HttpHeadersImpl();
115 this.userHeaders = new ImmutableHeaders();
116 this.uri = null;
117 this.proxy = null;
118 this.requestProcessor = HttpRequest.noBody();
119 this.version = java.net.http.HttpClient.Version.HTTP_1_1;
120 this.authority = authority;
121 this.secure = false;
122 this.expectContinue = false;
123 this.exchange = new MultiExchange(this);
124 this.timeval = 0; // block TODO: fix
125 }
126
127 @Override
128 public HttpClientImpl client() {
129 return client;
130 }
131
132 /**
133 * Creates a HttpRequestImpl from the given set of Headers and the associated
134 * "parent" request. Fields not taken from the headers are taken from the
135 * parent.
136 *
137 * @param parent
138 * @param headers
139 * @return
140 * @throws IOException
141 */
142 static HttpRequestImpl createPushRequest(HttpRequestImpl parent, HttpHeadersImpl headers) throws IOException {
143 return new HttpRequestImpl(parent, headers);
144 }
145
146 // only used for push requests
147 private HttpRequestImpl(HttpRequestImpl parent, HttpHeadersImpl headers) throws IOException {
148 this.method = headers.firstValue(":method")
149 .orElseThrow(() -> new IOException("No method in Push Promise"));
150 String path = headers.firstValue(":path")
151 .orElseThrow(() -> new IOException("No path in Push Promise"));
152 String scheme = headers.firstValue(":scheme")
153 .orElseThrow(() -> new IOException("No scheme in Push Promise"));
154 String authority = headers.firstValue(":authority")
155 .orElseThrow(() -> new IOException("No authority in Push Promise"));
156 StringBuilder sb = new StringBuilder();
157 sb.append(scheme).append("://").append(authority).append(path);
158 this.uri = URI.create(sb.toString());
159
160 this.client = parent.client;
161 this.userHeaders = new ImmutableHeaders(headers, Utils.ALLOWED_HEADERS);
162 this.followRedirects = parent.followRedirects;
163 this.systemHeaders = parent.systemHeaders;
164 this.expectContinue = parent.expectContinue;
165 this.secure = parent.secure;
166 this.requestProcessor = parent.requestProcessor;
167 this.proxy = parent.proxy;
168 this.version = parent.version;
169 this.acc = parent.acc;
170 this.exchange = parent.exchange;
171 this.timeval = parent.timeval;
172 }
173
174 @Override
175 public String toString() {
176 return (uri == null ? "" : uri.toString()) + " " + method;
177 }
178
179 @Override
180 public HttpHeaders headers() {
181 return userHeaders;
182 }
183
184 InetSocketAddress authority() { return authority; }
185
186 void setH2Upgrade() {
187 Http2ClientImpl h2client = client.client2();
188 systemHeaders.setHeader("Connection", "Upgrade, HTTP2-Settings");
189 systemHeaders.setHeader("Upgrade", "h2c");
190 systemHeaders.setHeader("HTTP2-Settings", h2client.getSettingsString());
191 }
192
193 private synchronized void receiving() {
194 if (receiving) {
195 throw new IllegalStateException("already receiving response");
196 }
197 receiving = true;
198 }
199
200 synchronized Stream.PushGroup<?> pushGroup() {
201 return pushGroup;
202 }
203
204 /*
205 * Response filters may result in a new HttpRequestImpl being created
206 * (but still associated with the same API HttpRequest) and the process
207 * is repeated.
208 */
209 @Override
210 public HttpResponse response() throws IOException, InterruptedException {
211 receiving(); // TODO: update docs
212 if (System.getSecurityManager() != null) {
213 acc = AccessController.getContext();
214 }
215 return exchange.response();
216 }
217
218 @Override
219 public synchronized CompletableFuture<HttpResponse> responseAsync() {
220 receiving(); // TODO: update docs
221 if (System.getSecurityManager() != null) {
222 acc = AccessController.getContext();
223 }
224 return exchange.responseAsync(null)
225 .thenApply((r) -> (HttpResponse)r);
226 }
227
228
229 @SuppressWarnings("unchecked")
230 @Override
231 public synchronized <U> CompletableFuture<U>
232 multiResponseAsync(MultiProcessor<U> rspproc) {
233 if (System.getSecurityManager() != null) {
234 acc = AccessController.getContext();
235 }
236 this.pushGroup = new Stream.PushGroup<>(rspproc, this);
237 CompletableFuture<HttpResponse> cf = pushGroup.mainResponse();
238 responseAsync()
239 .whenComplete((HttpResponse r, Throwable t) -> {
240 if (r != null)
241 cf.complete(r);
242 else
243 cf.completeExceptionally(t);
244 pushGroup.pushError(t);
245 });
246 return (CompletableFuture<U>)pushGroup.groupResult();
247 }
248
249 @Override
250 public boolean expectContinue() { return expectContinue; }
251
252 public boolean requestHttp2() {
253 return version.equals(HttpClient.Version.HTTP_2);
254 //return client.getHttp2Allowed();
255 }
256
257 AccessControlContext getAccessControlContext() { return acc; }
258
259 InetSocketAddress proxy() {
260 ProxySelector ps = this.proxy;
261 if (ps == null) {
262 ps = client.proxy().orElse(null);
263 }
264 if (ps == null || method.equalsIgnoreCase("CONNECT")) {
265 return null;
266 }
278 }
279
280 /** Returns the follow-redirects setting for this request. */
281 @Override
282 public java.net.http.HttpClient.Redirect followRedirects() {
283 return getRedirects(followRedirects);
284 }
285
286 HttpRedirectImpl followRedirectsImpl() { return followRedirects; }
287
288 /**
289 * Returns the request method for this request. If not set explicitly,
290 * the default method for any request is "GET".
291 */
292 @Override
293 public String method() { return method; }
294
295 @Override
296 public URI uri() { return uri; }
297
298 HttpHeaders getUserHeaders() { return userHeaders; }
299
300 HttpHeadersImpl getSystemHeaders() { return systemHeaders; }
301
302 HttpClientImpl getClient() { return client; }
303
304 BodyProcessor requestProcessor() { return requestProcessor; }
305
306 @Override
307 public Version version() { return version; }
308
309 void addSystemHeader(String name, String value) {
310 systemHeaders.addHeader(name, value);
311 }
312
313 void setSystemHeader(String name, String value) {
314 systemHeaders.setHeader(name, value);
315 }
316
317 long timeval() { return timeval; }
318 }
|