1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.spdy;
17
18 import com.jcraft.jzlib.Deflater;
19 import com.jcraft.jzlib.JZlib;
20 import io.netty.buffer.ByteBuf;
21 import io.netty.buffer.ByteBufAllocator;
22 import io.netty.buffer.Unpooled;
23 import io.netty.handler.codec.compression.CompressionException;
24
25 import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
26 import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE;
27
28 class SpdyHeaderBlockJZlibEncoder extends SpdyHeaderBlockRawEncoder {
29
30 private final Deflater z = new Deflater();
31
32 private boolean finished;
33
34 SpdyHeaderBlockJZlibEncoder(
35 SpdyVersion version, int compressionLevel, int windowBits, int memLevel) {
36 super(version);
37 if (compressionLevel < 0 || compressionLevel > 9) {
38 throw new IllegalArgumentException(
39 "compressionLevel: " + compressionLevel + " (expected: 0-9)");
40 }
41 if (windowBits < 9 || windowBits > 15) {
42 throw new IllegalArgumentException(
43 "windowBits: " + windowBits + " (expected: 9-15)");
44 }
45 if (memLevel < 1 || memLevel > 9) {
46 throw new IllegalArgumentException(
47 "memLevel: " + memLevel + " (expected: 1-9)");
48 }
49
50 int resultCode = z.deflateInit(
51 compressionLevel, windowBits, memLevel, JZlib.W_ZLIB);
52 if (resultCode != JZlib.Z_OK) {
53 throw new CompressionException(
54 "failed to initialize an SPDY header block deflater: " + resultCode);
55 } else {
56 resultCode = z.deflateSetDictionary(SPDY_DICT, SPDY_DICT.length);
57 if (resultCode != JZlib.Z_OK) {
58 throw new CompressionException(
59 "failed to set the SPDY dictionary: " + resultCode);
60 }
61 }
62 }
63
64 private void setInput(ByteBuf decompressed) {
65 int len = decompressed.readableBytes();
66
67 byte[] in;
68 int offset;
69 if (decompressed.hasArray()) {
70 in = decompressed.array();
71 offset = decompressed.arrayOffset() + decompressed.readerIndex();
72 } else {
73 in = new byte[len];
74 decompressed.getBytes(decompressed.readerIndex(), in);
75 offset = 0;
76 }
77 z.next_in = in;
78 z.next_in_index = offset;
79 z.avail_in = len;
80 }
81
82 private ByteBuf encode(ByteBufAllocator alloc) {
83 boolean release = true;
84 ByteBuf out = null;
85 try {
86 int oldNextInIndex = z.next_in_index;
87 int oldNextOutIndex = z.next_out_index;
88
89 int maxOutputLength = (int) Math.ceil(z.next_in.length * 1.001) + 12;
90 out = alloc.heapBuffer(maxOutputLength);
91 z.next_out = out.array();
92 z.next_out_index = out.arrayOffset() + out.writerIndex();
93 z.avail_out = maxOutputLength;
94
95 int resultCode;
96 try {
97 resultCode = z.deflate(JZlib.Z_SYNC_FLUSH);
98 } finally {
99 out.skipBytes(z.next_in_index - oldNextInIndex);
100 }
101 if (resultCode != JZlib.Z_OK) {
102 throw new CompressionException("compression failure: " + resultCode);
103 }
104
105 int outputLength = z.next_out_index - oldNextOutIndex;
106 if (outputLength > 0) {
107 out.writerIndex(out.writerIndex() + outputLength);
108 }
109 release = false;
110 return out;
111 } finally {
112
113
114
115
116 z.next_in = null;
117 z.next_out = null;
118 if (release && out != null) {
119 out.release();
120 }
121 }
122 }
123
124 @Override
125 public ByteBuf encode(ByteBufAllocator alloc, SpdyHeadersFrame frame) throws Exception {
126 checkNotNullWithIAE(alloc, "alloc");
127 checkNotNullWithIAE(frame, "frame");
128
129 if (finished) {
130 return Unpooled.EMPTY_BUFFER;
131 }
132
133 ByteBuf decompressed = super.encode(alloc, frame);
134 try {
135 if (!decompressed.isReadable()) {
136 return Unpooled.EMPTY_BUFFER;
137 }
138
139 setInput(decompressed);
140 return encode(alloc);
141 } finally {
142 decompressed.release();
143 }
144 }
145
146 @Override
147 public void end() {
148 if (finished) {
149 return;
150 }
151 finished = true;
152 z.deflateEnd();
153 z.next_in = null;
154 z.next_out = null;
155 }
156 }