24 */ |
24 */ |
25 |
25 |
26 package jdk.internal.net.http; |
26 package jdk.internal.net.http; |
27 |
27 |
28 import java.io.IOException; |
28 import java.io.IOException; |
|
29 import java.io.UncheckedIOException; |
29 import java.net.ConnectException; |
30 import java.net.ConnectException; |
30 import java.net.http.HttpConnectTimeoutException; |
31 import java.net.http.HttpConnectTimeoutException; |
31 import java.util.Iterator; |
32 import java.util.Iterator; |
32 import java.util.LinkedList; |
33 import java.util.LinkedList; |
33 import java.security.AccessControlContext; |
34 import java.security.AccessControlContext; |
34 import java.util.concurrent.CompletableFuture; |
35 import java.util.concurrent.CompletableFuture; |
|
36 import java.util.concurrent.CompletionStage; |
35 import java.util.concurrent.CompletionException; |
37 import java.util.concurrent.CompletionException; |
36 import java.util.concurrent.ExecutionException; |
38 import java.util.concurrent.ExecutionException; |
37 import java.util.concurrent.Executor; |
39 import java.util.concurrent.Executor; |
|
40 import java.util.concurrent.Flow; |
38 import java.util.concurrent.atomic.AtomicInteger; |
41 import java.util.concurrent.atomic.AtomicInteger; |
39 import java.util.function.Function; |
42 import java.util.function.Function; |
40 |
43 |
41 import java.net.http.HttpClient; |
44 import java.net.http.HttpClient; |
|
45 import java.net.http.HttpHeaders; |
42 import java.net.http.HttpRequest; |
46 import java.net.http.HttpRequest; |
43 import java.net.http.HttpResponse; |
47 import java.net.http.HttpResponse; |
|
48 import java.net.http.HttpResponse.BodySubscriber; |
44 import java.net.http.HttpResponse.PushPromiseHandler; |
49 import java.net.http.HttpResponse.PushPromiseHandler; |
45 import java.net.http.HttpTimeoutException; |
50 import java.net.http.HttpTimeoutException; |
46 import jdk.internal.net.http.common.Log; |
51 import jdk.internal.net.http.common.Log; |
47 import jdk.internal.net.http.common.Logger; |
52 import jdk.internal.net.http.common.Logger; |
48 import jdk.internal.net.http.common.MinimalFuture; |
53 import jdk.internal.net.http.common.MinimalFuture; |
198 CompletableFuture<HttpResponse<T>> cf = responseAsync0(start); |
203 CompletableFuture<HttpResponse<T>> cf = responseAsync0(start); |
199 start.completeAsync( () -> null, executor); // trigger execution |
204 start.completeAsync( () -> null, executor); // trigger execution |
200 return cf; |
205 return cf; |
201 } |
206 } |
202 |
207 |
|
208 // return true if the response is a type where a response body is never possible |
|
209 // and therefore doesn't have to include header information which indicates no |
|
210 // body is present. This is distinct from responses that also do not contain |
|
211 // response bodies (possibly ever) but which are required to have content length |
|
212 // info in the header (eg 205). Those cases do not have to be handled specially |
|
213 |
|
214 private static boolean bodyNotPermitted(Response r) { |
|
215 return r.statusCode == 204; |
|
216 } |
|
217 |
|
218 private boolean bodyIsPresent(Response r) { |
|
219 HttpHeaders headers = r.headers(); |
|
220 if (headers.firstValue("Content-length").isPresent()) |
|
221 return true; |
|
222 if (headers.firstValue("Transfer-encoding").isPresent()) |
|
223 return true; |
|
224 return false; |
|
225 } |
|
226 |
|
227 // Call the user's body handler to get an empty body object |
|
228 |
|
229 private CompletableFuture<HttpResponse<T>> handleNoBody(Response r, Exchange<T> exch) { |
|
230 BodySubscriber<T> bs = responseHandler.apply(new ResponseInfoImpl(r.statusCode(), |
|
231 r.headers(), r.version())); |
|
232 CompletionStage<T> cs = bs.getBody(); |
|
233 bs.onSubscribe(new NullSubscription()); |
|
234 bs.onComplete(); |
|
235 MinimalFuture<HttpResponse<T>> result = new MinimalFuture<>(); |
|
236 cs.whenComplete((nullBody, exception) -> { |
|
237 if (exception != null) |
|
238 result.completeExceptionally(exception); |
|
239 else { |
|
240 this.response = |
|
241 new HttpResponseImpl<>(r.request(), r, this.response, nullBody, exch); |
|
242 result.complete(this.response); |
|
243 } |
|
244 }); |
|
245 return result; |
|
246 } |
|
247 |
203 private CompletableFuture<HttpResponse<T>> |
248 private CompletableFuture<HttpResponse<T>> |
204 responseAsync0(CompletableFuture<Void> start) { |
249 responseAsync0(CompletableFuture<Void> start) { |
205 return start.thenCompose( v -> responseAsyncImpl()) |
250 return start.thenCompose( v -> responseAsyncImpl()) |
206 .thenCompose((Response r) -> { |
251 .thenCompose((Response r) -> { |
207 Exchange<T> exch = getExchange(); |
252 Exchange<T> exch = getExchange(); |
|
253 if (bodyNotPermitted(r)) { |
|
254 if (bodyIsPresent(r)) { |
|
255 IOException ioe = new IOException( |
|
256 "unexpected content length header with 204 response"); |
|
257 exch.cancel(ioe); |
|
258 return MinimalFuture.failedFuture(ioe); |
|
259 } else |
|
260 return handleNoBody(r, exch); |
|
261 } |
208 return exch.readBodyAsync(responseHandler) |
262 return exch.readBodyAsync(responseHandler) |
209 .thenApply((T body) -> { |
263 .thenApply((T body) -> { |
210 this.response = |
264 this.response = |
211 new HttpResponseImpl<>(r.request(), r, this.response, body, exch); |
265 new HttpResponseImpl<>(r.request(), r, this.response, body, exch); |
212 return this.response; |
266 return this.response; |
213 }); |
267 }); |
214 }); |
268 }); |
|
269 } |
|
270 |
|
271 static class NullSubscription implements Flow.Subscription { |
|
272 @Override |
|
273 public void request(long n) { |
|
274 } |
|
275 |
|
276 @Override |
|
277 public void cancel() { |
|
278 } |
215 } |
279 } |
216 |
280 |
217 private CompletableFuture<Response> responseAsyncImpl() { |
281 private CompletableFuture<Response> responseAsyncImpl() { |
218 CompletableFuture<Response> cf; |
282 CompletableFuture<Response> cf; |
219 if (attempts.incrementAndGet() > max_attempts) { |
283 if (attempts.incrementAndGet() > max_attempts) { |