Implemented PUSH_PROMISE generator/parser.
This commit is contained in:
parent
907d303774
commit
849360717e
|
@ -40,6 +40,7 @@ import org.eclipse.jetty.http2.frames.GoAwayFrame;
|
|||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.frames.PingFrame;
|
||||
import org.eclipse.jetty.http2.frames.PriorityFrame;
|
||||
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
|
||||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
import org.eclipse.jetty.http2.frames.SettingsFrame;
|
||||
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
|
||||
|
@ -178,6 +179,13 @@ public abstract class HTTP2Session implements ISession, Parser.Listener
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPushPromise(PushPromiseFrame frame)
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPing(PingFrame frame)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.http2.frames;
|
||||
|
||||
import org.eclipse.jetty.http2.hpack.MetaData;
|
||||
|
||||
public class PushPromiseFrame extends Frame
|
||||
{
|
||||
private final int streamId;
|
||||
private final int promisedStreamId;
|
||||
private final MetaData metaData;
|
||||
|
||||
public PushPromiseFrame(int streamId, int promisedStreamId, MetaData metaData)
|
||||
{
|
||||
super(FrameType.PUSH_PROMISE);
|
||||
this.streamId = streamId;
|
||||
this.promisedStreamId = promisedStreamId;
|
||||
this.metaData = metaData;
|
||||
}
|
||||
|
||||
public int getStreamId()
|
||||
{
|
||||
return streamId;
|
||||
}
|
||||
|
||||
public int getPromisedStreamId()
|
||||
{
|
||||
return promisedStreamId;
|
||||
}
|
||||
|
||||
public MetaData getMetaData()
|
||||
{
|
||||
return metaData;
|
||||
}
|
||||
}
|
|
@ -49,7 +49,7 @@ public class Generator
|
|||
this.generators[FrameType.PRIORITY.getType()] = new PriorityGenerator(headerGenerator);
|
||||
this.generators[FrameType.RST_STREAM.getType()] = new ResetGenerator(headerGenerator);
|
||||
this.generators[FrameType.SETTINGS.getType()] = new SettingsGenerator(headerGenerator);
|
||||
this.generators[FrameType.PUSH_PROMISE.getType()] = null; // TODO
|
||||
this.generators[FrameType.PUSH_PROMISE.getType()] = new PushPromiseGenerator(headerGenerator, encoder);
|
||||
this.generators[FrameType.PING.getType()] = new PingGenerator(headerGenerator);
|
||||
this.generators[FrameType.GO_AWAY.getType()] = new GoAwayGenerator(headerGenerator);
|
||||
this.generators[FrameType.WINDOW_UPDATE.getType()] = new WindowUpdateGenerator(headerGenerator);
|
||||
|
|
|
@ -43,10 +43,10 @@ public class HeadersGenerator extends FrameGenerator
|
|||
public void generate(ByteBufferPool.Lease lease, Frame frame)
|
||||
{
|
||||
HeadersFrame headersFrame = (HeadersFrame)frame;
|
||||
generate(lease, headersFrame.getStreamId(), headersFrame.getMetaData(), !headersFrame.isEndStream());
|
||||
generateHeaders(lease, headersFrame.getStreamId(), headersFrame.getMetaData(), !headersFrame.isEndStream());
|
||||
}
|
||||
|
||||
private void generate(ByteBufferPool.Lease lease, int streamId, MetaData metaData, boolean contentFollows)
|
||||
public void generateHeaders(ByteBufferPool.Lease lease, int streamId, MetaData metaData, boolean contentFollows)
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package org.eclipse.jetty.http2.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http2.frames.Flag;
|
||||
import org.eclipse.jetty.http2.frames.Frame;
|
||||
import org.eclipse.jetty.http2.frames.FrameType;
|
||||
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
|
||||
import org.eclipse.jetty.http2.hpack.HpackEncoder;
|
||||
import org.eclipse.jetty.http2.hpack.MetaData;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
public class PushPromiseGenerator extends FrameGenerator
|
||||
{
|
||||
private final HpackEncoder encoder;
|
||||
|
||||
public PushPromiseGenerator(HeaderGenerator headerGenerator, HpackEncoder encoder)
|
||||
{
|
||||
super(headerGenerator);
|
||||
this.encoder = encoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(ByteBufferPool.Lease lease, Frame frame)
|
||||
{
|
||||
PushPromiseFrame pushPromiseFrame = (PushPromiseFrame)frame;
|
||||
generatePushPromise(lease, pushPromiseFrame.getStreamId(), pushPromiseFrame.getPromisedStreamId(), pushPromiseFrame.getMetaData());
|
||||
}
|
||||
|
||||
public void generatePushPromise(ByteBufferPool.Lease lease, int streamId, int promisedStreamId, MetaData metaData)
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
if (promisedStreamId < 0)
|
||||
throw new IllegalArgumentException("Invalid promised stream id: " + promisedStreamId);
|
||||
|
||||
encoder.encode(metaData, lease);
|
||||
|
||||
long length = lease.getTotalLength();
|
||||
if (length > Frame.MAX_LENGTH)
|
||||
throw new IllegalArgumentException("Invalid headers, too big");
|
||||
|
||||
// Space for the promised streamId.
|
||||
length += 4;
|
||||
|
||||
int flags = Flag.END_HEADERS;
|
||||
|
||||
ByteBuffer header = generateHeader(lease, FrameType.PUSH_PROMISE, (int)length, flags, streamId);
|
||||
header.putInt(promisedStreamId);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.prepend(header, true);
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ import org.eclipse.jetty.http2.frames.GoAwayFrame;
|
|||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.frames.PingFrame;
|
||||
import org.eclipse.jetty.http2.frames.PriorityFrame;
|
||||
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
|
||||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
import org.eclipse.jetty.http2.frames.SettingsFrame;
|
||||
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
|
||||
|
@ -148,6 +149,19 @@ public abstract class BodyParser
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean notifyPushPromise(PushPromiseFrame frame)
|
||||
{
|
||||
try
|
||||
{
|
||||
return listener.onPushPromise(frame);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Failure while notifying listener " + listener, x);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean notifyPing(PingFrame frame)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.eclipse.jetty.http2.frames.GoAwayFrame;
|
|||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.frames.PingFrame;
|
||||
import org.eclipse.jetty.http2.frames.PriorityFrame;
|
||||
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
|
||||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
import org.eclipse.jetty.http2.frames.SettingsFrame;
|
||||
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
|
||||
|
@ -56,7 +57,7 @@ public class Parser
|
|||
bodyParsers[FrameType.PRIORITY.getType()] = new PriorityBodyParser(headerParser, listener);
|
||||
bodyParsers[FrameType.RST_STREAM.getType()] = new ResetBodyParser(headerParser, listener);
|
||||
bodyParsers[FrameType.SETTINGS.getType()] = new SettingsBodyParser(headerParser, listener);
|
||||
bodyParsers[FrameType.PUSH_PROMISE.getType()] = null; // TODO
|
||||
bodyParsers[FrameType.PUSH_PROMISE.getType()] = new PushPromiseBodyParser(headerParser, listener, headerBlockParser);
|
||||
bodyParsers[FrameType.PING.getType()] = new PingBodyParser(headerParser, listener);
|
||||
bodyParsers[FrameType.GO_AWAY.getType()] = new GoAwayBodyParser(headerParser, listener);
|
||||
bodyParsers[FrameType.WINDOW_UPDATE.getType()] = new WindowUpdateBodyParser(headerParser, listener);
|
||||
|
@ -172,6 +173,8 @@ public class Parser
|
|||
|
||||
public boolean onSettings(SettingsFrame frame);
|
||||
|
||||
public boolean onPushPromise(PushPromiseFrame frame);
|
||||
|
||||
public boolean onPing(PingFrame frame);
|
||||
|
||||
public boolean onGoAway(GoAwayFrame frame);
|
||||
|
@ -212,6 +215,12 @@ public class Parser
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPushPromise(PushPromiseFrame frame)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPing(PingFrame frame)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.http2.parser;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
|
||||
import org.eclipse.jetty.http2.hpack.MetaData;
|
||||
|
||||
public class PushPromiseBodyParser extends BodyParser
|
||||
{
|
||||
private final HeaderBlockParser headerBlockParser;
|
||||
private State state = State.PREPARE;
|
||||
private int cursor;
|
||||
private int length;
|
||||
private int paddingLength;
|
||||
private int streamId;
|
||||
|
||||
public PushPromiseBodyParser(HeaderParser headerParser, Parser.Listener listener, HeaderBlockParser headerBlockParser)
|
||||
{
|
||||
super(headerParser, listener);
|
||||
this.headerBlockParser = headerBlockParser;
|
||||
}
|
||||
|
||||
private void reset()
|
||||
{
|
||||
state = State.PREPARE;
|
||||
cursor = 0;
|
||||
length = 0;
|
||||
paddingLength = 0;
|
||||
streamId = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result parse(ByteBuffer buffer)
|
||||
{
|
||||
boolean loop = false;
|
||||
while (buffer.hasRemaining() || loop)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PREPARE:
|
||||
{
|
||||
// SPEC: wrong streamId is treated as connection error.
|
||||
if (getStreamId() == 0)
|
||||
{
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_push_promise_frame");
|
||||
}
|
||||
length = getBodyLength();
|
||||
if (isPaddingHigh())
|
||||
{
|
||||
state = State.PADDING_HIGH;
|
||||
}
|
||||
else if (isPaddingLow())
|
||||
{
|
||||
state = State.PADDING_LOW;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.STREAM_ID;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PADDING_HIGH:
|
||||
{
|
||||
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;
|
||||
--length;
|
||||
length -= paddingLength;
|
||||
state = State.STREAM_ID;
|
||||
loop = length == 0;
|
||||
if (length < 0)
|
||||
{
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_push_promise_frame_padding");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STREAM_ID:
|
||||
{
|
||||
if (buffer.remaining() >= 4)
|
||||
{
|
||||
streamId = buffer.getInt();
|
||||
streamId &= 0x7F_FF_FF_FF;
|
||||
length -= 4;
|
||||
state = State.HEADERS;
|
||||
if (length < 1)
|
||||
{
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_push_promise_frame");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.STREAM_ID_BYTES;
|
||||
cursor = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STREAM_ID_BYTES:
|
||||
{
|
||||
int currByte = buffer.get() & 0xFF;
|
||||
--cursor;
|
||||
streamId += currByte << (8 * cursor);
|
||||
--length;
|
||||
if (cursor > 0 && length <= 0)
|
||||
{
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_push_promise_frame");
|
||||
}
|
||||
if (cursor == 0)
|
||||
{
|
||||
streamId &= 0x7F_FF_FF_FF;
|
||||
state = State.HEADERS;
|
||||
if (length <= 0)
|
||||
{
|
||||
return notifyConnectionFailure(ErrorCode.PROTOCOL_ERROR, "invalid_push_promise_frame");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HEADERS:
|
||||
{
|
||||
MetaData metaData = headerBlockParser.parse(buffer, length);
|
||||
if (metaData != null)
|
||||
{
|
||||
state = State.PADDING;
|
||||
loop = paddingLength == 0;
|
||||
if (onPushPromise(streamId, metaData))
|
||||
{
|
||||
return Result.ASYNC;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PADDING:
|
||||
{
|
||||
int size = Math.min(buffer.remaining(), paddingLength);
|
||||
buffer.position(buffer.position() + size);
|
||||
paddingLength -= size;
|
||||
if (paddingLength == 0)
|
||||
{
|
||||
reset();
|
||||
return Result.COMPLETE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result.PENDING;
|
||||
}
|
||||
|
||||
private boolean onPushPromise(int streamId, MetaData metaData)
|
||||
{
|
||||
PushPromiseFrame frame = new PushPromiseFrame(getStreamId(), streamId, metaData);
|
||||
return notifyPushPromise(frame);
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
PREPARE, PADDING_HIGH, PADDING_LOW, STREAM_ID, STREAM_ID_BYTES, HEADERS, PADDING
|
||||
}
|
||||
}
|
|
@ -18,17 +18,131 @@
|
|||
|
||||
package org.eclipse.jetty.http2.frames;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.generator.HeadersGenerator;
|
||||
import org.eclipse.jetty.http2.hpack.HpackEncoder;
|
||||
import org.eclipse.jetty.http2.hpack.MetaData;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HeadersGenerateParseTest
|
||||
{
|
||||
private final ByteBufferPool byteBufferPool = new MappedByteBufferPool();
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testGenerateParse() throws Exception
|
||||
{
|
||||
// TODO
|
||||
HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder());
|
||||
|
||||
int streamId = 13;
|
||||
HttpFields fields = new HttpFields();
|
||||
fields.put("Accept", "text/html");
|
||||
fields.put("User-Agent", "Jetty");
|
||||
MetaData.Request metaData = new MetaData.Request(HttpScheme.HTTP, "GET", "localhost:8080", "localhost", 8080, "/path", fields);
|
||||
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
final List<HeadersFrame> frames = new ArrayList<>();
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateHeaders(lease, streamId, metaData, false);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public boolean onHeaders(HeadersFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
HeadersFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertTrue(frame.isEndStream());
|
||||
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
||||
Assert.assertSame(metaData.getScheme(), request.getScheme());
|
||||
Assert.assertEquals(metaData.getMethod(), request.getMethod());
|
||||
Assert.assertEquals(metaData.getAuthority(), request.getAuthority());
|
||||
Assert.assertEquals(metaData.getHost(), request.getHost());
|
||||
Assert.assertEquals(metaData.getPort(), request.getPort());
|
||||
Assert.assertEquals(metaData.getPath(), request.getPath());
|
||||
for (int j = 0; j < fields.size(); ++j)
|
||||
{
|
||||
HttpField field = fields.getField(j);
|
||||
Assert.assertTrue(request.getFields().contains(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateParseOneByteAtATime() throws Exception
|
||||
{
|
||||
HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder());
|
||||
|
||||
int streamId = 13;
|
||||
HttpFields fields = new HttpFields();
|
||||
fields.put("Accept", "text/html");
|
||||
fields.put("User-Agent", "Jetty");
|
||||
MetaData.Request metaData = new MetaData.Request(HttpScheme.HTTP, "GET", "localhost:8080", "localhost", 8080, "/path", fields);
|
||||
|
||||
final List<HeadersFrame> frames = new ArrayList<>();
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateHeaders(lease, streamId, metaData, false);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public boolean onHeaders(HeadersFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
HeadersFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertTrue(frame.isEndStream());
|
||||
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
||||
Assert.assertSame(metaData.getScheme(), request.getScheme());
|
||||
Assert.assertEquals(metaData.getMethod(), request.getMethod());
|
||||
Assert.assertEquals(metaData.getAuthority(), request.getAuthority());
|
||||
Assert.assertEquals(metaData.getHost(), request.getHost());
|
||||
Assert.assertEquals(metaData.getPort(), request.getPort());
|
||||
Assert.assertEquals(metaData.getPath(), request.getPath());
|
||||
for (int j = 0; j < fields.size(); ++j)
|
||||
{
|
||||
HttpField field = fields.getField(j);
|
||||
Assert.assertTrue(request.getFields().contains(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.http2.frames;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.generator.PushPromiseGenerator;
|
||||
import org.eclipse.jetty.http2.hpack.HpackEncoder;
|
||||
import org.eclipse.jetty.http2.hpack.MetaData;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PushPromiseGenerateParseTest
|
||||
{
|
||||
private final ByteBufferPool byteBufferPool = new MappedByteBufferPool();
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testGenerateParse() throws Exception
|
||||
{
|
||||
PushPromiseGenerator generator = new PushPromiseGenerator(new HeaderGenerator(), new HpackEncoder());
|
||||
|
||||
int streamId = 13;
|
||||
int promisedStreamId = 17;
|
||||
HttpFields fields = new HttpFields();
|
||||
fields.put("Accept", "text/html");
|
||||
fields.put("User-Agent", "Jetty");
|
||||
MetaData.Request metaData = new MetaData.Request(HttpScheme.HTTP, "GET", "localhost:8080", "localhost", 8080, "/path", fields);
|
||||
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
final List<PushPromiseFrame> frames = new ArrayList<>();
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePushPromise(lease, streamId, promisedStreamId, metaData);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public boolean onPushPromise(PushPromiseFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
PushPromiseFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertEquals(promisedStreamId, frame.getPromisedStreamId());
|
||||
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
||||
Assert.assertSame(metaData.getScheme(), request.getScheme());
|
||||
Assert.assertEquals(metaData.getMethod(), request.getMethod());
|
||||
Assert.assertEquals(metaData.getAuthority(), request.getAuthority());
|
||||
Assert.assertEquals(metaData.getHost(), request.getHost());
|
||||
Assert.assertEquals(metaData.getPort(), request.getPort());
|
||||
Assert.assertEquals(metaData.getPath(), request.getPath());
|
||||
for (int j = 0; j < fields.size(); ++j)
|
||||
{
|
||||
HttpField field = fields.getField(j);
|
||||
Assert.assertTrue(request.getFields().contains(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateParseOneByteAtATime() throws Exception
|
||||
{
|
||||
PushPromiseGenerator generator = new PushPromiseGenerator(new HeaderGenerator(), new HpackEncoder());
|
||||
|
||||
int streamId = 13;
|
||||
int promisedStreamId = 17;
|
||||
HttpFields fields = new HttpFields();
|
||||
fields.put("Accept", "text/html");
|
||||
fields.put("User-Agent", "Jetty");
|
||||
MetaData.Request metaData = new MetaData.Request(HttpScheme.HTTP, "GET", "localhost:8080", "localhost", 8080, "/path", fields);
|
||||
|
||||
final List<PushPromiseFrame> frames = new ArrayList<>();
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePushPromise(lease, streamId, promisedStreamId, metaData);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public boolean onPushPromise(PushPromiseFrame frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
PushPromiseFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertEquals(promisedStreamId, frame.getPromisedStreamId());
|
||||
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
||||
Assert.assertSame(metaData.getScheme(), request.getScheme());
|
||||
Assert.assertEquals(metaData.getMethod(), request.getMethod());
|
||||
Assert.assertEquals(metaData.getAuthority(), request.getAuthority());
|
||||
Assert.assertEquals(metaData.getHost(), request.getHost());
|
||||
Assert.assertEquals(metaData.getPort(), request.getPort());
|
||||
Assert.assertEquals(metaData.getPath(), request.getPath());
|
||||
for (int j = 0; j < fields.size(); ++j)
|
||||
{
|
||||
HttpField field = fields.getField(j);
|
||||
Assert.assertTrue(request.getFields().contains(field));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue