Search in sources :

Example 6 with ETag

use of io.undertow.util.ETag in project divolte-collector by divolte.

the class JavaScriptHandler method handleRequest.

@Override
public void handleRequest(final HttpServerExchange exchange) throws Exception {
    if (logger.isDebugEnabled()) {
        logger.debug("Requested received for {} from {}", resource.getResourceName(), exchange.getSourceAddress().getHostString());
    }
    // Start with headers that we always set the same way.
    final HeaderMap responseHeaders = exchange.getResponseHeaders();
    responseHeaders.put(Headers.CACHE_CONTROL, CACHE_CONTROL_HEADER_VALUE);
    // Figure out if we possibly need to deal with a compressed response,
    // based on client capability.
    final GzippableHttpBody uncompressedBody = resource.getEntityBody();
    final Optional<HttpBody> gzippedBody = uncompressedBody.getGzippedBody();
    final HttpBody bodyToSend;
    if (gzippedBody.isPresent()) {
        /*
             * Compressed responses can use Content-Encoding and/or Transfer-Encoding.
             * The semantics differ slightly, but it is suffice to say that most user
             * agents don't advertise their Transfer-Encoding support.
             * So for now we only support the Content-Encoding mechanism.
             * Some other notes:
             *  - Some clients implement 'deflate' incorrectly. Hence we only support 'gzip',
             *    despite it having slightly more overhead.
             *  - We don't use Undertow's built-in compression support because we've
             *    pre-calculated the compressed response and expect to serve it up
             *    repeatedly, instead of calculating it on-the-fly for every request.
             */
        responseHeaders.put(Headers.VARY, Headers.ACCEPT_ENCODING_STRING);
        final HeaderValues acceptEncoding = exchange.getRequestHeaders().get(Headers.ACCEPT_ENCODING);
        if (null != acceptEncoding && acceptEncoding.stream().anyMatch((header) -> Iterables.contains(HEADER_SPLITTER.split(header), "gzip"))) {
            responseHeaders.put(Headers.CONTENT_ENCODING, "gzip");
            bodyToSend = gzippedBody.get();
        } else {
            bodyToSend = uncompressedBody;
        }
    } else {
        bodyToSend = uncompressedBody;
    }
    // Now we know which version of the entity is visible to this user-agent,
    // figure out if the client already has the current version or not.
    final ETag eTag = bodyToSend.getETag();
    responseHeaders.put(Headers.ETAG, eTag.toString());
    if (ETagUtils.handleIfNoneMatch(exchange, eTag, true)) {
        final ByteBuffer entityBody = bodyToSend.getBody();
        responseHeaders.put(Headers.CONTENT_TYPE, "application/javascript");
        exchange.getResponseSender().send(entityBody);
    } else {
        exchange.setStatusCode(StatusCodes.NOT_MODIFIED);
        exchange.endExchange();
    }
}
Also used : ETagUtils(io.undertow.util.ETagUtils) Iterables(com.google.common.collect.Iterables) Logger(org.slf4j.Logger) HttpBody(io.divolte.server.js.HttpBody) GzippableHttpBody(io.divolte.server.js.GzippableHttpBody) ETag(io.undertow.util.ETag) HttpServerExchange(io.undertow.server.HttpServerExchange) LoggerFactory(org.slf4j.LoggerFactory) ByteBuffer(java.nio.ByteBuffer) ParametersAreNonnullByDefault(javax.annotation.ParametersAreNonnullByDefault) HttpHandler(io.undertow.server.HttpHandler) HeaderMap(io.undertow.util.HeaderMap) HeaderValues(io.undertow.util.HeaderValues) Objects(java.util.Objects) Duration(java.time.Duration) JavaScriptResource(io.divolte.server.js.JavaScriptResource) Headers(io.undertow.util.Headers) Optional(java.util.Optional) StatusCodes(io.undertow.util.StatusCodes) Splitter(com.google.common.base.Splitter) HeaderMap(io.undertow.util.HeaderMap) ETag(io.undertow.util.ETag) HeaderValues(io.undertow.util.HeaderValues) HttpBody(io.divolte.server.js.HttpBody) GzippableHttpBody(io.divolte.server.js.GzippableHttpBody) ByteBuffer(java.nio.ByteBuffer) GzippableHttpBody(io.divolte.server.js.GzippableHttpBody)

Example 7 with ETag

use of io.undertow.util.ETag in project divolte-collector by divolte.

the class JavaScriptResource method generateETag.

private static ETag generateETag(final byte[] entityBytes) {
    final MessageDigest digester = createDigester();
    final byte[] digest = digester.digest(entityBytes);
    return new ETag(false, Base64.getEncoder().encodeToString(digest));
}
Also used : ETag(io.undertow.util.ETag) MessageDigest(java.security.MessageDigest)

Example 8 with ETag

use of io.undertow.util.ETag in project undertow by undertow-io.

the class DefaultServlet method serveFileBlocking.

private void serveFileBlocking(final HttpServletRequest req, final HttpServletResponse resp, final Resource resource, HttpServerExchange exchange) throws IOException {
    final ETag etag = resource.getETag();
    final Date lastModified = resource.getLastModified();
    if (req.getDispatcherType() != DispatcherType.INCLUDE) {
        if (!ETagUtils.handleIfMatch(req.getHeader(Headers.IF_MATCH_STRING), etag, false) || !DateUtils.handleIfUnmodifiedSince(req.getHeader(Headers.IF_UNMODIFIED_SINCE_STRING), lastModified)) {
            resp.setStatus(StatusCodes.PRECONDITION_FAILED);
            return;
        }
        if (!ETagUtils.handleIfNoneMatch(req.getHeader(Headers.IF_NONE_MATCH_STRING), etag, true) || !DateUtils.handleIfModifiedSince(req.getHeader(Headers.IF_MODIFIED_SINCE_STRING), lastModified)) {
            if (req.getMethod().equals(Methods.GET_STRING) || req.getMethod().equals(Methods.HEAD_STRING)) {
                resp.setStatus(StatusCodes.NOT_MODIFIED);
            } else {
                resp.setStatus(StatusCodes.PRECONDITION_FAILED);
            }
            return;
        }
    }
    // we are going to proceed. Set the appropriate headers
    if (resp.getContentType() == null) {
        if (!resource.isDirectory()) {
            final String contentType = deployment.getServletContext().getMimeType(resource.getName());
            if (contentType != null) {
                resp.setContentType(contentType);
            } else {
                resp.setContentType("application/octet-stream");
            }
        }
    }
    if (lastModified != null) {
        resp.setHeader(Headers.LAST_MODIFIED_STRING, resource.getLastModifiedString());
    }
    if (etag != null) {
        resp.setHeader(Headers.ETAG_STRING, etag.toString());
    }
    ByteRange.RangeResponseResult rangeResponse = null;
    long start = -1, end = -1;
    try {
        // only set the content length if we are using a stream
        // if we are using a writer who knows what the length will end up being
        // todo: if someone installs a filter this can cause problems
        // not sure how best to deal with this
        // we also can't deal with range requests if a writer is in use
        Long contentLength = resource.getContentLength();
        if (contentLength != null) {
            resp.getOutputStream();
            if (contentLength > Integer.MAX_VALUE) {
                resp.setContentLengthLong(contentLength);
            } else {
                resp.setContentLength(contentLength.intValue());
            }
            if (resource instanceof RangeAwareResource && ((RangeAwareResource) resource).isRangeSupported() && resource.getContentLength() != null) {
                resp.setHeader(Headers.ACCEPT_RANGES_STRING, "bytes");
                // TODO: figure out what to do with the content encoded resource manager
                final ByteRange range = ByteRange.parse(req.getHeader(Headers.RANGE_STRING));
                if (range != null) {
                    rangeResponse = range.getResponseResult(resource.getContentLength(), req.getHeader(Headers.IF_RANGE_STRING), resource.getLastModified(), resource.getETag() == null ? null : resource.getETag().getTag());
                    if (rangeResponse != null) {
                        start = rangeResponse.getStart();
                        end = rangeResponse.getEnd();
                        resp.setStatus(rangeResponse.getStatusCode());
                        resp.setHeader(Headers.CONTENT_RANGE_STRING, rangeResponse.getContentRange());
                        long length = rangeResponse.getContentLength();
                        if (length > Integer.MAX_VALUE) {
                            resp.setContentLengthLong(length);
                        } else {
                            resp.setContentLength((int) length);
                        }
                        if (rangeResponse.getStatusCode() == StatusCodes.REQUEST_RANGE_NOT_SATISFIABLE) {
                            return;
                        }
                    }
                }
            }
        }
    } catch (IllegalStateException e) {
    }
    final boolean include = req.getDispatcherType() == DispatcherType.INCLUDE;
    if (!req.getMethod().equals(Methods.HEAD_STRING)) {
        if (rangeResponse == null) {
            resource.serve(exchange.getResponseSender(), exchange, completionCallback(include));
        } else {
            ((RangeAwareResource) resource).serveRange(exchange.getResponseSender(), exchange, start, end, completionCallback(include));
        }
    }
}
Also used : ETag(io.undertow.util.ETag) ByteRange(io.undertow.util.ByteRange) RangeAwareResource(io.undertow.server.handlers.resource.RangeAwareResource) Date(java.util.Date)

Example 9 with ETag

use of io.undertow.util.ETag in project undertow by undertow-io.

the class DirectoryUtils method sendRequestedBlobs.

/**
 * Serve static resource for the directory listing
 *
 * @param exchange The exchange
 * @return true if resources were served
 */
public static boolean sendRequestedBlobs(HttpServerExchange exchange) {
    ByteBuffer buffer = null;
    String type = null;
    String etag = null;
    String quotedEtag = null;
    if ("css".equals(exchange.getQueryString())) {
        buffer = Blobs.FILE_CSS_BUFFER.duplicate();
        type = "text/css";
        etag = Blobs.FILE_CSS_ETAG;
        quotedEtag = Blobs.FILE_CSS_ETAG_QUOTED;
    } else if ("js".equals(exchange.getQueryString())) {
        buffer = Blobs.FILE_JS_BUFFER.duplicate();
        type = "application/javascript";
        etag = Blobs.FILE_JS_ETAG;
        quotedEtag = Blobs.FILE_JS_ETAG_QUOTED;
    }
    if (buffer != null) {
        if (!ETagUtils.handleIfNoneMatch(exchange, new ETag(false, etag), false)) {
            exchange.setStatusCode(StatusCodes.NOT_MODIFIED);
            return true;
        }
        exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, String.valueOf(buffer.limit()));
        exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, type);
        exchange.getResponseHeaders().put(Headers.ETAG, quotedEtag);
        if (Methods.HEAD.equals(exchange.getRequestMethod())) {
            exchange.endExchange();
            return true;
        }
        exchange.getResponseSender().send(buffer);
        return true;
    }
    return false;
}
Also used : ETag(io.undertow.util.ETag) ByteBuffer(java.nio.ByteBuffer)

Example 10 with ETag

use of io.undertow.util.ETag in project undertow by undertow-io.

the class ResourceHandler method serveResource.

private void serveResource(final HttpServerExchange exchange, final boolean sendContent) throws Exception {
    if (DirectoryUtils.sendRequestedBlobs(exchange)) {
        return;
    }
    if (!allowed.resolve(exchange)) {
        exchange.setStatusCode(StatusCodes.FORBIDDEN);
        exchange.endExchange();
        return;
    }
    ResponseCache cache = exchange.getAttachment(ResponseCache.ATTACHMENT_KEY);
    final boolean cachable = this.cachable.resolve(exchange);
    // we set caching headers before we try and serve from the cache
    if (cachable && cacheTime != null) {
        exchange.getResponseHeaders().put(Headers.CACHE_CONTROL, "public, max-age=" + cacheTime);
        long date = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(cacheTime);
        String dateHeader = DateUtils.toDateString(new Date(date));
        exchange.getResponseHeaders().put(Headers.EXPIRES, dateHeader);
    }
    if (cache != null && cachable) {
        if (cache.tryServeResponse()) {
            return;
        }
    }
    // we now dispatch to a worker thread
    // as resource manager methods are potentially blocking
    HttpHandler dispatchTask = new HttpHandler() {

        @Override
        public void handleRequest(HttpServerExchange exchange) throws Exception {
            Resource resource = null;
            try {
                if (File.separatorChar == '/' || !exchange.getRelativePath().contains(File.separator)) {
                    // we don't process resources that contain the sperator character if this is not /
                    // this prevents attacks where people use windows path seperators in file URLS's
                    resource = resourceSupplier.getResource(exchange, canonicalize(exchange.getRelativePath()));
                }
            } catch (IOException e) {
                clearCacheHeaders(exchange);
                UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
                exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
                exchange.endExchange();
                return;
            }
            if (resource == null) {
                clearCacheHeaders(exchange);
                // usually a 404 handler
                next.handleRequest(exchange);
                return;
            }
            if (resource.isDirectory()) {
                Resource indexResource;
                try {
                    indexResource = getIndexFiles(exchange, resourceSupplier, resource.getPath(), welcomeFiles);
                } catch (IOException e) {
                    UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
                    exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
                    exchange.endExchange();
                    return;
                }
                if (indexResource == null) {
                    if (directoryListingEnabled) {
                        DirectoryUtils.renderDirectoryListing(exchange, resource);
                        return;
                    } else {
                        exchange.setStatusCode(StatusCodes.FORBIDDEN);
                        exchange.endExchange();
                        return;
                    }
                } else if (!exchange.getRequestPath().endsWith("/")) {
                    exchange.setStatusCode(StatusCodes.FOUND);
                    exchange.getResponseHeaders().put(Headers.LOCATION, RedirectBuilder.redirect(exchange, exchange.getRelativePath() + "/", true));
                    exchange.endExchange();
                    return;
                }
                resource = indexResource;
            } else if (exchange.getRelativePath().endsWith("/")) {
                // UNDERTOW-432
                exchange.setStatusCode(StatusCodes.NOT_FOUND);
                exchange.endExchange();
                return;
            }
            final ETag etag = resource.getETag();
            final Date lastModified = resource.getLastModified();
            if (!ETagUtils.handleIfMatch(exchange, etag, false) || !DateUtils.handleIfUnmodifiedSince(exchange, lastModified)) {
                exchange.setStatusCode(StatusCodes.PRECONDITION_FAILED);
                exchange.endExchange();
                return;
            }
            if (!ETagUtils.handleIfNoneMatch(exchange, etag, true) || !DateUtils.handleIfModifiedSince(exchange, lastModified)) {
                exchange.setStatusCode(StatusCodes.NOT_MODIFIED);
                exchange.endExchange();
                return;
            }
            final ContentEncodedResourceManager contentEncodedResourceManager = ResourceHandler.this.contentEncodedResourceManager;
            Long contentLength = resource.getContentLength();
            if (contentLength != null && !exchange.getResponseHeaders().contains(Headers.TRANSFER_ENCODING)) {
                exchange.setResponseContentLength(contentLength);
            }
            ByteRange.RangeResponseResult rangeResponse = null;
            long start = -1, end = -1;
            if (resource instanceof RangeAwareResource && ((RangeAwareResource) resource).isRangeSupported() && contentLength != null && contentEncodedResourceManager == null) {
                exchange.getResponseHeaders().put(Headers.ACCEPT_RANGES, "bytes");
                // TODO: figure out what to do with the content encoded resource manager
                ByteRange range = ByteRange.parse(exchange.getRequestHeaders().getFirst(Headers.RANGE));
                if (range != null && range.getRanges() == 1 && resource.getContentLength() != null) {
                    rangeResponse = range.getResponseResult(resource.getContentLength(), exchange.getRequestHeaders().getFirst(Headers.IF_RANGE), resource.getLastModified(), resource.getETag() == null ? null : resource.getETag().getTag());
                    if (rangeResponse != null) {
                        start = rangeResponse.getStart();
                        end = rangeResponse.getEnd();
                        exchange.setStatusCode(rangeResponse.getStatusCode());
                        exchange.getResponseHeaders().put(Headers.CONTENT_RANGE, rangeResponse.getContentRange());
                        long length = rangeResponse.getContentLength();
                        exchange.setResponseContentLength(length);
                        if (rangeResponse.getStatusCode() == StatusCodes.REQUEST_RANGE_NOT_SATISFIABLE) {
                            return;
                        }
                    }
                }
            }
            if (!exchange.getResponseHeaders().contains(Headers.CONTENT_TYPE)) {
                final String contentType = resource.getContentType(mimeMappings);
                if (contentType != null) {
                    exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, contentType);
                } else {
                    exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/octet-stream");
                }
            }
            if (lastModified != null) {
                exchange.getResponseHeaders().put(Headers.LAST_MODIFIED, resource.getLastModifiedString());
            }
            if (etag != null) {
                exchange.getResponseHeaders().put(Headers.ETAG, etag.toString());
            }
            if (contentEncodedResourceManager != null) {
                try {
                    ContentEncodedResource encoded = contentEncodedResourceManager.getResource(resource, exchange);
                    if (encoded != null) {
                        exchange.getResponseHeaders().put(Headers.CONTENT_ENCODING, encoded.getContentEncoding());
                        exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, encoded.getResource().getContentLength());
                        encoded.getResource().serve(exchange.getResponseSender(), exchange, IoCallback.END_EXCHANGE);
                        return;
                    }
                } catch (IOException e) {
                    // TODO: should this be fatal
                    UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
                    exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
                    exchange.endExchange();
                    return;
                }
            }
            if (!sendContent) {
                exchange.endExchange();
            } else if (rangeResponse != null) {
                ((RangeAwareResource) resource).serveRange(exchange.getResponseSender(), exchange, start, end, IoCallback.END_EXCHANGE);
            } else {
                resource.serve(exchange.getResponseSender(), exchange, IoCallback.END_EXCHANGE);
            }
        }
    };
    if (exchange.isInIoThread()) {
        exchange.dispatch(dispatchTask);
    } else {
        dispatchTask.handleRequest(exchange);
    }
}
Also used : HttpHandler(io.undertow.server.HttpHandler) ByteRange(io.undertow.util.ByteRange) ContentEncodedResource(io.undertow.server.handlers.encoding.ContentEncodedResource) HttpString(io.undertow.util.HttpString) IOException(java.io.IOException) ContentEncodedResource(io.undertow.server.handlers.encoding.ContentEncodedResource) Date(java.util.Date) HttpServerExchange(io.undertow.server.HttpServerExchange) ContentEncodedResourceManager(io.undertow.server.handlers.encoding.ContentEncodedResourceManager) ETag(io.undertow.util.ETag) ResponseCache(io.undertow.server.handlers.cache.ResponseCache)

Aggregations

ETag (io.undertow.util.ETag)11 HttpServerExchange (io.undertow.server.HttpServerExchange)4 Date (java.util.Date)4 ByteRange (io.undertow.util.ByteRange)3 ByteBuffer (java.nio.ByteBuffer)3 Test (org.junit.Test)3 HttpHandler (io.undertow.server.HttpHandler)2 RangeAwareResource (io.undertow.server.handlers.resource.RangeAwareResource)2 ResourceManager (io.undertow.server.handlers.resource.ResourceManager)2 IOException (java.io.IOException)2 Path (java.nio.file.Path)2 Splitter (com.google.common.base.Splitter)1 Iterables (com.google.common.collect.Iterables)1 GzippableHttpBody (io.divolte.server.js.GzippableHttpBody)1 HttpBody (io.divolte.server.js.HttpBody)1 JavaScriptResource (io.divolte.server.js.JavaScriptResource)1 IoCallback (io.undertow.io.IoCallback)1 Sender (io.undertow.io.Sender)1 ResponseCache (io.undertow.server.handlers.cache.ResponseCache)1 ContentEncodedResource (io.undertow.server.handlers.encoding.ContentEncodedResource)1