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.WebSocketExtensionDecoder;
31 import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilter;
32
33 import java.util.List;
34
35 import static io.netty.util.internal.ObjectUtil.*;
36
37
38
39
40
41 abstract class DeflateDecoder extends WebSocketExtensionDecoder {
42
43 static final ByteBuf FRAME_TAIL = Unpooled.unreleasableBuffer(
44 Unpooled.wrappedBuffer(new byte[] {0x00, 0x00, (byte) 0xff, (byte) 0xff}))
45 .asReadOnly();
46
47 static final ByteBuf EMPTY_DEFLATE_BLOCK = Unpooled.unreleasableBuffer(
48 Unpooled.wrappedBuffer(new byte[] { 0x00 }))
49 .asReadOnly();
50
51 private final boolean noContext;
52 private final WebSocketExtensionFilter extensionDecoderFilter;
53 private final int maxAllocation;
54
55 private EmbeddedChannel decoder;
56
57
58
59
60
61
62
63 DeflateDecoder(boolean noContext, WebSocketExtensionFilter extensionDecoderFilter, int maxAllocation) {
64 this.noContext = noContext;
65 this.extensionDecoderFilter = checkNotNull(extensionDecoderFilter, "extensionDecoderFilter");
66 this.maxAllocation = maxAllocation;
67 }
68
69
70
71
72 protected WebSocketExtensionFilter extensionDecoderFilter() {
73 return extensionDecoderFilter;
74 }
75
76 protected abstract boolean appendFrameTail(WebSocketFrame msg);
77
78 protected abstract int newRsv(WebSocketFrame msg);
79
80 @Override
81 protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg, List<Object> out) throws Exception {
82 final ByteBuf decompressedContent = decompressContent(ctx, msg);
83
84 final WebSocketFrame outMsg;
85 if (msg instanceof TextWebSocketFrame) {
86 outMsg = new TextWebSocketFrame(msg.isFinalFragment(), newRsv(msg), decompressedContent);
87 } else if (msg instanceof BinaryWebSocketFrame) {
88 outMsg = new BinaryWebSocketFrame(msg.isFinalFragment(), newRsv(msg), decompressedContent);
89 } else if (msg instanceof ContinuationWebSocketFrame) {
90 outMsg = new ContinuationWebSocketFrame(msg.isFinalFragment(), newRsv(msg), decompressedContent);
91 } else {
92 throw new CodecException("unexpected frame type: " + msg.getClass().getName());
93 }
94
95 out.add(outMsg);
96 }
97
98 @Override
99 public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
100 cleanup();
101 super.handlerRemoved(ctx);
102 }
103
104 @Override
105 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
106 cleanup();
107 super.channelInactive(ctx);
108 }
109
110 private ByteBuf decompressContent(ChannelHandlerContext ctx, WebSocketFrame msg) {
111 if (decoder == null) {
112 if (!(msg instanceof TextWebSocketFrame) && !(msg instanceof BinaryWebSocketFrame)) {
113 throw new CodecException("unexpected initial frame type: " + msg.getClass().getName());
114 }
115 decoder = new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.NONE, maxAllocation));
116 }
117
118 boolean readable = msg.content().isReadable();
119 boolean emptyDeflateBlock = EMPTY_DEFLATE_BLOCK.equals(msg.content());
120
121 decoder.writeInbound(msg.content().retain());
122 if (appendFrameTail(msg)) {
123 decoder.writeInbound(FRAME_TAIL.duplicate());
124 }
125
126 CompositeByteBuf compositeDecompressedContent = ctx.alloc().compositeBuffer();
127 for (;;) {
128 ByteBuf partUncompressedContent = decoder.readInbound();
129 if (partUncompressedContent == null) {
130 break;
131 }
132 if (!partUncompressedContent.isReadable()) {
133 partUncompressedContent.release();
134 continue;
135 }
136 compositeDecompressedContent.addComponent(true, partUncompressedContent);
137 }
138
139
140 if (!emptyDeflateBlock && readable && compositeDecompressedContent.numComponents() <= 0) {
141
142
143 if (!(msg instanceof ContinuationWebSocketFrame)) {
144 compositeDecompressedContent.release();
145 throw new CodecException("cannot read uncompressed buffer");
146 }
147 }
148
149 if (msg.isFinalFragment() && noContext) {
150 cleanup();
151 }
152
153 return compositeDecompressedContent;
154 }
155
156 private void cleanup() {
157 if (decoder != null) {
158
159 decoder.finishAndReleaseAll();
160 decoder = null;
161 }
162 }
163 }