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

com.artipie.http.slice.GzipSlice Maven / Gradle / Ivy

There is a newer version: v1.17.16
Show newest version
/*
 * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
 * https://github.com/artipie/artipie/blob/master/LICENSE.txt
 */
package com.artipie.http.slice;

import com.artipie.asto.ArtipieIOException;
import com.artipie.asto.Content;
import com.artipie.http.Connection;
import com.artipie.http.Headers;
import com.artipie.http.Response;
import com.artipie.http.Slice;
import com.artipie.http.rq.RequestLine;
import com.artipie.http.rs.RsStatus;
import org.cqfn.rio.Buffers;
import org.cqfn.rio.WriteGreed;
import org.cqfn.rio.stream.ReactiveInputStream;
import org.cqfn.rio.stream.ReactiveOutputStream;
import org.reactivestreams.Publisher;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.zip.GZIPOutputStream;

/**
 * Slice that gzips requested content.
 * @since 1.1
 */
final class GzipSlice implements Slice {

    /**
     * Origin.
     */
    private final Slice origin;

    /**
     * Ctor.
     * @param origin Origin slice
     */
    GzipSlice(final Slice origin) {
        this.origin = origin;
    }

    @Override
    public Response response(final RequestLine line, final Headers headers,
                             final Content body) {
        return connection -> this.origin.response(line, headers, body).send(
            (status, rsheaders, rsbody) -> GzipSlice.gzip(connection, status, rsbody, rsheaders)
        );
    }

    /**
     * Gzip origin response publisher and pass it to connection along with status and headers.
     * @param connection Connection
     * @param stat Response status
     * @param body Origin response body
     * @param headers Origin response headers
     * @return Completable action
     */
    @SuppressWarnings("PMD.CloseResource")
    private static CompletionStage gzip(final Connection connection, final RsStatus stat,
        final Publisher body, final Headers headers) {
        final CompletionStage future;
        final CompletableFuture tmp;
        try (PipedOutputStream resout = new PipedOutputStream();
            PipedInputStream oinput = new PipedInputStream();
            PipedOutputStream tmpout = new PipedOutputStream(oinput)
        ) {
            tmp = CompletableFuture.allOf().thenCompose(
                nothing -> new ReactiveOutputStream(tmpout).write(body, WriteGreed.SYSTEM)
            );
            final PipedInputStream src = new PipedInputStream(resout);
            future = tmp.thenCompose(
                nothing -> connection.accept(
                    stat, headers.copy().add("Content-encoding", "gzip"),
                    new Content.From(new ReactiveInputStream(src).read(Buffers.Standard.K8))
                )
            );
            try (GZIPOutputStream gzos = new GZIPOutputStream(resout)) {
                final byte[] buffer = new byte[1024 * 8];
                while (true) {
                    final int length = oinput.read(buffer);
                    if (length < 0) {
                        break;
                    }
                    gzos.write(buffer, 0, length);
                }
                gzos.finish();
            } catch (final IOException err) {
                throw new ArtipieIOException(err);
            }
        } catch (final IOException err) {
            throw new ArtipieIOException(err);
        }
        return future;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy