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

com.fireflysource.net.websocket.common.extension.compress.PerMessageDeflateExtension Maven / Gradle / Ivy

There is a newer version: 5.0.2
Show newest version
package com.fireflysource.net.websocket.common.extension.compress;

import com.fireflysource.common.slf4j.LazyLogger;
import com.fireflysource.common.sys.Result;
import com.fireflysource.common.sys.SystemLogger;
import com.fireflysource.net.websocket.common.exception.BadPayloadException;
import com.fireflysource.net.websocket.common.exception.ProtocolException;
import com.fireflysource.net.websocket.common.frame.Frame;
import com.fireflysource.net.websocket.common.model.ExtensionConfig;
import com.fireflysource.net.websocket.common.model.OpCode;

import java.nio.ByteBuffer;
import java.util.function.Consumer;
import java.util.zip.DataFormatException;

/**
 * Per Message Deflate Compression extension for WebSocket.
 * 

* Attempts to follow Compression Extensions for WebSocket */ public class PerMessageDeflateExtension extends CompressExtension { private static LazyLogger LOG = SystemLogger.create(PerMessageDeflateExtension.class); private ExtensionConfig configRequested; private ExtensionConfig configNegotiated; private boolean incomingContextTakeover = true; private boolean outgoingContextTakeover = true; private boolean incomingCompressed; @Override public String getName() { return "permessage-deflate"; } @Override public void incomingFrame(Frame frame) { // Incoming frames are always non concurrent because // they are read and parsed with a single thread, and // therefore there is no need for synchronization. // This extension requires the RSV1 bit set only in the first frame. // Subsequent continuation frames don't have RSV1 set, but are compressed. if (frame.getType().isData()) { incomingCompressed = frame.isRsv1(); } if (OpCode.isControlFrame(frame.getOpCode()) || !incomingCompressed) { nextIncomingFrame(frame); return; } if (frame.getOpCode() == OpCode.CONTINUATION && frame.isRsv1()) { // Per RFC7692 we MUST Fail the websocket connection throw new ProtocolException("Invalid RSV1 set on permessage-deflate CONTINUATION frame"); } ByteAccumulator accumulator = newByteAccumulator(); try { ByteBuffer payload = frame.getPayload(); decompress(accumulator, payload); if (frame.isFin()) { decompress(accumulator, TAIL_BYTES_BUF.slice()); } forwardIncoming(frame, accumulator); } catch (DataFormatException e) { throw new BadPayloadException(e); } if (frame.isFin()) incomingCompressed = false; } @Override protected void nextIncomingFrame(Frame frame) { if (frame.isFin() && !incomingContextTakeover) { LOG.debug("Incoming Context Reset"); decompressCount.set(0); getInflater().reset(); } super.nextIncomingFrame(frame); } @Override protected void nextOutgoingFrame(Frame frame, Consumer> result) { if (frame.isFin() && !outgoingContextTakeover) { LOG.debug("Outgoing Context Reset"); getDeflater().reset(); } super.nextOutgoingFrame(frame, result); } @Override int getRsvUseMode() { return RSV_USE_ONLY_FIRST; } @Override int getTailDropMode() { return TAIL_DROP_FIN_ONLY; } @Override public void setConfig(final ExtensionConfig config) { configRequested = new ExtensionConfig(config); configNegotiated = new ExtensionConfig(config.getName()); for (String key : config.getParameterKeys()) { key = key.trim(); switch (key) { case "client_max_window_bits": case "server_max_window_bits": { // Don't negotiate these parameters break; } case "client_no_context_takeover": { configNegotiated.setParameter("client_no_context_takeover"); switch (getPolicy().getBehavior()) { case CLIENT: incomingContextTakeover = false; break; case SERVER: outgoingContextTakeover = false; break; } break; } case "server_no_context_takeover": { configNegotiated.setParameter("server_no_context_takeover"); switch (getPolicy().getBehavior()) { case CLIENT: outgoingContextTakeover = false; break; case SERVER: incomingContextTakeover = false; break; } break; } default: { throw new IllegalArgumentException(); } } } LOG.debug("config: outgoingContextTakeover={}, incomingContextTakeover={} : {}", outgoingContextTakeover, incomingContextTakeover, this); super.setConfig(configNegotiated); } @Override public String toString() { return String.format("%s[requested=\"%s\", negotiated=\"%s\"]", getClass().getSimpleName(), configRequested.getParameterizedName(), configNegotiated.getParameterizedName()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy