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