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
27 class SpdyHeaderBlockJZlibEncoder extends SpdyHeaderBlockRawEncoder {
28
29 private final Deflater z = new Deflater();
30
31 private boolean finished;
32
33 SpdyHeaderBlockJZlibEncoder(
34 SpdyVersion version, int compressionLevel, int windowBits, int memLevel) {
35 super(version);
36 if (compressionLevel < 0 || compressionLevel > 9) {
37 throw new IllegalArgumentException(
38 "compressionLevel: " + compressionLevel + " (expected: 0-9)");
39 }
40 if (windowBits < 9 || windowBits > 15) {
41 throw new IllegalArgumentException(
42 "windowBits: " + windowBits + " (expected: 9-15)");
43 }
44 if (memLevel < 1 || memLevel > 9) {
45 throw new IllegalArgumentException(
46 "memLevel: " + memLevel + " (expected: 1-9)");
47 }
48
49 int resultCode = z.deflateInit(
50 compressionLevel, windowBits, memLevel, JZlib.W_ZLIB);
51 if (resultCode != JZlib.Z_OK) {
52 throw new CompressionException(
53 "failed to initialize an SPDY header block deflater: " + resultCode);
54 } else {
55 resultCode = z.deflateSetDictionary(SPDY_DICT, SPDY_DICT.length);
56 if (resultCode != JZlib.Z_OK) {
57 throw new CompressionException(
58 "failed to set the SPDY dictionary: " + resultCode);
59 }
60 }
61 }
62
63 private void setInput(ByteBuf decompressed) {
64 int len = decompressed.readableBytes();
65
66 byte[] in;
67 int offset;
68 if (decompressed.hasArray()) {
69 in = decompressed.array();
70 offset = decompressed.arrayOffset() + decompressed.readerIndex();
71 } else {
72 in = new byte[len];
73 decompressed.getBytes(decompressed.readerIndex(), in);
74 offset = 0;
75 }
76 z.next_in = in;
77 z.next_in_index = offset;
78 z.avail_in = len;
79 }
80
81 private ByteBuf encode(ByteBufAllocator alloc) {
82 boolean release = true;
83 ByteBuf out = null;
84 try {
85 int oldNextInIndex = z.next_in_index;
86 int oldNextOutIndex = z.next_out_index;
87
88 int maxOutputLength = (int) Math.ceil(z.next_in.length * 1.001) + 12;
89 out = alloc.heapBuffer(maxOutputLength);
90 z.next_out = out.array();
91 z.next_out_index = out.arrayOffset() + out.writerIndex();
92 z.avail_out = maxOutputLength;
93
94 int resultCode;
95 try {
96 resultCode = z.deflate(JZlib.Z_SYNC_FLUSH);
97 } finally {
98 out.skipBytes(z.next_in_index - oldNextInIndex);
99 }
100 if (resultCode != JZlib.Z_OK) {
101 throw new CompressionException("compression failure: " + resultCode);
102 }
103
104 int outputLength = z.next_out_index - oldNextOutIndex;
105 if (outputLength > 0) {
106 out.writerIndex(out.writerIndex() + outputLength);
107 }
108 release = false;
109 return out;
110 } finally {
111
112
113
114
115 z.next_in = null;
116 z.next_out = null;
117 if (release && out != null) {
118 out.release();
119 }
120 }
121 }
122
123 @Override
124 public ByteBuf encode(ByteBufAllocator alloc, SpdyHeadersFrame frame) throws Exception {
125 if (frame == null) {
126 throw new IllegalArgumentException("frame");
127 }
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 }