1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http.websocketx.extensions.compression;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.CompositeByteBuf;
20 import io.netty.channel.ChannelHandler;
21 import io.netty.channel.ChannelHandlerContext;
22 import io.netty.channel.embedded.EmbeddedChannel;
23 import io.netty.handler.codec.CodecException;
24 import io.netty.handler.codec.compression.ZlibCodecFactory;
25 import io.netty.handler.codec.compression.ZlibWrapper;
26 import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
27 import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
28 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
29 import io.netty.handler.codec.http.websocketx.WebSocketFrame;
30 import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionEncoder;
31 import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter;
32
33 import java.util.List;
34
35 import static io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateDecoder.*;
36 import static io.netty.util.internal.ObjectUtil.*;
37
38
39
40
41
42 abstract class DeflateEncoder extends WebSocketExtensionEncoder {
43
44 static final int DEFAULT_MEM_LEVEL = 8;
45
46 private final int compressionLevel;
47 private final int windowSize;
48 private final int memLevel;
49 private final boolean noContext;
50 private final WebSocketExtensionFilter extensionEncoderFilter;
51
52 private EmbeddedChannel encoder;
53
54
55
56
57
58
59
60
61 DeflateEncoder(int compressionLevel, int windowSize, boolean noContext,
62 WebSocketExtensionFilter extensionEncoderFilter) {
63 this(compressionLevel, windowSize, DEFAULT_MEM_LEVEL, noContext, extensionEncoderFilter);
64 }
65
66
67
68
69
70
71
72
73
74 DeflateEncoder(int compressionLevel, int windowSize, int memLevel, boolean noContext,
75 WebSocketExtensionFilter extensionEncoderFilter) {
76 this.compressionLevel = compressionLevel;
77 this.windowSize = windowSize;
78 this.memLevel = memLevel;
79 this.noContext = noContext;
80 this.extensionEncoderFilter = checkNotNull(extensionEncoderFilter, "extensionEncoderFilter");
81 }
82
83
84
85
86 protected WebSocketExtensionFilter extensionEncoderFilter() {
87 return extensionEncoderFilter;
88 }
89
90
91
92
93
94 protected abstract int rsv(WebSocketFrame msg);
95
96
97
98
99
100 protected abstract boolean removeFrameTail(WebSocketFrame msg);
101
102 @Override
103 protected void encode(ChannelHandlerContext ctx, WebSocketFrame msg, List<Object> out) throws Exception {
104 final ByteBuf compressedContent;
105 if (msg.content().isReadable()) {
106 compressedContent = compressContent(ctx, msg);
107 } else if (msg.isFinalFragment()) {
108
109
110 compressedContent = EMPTY_DEFLATE_BLOCK.duplicate();
111 if (noContext) {
112
113
114 cleanup();
115 }
116 } else {
117 throw new CodecException("cannot compress content buffer");
118 }
119
120 final WebSocketFrame outMsg;
121 if (msg instanceof TextWebSocketFrame) {
122 outMsg = new TextWebSocketFrame(msg.isFinalFragment(), rsv(msg), compressedContent);
123 } else if (msg instanceof BinaryWebSocketFrame) {
124 outMsg = new BinaryWebSocketFrame(msg.isFinalFragment(), rsv(msg), compressedContent);
125 } else if (msg instanceof ContinuationWebSocketFrame) {
126 outMsg = new ContinuationWebSocketFrame(msg.isFinalFragment(), rsv(msg), compressedContent);
127 } else {
128 throw new CodecException("unexpected frame type: " + msg.getClass().getName());
129 }
130
131 out.add(outMsg);
132 }
133
134 @Override
135 public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
136 cleanup();
137 super.handlerRemoved(ctx);
138 }
139
140 private ByteBuf compressContent(ChannelHandlerContext ctx, WebSocketFrame msg) {
141 if (encoder == null) {
142 encoder = EmbeddedChannel.builder()
143 .handlers(ZlibCodecFactory.newZlibEncoder(
144 ZlibWrapper.NONE, compressionLevel, windowSize, memLevel))
145 .build();
146 }
147
148 encoder.writeOutbound(msg.content().retain());
149
150 CompositeByteBuf fullCompressedContent = ctx.alloc().compositeBuffer();
151 for (;;) {
152 ByteBuf partCompressedContent = encoder.readOutbound();
153 if (partCompressedContent == null) {
154 break;
155 }
156 if (!partCompressedContent.isReadable()) {
157 partCompressedContent.release();
158 continue;
159 }
160 fullCompressedContent.addComponent(true, partCompressedContent);
161 }
162
163 if (fullCompressedContent.numComponents() <= 0) {
164 fullCompressedContent.release();
165 throw new CodecException("cannot read compressed buffer");
166 }
167
168 if (msg.isFinalFragment() && noContext) {
169 cleanup();
170 }
171
172 ByteBuf compressedContent;
173 if (removeFrameTail(msg)) {
174 int realLength = fullCompressedContent.readableBytes() - FRAME_TAIL.readableBytes();
175 compressedContent = fullCompressedContent.slice(0, realLength);
176 } else {
177 compressedContent = fullCompressedContent;
178 }
179
180 return compressedContent;
181 }
182
183 private void cleanup() {
184 if (encoder != null) {
185
186 encoder.finishAndReleaseAll();
187 encoder = null;
188 }
189 }
190 }