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

io.datakernel.http.stream.BufsConsumerGzipDeflater Maven / Gradle / Ivy

/*
 * Copyright (C) 2015-2018 SoftIndex LLC.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.datakernel.http.stream;

import io.datakernel.bytebuf.ByteBuf;
import io.datakernel.bytebuf.ByteBufPool;
import io.datakernel.bytebuf.ByteBufQueue;
import io.datakernel.csp.*;
import io.datakernel.csp.dsl.WithChannelTransformer;

import java.util.zip.CRC32;
import java.util.zip.Deflater;

import static io.datakernel.util.Preconditions.checkArgument;
import static io.datakernel.util.Preconditions.checkState;

/**
 * This is a binary channel transformer, that converts channels of {@link ByteBuf ByteBufs}
 * compressing the data using the DEFALTE algorithm with standard implementation from the java.util.zip package.
 * 

* It is used in HTTP when {@link io.datakernel.http.HttpMessage#setBodyGzipCompression HttpMessage#setBodyGzipCompression} * method is used. */ public final class BufsConsumerGzipDeflater extends AbstractCommunicatingProcess implements WithChannelTransformer { public static final int DEFAULT_MAX_BUF_SIZE = 512; // rfc 1952 section 2.3.1 private static final byte[] GZIP_HEADER = {(byte) 0x1f, (byte) 0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0}; private static final int GZIP_FOOTER_SIZE = 8; private final CRC32 crc32 = new CRC32(); private Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true); private int maxBufSize = DEFAULT_MAX_BUF_SIZE; private ChannelSupplier input; private ChannelConsumer output; // region creators private BufsConsumerGzipDeflater() { } public static BufsConsumerGzipDeflater create() { return new BufsConsumerGzipDeflater(); } public BufsConsumerGzipDeflater withDeflater(Deflater deflater) { checkArgument(deflater != null, "Cannot use null Deflater"); this.deflater = deflater; return this; } public BufsConsumerGzipDeflater withMaxBufSize(int maxBufSize) { checkArgument(maxBufSize > 0, "Cannot use buf size that is less than 0"); this.maxBufSize = maxBufSize; return this; } @SuppressWarnings("ConstantConditions") //check input for clarity @Override public ChannelInput getInput() { return input -> { checkState(this.input == null, "Input already set"); this.input = sanitize(input); if (this.input != null && this.output != null) startProcess(); return getProcessCompletion(); }; } @SuppressWarnings("ConstantConditions") //check output for clarity @Override public ChannelOutput getOutput() { return output -> { checkState(this.output == null, "Output already set"); this.output = sanitize(output); if (this.input != null && this.output != null) startProcess(); }; } // endregion @Override protected void beforeProcess() { checkState(input != null, "Input was not set"); checkState(output != null, "Output was not set"); } @Override protected void doProcess() { writeHeader(); } private void writeHeader() { output.accept(ByteBuf.wrapForReading(GZIP_HEADER)) .whenResult($ -> writeBody()); } private void writeBody() { input.get() .whenComplete((buf, e) -> { if (buf != null) { if (buf.canRead()) { crc32.update(buf.array(), buf.head(), buf.readRemaining()); deflater.setInput(buf.array(), buf.head(), buf.readRemaining()); buf.recycle(); ByteBufQueue queue = deflate(); output.acceptAll(queue.asIterator()) .whenResult($ -> writeBody()); } else { buf.recycle(); } } else { writeFooter(); } }); } private void writeFooter() { deflater.finish(); ByteBufQueue queue = deflate(); ByteBuf footer = ByteBufPool.allocate(GZIP_FOOTER_SIZE); footer.writeInt(Integer.reverseBytes((int) crc32.getValue())); footer.writeInt(Integer.reverseBytes(deflater.getTotalIn())); queue.add(footer); output.acceptAll(queue.asIterator()) .then($ -> output.accept(null)) .whenResult($ -> completeProcess()); } private ByteBufQueue deflate() { ByteBufQueue queue = new ByteBufQueue(); while (true) { ByteBuf out = ByteBufPool.allocate(maxBufSize); int len = deflater.deflate(out.array(), out.tail(), out.writeRemaining()); if (len > 0) { out.tail(len); queue.add(out); } else { out.recycle(); return queue; } } } @Override protected void doClose(Throwable e) { deflater.end(); input.close(e); output.close(e); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy