Search in sources :

Example 1 with Http2PushMapping

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);
}
Also used : Http2PushMapping(io.vertx.ext.web.Http2PushMapping) ArrayList(java.util.ArrayList) Test(org.junit.Test)

Example 2 with Http2PushMapping

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());
                    }
                });
            });
        }
    }
}
Also used : HttpServerRequest(io.vertx.core.http.HttpServerRequest) java.util(java.util) io.vertx.core(io.vertx.core) Callable(java.util.concurrent.Callable) RoutingContext(io.vertx.ext.web.RoutingContext) LoggerFactory(io.vertx.core.logging.LoggerFactory) FileProps(io.vertx.core.file.FileProps) Utils(io.vertx.ext.web.impl.Utils) Matcher(java.util.regex.Matcher) Charset(java.nio.charset.Charset) HttpVersion(io.vertx.core.http.HttpVersion) Logger(io.vertx.core.logging.Logger) ParseException(java.text.ParseException) DateFormat(java.text.DateFormat) LRUCache(io.vertx.ext.web.impl.LRUCache) StaticHandler(io.vertx.ext.web.handler.StaticHandler) HttpResponseStatus(io.netty.handler.codec.http.HttpResponseStatus) MimeMapping(io.vertx.core.http.impl.MimeMapping) File(java.io.File) JsonArray(io.vertx.core.json.JsonArray) Http2PushMapping(io.vertx.ext.web.Http2PushMapping) HttpMethod(io.vertx.core.http.HttpMethod) HttpServerResponse(io.vertx.core.http.HttpServerResponse) FileSystem(io.vertx.core.file.FileSystem) Pattern(java.util.regex.Pattern) Matcher(java.util.regex.Matcher) HttpServerRequest(io.vertx.core.http.HttpServerRequest) Http2PushMapping(io.vertx.ext.web.Http2PushMapping) HttpServerResponse(io.vertx.core.http.HttpServerResponse)

Example 3 with Http2PushMapping

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();
}
Also used : Arrays(java.util.Arrays) Date(java.util.Date) HttpServer(io.vertx.core.http.HttpServer) Router(io.vertx.ext.web.Router) AtomicReference(java.util.concurrent.atomic.AtomicReference) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) HttpClientRequest(io.vertx.core.http.HttpClientRequest) Utils(io.vertx.ext.web.impl.Utils) HttpVersion(io.vertx.core.http.HttpVersion) PemKeyCertOptions(io.vertx.core.net.PemKeyCertOptions) HttpClientOptions(io.vertx.core.http.HttpClientOptions) PemTrustOptions(io.vertx.core.net.PemTrustOptions) WebTestBase(io.vertx.ext.web.WebTestBase) DateFormat(java.text.DateFormat) Set(java.util.Set) Test(org.junit.Test) File(java.io.File) JsonArray(io.vertx.core.json.JsonArray) CountDownLatch(java.util.concurrent.CountDownLatch) List(java.util.List) Http2PushMapping(io.vertx.ext.web.Http2PushMapping) HttpMethod(io.vertx.core.http.HttpMethod) HttpServerOptions(io.vertx.core.http.HttpServerOptions) HttpClient(io.vertx.core.http.HttpClient) HttpClientRequest(io.vertx.core.http.HttpClientRequest) PemKeyCertOptions(io.vertx.core.net.PemKeyCertOptions) Http2PushMapping(io.vertx.ext.web.Http2PushMapping) HttpClient(io.vertx.core.http.HttpClient) ArrayList(java.util.ArrayList) HttpServer(io.vertx.core.http.HttpServer) HttpServerOptions(io.vertx.core.http.HttpServerOptions) CountDownLatch(java.util.concurrent.CountDownLatch) PemTrustOptions(io.vertx.core.net.PemTrustOptions) HttpClientOptions(io.vertx.core.http.HttpClientOptions) Test(org.junit.Test)

Aggregations

Http2PushMapping (io.vertx.ext.web.Http2PushMapping)3 HttpMethod (io.vertx.core.http.HttpMethod)2 HttpVersion (io.vertx.core.http.HttpVersion)2 JsonArray (io.vertx.core.json.JsonArray)2 Utils (io.vertx.ext.web.impl.Utils)2 File (java.io.File)2 DateFormat (java.text.DateFormat)2 ArrayList (java.util.ArrayList)2 Test (org.junit.Test)2 HttpResponseStatus (io.netty.handler.codec.http.HttpResponseStatus)1 io.vertx.core (io.vertx.core)1 FileProps (io.vertx.core.file.FileProps)1 FileSystem (io.vertx.core.file.FileSystem)1 HttpClient (io.vertx.core.http.HttpClient)1 HttpClientOptions (io.vertx.core.http.HttpClientOptions)1 HttpClientRequest (io.vertx.core.http.HttpClientRequest)1 HttpServer (io.vertx.core.http.HttpServer)1 HttpServerOptions (io.vertx.core.http.HttpServerOptions)1 HttpServerRequest (io.vertx.core.http.HttpServerRequest)1 HttpServerResponse (io.vertx.core.http.HttpServerResponse)1