All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.compression.server.internal.CompressionResponse Maven / Gradle / Ivy

//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.compression.server.internal;

import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.jetty.compression.Compression;
import org.eclipse.jetty.compression.EncoderSink;
import org.eclipse.jetty.compression.server.CompressionConfig;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CompressionResponse extends Response.Wrapper
{
    private static final Logger LOG = LoggerFactory.getLogger(CompressionResponse.class);

    private final Compression compression;
    private final CompressionConfig config;
    private final AtomicReference state = new AtomicReference<>(State.MIGHT_COMPRESS);
    private EncoderSink encoderSink;

    public CompressionResponse(Request request, Response wrapped, Compression compression, CompressionConfig config)
    {
        super(request, wrapped);
        this.compression = compression;
        this.config = config;
    }

    @Override
    public void write(boolean last, ByteBuffer content, Callback callback)
    {
        switch (state.get())
        {
            case MIGHT_COMPRESS ->
            {
                int status = getStatus();
                if (status > 0 && (
                    HttpStatus.isInformational(status) ||
                    status == HttpStatus.NO_CONTENT_204 ||
                    status == HttpStatus.RESET_CONTENT_205) &&
                    !HttpMethod.HEAD.is(getRequest().getMethod()))
                {
                    if (LOG.isDebugEnabled())
                        LOG.debug("no compression for status {} {}", status, this);
                    state.compareAndSet(State.MIGHT_COMPRESS, State.NOT_COMPRESSING);
                    super.write(last, content, callback);
                    return;
                }

                // TODO: handle 304's etag.

                HttpField contentTypeField = getHeaders().getField(HttpHeader.CONTENT_TYPE);
                if (contentTypeField != null)
                {
                    String mimeType = MimeTypes.getContentTypeWithoutCharset(contentTypeField.getValue());
                    if (!config.isCompressMimeTypeSupported(mimeType))
                    {
                        if (LOG.isDebugEnabled())
                            LOG.debug("no compression for unsupported content type {} {}", mimeType, this);
                        state.compareAndSet(State.MIGHT_COMPRESS, State.NOT_COMPRESSING);
                        super.write(last, content, callback);
                        return;
                    }
                }

                // Did the application explicitly set the Content-Encoding?
                String contentEncoding = getHeaders().get(HttpHeader.CONTENT_ENCODING);
                if (contentEncoding != null)
                {
                    if (LOG.isDebugEnabled())
                        LOG.debug("no compression for explicit content encoding {} {}", contentEncoding, this);
                    state.compareAndSet(State.MIGHT_COMPRESS, State.NOT_COMPRESSING);
                    super.write(last, content, callback);
                    return;
                }

                // If there is nothing to write, don't compress.
                if (last && BufferUtil.isEmpty(content))
                {
                    if (LOG.isDebugEnabled())
                        LOG.debug("no compression, nothing to write {}", this);
                    state.compareAndSet(State.MIGHT_COMPRESS, State.NOT_COMPRESSING);
                    super.write(last, content, callback);
                    return;
                }

                long contentLength = getHeaders().getLongField(HttpHeader.CONTENT_LENGTH);
                if (contentLength < 0 && last)
                    contentLength = BufferUtil.length(content);
                if (contentLength >= 0 && contentLength < compression.getMinCompressSize())
                {
                    if (LOG.isDebugEnabled())
                        LOG.debug("no compression, too few content bytes {} {}", contentLength, this);
                    state.compareAndSet(State.MIGHT_COMPRESS, State.NOT_COMPRESSING);
                    super.write(last, content, callback);
                    return;
                }

                if (LOG.isDebugEnabled())
                    LOG.debug("compressing {} {}", compression.getEncodingName(), this);

                state.compareAndSet(State.MIGHT_COMPRESS, State.COMPRESSING);
                this.encoderSink = compression.newEncoderSink(getWrapped());

                // Adjust the headers.
                getHeaders().put(compression.getContentEncodingField());
                getHeaders().remove(HttpHeader.CONTENT_LENGTH);
                // TODO: etag.

                this.write(last, content, callback);
            }
            case COMPRESSING -> encoderSink.write(last, content, callback);
            case NOT_COMPRESSING -> super.write(last, content, callback);
        }
    }

    enum State
    {
        MIGHT_COMPRESS,
        NOT_COMPRESSING,
        COMPRESSING
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy