Updated implementation to draft-14.
This commit is contained in:
parent
97d723c516
commit
6b6267ed31
|
@ -176,6 +176,7 @@ public abstract class HTTP2Session implements ISession, Parser.Listener
|
|||
return false;
|
||||
|
||||
Map<Integer, Integer> settings = frame.getSettings();
|
||||
// TODO: handle other settings
|
||||
if (settings.containsKey(SettingsFrame.MAX_CONCURRENT_STREAMS))
|
||||
{
|
||||
maxStreamCount = settings.get(SettingsFrame.MAX_CONCURRENT_STREAMS);
|
||||
|
@ -187,7 +188,17 @@ public abstract class HTTP2Session implements ISession, Parser.Listener
|
|||
int windowSize = settings.get(SettingsFrame.INITIAL_WINDOW_SIZE);
|
||||
flowControl.updateInitialWindowSize(this, windowSize);
|
||||
}
|
||||
// TODO: handle other settings
|
||||
if (settings.containsKey(SettingsFrame.MAX_FRAME_SIZE))
|
||||
{
|
||||
int maxFrameSize = settings.get(SettingsFrame.MAX_FRAME_SIZE);
|
||||
// Spec: check the max frame size is sane.
|
||||
if (maxFrameSize < Frame.DEFAULT_MAX_LENGTH || maxFrameSize > Frame.MAX_MAX_LENGTH)
|
||||
{
|
||||
onConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_settings_max_frame_size");
|
||||
return false;
|
||||
}
|
||||
generator.setMaxFrameSize(maxFrameSize);
|
||||
}
|
||||
notifySettings(this, frame);
|
||||
|
||||
// SPEC: SETTINGS frame MUST be replied.
|
||||
|
|
|
@ -22,11 +22,8 @@ public interface Flag
|
|||
{
|
||||
public static final int NONE = 0x00;
|
||||
public static final int END_STREAM = 0x01;
|
||||
public static final int ACK = END_STREAM;
|
||||
public static final int END_SEGMENT = 0x02;
|
||||
public static final int ACK = 0x01;
|
||||
public static final int END_HEADERS = 0x04;
|
||||
public static final int PADDING_LOW = 0x08;
|
||||
public static final int PADDING_HIGH = 0x10;
|
||||
public static final int COMPRESS = 0x20;
|
||||
public static final int PRIORITY = COMPRESS;
|
||||
public static final int PADDING = 0x08;
|
||||
public static final int PRIORITY = 0x20;
|
||||
}
|
||||
|
|
|
@ -20,8 +20,9 @@ package org.eclipse.jetty.http2.frames;
|
|||
|
||||
public abstract class Frame
|
||||
{
|
||||
public static final int HEADER_LENGTH = 8;
|
||||
public static final int MAX_LENGTH = 0x3F_FF;
|
||||
public static final int HEADER_LENGTH = 9;
|
||||
public static final int DEFAULT_MAX_LENGTH = 0x40_00;
|
||||
public static final int MAX_MAX_LENGTH = 0xFF_FF_FF;
|
||||
|
||||
private final FrameType type;
|
||||
|
||||
|
|
|
@ -32,9 +32,7 @@ public enum FrameType
|
|||
PING(6),
|
||||
GO_AWAY(7),
|
||||
WINDOW_UPDATE(8),
|
||||
CONTINUATION(9),
|
||||
ALTSVC(10),
|
||||
BLOCKED(11);
|
||||
CONTINUATION(9);
|
||||
|
||||
public static FrameType from(int type)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@ public class SettingsFrame extends Frame
|
|||
public static final int ENABLE_PUSH = 2;
|
||||
public static final int MAX_CONCURRENT_STREAMS = 3;
|
||||
public static final int INITIAL_WINDOW_SIZE = 4;
|
||||
public static final int MAX_FRAME_SIZE = 5;
|
||||
|
||||
private final Map<Integer, Integer> settings;
|
||||
private final boolean reply;
|
||||
|
|
|
@ -48,7 +48,8 @@ public class DataGenerator
|
|||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
|
||||
int dataLength = data.remaining();
|
||||
if (dataLength <= maxLength && dataLength <= Frame.MAX_LENGTH)
|
||||
int maxFrameSize = headerGenerator.getMaxFrameSize();
|
||||
if (dataLength <= maxLength && dataLength <= maxFrameSize)
|
||||
{
|
||||
// Single frame.
|
||||
generateFrame(lease, streamId, data, last);
|
||||
|
@ -58,16 +59,15 @@ public class DataGenerator
|
|||
// Other cases, we need to slice the original buffer into multiple frames.
|
||||
|
||||
int length = Math.min(maxLength, dataLength);
|
||||
int dataBytesPerFrame = Frame.MAX_LENGTH;
|
||||
int frames = length / dataBytesPerFrame;
|
||||
if (frames * dataBytesPerFrame != length)
|
||||
int frames = length / maxFrameSize;
|
||||
if (frames * maxFrameSize != length)
|
||||
++frames;
|
||||
|
||||
int begin = data.position();
|
||||
int end = data.limit();
|
||||
for (int i = 1; i <= frames; ++i)
|
||||
{
|
||||
int limit = begin + Math.min(dataBytesPerFrame * i, length);
|
||||
int limit = begin + Math.min(maxFrameSize * i, length);
|
||||
data.limit(limit);
|
||||
ByteBuffer slice = data.slice();
|
||||
data.position(limit);
|
||||
|
|
|
@ -39,4 +39,9 @@ public abstract class FrameGenerator
|
|||
{
|
||||
return headerGenerator.generate(lease, frameType, Frame.HEADER_LENGTH + length, length, flags, streamId);
|
||||
}
|
||||
|
||||
public int getMaxFrameSize()
|
||||
{
|
||||
return headerGenerator.getMaxFrameSize();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ public class Generator
|
|||
{
|
||||
private final ByteBufferPool byteBufferPool;
|
||||
private final int headerTableSize;
|
||||
private final HeaderGenerator headerGenerator;
|
||||
private final FrameGenerator[] generators;
|
||||
private final DataGenerator dataGenerator;
|
||||
|
||||
|
@ -41,7 +42,7 @@ public class Generator
|
|||
this.byteBufferPool = byteBufferPool;
|
||||
this.headerTableSize = headerTableSize;
|
||||
|
||||
HeaderGenerator headerGenerator = new HeaderGenerator();
|
||||
headerGenerator = new HeaderGenerator();
|
||||
HpackEncoder encoder = new HpackEncoder(headerTableSize);
|
||||
|
||||
this.generators = new FrameGenerator[FrameType.values().length];
|
||||
|
@ -54,8 +55,6 @@ public class Generator
|
|||
this.generators[FrameType.GO_AWAY.getType()] = new GoAwayGenerator(headerGenerator);
|
||||
this.generators[FrameType.WINDOW_UPDATE.getType()] = new WindowUpdateGenerator(headerGenerator);
|
||||
this.generators[FrameType.CONTINUATION.getType()] = null; // TODO
|
||||
this.generators[FrameType.ALTSVC.getType()] = null; // TODO
|
||||
this.generators[FrameType.BLOCKED.getType()] = null; // TODO
|
||||
|
||||
this.dataGenerator = new DataGenerator(headerGenerator);
|
||||
}
|
||||
|
@ -70,6 +69,11 @@ public class Generator
|
|||
return headerTableSize;
|
||||
}
|
||||
|
||||
public void setMaxFrameSize(int maxFrameSize)
|
||||
{
|
||||
headerGenerator.setMaxFrameSize(maxFrameSize);
|
||||
}
|
||||
|
||||
public void control(ByteBufferPool.Lease lease, Frame frame)
|
||||
{
|
||||
generators[frame.getType().getType()].generate(lease, frame);
|
||||
|
|
|
@ -50,8 +50,8 @@ public class GoAwayGenerator extends FrameGenerator
|
|||
// The last streamId + the error code.
|
||||
int fixedLength = 4 + 4;
|
||||
|
||||
// Make sure we don't exceed the frame max length.
|
||||
int maxPayloadLength = Frame.MAX_LENGTH - fixedLength;
|
||||
// Make sure we don't exceed the default frame max length.
|
||||
int maxPayloadLength = Frame.DEFAULT_MAX_LENGTH - fixedLength;
|
||||
if (payload != null && payload.length > maxPayloadLength)
|
||||
payload = Arrays.copyOfRange(payload, 0, maxPayloadLength);
|
||||
|
||||
|
|
|
@ -20,18 +20,33 @@ package org.eclipse.jetty.http2.generator;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http2.frames.Frame;
|
||||
import org.eclipse.jetty.http2.frames.FrameType;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
|
||||
public class HeaderGenerator
|
||||
{
|
||||
private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH;
|
||||
|
||||
public ByteBuffer generate(ByteBufferPool.Lease lease, FrameType frameType, int capacity, int length, int flags, int streamId)
|
||||
{
|
||||
ByteBuffer header = lease.acquire(capacity, true);
|
||||
header.putShort((short)length);
|
||||
header.put((byte)((length & 0x00_FF_00_00) >>> 16));
|
||||
header.put((byte)((length & 0x00_00_FF_00) >>> 8));
|
||||
header.put((byte)((length & 0x00_00_00_FF)));
|
||||
header.put((byte)frameType.getType());
|
||||
header.put((byte)flags);
|
||||
header.putInt(streamId);
|
||||
return header;
|
||||
}
|
||||
|
||||
public int getMaxFrameSize()
|
||||
{
|
||||
return maxFrameSize;
|
||||
}
|
||||
|
||||
public void setMaxFrameSize(int maxFrameSize)
|
||||
{
|
||||
this.maxFrameSize = maxFrameSize;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,10 +51,11 @@ public class HeadersGenerator extends FrameGenerator
|
|||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
|
||||
encoder.encode(metaData, lease,Frame.MAX_LENGTH);
|
||||
int maxFrameSize = getMaxFrameSize();
|
||||
encoder.encode(metaData, lease, maxFrameSize);
|
||||
|
||||
long length = lease.getTotalLength();
|
||||
if (length > Frame.MAX_LENGTH)
|
||||
if (length > maxFrameSize)
|
||||
throw new IllegalArgumentException("Invalid headers, too big");
|
||||
|
||||
int flags = Flag.END_HEADERS;
|
||||
|
|
|
@ -53,13 +53,14 @@ public class PushPromiseGenerator extends FrameGenerator
|
|||
if (promisedStreamId < 0)
|
||||
throw new IllegalArgumentException("Invalid promised stream id: " + promisedStreamId);
|
||||
|
||||
encoder.encode(metaData, lease, Frame.MAX_LENGTH);
|
||||
int maxFrameSize = getMaxFrameSize();
|
||||
encoder.encode(metaData, lease, maxFrameSize);
|
||||
|
||||
// The promised streamId.
|
||||
int fixedLength = 4;
|
||||
|
||||
long length = lease.getTotalLength();
|
||||
if (length > Frame.MAX_LENGTH - fixedLength)
|
||||
if (length > maxFrameSize - fixedLength)
|
||||
throw new IllegalArgumentException("Invalid headers, too big");
|
||||
|
||||
// Space for the promised streamId.
|
||||
|
|
|
@ -44,17 +44,17 @@ public class SettingsGenerator extends FrameGenerator
|
|||
|
||||
public void generateSettings(ByteBufferPool.Lease lease, Map<Integer, Integer> settings, boolean reply)
|
||||
{
|
||||
// One byte for the identifier, 4 bytes for the value.
|
||||
int entryLength = 1 + 4;
|
||||
// Two bytes for the identifier, four bytes for the value.
|
||||
int entryLength = 2 + 4;
|
||||
int length = entryLength * settings.size();
|
||||
if (length > Frame.MAX_LENGTH)
|
||||
if (length > getMaxFrameSize())
|
||||
throw new IllegalArgumentException("Invalid settings, too big");
|
||||
|
||||
ByteBuffer header = generateHeader(lease, FrameType.SETTINGS, length, reply ? Flag.ACK : Flag.NONE, 0);
|
||||
|
||||
for (Map.Entry<Integer, Integer> entry : settings.entrySet())
|
||||
{
|
||||
header.put(entry.getKey().byteValue());
|
||||
header.putShort(entry.getKey().shortValue());
|
||||
header.putInt(entry.getValue());
|
||||
}
|
||||
|
||||
|
|
|
@ -59,14 +59,9 @@ public abstract class BodyParser
|
|||
return headerParser.hasFlag(bit);
|
||||
}
|
||||
|
||||
protected boolean isPaddingHigh()
|
||||
protected boolean isPadding()
|
||||
{
|
||||
return headerParser.hasFlag(Flag.PADDING_HIGH);
|
||||
}
|
||||
|
||||
protected boolean isPaddingLow()
|
||||
{
|
||||
return headerParser.hasFlag(Flag.PADDING_LOW);
|
||||
return headerParser.hasFlag(Flag.PADDING);
|
||||
}
|
||||
|
||||
protected boolean isEndStream()
|
||||
|
|
|
@ -44,7 +44,7 @@ public class DataBodyParser extends BodyParser
|
|||
@Override
|
||||
protected boolean emptyBody()
|
||||
{
|
||||
if (isPaddingHigh() || isPaddingLow())
|
||||
if (isPadding())
|
||||
{
|
||||
notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_data_frame");
|
||||
return false;
|
||||
|
@ -68,13 +68,9 @@ public class DataBodyParser extends BodyParser
|
|||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_data_frame");
|
||||
}
|
||||
length = getBodyLength();
|
||||
if (isPaddingHigh())
|
||||
if (isPadding())
|
||||
{
|
||||
state = State.PADDING_HIGH;
|
||||
}
|
||||
else if (isPaddingLow())
|
||||
{
|
||||
state = State.PADDING_LOW;
|
||||
state = State.PADDING_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -82,20 +78,9 @@ public class DataBodyParser extends BodyParser
|
|||
}
|
||||
break;
|
||||
}
|
||||
case PADDING_HIGH:
|
||||
case PADDING_LENGTH:
|
||||
{
|
||||
paddingLength = (buffer.get() & 0xFF) << 8;
|
||||
--length;
|
||||
if (length < 1 + 256)
|
||||
{
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_data_frame_padding");
|
||||
}
|
||||
state = State.PADDING_LOW;
|
||||
break;
|
||||
}
|
||||
case PADDING_LOW:
|
||||
{
|
||||
paddingLength += buffer.get() & 0xFF;
|
||||
paddingLength = buffer.get() & 0xFF;
|
||||
--length;
|
||||
length -= paddingLength;
|
||||
state = State.DATA;
|
||||
|
@ -128,7 +113,6 @@ public class DataBodyParser extends BodyParser
|
|||
}
|
||||
else
|
||||
{
|
||||
// TODO: check the semantic of Flag.END_SEGMENT.
|
||||
// We got partial data, simulate a smaller frame, and stay in DATA state.
|
||||
if (onData(slice, true))
|
||||
{
|
||||
|
@ -166,6 +150,6 @@ public class DataBodyParser extends BodyParser
|
|||
|
||||
private enum State
|
||||
{
|
||||
PREPARE, PADDING_HIGH, PADDING_LOW, DATA, PADDING
|
||||
PREPARE, PADDING_LENGTH, DATA, PADDING
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,12 +59,11 @@ public class HeaderParser
|
|||
{
|
||||
case LENGTH:
|
||||
{
|
||||
int halfShort = buffer.get() & 0xFF;
|
||||
length = (length << 8) + halfShort;
|
||||
if (++cursor == 2)
|
||||
int octect = buffer.get() & 0xFF;
|
||||
length = (length << 8) + octect;
|
||||
if (++cursor == 3)
|
||||
{
|
||||
// First 2 most significant bits MUST be ignored as per specification.
|
||||
length &= Frame.MAX_LENGTH;
|
||||
length &= Frame.MAX_MAX_LENGTH;
|
||||
state = State.TYPE;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -87,13 +87,9 @@ public class HeadersBodyParser extends BodyParser
|
|||
|
||||
length = getBodyLength();
|
||||
|
||||
if (isPaddingHigh())
|
||||
if (isPadding())
|
||||
{
|
||||
state = State.PADDING_HIGH;
|
||||
}
|
||||
else if (isPaddingLow())
|
||||
{
|
||||
state = State.PADDING_LOW;
|
||||
state = State.PADDING_LENGTH;
|
||||
}
|
||||
else if (hasFlag(Flag.PRIORITY))
|
||||
{
|
||||
|
@ -105,20 +101,9 @@ public class HeadersBodyParser extends BodyParser
|
|||
}
|
||||
break;
|
||||
}
|
||||
case PADDING_HIGH:
|
||||
case PADDING_LENGTH:
|
||||
{
|
||||
paddingLength = (buffer.get() & 0xFF) << 8;
|
||||
--length;
|
||||
state = State.PADDING_LOW;
|
||||
if (length < 1 + 256)
|
||||
{
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_headers_frame_padding");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PADDING_LOW:
|
||||
{
|
||||
paddingLength += buffer.get() & 0xFF;
|
||||
paddingLength = buffer.get() & 0xFF;
|
||||
--length;
|
||||
length -= paddingLength;
|
||||
state = hasFlag(Flag.PRIORITY) ? State.EXCLUSIVE : State.HEADERS;
|
||||
|
@ -235,6 +220,6 @@ public class HeadersBodyParser extends BodyParser
|
|||
|
||||
private enum State
|
||||
{
|
||||
PREPARE, PADDING_HIGH, PADDING_LOW, EXCLUSIVE, STREAM_ID, STREAM_ID_BYTES, WEIGHT, HEADERS, PADDING
|
||||
PREPARE, PADDING_LENGTH, EXCLUSIVE, STREAM_ID, STREAM_ID_BYTES, WEIGHT, HEADERS, PADDING
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,8 +62,6 @@ public class Parser
|
|||
bodyParsers[FrameType.GO_AWAY.getType()] = new GoAwayBodyParser(headerParser, listener);
|
||||
bodyParsers[FrameType.WINDOW_UPDATE.getType()] = new WindowUpdateBodyParser(headerParser, listener);
|
||||
bodyParsers[FrameType.CONTINUATION.getType()] = null; // TODO
|
||||
bodyParsers[FrameType.ALTSVC.getType()] = null; // TODO
|
||||
bodyParsers[FrameType.BLOCKED.getType()] = null; // TODO
|
||||
}
|
||||
|
||||
private void reset()
|
||||
|
|
|
@ -72,13 +72,9 @@ public class PushPromiseBodyParser extends BodyParser
|
|||
|
||||
length = getBodyLength();
|
||||
|
||||
if (isPaddingHigh())
|
||||
if (isPadding())
|
||||
{
|
||||
state = State.PADDING_HIGH;
|
||||
}
|
||||
else if (isPaddingLow())
|
||||
{
|
||||
state = State.PADDING_LOW;
|
||||
state = State.PADDING_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -86,26 +82,15 @@ public class PushPromiseBodyParser extends BodyParser
|
|||
}
|
||||
break;
|
||||
}
|
||||
case PADDING_HIGH:
|
||||
case PADDING_LENGTH:
|
||||
{
|
||||
paddingLength = (buffer.get() & 0xFF) << 8;
|
||||
--length;
|
||||
state = State.PADDING_LOW;
|
||||
if (length < 1 + 256)
|
||||
{
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_push_promise_frame_padding");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PADDING_LOW:
|
||||
{
|
||||
paddingLength += buffer.get() & 0xFF;
|
||||
paddingLength = buffer.get() & 0xFF;
|
||||
--length;
|
||||
length -= paddingLength;
|
||||
state = State.STREAM_ID;
|
||||
if (length < 4)
|
||||
{
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_push_promise_frame_padding");
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_push_promise_frame");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -187,6 +172,6 @@ public class PushPromiseBodyParser extends BodyParser
|
|||
|
||||
private enum State
|
||||
{
|
||||
PREPARE, PADDING_HIGH, PADDING_LOW, STREAM_ID, STREAM_ID_BYTES, HEADERS, PADDING
|
||||
PREPARE, PADDING_LENGTH, STREAM_ID, STREAM_ID_BYTES, HEADERS, PADDING
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,13 +76,38 @@ public class SettingsBodyParser extends BodyParser
|
|||
}
|
||||
case SETTING_ID:
|
||||
{
|
||||
settingId = buffer.get() & 0xFF;
|
||||
state = State.SETTING_VALUE;
|
||||
if (buffer.remaining() >= 2)
|
||||
{
|
||||
settingId = buffer.getShort() & 0xFF_FF;
|
||||
state = State.SETTING_VALUE;
|
||||
length -= 2;
|
||||
if (length <= 0)
|
||||
{
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_settings_frame");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor = 2;
|
||||
settingId = 0;
|
||||
state = State.SETTING_ID_BYTES;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SETTING_ID_BYTES:
|
||||
{
|
||||
int currByte = buffer.get() & 0xFF;
|
||||
--cursor;
|
||||
settingId += currByte << (8 * cursor);
|
||||
--length;
|
||||
if (length <= 0)
|
||||
{
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_settings_frame");
|
||||
}
|
||||
if (cursor == 0)
|
||||
{
|
||||
state = State.SETTING_VALUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SETTING_VALUE:
|
||||
|
@ -145,6 +170,6 @@ public class SettingsBodyParser extends BodyParser
|
|||
|
||||
private enum State
|
||||
{
|
||||
PREPARE, SETTING_ID, SETTING_VALUE, SETTING_VALUE_BYTES
|
||||
PREPARE, SETTING_ID, SETTING_ID_BYTES, SETTING_VALUE, SETTING_VALUE_BYTES
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ public class DataGenerateParseTest
|
|||
{
|
||||
ByteBuffer content = ByteBuffer.wrap(largeContent);
|
||||
List<DataFrame> frames = testGenerateParse(content);
|
||||
Assert.assertEquals(9, frames.size());
|
||||
Assert.assertEquals(8, frames.size());
|
||||
ByteBuffer aggregate = ByteBuffer.allocate(content.remaining());
|
||||
for (int i = 1; i <= frames.size(); ++i)
|
||||
{
|
||||
|
|
|
@ -123,7 +123,7 @@ public class SettingsGenerateParseTest
|
|||
generator.generateSettings(lease, settings1, true);
|
||||
// Modify the length of the frame to make it invalid
|
||||
ByteBuffer bytes = lease.getByteBuffers().get(0);
|
||||
bytes.putShort(0, (short)(bytes.getShort(0) - 1));
|
||||
bytes.putShort(1, (short)(bytes.getShort(1) - 1));
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
|
|
|
@ -42,7 +42,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
|
|||
|
||||
public AbstractHTTP2ServerConnectionFactory()
|
||||
{
|
||||
super("h2-12");
|
||||
super("h2-14");
|
||||
}
|
||||
|
||||
public int getMaxHeaderTableSize()
|
||||
|
|
Loading…
Reference in New Issue