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