33 import java.nio.charset.Charset; |
33 import java.nio.charset.Charset; |
34 import java.nio.channels.FileChannel; |
34 import java.nio.channels.FileChannel; |
35 import java.nio.charset.StandardCharsets; |
35 import java.nio.charset.StandardCharsets; |
36 import java.nio.file.OpenOption; |
36 import java.nio.file.OpenOption; |
37 import java.nio.file.Path; |
37 import java.nio.file.Path; |
38 import java.nio.file.Paths; |
|
39 import java.nio.file.StandardOpenOption; |
38 import java.nio.file.StandardOpenOption; |
40 import java.security.AccessControlContext; |
|
41 import java.util.List; |
39 import java.util.List; |
42 import java.util.Objects; |
40 import java.util.Objects; |
43 import java.util.Optional; |
41 import java.util.Optional; |
44 import java.util.concurrent.CompletableFuture; |
42 import java.util.concurrent.CompletableFuture; |
45 import java.util.concurrent.CompletionStage; |
43 import java.util.concurrent.CompletionStage; |
46 import java.util.concurrent.ConcurrentMap; |
44 import java.util.concurrent.ConcurrentMap; |
47 import java.util.concurrent.Flow; |
45 import java.util.concurrent.Flow; |
48 import java.util.concurrent.Flow.Subscriber; |
46 import java.util.concurrent.Flow.Subscriber; |
49 import java.util.function.BiFunction; |
|
50 import java.util.function.Consumer; |
47 import java.util.function.Consumer; |
51 import java.util.function.Function; |
48 import java.util.function.Function; |
52 import java.util.stream.Stream; |
49 import java.util.stream.Stream; |
53 import javax.net.ssl.SSLParameters; |
50 import javax.net.ssl.SSLParameters; |
54 import jdk.incubator.http.internal.BufferingSubscriber; |
51 import jdk.incubator.http.internal.BufferingSubscriber; |
55 import jdk.incubator.http.internal.LineSubscriberAdapter; |
52 import jdk.incubator.http.internal.LineSubscriberAdapter; |
|
53 import jdk.incubator.http.internal.ResponseBodyHandlers.FileDownloadBodyHandler; |
|
54 import jdk.incubator.http.internal.ResponseBodyHandlers.PathBodyHandler; |
|
55 import jdk.incubator.http.internal.ResponseBodyHandlers.PushPromisesHandlerWithMap; |
56 import jdk.incubator.http.internal.ResponseSubscribers; |
56 import jdk.incubator.http.internal.ResponseSubscribers; |
57 import static jdk.incubator.http.internal.common.Utils.unchecked; |
|
58 import static jdk.incubator.http.internal.common.Utils.charsetFrom; |
57 import static jdk.incubator.http.internal.common.Utils.charsetFrom; |
59 |
58 |
60 /** |
59 /** |
61 * Represents a response to a {@link HttpRequest}. |
60 * Represents a response to a {@link HttpRequest}. |
62 * {@Incubating} |
61 * {@Incubating} |
174 |
173 |
175 private static String pathForSecurityCheck(Path path) { |
174 private static String pathForSecurityCheck(Path path) { |
176 return path.toFile().getPath(); |
175 return path.toFile().getPath(); |
177 } |
176 } |
178 |
177 |
179 /** A body handler that is further restricted by a given ACC. */ |
|
180 interface UntrustedBodyHandler<T> extends BodyHandler<T> { |
|
181 void setAccessControlContext(AccessControlContext acc); |
|
182 } |
|
183 |
|
184 /** |
|
185 * A Path body handler. |
|
186 * |
|
187 * Note: Exists mainly too allow setting of the senders ACC post creation of |
|
188 * the handler. |
|
189 */ |
|
190 static class PathBodyHandler implements UntrustedBodyHandler<Path> { |
|
191 private final Path file; |
|
192 private final OpenOption[]openOptions; |
|
193 private volatile AccessControlContext acc; |
|
194 |
|
195 PathBodyHandler(Path file, OpenOption... openOptions) { |
|
196 this.file = file; |
|
197 this.openOptions = openOptions; |
|
198 } |
|
199 |
|
200 @Override |
|
201 public void setAccessControlContext(AccessControlContext acc) { |
|
202 this.acc = acc; |
|
203 } |
|
204 |
|
205 @Override |
|
206 public BodySubscriber<Path> apply(int statusCode, HttpHeaders headers) { |
|
207 ResponseSubscribers.PathSubscriber bs = (ResponseSubscribers.PathSubscriber) |
|
208 BodySubscriber.asFileImpl(file, openOptions); |
|
209 bs.setAccessControlContext(acc); |
|
210 return bs; |
|
211 } |
|
212 } |
|
213 |
|
214 /* package-private with push promise Map implementation */ |
|
215 static class PushPromisesHandlerWithMap<T> implements PushPromiseHandler<T> { |
|
216 |
|
217 private final ConcurrentMap<HttpRequest,CompletableFuture<HttpResponse<T>>> pushPromisesMap; |
|
218 private final Function<HttpRequest,BodyHandler<T>> pushPromiseHandler; |
|
219 |
|
220 PushPromisesHandlerWithMap(Function<HttpRequest,BodyHandler<T>> pushPromiseHandler, |
|
221 ConcurrentMap<HttpRequest,CompletableFuture<HttpResponse<T>>> pushPromisesMap) { |
|
222 this.pushPromiseHandler = pushPromiseHandler; |
|
223 this.pushPromisesMap = pushPromisesMap; |
|
224 } |
|
225 |
|
226 @Override |
|
227 public void applyPushPromise( |
|
228 HttpRequest initiatingRequest, HttpRequest pushRequest, |
|
229 Function<BodyHandler<T>,CompletableFuture<HttpResponse<T>>> acceptor) |
|
230 { |
|
231 URI initiatingURI = initiatingRequest.uri(); |
|
232 URI pushRequestURI = pushRequest.uri(); |
|
233 if (!initiatingURI.getHost().equalsIgnoreCase(pushRequestURI.getHost())) |
|
234 return; |
|
235 |
|
236 int initiatingPort = initiatingURI.getPort(); |
|
237 if (initiatingPort == -1 ) { |
|
238 if ("https".equalsIgnoreCase(initiatingURI.getScheme())) |
|
239 initiatingPort = 443; |
|
240 else |
|
241 initiatingPort = 80; |
|
242 } |
|
243 int pushPort = pushRequestURI.getPort(); |
|
244 if (pushPort == -1 ) { |
|
245 if ("https".equalsIgnoreCase(pushRequestURI.getScheme())) |
|
246 pushPort = 443; |
|
247 else |
|
248 pushPort = 80; |
|
249 } |
|
250 if (initiatingPort != pushPort) |
|
251 return; |
|
252 |
|
253 CompletableFuture<HttpResponse<T>> cf = |
|
254 acceptor.apply(pushPromiseHandler.apply(pushRequest)); |
|
255 pushPromisesMap.put(pushRequest, cf); |
|
256 } |
|
257 } |
|
258 |
|
259 // Similar to Path body handler, but for file download. Supports setting ACC. |
|
260 static class FileDownloadBodyHandler implements UntrustedBodyHandler<Path> { |
|
261 private final Path directory; |
|
262 private final OpenOption[]openOptions; |
|
263 private volatile AccessControlContext acc; |
|
264 |
|
265 FileDownloadBodyHandler(Path directory, OpenOption... openOptions) { |
|
266 this.directory = directory; |
|
267 this.openOptions = openOptions; |
|
268 } |
|
269 |
|
270 @Override |
|
271 public void setAccessControlContext(AccessControlContext acc) { |
|
272 this.acc = acc; |
|
273 } |
|
274 |
|
275 @Override |
|
276 public BodySubscriber<Path> apply(int statusCode, HttpHeaders headers) { |
|
277 String dispoHeader = headers.firstValue("Content-Disposition") |
|
278 .orElseThrow(() -> unchecked(new IOException("No Content-Disposition"))); |
|
279 if (!dispoHeader.startsWith("attachment;")) { |
|
280 throw unchecked(new IOException("Unknown Content-Disposition type")); |
|
281 } |
|
282 int n = dispoHeader.indexOf("filename="); |
|
283 if (n == -1) { |
|
284 throw unchecked(new IOException("Bad Content-Disposition type")); |
|
285 } |
|
286 int lastsemi = dispoHeader.lastIndexOf(';'); |
|
287 String disposition; |
|
288 if (lastsemi < n) { |
|
289 disposition = dispoHeader.substring(n + 9); |
|
290 } else { |
|
291 disposition = dispoHeader.substring(n + 9, lastsemi); |
|
292 } |
|
293 Path file = Paths.get(directory.toString(), disposition); |
|
294 |
|
295 ResponseSubscribers.PathSubscriber bs = (ResponseSubscribers.PathSubscriber) |
|
296 BodySubscriber.asFileImpl(file, openOptions); |
|
297 bs.setAccessControlContext(acc); |
|
298 return bs; |
|
299 } |
|
300 } |
|
301 |
178 |
302 /** |
179 /** |
303 * A handler for response bodies. |
180 * A handler for response bodies. |
304 * {@Incubating} |
181 * {@Incubating} |
305 * |
182 * |