SPDY: use jdk 1.7 java.util.zip instead of jzlib

This commit is contained in:
Jeff Pinner 2012-02-29 12:38:52 -08:00 committed by Trustin Lee
parent 9e8f8ac08c
commit 5aae8279b9
6 changed files with 367 additions and 35 deletions

View File

@ -16,6 +16,7 @@
package io.netty.handler.codec.spdy;
import io.netty.buffer.ChannelBuffer;
import io.netty.buffer.ChannelBuffers;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.compression.ZlibDecoder;
@ -33,8 +34,7 @@ public class SpdyFrameDecoder extends FrameDecoder {
private final int maxFrameSize;
private final int maxHeaderSize;
private final DecoderEmbedder<ChannelBuffer> headerBlockDecompressor =
new DecoderEmbedder<ChannelBuffer>(new ZlibDecoder(SPDY_DICT));
private final SpdyZlibDecoder headerBlockDecompressor = new SpdyZlibDecoder();
/**
* Creates a new instance with the default {@code maxChunkSize (8192)},
@ -114,7 +114,9 @@ public class SpdyFrameDecoder extends FrameDecoder {
int type = getUnsignedShort(buffer, typeOffset);
buffer.skipBytes(SPDY_HEADER_SIZE);
return decodeControlFrame(type, flags, buffer.readBytes(dataLength));
int readerIndex = buffer.readerIndex();
buffer.skipBytes(dataLength);
return decodeControlFrame(type, flags, buffer.slice(readerIndex, dataLength));
} else {
// Decode data frame common header
int streamID = getUnsignedInt(buffer, frameOffset);
@ -166,7 +168,7 @@ public class SpdyFrameDecoder extends FrameDecoder {
spdySynStreamFrame.setLast(last);
spdySynStreamFrame.setUnidirectional(unid);
decodeHeaderBlock(spdySynStreamFrame, decompress(data));
decodeHeaderBlock(spdySynStreamFrame, data);
return spdySynStreamFrame;
@ -184,7 +186,7 @@ public class SpdyFrameDecoder extends FrameDecoder {
last = (flags & SPDY_FLAG_FIN) != 0;
spdySynReplyFrame.setLast(last);
decodeHeaderBlock(spdySynReplyFrame, decompress(data));
decodeHeaderBlock(spdySynReplyFrame, data);
return spdySynReplyFrame;
@ -284,7 +286,7 @@ public class SpdyFrameDecoder extends FrameDecoder {
SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamID);
decodeHeaderBlock(spdyHeadersFrame, decompress(data));
decodeHeaderBlock(spdyHeadersFrame, data);
return spdyHeadersFrame;
@ -296,31 +298,38 @@ public class SpdyFrameDecoder extends FrameDecoder {
}
}
private ChannelBuffer decompress(ChannelBuffer compressed) throws Exception {
if ((compressed.readableBytes() == 2) &&
(compressed.getShort(compressed.readerIndex()) == 0)) {
return compressed;
private boolean ensureBytes(ChannelBuffer decompressed, int bytes) throws Exception {
if (decompressed.readableBytes() >= bytes) {
return true;
}
headerBlockDecompressor.offer(compressed);
return headerBlockDecompressor.poll();
decompressed.discardReadBytes();
headerBlockDecompressor.decode(decompressed);
return decompressed.readableBytes() >= bytes;
}
private void decodeHeaderBlock(SpdyHeaderBlock headerFrame, ChannelBuffer headerBlock)
throws Exception {
if (headerBlock.readableBytes() < 2) {
if ((headerBlock.readableBytes() == 2) &&
(headerBlock.getShort(headerBlock.readerIndex()) == 0)) {
return;
}
headerBlockDecompressor.setInput(headerBlock);
ChannelBuffer decompressed = ChannelBuffers.dynamicBuffer(8192);
headerBlockDecompressor.decode(decompressed);
if (decompressed.readableBytes() < 2) {
throw new SpdyProtocolException(
"Received invalid header block");
}
int headerSize = 0;
int numEntries = getUnsignedShort(headerBlock, headerBlock.readerIndex());
headerBlock.skipBytes(2);
int numEntries = decompressed.readUnsignedShort();
for (int i = 0; i < numEntries; i ++) {
if (headerBlock.readableBytes() < 2) {
if (!ensureBytes(decompressed, 2)) {
throw new SpdyProtocolException(
"Received invalid header block");
}
int nameLength = getUnsignedShort(headerBlock, headerBlock.readerIndex());
headerBlock.skipBytes(2);
int nameLength = decompressed.readUnsignedShort();
if (nameLength == 0) {
headerFrame.setInvalid();
return;
@ -330,23 +339,22 @@ public class SpdyFrameDecoder extends FrameDecoder {
throw new SpdyProtocolException(
"Header block exceeds " + maxHeaderSize);
}
if (headerBlock.readableBytes() < nameLength) {
if (!ensureBytes(decompressed, nameLength)) {
throw new SpdyProtocolException(
"Received invalid header block");
}
byte[] nameBytes = new byte[nameLength];
headerBlock.readBytes(nameBytes);
decompressed.readBytes(nameBytes);
String name = new String(nameBytes, "UTF-8");
if (headerFrame.containsHeader(name)) {
throw new SpdyProtocolException(
"Received duplicate header name: " + name);
}
if (headerBlock.readableBytes() < 2) {
if (!ensureBytes(decompressed, 2)) {
throw new SpdyProtocolException(
"Received invalid header block");
}
int valueLength = getUnsignedShort(headerBlock, headerBlock.readerIndex());
headerBlock.skipBytes(2);
int valueLength = decompressed.readUnsignedShort();
if (valueLength == 0) {
headerFrame.setInvalid();
return;
@ -356,15 +364,15 @@ public class SpdyFrameDecoder extends FrameDecoder {
throw new SpdyProtocolException(
"Header block exceeds " + maxHeaderSize);
}
if (headerBlock.readableBytes() < valueLength) {
if (!ensureBytes(decompressed, valueLength)) {
throw new SpdyProtocolException(
"Received invalid header block");
}
byte[] valueBytes = new byte[valueLength];
headerBlock.readBytes(valueBytes);
decompressed.readBytes(valueBytes);
int index = 0;
int offset = 0;
while (index < valueBytes.length) {
while (index < valueLength) {
while (index < valueBytes.length && valueBytes[index] != (byte) 0) {
index ++;
}

View File

@ -15,25 +15,26 @@
*/
package io.netty.handler.codec.spdy;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
import java.nio.ByteOrder;
import java.util.Set;
import io.netty.buffer.ChannelBuffer;
import io.netty.buffer.ChannelBuffers;
import io.netty.channel.Channel;
import io.netty.channel.ChannelEvent;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.compression.ZlibEncoder;
import io.netty.handler.codec.embedder.EncoderEmbedder;
import io.netty.channel.ChannelStateEvent;
import io.netty.handler.codec.oneone.OneToOneEncoder;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
/**
* Encodes a SPDY Data or Control Frame into a {@link ChannelBuffer}.
*/
public class SpdyFrameEncoder extends OneToOneEncoder {
private final EncoderEmbedder<ChannelBuffer> headerBlockCompressor;
private volatile boolean finished;
private final SpdyZlibEncoder headerBlockCompressor;
/**
* Creates a new instance with the default {@code compressionLevel (6)},
@ -48,8 +49,27 @@ public class SpdyFrameEncoder extends OneToOneEncoder {
*/
public SpdyFrameEncoder(int compressionLevel, int windowBits, int memLevel) {
super();
headerBlockCompressor = new EncoderEmbedder<ChannelBuffer>(
new ZlibEncoder(compressionLevel, windowBits, memLevel, SPDY_DICT));
headerBlockCompressor = new SpdyZlibEncoder(compressionLevel);
}
@Override
public void handleDownstream(
ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
if (evt instanceof ChannelStateEvent) {
ChannelStateEvent e = (ChannelStateEvent) evt;
switch (e.getState()) {
case OPEN:
case CONNECTED:
case BOUND:
if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) {
synchronized (headerBlockCompressor) {
finished = true;
headerBlockCompressor.end();
}
}
}
}
super.handleDownstream(ctx, evt);
}
@Override
@ -263,7 +283,13 @@ public class SpdyFrameEncoder extends OneToOneEncoder {
if (uncompressed.readableBytes() == 0) {
return ChannelBuffers.EMPTY_BUFFER;
}
headerBlockCompressor.offer(uncompressed);
return headerBlockCompressor.poll();
ChannelBuffer compressed = ChannelBuffers.dynamicBuffer();
synchronized (headerBlockCompressor) {
if (!finished) {
headerBlockCompressor.setInput(uncompressed);
headerBlockCompressor.encode(compressed);
}
}
return compressed;
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/*
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.netty.handler.codec.spdy;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
import io.netty.buffer.ChannelBuffer;
import io.netty.handler.codec.compression.CompressionException;
import io.netty.util.internal.jzlib.JZlib;
import io.netty.util.internal.jzlib.ZStream;
class SpdyZlibDecoder {
private final byte[] out = new byte[8192];
private final ZStream z = new ZStream();
public SpdyZlibDecoder() {
int resultCode;
resultCode = z.inflateInit(JZlib.W_ZLIB);
if (resultCode != JZlib.Z_OK) {
throw new CompressionException("ZStream initialization failure");
}
z.next_out = out;
}
public void setInput(ChannelBuffer compressed) {
byte[] in = new byte[compressed.readableBytes()];
compressed.readBytes(in);
z.next_in = in;
z.next_in_index = 0;
z.avail_in = in.length;
}
public void decode(ChannelBuffer decompressed) {
z.next_out_index = 0;
z.avail_out = out.length;
int resultCode = z.inflate(JZlib.Z_SYNC_FLUSH);
if (resultCode == JZlib.Z_NEED_DICT) {
resultCode = z.inflateSetDictionary(SPDY_DICT, SPDY_DICT.length);
if (resultCode != JZlib.Z_OK) {
throw new CompressionException("ZStream dictionary failure");
}
z.inflate(JZlib.Z_SYNC_FLUSH);
}
if (z.next_out_index > 0) {
decompressed.writeBytes(out, 0, z.next_out_index);
}
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/*
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.netty.handler.codec.spdy;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
import java.util.zip.Deflater;
import io.netty.buffer.ChannelBuffer;
class SpdyZlibEncoder {
private final byte[] out = new byte[8192];
private final Deflater compressor;
public SpdyZlibEncoder(int compressionLevel) {
if (compressionLevel < 0 || compressionLevel > 9) {
throw new IllegalArgumentException(
"compressionLevel: " + compressionLevel + " (expected: 0-9)");
}
compressor = new Deflater(compressionLevel);
compressor.setDictionary(SPDY_DICT);
}
public void setInput(ChannelBuffer decompressed) {
byte[] in = new byte[decompressed.readableBytes()];
decompressed.readBytes(in);
compressor.setInput(in);
}
public void encode(ChannelBuffer compressed) {
while (!compressor.needsInput()) {
int numBytes = compressor.deflate(out, 0, out.length, Deflater.SYNC_FLUSH);
compressed.writeBytes(out, 0, numBytes);
}
}
public void end() {
compressor.end();
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/*
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.netty.handler.codec.spdy;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.handler.codec.compression.CompressionException;
import org.jboss.netty.util.internal.jzlib.JZlib;
import org.jboss.netty.util.internal.jzlib.ZStream;
import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
class SpdyZlibDecoder {
private final byte[] out = new byte[8192];
private final ZStream z = new ZStream();
public SpdyZlibDecoder() {
int resultCode;
resultCode = z.inflateInit(JZlib.W_ZLIB);
if (resultCode != JZlib.Z_OK) {
throw new CompressionException("ZStream initialization failure");
}
z.next_out = out;
}
public void setInput(ChannelBuffer compressed) {
byte[] in = new byte[compressed.readableBytes()];
compressed.readBytes(in);
z.next_in = in;
z.next_in_index = 0;
z.avail_in = in.length;
}
public void decode(ChannelBuffer decompressed) {
z.next_out_index = 0;
z.avail_out = out.length;
int resultCode = z.inflate(JZlib.Z_SYNC_FLUSH);
if (resultCode == JZlib.Z_NEED_DICT) {
resultCode = z.inflateSetDictionary(SPDY_DICT, SPDY_DICT.length);
if (resultCode != JZlib.Z_OK) {
throw new CompressionException("ZStream dictionary failure");
}
z.inflate(JZlib.Z_SYNC_FLUSH);
}
if (z.next_out_index > 0) {
decompressed.writeBytes(out, 0, z.next_out_index);
}
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/*
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.netty.handler.codec.spdy;
import java.util.zip.Deflater;
import org.jboss.netty.buffer.ChannelBuffer;
import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
class SpdyZlibEncoder {
private final byte[] out = new byte[8192];
private final Deflater compressor;
public SpdyZlibEncoder(int compressionLevel) {
if (compressionLevel < 0 || compressionLevel > 9) {
throw new IllegalArgumentException(
"compressionLevel: " + compressionLevel + " (expected: 0-9)");
}
compressor = new Deflater(compressionLevel);
compressor.setDictionary(SPDY_DICT);
}
public void setInput(ChannelBuffer decompressed) {
byte[] in = new byte[decompressed.readableBytes()];
decompressed.readBytes(in);
compressor.setInput(in);
}
public void encode(ChannelBuffer compressed) {
while (!compressor.needsInput()) {
int numBytes = compressor.deflate(out, 0, out.length, Deflater.SYNC_FLUSH);
compressed.writeBytes(out, 0, numBytes);
}
}
public void end() {
compressor.end();
}
}