public boolean streamResource()

in tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ResourceStreamerImpl.java [130:238]


    public boolean streamResource(Resource resource, StreamableResource streamable, String providedChecksum, Set<Options> options) throws IOException
    {
        assert streamable != null;
        assert options != null;

        String actualChecksum = streamable.getChecksum();

        if (providedChecksum != null && !providedChecksum.isEmpty() && !providedChecksum.equals(actualChecksum))
        {
            
            // TAP5-2185: Trying to find the wrongly-checksummed resource in the classpath and context,
            // so we can create an Asset with the correct checksum and redirect to it.
            Asset asset = null;
            if (resource != null)
            {
                asset = findAssetInsideWebapp(resource);
            }
            if (asset != null)
            {
                response.sendRedirect(asset.toClientURL());
                return true;
            }
            return false;
        }


        // ETag should be surrounded with quotes.
        String token = QUOTE + actualChecksum + QUOTE;

        // Even when sending a 304, we want the ETag associated with the request.
        // In most cases (except JavaScript modules), the checksum is also embedded into the URL.
        // However, E-Tags are also useful for enabling caching inside intermediate servers, CDNs, etc.
        response.setHeader("ETag", token);

        // If the client can send the correct ETag token, then its cache already contains the correct
        // content.
        String providedToken = request.getHeader("If-None-Match");

        if (token.equals(providedToken))
        {
            response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return true;
        }

        long lastModified = streamable.getLastModified();

        long ifModifiedSince;

        try
        {
            ifModifiedSince = request.getDateHeader(IF_MODIFIED_SINCE_HEADER);
        } catch (IllegalArgumentException ex)
        {
            // Simulate the header being missing if it is poorly formatted.

            ifModifiedSince = -1;
        }

        if (ifModifiedSince > 0 && ifModifiedSince >= lastModified)
        {
            response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return true;
        }

        // Prevent the upstream code from compressing when we don't want to.

        response.disableCompression();

        response.setDateHeader("Last-Modified", lastModified);


        if (productionMode && !options.contains(Options.OMIT_EXPIRATION))
        {
            // Starting in 5.4, this is a lot less necessary; any change to a Resource will result
            // in a new asset URL with the changed checksum incorporated into the URL.
            response.setDateHeader("Expires", lastModified + InternalConstants.TEN_YEARS);
        }

        // This is really for modules, which can not have a content hash code in the URL; therefore, we want
        // the browser to re-validate the resources on each new page render; because of the ETags, that will
        // mostly result in quick SC_NOT_MODIFIED responses.
        if (options.contains(Options.OMIT_EXPIRATION))
        {
            response.setHeader("Cache-Control", omitExpirationCacheControlHeader);
        }

        if (streamable.getCompression() == CompressionStatus.COMPRESSED)
        {
            response.setHeader(TapestryHttpInternalConstants.CONTENT_ENCODING_HEADER, TapestryHttpInternalConstants.GZIP_CONTENT_ENCODING);
        }

        ResponseCustomizer responseCustomizer = streamable.getResponseCustomizer();

        if (responseCustomizer != null)
        {
            responseCustomizer.customizeResponse(streamable, response);
        }

        if (!request.getMethod().equals("HEAD"))
        {
            response.setContentLength(streamable.getSize());
            
            OutputStream os = response.getOutputStream(streamable.getContentType().toString());
            streamable.streamTo(os);
            os.close();
        }

        return true;
    }