use of io.vertx.ext.web.Http2PushMapping in project vertx-web by vert-x3.
the class StaticHandlerTest method testLinkPreload.
@Test
public void testLinkPreload() throws Exception {
List<Http2PushMapping> mappings = new ArrayList<>();
mappings.add(new Http2PushMapping("style.css", "style", false));
mappings.add(new Http2PushMapping("coin.png", "image", false));
stat.setHttp2PushMapping(mappings).setWebRoot("webroot/somedir3");
testRequest(HttpMethod.GET, "/testLinkPreload.html", null, res -> {
List<String> linkHeaders = res.headers().getAll("Link");
assertTrue(linkHeaders.contains("<style.css>; rel=preload; as=style"));
assertTrue(linkHeaders.contains("<coin.png>; rel=preload; as=image"));
}, 200, "OK", null);
}
use of io.vertx.ext.web.Http2PushMapping in project vertx-web by vert-x3.
the class StaticHandlerImpl method sendFile.
private void sendFile(RoutingContext context, String file, FileProps fileProps) {
HttpServerRequest request = context.request();
Long offset = null;
Long end = null;
MultiMap headers = null;
if (rangeSupport) {
// check if the client is making a range request
String range = request.getHeader("Range");
// end byte is length - 1
end = fileProps.size() - 1;
if (range != null) {
Matcher m = RANGE.matcher(range);
if (m.matches()) {
try {
String part = m.group(1);
// offset cannot be empty
offset = Long.parseLong(part);
// offset must fall inside the limits of the file
if (offset < 0 || offset >= fileProps.size()) {
throw new IndexOutOfBoundsException();
}
// length can be empty
part = m.group(2);
if (part != null && part.length() > 0) {
// ranges are inclusive
end = Math.min(end, Long.parseLong(part));
// end offset must not be smaller than start offset
if (end < offset) {
throw new IndexOutOfBoundsException();
}
}
} catch (NumberFormatException | IndexOutOfBoundsException e) {
context.response().putHeader("Content-Range", "bytes */" + fileProps.size());
context.fail(REQUESTED_RANGE_NOT_SATISFIABLE.code());
return;
}
}
}
// notify client we support range requests
headers = request.response().headers();
headers.set("Accept-Ranges", "bytes");
// send the content length even for HEAD requests
headers.set("Content-Length", Long.toString(end + 1 - (offset == null ? 0 : offset)));
}
writeCacheHeaders(request, fileProps);
if (request.method() == HttpMethod.HEAD) {
request.response().end();
} else {
if (rangeSupport && offset != null) {
// must return content range
headers.set("Content-Range", "bytes " + offset + "-" + end + "/" + fileProps.size());
// return a partial response
request.response().setStatusCode(PARTIAL_CONTENT.code());
// Wrap the sendFile operation into a TCCL switch, so the file resolver would find the file from the set
// classloader (if any).
final Long finalOffset = offset;
final Long finalEnd = end;
wrapInTCCLSwitch(() -> {
// guess content type
String contentType = MimeMapping.getMimeTypeForFilename(file);
if (contentType != null) {
if (contentType.startsWith("text")) {
request.response().putHeader("Content-Type", contentType + ";charset=" + defaultContentEncoding);
} else {
request.response().putHeader("Content-Type", contentType);
}
}
return request.response().sendFile(file, finalOffset, finalEnd + 1, res2 -> {
if (res2.failed()) {
context.fail(res2.cause());
}
});
});
} else {
// Wrap the sendFile operation into a TCCL switch, so the file resolver would find the file from the set
// classloader (if any).
wrapInTCCLSwitch(() -> {
// guess content type
String contentType = MimeMapping.getMimeTypeForFilename(file);
if (contentType != null) {
if (contentType.startsWith("text")) {
request.response().putHeader("Content-Type", contentType + ";charset=" + defaultContentEncoding);
} else {
request.response().putHeader("Content-Type", contentType);
}
}
// http2 pushing support
if (request.version() == HttpVersion.HTTP_2 && http2PushMappings != null) {
for (Http2PushMapping dependency : http2PushMappings) {
if (!dependency.isNoPush()) {
final String dep = webRoot + "/" + dependency.getFilePath();
HttpServerResponse response = request.response();
// get the file props
getFileProps(context, dep, filePropsAsyncResult -> {
if (filePropsAsyncResult.succeeded()) {
// push
writeCacheHeaders(request, filePropsAsyncResult.result());
response.push(HttpMethod.GET, "/" + dependency.getFilePath(), pushAsyncResult -> {
if (pushAsyncResult.succeeded()) {
HttpServerResponse res = pushAsyncResult.result();
final String depContentType = MimeMapping.getMimeTypeForExtension(file);
if (depContentType != null) {
if (depContentType.startsWith("text")) {
res.putHeader("Content-Type", contentType + ";charset=" + defaultContentEncoding);
} else {
res.putHeader("Content-Type", contentType);
}
}
res.sendFile(webRoot + "/" + dependency.getFilePath());
}
});
}
});
}
}
} else if (http2PushMappings != null) {
// Link preload when file push is not supported
HttpServerResponse response = request.response();
List<String> links = new ArrayList<>();
for (Http2PushMapping dependency : http2PushMappings) {
final String dep = webRoot + "/" + dependency.getFilePath();
// get the file props
getFileProps(context, dep, filePropsAsyncResult -> {
if (filePropsAsyncResult.succeeded()) {
// push
writeCacheHeaders(request, filePropsAsyncResult.result());
links.add("<" + dependency.getFilePath() + ">; rel=preload; as=" + dependency.getExtensionTarget() + (dependency.isNoPush() ? "; nopush" : ""));
}
});
}
response.putHeader("Link", links);
}
return request.response().sendFile(file, res2 -> {
if (res2.failed()) {
context.fail(res2.cause());
}
});
});
}
}
}
use of io.vertx.ext.web.Http2PushMapping in project vertx-web by vert-x3.
the class StaticHandlerTest method testHttp2Push.
@Test
public void testHttp2Push() throws Exception {
List<Http2PushMapping> mappings = new ArrayList<>();
mappings.add(new Http2PushMapping("style.css", "style", false));
mappings.add(new Http2PushMapping("coin.png", "image", false));
stat.setHttp2PushMapping(mappings).setWebRoot("webroot/somedir3");
router.route().handler(stat);
HttpServer http2Server = vertx.createHttpServer(new HttpServerOptions().setUseAlpn(true).setSsl(true).setPemKeyCertOptions(new PemKeyCertOptions().setKeyPath("tls/server-key.pem").setCertPath("tls/server-cert.pem")));
http2Server.requestHandler(router).listen(8443);
HttpClientOptions options = new HttpClientOptions().setSsl(true).setUseAlpn(true).setProtocolVersion(HttpVersion.HTTP_2).setPemTrustOptions(new PemTrustOptions().addCertPath("tls/server-cert.pem"));
HttpClient client = vertx.createHttpClient(options);
HttpClientRequest request = client.get(8443, "localhost", "/testLinkPreload.html", resp -> {
assertEquals(200, resp.statusCode());
assertEquals(HttpVersion.HTTP_2, resp.version());
resp.bodyHandler(this::assertNotNull);
});
CountDownLatch latch = new CountDownLatch(2);
request.pushHandler(pushedReq -> pushedReq.handler(pushedResp -> {
assertNotNull(pushedResp);
pushedResp.bodyHandler(this::assertNotNull);
latch.countDown();
}));
request.end();
latch.await();
}
Aggregations