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