Reworked generation of frames (split into different generators) and
sketched server-side handling and linking with channel.
This commit is contained in:
parent
1d2e9da29c
commit
ad034f4d54
|
@ -18,9 +18,14 @@
|
|||
|
||||
package org.eclipse.jetty.http2;
|
||||
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
import org.eclipse.jetty.http2.frames.Frame;
|
||||
import org.eclipse.jetty.http2.frames.GoAwayFrame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.frames.PingFrame;
|
||||
|
@ -28,27 +33,38 @@ import org.eclipse.jetty.http2.frames.PriorityFrame;
|
|||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
import org.eclipse.jetty.http2.frames.SettingsFrame;
|
||||
import org.eclipse.jetty.http2.frames.WindowUpdateFrame;
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.util.ArrayQueue;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IteratingCallback;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public abstract class HTTP2Session implements Session, Parser.Listener
|
||||
public abstract class HTTP2Session implements ISession, Parser.Listener
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(HTTP2Session.class);
|
||||
|
||||
private final ConcurrentMap<Integer, IStream> streams = new ConcurrentHashMap<>();
|
||||
private final Flusher flusher = new Flusher();
|
||||
private final EndPoint endPoint;
|
||||
private final Generator generator;
|
||||
private final Listener listener;
|
||||
|
||||
public HTTP2Session(Session.Listener listener)
|
||||
public HTTP2Session(EndPoint endPoint, Generator generator, Listener listener)
|
||||
{
|
||||
this.endPoint = endPoint;
|
||||
this.generator = generator;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onData(DataFrame frame)
|
||||
{
|
||||
return false;
|
||||
IStream stream = streams.get(frame.getStreamId());
|
||||
return stream.process(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -126,6 +142,18 @@ public abstract class HTTP2Session implements Session, Parser.Listener
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void frame(Frame frame, Callback callback)
|
||||
{
|
||||
Generator.LeaseCallback lease = generator.generate(frame, callback);
|
||||
flusher.flush(lease);
|
||||
}
|
||||
|
||||
protected IStream putIfAbsent(IStream stream)
|
||||
{
|
||||
return streams.putIfAbsent(stream.getId(), stream);
|
||||
}
|
||||
|
||||
protected Stream.Listener notifyNewStream(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
try
|
||||
|
@ -138,4 +166,55 @@ public abstract class HTTP2Session implements Session, Parser.Listener
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class Flusher extends IteratingCallback
|
||||
{
|
||||
private final ArrayQueue<Generator.LeaseCallback> queue = new ArrayQueue<>(ArrayQueue.DEFAULT_CAPACITY, ArrayQueue.DEFAULT_GROWTH);
|
||||
private Generator.LeaseCallback active;
|
||||
|
||||
private void flush(Generator.LeaseCallback lease)
|
||||
{
|
||||
synchronized (queue)
|
||||
{
|
||||
queue.offer(lease);
|
||||
}
|
||||
iterate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Action process() throws Exception
|
||||
{
|
||||
synchronized (queue)
|
||||
{
|
||||
active = queue.poll();
|
||||
}
|
||||
if (active == null)
|
||||
{
|
||||
return Action.IDLE;
|
||||
}
|
||||
|
||||
List<ByteBuffer> byteBuffers = active.getByteBuffers();
|
||||
endPoint.write(this, byteBuffers.toArray(new ByteBuffer[byteBuffers.size()]));
|
||||
return Action.SCHEDULED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
active.succeeded();
|
||||
super.succeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
active.failed(x);
|
||||
super.failed(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void completed()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
|
||||
package org.eclipse.jetty.http2;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
|
@ -25,6 +29,15 @@ import org.eclipse.jetty.util.Callback;
|
|||
|
||||
public class HTTP2Stream implements IStream
|
||||
{
|
||||
private final AtomicReference<ConcurrentMap<String, Object>> attributes = new AtomicReference<>();
|
||||
private final ISession session;
|
||||
private Listener listener;
|
||||
|
||||
public HTTP2Stream(ISession session)
|
||||
{
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId()
|
||||
{
|
||||
|
@ -40,13 +53,45 @@ public class HTTP2Stream implements IStream
|
|||
@Override
|
||||
public void headers(HeadersFrame frame, Callback callback)
|
||||
{
|
||||
|
||||
session.frame(frame, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void data(DataFrame frame, Callback callback)
|
||||
{
|
||||
session.frame(frame, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String key)
|
||||
{
|
||||
return attributes().get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String key, Object value)
|
||||
{
|
||||
attributes().put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object removeAttribute(String key)
|
||||
{
|
||||
return attributes().remove(key);
|
||||
}
|
||||
|
||||
private ConcurrentMap<String, Object> attributes()
|
||||
{
|
||||
ConcurrentMap<String, Object> map = attributes.get();
|
||||
if (map == null)
|
||||
{
|
||||
map = new ConcurrentHashMap<>();
|
||||
if (!attributes.compareAndSet(null, map))
|
||||
{
|
||||
map = attributes.get();
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,4 +99,30 @@ public class HTTP2Stream implements IStream
|
|||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(DataFrame frame)
|
||||
{
|
||||
return notifyData(frame);
|
||||
}
|
||||
|
||||
protected boolean notifyData(DataFrame frame)
|
||||
{
|
||||
final Listener listener = this.listener;
|
||||
listener.onData(this, frame, new Callback()
|
||||
{
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
// TODO: notify flow control
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
// TODO: bail out
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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;
|
||||
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.frames.Frame;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public interface ISession extends Session
|
||||
{
|
||||
public void frame(Frame frame, Callback callback);
|
||||
}
|
|
@ -19,8 +19,11 @@
|
|||
package org.eclipse.jetty.http2;
|
||||
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
|
||||
public interface IStream extends Stream
|
||||
{
|
||||
public void setListener(Listener listener);
|
||||
|
||||
public boolean process(DataFrame frame);
|
||||
}
|
||||
|
|
|
@ -32,11 +32,17 @@ public interface Stream
|
|||
|
||||
public void data(DataFrame frame, Callback callback);
|
||||
|
||||
public Object getAttribute(String key);
|
||||
|
||||
public void setAttribute(String key, Object value);
|
||||
|
||||
public Object removeAttribute(String key);
|
||||
|
||||
// TODO: see SPDY's Stream
|
||||
|
||||
public interface Listener
|
||||
{
|
||||
public void onData(Stream stream, DataFrame frame);
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback);
|
||||
|
||||
public void onFailure(Stream stream, Throwable x);
|
||||
|
||||
|
@ -45,7 +51,7 @@ public interface Stream
|
|||
public static class Adapter implements Listener
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataFrame frame)
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.http2.frames;
|
|||
|
||||
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;
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
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.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class DataGenerator extends FrameGenerator
|
||||
{
|
||||
public DataGenerator(HeaderGenerator headerGenerator)
|
||||
{
|
||||
super(headerGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(ByteBufferPool.Lease lease, Frame frame, Callback callback)
|
||||
{
|
||||
DataFrame dataFrame = (DataFrame)frame;
|
||||
generateData(lease, dataFrame.getStreamId(), dataFrame.getData(), dataFrame.isEndStream(), false, null);
|
||||
}
|
||||
|
||||
public void generateData(ByteBufferPool.Lease lease, int streamId, ByteBuffer data, boolean last, boolean compress, byte[] paddingBytes)
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
int paddingLength = paddingBytes == null ? 0 : paddingBytes.length;
|
||||
// Leave space for at least one byte of content.
|
||||
if (paddingLength > Frame.MAX_LENGTH - 3)
|
||||
throw new IllegalArgumentException("Invalid padding length: " + paddingLength);
|
||||
if (compress)
|
||||
throw new IllegalArgumentException("Data compression not supported");
|
||||
|
||||
int extraPaddingBytes = paddingLength > 0xFF ? 2 : paddingLength > 0 ? 1 : 0;
|
||||
|
||||
int dataLength = data.remaining();
|
||||
|
||||
// Can we fit just one frame ?
|
||||
if (dataLength + extraPaddingBytes + paddingLength <= Frame.MAX_LENGTH)
|
||||
{
|
||||
generateData(lease, streamId, data, last, compress, extraPaddingBytes, paddingBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
int dataBytesPerFrame = Frame.MAX_LENGTH - extraPaddingBytes - paddingLength;
|
||||
int frames = dataLength / dataBytesPerFrame;
|
||||
if (frames * dataBytesPerFrame != dataLength)
|
||||
{
|
||||
++frames;
|
||||
}
|
||||
int limit = data.limit();
|
||||
for (int i = 1; i <= frames; ++i)
|
||||
{
|
||||
data.limit(Math.min(dataBytesPerFrame * i, limit));
|
||||
ByteBuffer slice = data.slice();
|
||||
data.position(data.limit());
|
||||
generateData(lease, streamId, slice, i == frames && last, compress, extraPaddingBytes, paddingBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateData(ByteBufferPool.Lease lease, int streamId, ByteBuffer data, boolean last, boolean compress, int extraPaddingBytes, byte[] paddingBytes)
|
||||
{
|
||||
int paddingLength = paddingBytes == null ? 0 : paddingBytes.length;
|
||||
int length = extraPaddingBytes + data.remaining() + paddingLength;
|
||||
|
||||
int flags = Flag.NONE;
|
||||
if (last)
|
||||
flags |= Flag.END_STREAM;
|
||||
if (extraPaddingBytes > 0)
|
||||
flags |= Flag.PADDING_LOW;
|
||||
if (extraPaddingBytes > 1)
|
||||
flags |= Flag.PADDING_HIGH;
|
||||
if (compress)
|
||||
flags |= Flag.COMPRESS;
|
||||
|
||||
ByteBuffer header = generateHeader(lease, FrameType.DATA, Frame.HEADER_LENGTH + extraPaddingBytes, length, flags, streamId);
|
||||
|
||||
if (extraPaddingBytes == 2)
|
||||
{
|
||||
header.putShort((short)paddingLength);
|
||||
}
|
||||
else if (extraPaddingBytes == 1)
|
||||
{
|
||||
header.put((byte)paddingLength);
|
||||
}
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
|
||||
lease.append(data, false);
|
||||
|
||||
if (paddingBytes != null)
|
||||
{
|
||||
lease.append(ByteBuffer.wrap(paddingBytes), false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.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;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public abstract class FrameGenerator
|
||||
{
|
||||
private final HeaderGenerator headerGenerator;
|
||||
|
||||
protected FrameGenerator(HeaderGenerator headerGenerator)
|
||||
{
|
||||
this.headerGenerator = headerGenerator;
|
||||
}
|
||||
|
||||
public abstract void generate(ByteBufferPool.Lease lease, Frame frame, Callback callback);
|
||||
|
||||
protected ByteBuffer generateHeader(ByteBufferPool.Lease lease, FrameType frameType, int length, int flags, int streamId)
|
||||
{
|
||||
return generateHeader(lease, frameType, Frame.HEADER_LENGTH + length, length, flags, streamId);
|
||||
}
|
||||
|
||||
protected ByteBuffer generateHeader(ByteBufferPool.Lease lease, FrameType frameType, int capacity, int length, int flags, int streamId)
|
||||
{
|
||||
return headerGenerator.generate(lease, frameType, capacity, length, flags, streamId);
|
||||
}
|
||||
}
|
|
@ -18,284 +18,69 @@
|
|||
|
||||
package org.eclipse.jetty.http2.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
|
||||
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.hpack.HpackEncoder;
|
||||
import org.eclipse.jetty.http2.hpack.MetaData;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class Generator
|
||||
{
|
||||
private final ByteBufferPool byteBufferPool;
|
||||
private final HpackEncoder encoder;
|
||||
private final FrameGenerator[] generators;
|
||||
|
||||
public Generator(ByteBufferPool byteBufferPool)
|
||||
{
|
||||
this.byteBufferPool = byteBufferPool;
|
||||
this.encoder = new HpackEncoder();
|
||||
|
||||
HeaderGenerator headerGenerator = new HeaderGenerator();
|
||||
HpackEncoder encoder = new HpackEncoder();
|
||||
|
||||
this.generators = new FrameGenerator[FrameType.values().length];
|
||||
this.generators[FrameType.DATA.getType()] = new DataGenerator(headerGenerator);
|
||||
this.generators[FrameType.HEADERS.getType()] = new HeadersGenerator(headerGenerator, encoder);
|
||||
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.PING.getType()] = new PingGenerator(headerGenerator);
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
public ByteBufferPool.Lease generateData(int streamId, ByteBuffer data, boolean last, boolean compress, byte[] paddingBytes)
|
||||
public LeaseCallback generate(Frame frame, Callback callback)
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
int paddingLength = paddingBytes == null ? 0 : paddingBytes.length;
|
||||
// Leave space for at least one byte of content.
|
||||
if (paddingLength > Frame.MAX_LENGTH - 3)
|
||||
throw new IllegalArgumentException("Invalid padding length: " + paddingLength);
|
||||
|
||||
int extraPaddingBytes = paddingLength > 0xFF ? 2 : paddingLength > 0 ? 1 : 0;
|
||||
|
||||
// TODO: here we should compress the data, and then reason on the data length !
|
||||
|
||||
int dataLength = data.remaining();
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
|
||||
// Can we fit just one frame ?
|
||||
if (dataLength + extraPaddingBytes + paddingLength <= Frame.MAX_LENGTH)
|
||||
{
|
||||
generateData(lease, streamId, data, last, compress, extraPaddingBytes, paddingBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
int dataBytesPerFrame = Frame.MAX_LENGTH - extraPaddingBytes - paddingLength;
|
||||
int frames = dataLength / dataBytesPerFrame;
|
||||
if (frames * dataBytesPerFrame != dataLength)
|
||||
{
|
||||
++frames;
|
||||
}
|
||||
int limit = data.limit();
|
||||
for (int i = 1; i <= frames; ++i)
|
||||
{
|
||||
data.limit(Math.min(dataBytesPerFrame * i, limit));
|
||||
ByteBuffer slice = data.slice();
|
||||
data.position(data.limit());
|
||||
generateData(lease, streamId, slice, i == frames && last, compress, extraPaddingBytes, paddingBytes);
|
||||
}
|
||||
}
|
||||
LeaseCallback lease = new LeaseCallback(byteBufferPool, callback);
|
||||
generators[frame.getType().getType()].generate(lease, frame, callback);
|
||||
return lease;
|
||||
}
|
||||
|
||||
public ByteBufferPool.Lease generateHeaders(int streamId, MetaData metaData, boolean contentFollows, byte[] paddingBytes)
|
||||
public static class LeaseCallback extends ByteBufferPool.Lease implements Callback
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
int paddingLength = paddingBytes == null ? 0 : paddingBytes.length;
|
||||
// Leave space for at least one byte of content.
|
||||
if (paddingLength > Frame.MAX_LENGTH - 3)
|
||||
throw new IllegalArgumentException("Invalid padding length: " + paddingLength);
|
||||
private final Callback callback;
|
||||
|
||||
int extraPaddingBytes = paddingLength > 0xFF ? 2 : paddingLength > 0 ? 1 : 0;
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
|
||||
encoder.encode(metaData, lease);
|
||||
|
||||
long hpackLength = lease.getTotalLength();
|
||||
|
||||
long length = extraPaddingBytes + hpackLength + paddingLength;
|
||||
if (length > Frame.MAX_LENGTH)
|
||||
throw new IllegalArgumentException("Invalid headers, too big");
|
||||
|
||||
int flags = Flag.END_HEADERS;
|
||||
if (!contentFollows)
|
||||
flags |= Flag.END_STREAM;
|
||||
if (extraPaddingBytes > 0)
|
||||
flags |= Flag.PADDING_LOW;
|
||||
if (extraPaddingBytes > 1)
|
||||
flags |= Flag.PADDING_HIGH;
|
||||
|
||||
ByteBuffer header = generateHeader(FrameType.HEADERS, Frame.HEADER_LENGTH + extraPaddingBytes, (int)length, flags, streamId);
|
||||
|
||||
if (extraPaddingBytes == 2)
|
||||
header.putShort((short)paddingLength);
|
||||
else if (extraPaddingBytes == 1)
|
||||
header.put((byte)paddingLength);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.prepend(header, true);
|
||||
|
||||
if (paddingBytes != null)
|
||||
public LeaseCallback(ByteBufferPool byteBufferPool, Callback callback)
|
||||
{
|
||||
lease.append(ByteBuffer.wrap(paddingBytes), false);
|
||||
super(byteBufferPool);
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
return lease;
|
||||
}
|
||||
|
||||
public ByteBufferPool.Lease generatePriority(int streamId, int dependentStreamId, int weight, boolean exclusive)
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
if (dependentStreamId < 0)
|
||||
throw new IllegalArgumentException("Invalid dependent stream id: " + dependentStreamId);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
|
||||
ByteBuffer header = generateHeader(FrameType.PRIORITY, 5, 0, dependentStreamId);
|
||||
|
||||
if (exclusive)
|
||||
streamId |= 0x80_00_00_00;
|
||||
|
||||
header.putInt(streamId);
|
||||
|
||||
header.put((byte)weight);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
|
||||
return lease;
|
||||
}
|
||||
|
||||
public ByteBufferPool.Lease generateReset(int streamId, int error)
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
|
||||
ByteBuffer header = generateHeader(FrameType.RST_STREAM, 4, 0, streamId);
|
||||
|
||||
header.putInt(error);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
|
||||
return lease;
|
||||
}
|
||||
|
||||
public ByteBufferPool.Lease generateSettings(Map<Integer, Integer> settings, boolean reply)
|
||||
{
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
|
||||
ByteBuffer header = generateHeader(FrameType.SETTINGS, 5 * settings.size(), reply ? 0x01 : 0x00, 0);
|
||||
|
||||
for (Map.Entry<Integer, Integer> entry : settings.entrySet())
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
header.put(entry.getKey().byteValue());
|
||||
header.putInt(entry.getValue());
|
||||
recycle();
|
||||
callback.succeeded();
|
||||
}
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
|
||||
return lease;
|
||||
}
|
||||
|
||||
public ByteBufferPool.Lease generatePing(byte[] payload, boolean reply)
|
||||
{
|
||||
if (payload.length != 8)
|
||||
throw new IllegalArgumentException("Invalid payload length: " + payload.length);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
|
||||
ByteBuffer header = generateHeader(FrameType.PING, 8, reply ? 0x01 : 0x00, 0);
|
||||
|
||||
header.put(payload);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
|
||||
return lease;
|
||||
}
|
||||
|
||||
public ByteBufferPool.Lease generateGoAway(int lastStreamId, int error, byte[] payload)
|
||||
{
|
||||
if (lastStreamId < 0)
|
||||
throw new IllegalArgumentException("Invalid last stream id: " + lastStreamId);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
|
||||
int length = 4 + 4 + (payload != null ? payload.length : 0);
|
||||
ByteBuffer header = generateHeader(FrameType.GO_AWAY, length, 0, 0);
|
||||
|
||||
header.putInt(lastStreamId);
|
||||
header.putInt(error);
|
||||
|
||||
if (payload != null)
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
header.put(payload);
|
||||
recycle();
|
||||
callback.failed(x);
|
||||
}
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
|
||||
return lease;
|
||||
}
|
||||
|
||||
public ByteBufferPool.Lease generateWindowUpdate(int streamId, int windowUpdate)
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
if (windowUpdate < 0)
|
||||
throw new IllegalArgumentException("Invalid window update: " + windowUpdate);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
|
||||
ByteBuffer header = generateHeader(FrameType.WINDOW_UPDATE, 4, 0, streamId);
|
||||
|
||||
header.putInt(windowUpdate);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
|
||||
return lease;
|
||||
}
|
||||
|
||||
private void generateData(ByteBufferPool.Lease lease, int streamId, ByteBuffer data, boolean last, boolean compress, int extraPaddingBytes, byte[] paddingBytes)
|
||||
{
|
||||
int paddingLength = paddingBytes == null ? 0 : paddingBytes.length;
|
||||
int length = extraPaddingBytes + data.remaining() + paddingLength;
|
||||
|
||||
int flags = 0;
|
||||
if (last)
|
||||
flags |= Flag.END_STREAM;
|
||||
if (extraPaddingBytes > 0)
|
||||
flags |= Flag.PADDING_LOW;
|
||||
if (extraPaddingBytes > 1)
|
||||
flags |= Flag.PADDING_HIGH;
|
||||
if (compress)
|
||||
flags |= Flag.COMPRESS;
|
||||
|
||||
ByteBuffer header = generateHeader(FrameType.DATA, Frame.HEADER_LENGTH + extraPaddingBytes, length, flags, streamId);
|
||||
|
||||
if (extraPaddingBytes == 2)
|
||||
header.putShort((short)paddingLength);
|
||||
else if (extraPaddingBytes == 1)
|
||||
header.put((byte)paddingLength);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
|
||||
lease.append(data, false);
|
||||
|
||||
if (paddingBytes != null)
|
||||
{
|
||||
lease.append(ByteBuffer.wrap(paddingBytes), false);
|
||||
}
|
||||
}
|
||||
|
||||
private ByteBuffer generateHeader(FrameType frameType, int length, int flags, int streamId)
|
||||
{
|
||||
return generateHeader(frameType, Frame.HEADER_LENGTH + length, length, flags, streamId);
|
||||
}
|
||||
|
||||
private ByteBuffer generateHeader(FrameType frameType, int capacity, int length, int flags, int streamId)
|
||||
{
|
||||
ByteBuffer header = byteBufferPool.acquire(capacity, true);
|
||||
BufferUtil.clearToFill(header);
|
||||
|
||||
header.putShort((short)length);
|
||||
header.put((byte)frameType.getType());
|
||||
header.put((byte)flags);
|
||||
header.putInt(streamId);
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.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.GoAwayFrame;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class GoAwayGenerator extends FrameGenerator
|
||||
{
|
||||
public GoAwayGenerator(HeaderGenerator headerGenerator)
|
||||
{
|
||||
super(headerGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(ByteBufferPool.Lease lease, Frame frame, Callback callback)
|
||||
{
|
||||
GoAwayFrame goAwayFrame = (GoAwayFrame)frame;
|
||||
generateGoAway(lease, goAwayFrame.getLastStreamId(), goAwayFrame.getError(), null);
|
||||
}
|
||||
|
||||
public void generateGoAway(ByteBufferPool.Lease lease, int lastStreamId, int error, byte[] payload)
|
||||
{
|
||||
if (lastStreamId < 0)
|
||||
throw new IllegalArgumentException("Invalid last stream id: " + lastStreamId);
|
||||
|
||||
int length = 4 + 4 + (payload != null ? payload.length : 0);
|
||||
ByteBuffer header = generateHeader(lease, FrameType.GO_AWAY, length, Flag.NONE, 0);
|
||||
|
||||
header.putInt(lastStreamId);
|
||||
header.putInt(error);
|
||||
|
||||
if (payload != null)
|
||||
{
|
||||
header.put(payload);
|
||||
}
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http2.frames.FrameType;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
|
||||
public class HeaderGenerator
|
||||
{
|
||||
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)frameType.getType());
|
||||
header.put((byte)flags);
|
||||
header.putInt(streamId);
|
||||
return header;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.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.HeadersFrame;
|
||||
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;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class HeadersGenerator extends FrameGenerator
|
||||
{
|
||||
private final HpackEncoder encoder;
|
||||
|
||||
public HeadersGenerator(HeaderGenerator headerGenerator, HpackEncoder encoder)
|
||||
{
|
||||
super(headerGenerator);
|
||||
this.encoder = encoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(ByteBufferPool.Lease lease, Frame frame, Callback callback)
|
||||
{
|
||||
HeadersFrame headersFrame = (HeadersFrame)frame;
|
||||
generate(lease, headersFrame.getStreamId(), headersFrame.getMetaData(), !headersFrame.isEndStream(), null);
|
||||
}
|
||||
|
||||
private void generate(ByteBufferPool.Lease lease, int streamId, MetaData metaData, boolean contentFollows, byte[] paddingBytes)
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
int paddingLength = paddingBytes == null ? 0 : paddingBytes.length;
|
||||
// Leave space for at least one byte of content.
|
||||
if (paddingLength > Frame.MAX_LENGTH - 3)
|
||||
throw new IllegalArgumentException("Invalid padding length: " + paddingLength);
|
||||
|
||||
int extraPaddingBytes = paddingLength > 0xFF ? 2 : paddingLength > 0 ? 1 : 0;
|
||||
|
||||
encoder.encode(metaData, lease);
|
||||
|
||||
long hpackLength = lease.getTotalLength();
|
||||
|
||||
long length = extraPaddingBytes + hpackLength + paddingLength;
|
||||
if (length > Frame.MAX_LENGTH)
|
||||
throw new IllegalArgumentException("Invalid headers, too big");
|
||||
|
||||
int flags = Flag.END_HEADERS;
|
||||
if (!contentFollows)
|
||||
flags |= Flag.END_STREAM;
|
||||
if (extraPaddingBytes > 0)
|
||||
flags |= Flag.PADDING_LOW;
|
||||
if (extraPaddingBytes > 1)
|
||||
flags |= Flag.PADDING_HIGH;
|
||||
|
||||
ByteBuffer header = generateHeader(lease, FrameType.HEADERS, Frame.HEADER_LENGTH + extraPaddingBytes, (int)length, flags, streamId);
|
||||
|
||||
if (extraPaddingBytes == 2)
|
||||
header.putShort((short)paddingLength);
|
||||
else if (extraPaddingBytes == 1)
|
||||
header.put((byte)paddingLength);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.prepend(header, true);
|
||||
|
||||
if (paddingBytes != null)
|
||||
{
|
||||
lease.append(ByteBuffer.wrap(paddingBytes), false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.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.PingFrame;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class PingGenerator extends FrameGenerator
|
||||
{
|
||||
public PingGenerator(HeaderGenerator headerGenerator)
|
||||
{
|
||||
super(headerGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(ByteBufferPool.Lease lease, Frame frame, Callback callback)
|
||||
{
|
||||
PingFrame pingFrame = (PingFrame)frame;
|
||||
generatePing(lease, pingFrame.getPayload(), pingFrame.isReply());
|
||||
}
|
||||
|
||||
public void generatePing(ByteBufferPool.Lease lease, byte[] payload, boolean reply)
|
||||
{
|
||||
if (payload.length != 8)
|
||||
throw new IllegalArgumentException("Invalid payload length: " + payload.length);
|
||||
|
||||
ByteBuffer header = generateHeader(lease, FrameType.PING, 8, reply ? Flag.ACK : Flag.NONE, 0);
|
||||
|
||||
header.put(payload);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.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.PriorityFrame;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class PriorityGenerator extends FrameGenerator
|
||||
{
|
||||
public PriorityGenerator(HeaderGenerator headerGenerator)
|
||||
{
|
||||
super(headerGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(ByteBufferPool.Lease lease, Frame frame, Callback callback)
|
||||
{
|
||||
PriorityFrame priorityFrame = (PriorityFrame)frame;
|
||||
generatePriority(lease, priorityFrame.getStreamId(), priorityFrame.getDependentStreamId(), priorityFrame.getWeight(), priorityFrame.isExclusive());
|
||||
}
|
||||
|
||||
public void generatePriority(ByteBufferPool.Lease lease, int streamId, int dependentStreamId, int weight, boolean exclusive)
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
if (dependentStreamId < 0)
|
||||
throw new IllegalArgumentException("Invalid dependent stream id: " + dependentStreamId);
|
||||
|
||||
ByteBuffer header = generateHeader(lease, FrameType.PRIORITY, 5, Flag.NONE, dependentStreamId);
|
||||
|
||||
if (exclusive)
|
||||
streamId |= 0x80_00_00_00;
|
||||
|
||||
header.putInt(streamId);
|
||||
|
||||
header.put((byte)weight);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.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.ResetFrame;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class ResetGenerator extends FrameGenerator
|
||||
{
|
||||
public ResetGenerator(HeaderGenerator headerGenerator)
|
||||
{
|
||||
super(headerGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(ByteBufferPool.Lease lease, Frame frame, Callback callback)
|
||||
{
|
||||
ResetFrame resetFrame = (ResetFrame)frame;
|
||||
generateReset(lease, resetFrame.getStreamId(), resetFrame.getError());
|
||||
}
|
||||
|
||||
public void generateReset(ByteBufferPool.Lease lease, int streamId, int error)
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
|
||||
ByteBuffer header = generateHeader(lease, FrameType.RST_STREAM, 4, Flag.NONE, streamId);
|
||||
|
||||
header.putInt(error);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
|
||||
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.SettingsFrame;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class SettingsGenerator extends FrameGenerator
|
||||
{
|
||||
public SettingsGenerator(HeaderGenerator headerGenerator)
|
||||
{
|
||||
super(headerGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(ByteBufferPool.Lease lease, Frame frame, Callback callback)
|
||||
{
|
||||
SettingsFrame settingsFrame = (SettingsFrame)frame;
|
||||
generateSettings(lease, settingsFrame.getSettings(), settingsFrame.isReply());
|
||||
}
|
||||
|
||||
public void generateSettings(ByteBufferPool.Lease lease, Map<Integer, Integer> settings, boolean reply)
|
||||
{
|
||||
ByteBuffer header = generateHeader(lease, FrameType.SETTINGS, 5 * settings.size(), reply ? Flag.ACK : Flag.NONE, 0);
|
||||
|
||||
for (Map.Entry<Integer, Integer> entry : settings.entrySet())
|
||||
{
|
||||
header.put(entry.getKey().byteValue());
|
||||
header.putInt(entry.getValue());
|
||||
}
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.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.WindowUpdateFrame;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class WindowUpdateGenerator extends FrameGenerator
|
||||
{
|
||||
public WindowUpdateGenerator(HeaderGenerator headerGenerator)
|
||||
{
|
||||
super(headerGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(ByteBufferPool.Lease lease, Frame frame, Callback callback)
|
||||
{
|
||||
WindowUpdateFrame windowUpdateFrame = (WindowUpdateFrame)frame;
|
||||
generateWindowUpdate(lease, windowUpdateFrame.getStreamId(), windowUpdateFrame.getWindowDelta());
|
||||
}
|
||||
|
||||
public void generateWindowUpdate(ByteBufferPool.Lease lease, int streamId, int windowUpdate)
|
||||
{
|
||||
if (streamId < 0)
|
||||
throw new IllegalArgumentException("Invalid stream id: " + streamId);
|
||||
if (windowUpdate < 0)
|
||||
throw new IllegalArgumentException("Invalid window update: " + windowUpdate);
|
||||
|
||||
ByteBuffer header = generateHeader(lease, FrameType.WINDOW_UPDATE, 4, Flag.NONE, streamId);
|
||||
|
||||
header.putInt(windowUpdate);
|
||||
|
||||
BufferUtil.flipToFlush(header, 0);
|
||||
lease.append(header, true);
|
||||
}
|
||||
}
|
|
@ -23,7 +23,8 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.http2.generator.DataGenerator;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
|
@ -132,7 +133,7 @@ public class DataGenerateParseTest
|
|||
|
||||
private List<DataFrame> testGenerateParse(int paddingLength, ByteBuffer... data)
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
DataGenerator generator = new DataGenerator(new HeaderGenerator());
|
||||
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
final List<DataFrame> frames = new ArrayList<>();
|
||||
|
@ -141,7 +142,7 @@ public class DataGenerateParseTest
|
|||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
for (int j = 1; j <= data.length; ++j)
|
||||
{
|
||||
lease = lease.merge(generator.generateData(13, data[j - 1].slice(), j == data.length, false, new byte[paddingLength]));
|
||||
generator.generateData(lease, 13, data[j - 1].slice(), j == data.length, false, new byte[paddingLength]);
|
||||
}
|
||||
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
|
@ -167,9 +168,10 @@ public class DataGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParseOneByteAtATime()
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
DataGenerator generator = new DataGenerator(new HeaderGenerator());
|
||||
|
||||
ByteBufferPool.Lease lease = generator.generateData(13, ByteBuffer.wrap(largeContent).slice(), true, false, new byte[1024]);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateData(lease, 13, ByteBuffer.wrap(largeContent).slice(), true, false, new byte[1024]);
|
||||
|
||||
final List<DataFrame> frames = new ArrayList<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
|
|
|
@ -23,7 +23,8 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.http2.generator.GoAwayGenerator;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
|
@ -37,7 +38,7 @@ public class GoAwayGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParse() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
GoAwayGenerator generator = new GoAwayGenerator(new HeaderGenerator());
|
||||
|
||||
int lastStreamId = 13;
|
||||
int error = 17;
|
||||
|
@ -46,7 +47,8 @@ public class GoAwayGenerateParseTest
|
|||
final List<GoAwayFrame> frames = new ArrayList<>();
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
ByteBufferPool.Lease lease = generator.generateGoAway(lastStreamId, error, null);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateGoAway(lease, lastStreamId, error, null);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -77,7 +79,7 @@ public class GoAwayGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParseOneByteAtATime() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
GoAwayGenerator generator = new GoAwayGenerator(new HeaderGenerator());
|
||||
|
||||
int lastStreamId = 13;
|
||||
int error = 17;
|
||||
|
@ -85,7 +87,8 @@ public class GoAwayGenerateParseTest
|
|||
new Random().nextBytes(payload);
|
||||
|
||||
final List<GoAwayFrame> frames = new ArrayList<>();
|
||||
ByteBufferPool.Lease lease = generator.generateGoAway(lastStreamId, error, payload);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateGoAway(lease, lastStreamId, error, payload);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -23,7 +23,8 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.generator.PingGenerator;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
|
@ -37,7 +38,7 @@ public class PingGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParse() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
PingGenerator generator = new PingGenerator(new HeaderGenerator());
|
||||
|
||||
byte[] payload = new byte[8];
|
||||
new Random().nextBytes(payload);
|
||||
|
@ -46,7 +47,8 @@ public class PingGenerateParseTest
|
|||
final List<PingFrame> frames = new ArrayList<>();
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
ByteBufferPool.Lease lease = generator.generatePing(payload, true);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePing(lease, payload, true);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -76,13 +78,14 @@ public class PingGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParseOneByteAtATime() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
PingGenerator generator = new PingGenerator(new HeaderGenerator());
|
||||
|
||||
byte[] payload = new byte[8];
|
||||
new Random().nextBytes(payload);
|
||||
|
||||
final List<PingFrame> frames = new ArrayList<>();
|
||||
ByteBufferPool.Lease lease = generator.generatePing(payload, true);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePing(lease, payload, true);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -22,7 +22,8 @@ import java.nio.ByteBuffer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.generator.PriorityGenerator;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
|
@ -36,7 +37,7 @@ public class PriorityGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParse() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
PriorityGenerator generator = new PriorityGenerator(new HeaderGenerator());
|
||||
|
||||
int streamId = 13;
|
||||
int dependentStreamId = 17;
|
||||
|
@ -47,7 +48,8 @@ public class PriorityGenerateParseTest
|
|||
final List<PriorityFrame> frames = new ArrayList<>();
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
ByteBufferPool.Lease lease = generator.generatePriority(streamId, dependentStreamId, weight, exclusive);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePriority(lease, streamId, dependentStreamId, weight, exclusive);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -79,7 +81,7 @@ public class PriorityGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParseOneByteAtATime() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
PriorityGenerator generator = new PriorityGenerator(new HeaderGenerator());
|
||||
|
||||
int streamId = 13;
|
||||
int dependentStreamId = 17;
|
||||
|
@ -87,7 +89,8 @@ public class PriorityGenerateParseTest
|
|||
boolean exclusive = true;
|
||||
|
||||
final List<PriorityFrame> frames = new ArrayList<>();
|
||||
ByteBufferPool.Lease lease = generator.generatePriority(streamId, dependentStreamId, weight, exclusive);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePriority(lease, streamId, dependentStreamId, weight, exclusive);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -22,7 +22,8 @@ import java.nio.ByteBuffer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.generator.ResetGenerator;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
|
@ -36,7 +37,7 @@ public class ResetGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParse() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
ResetGenerator generator = new ResetGenerator(new HeaderGenerator());
|
||||
|
||||
int streamId = 13;
|
||||
int error = 17;
|
||||
|
@ -45,7 +46,8 @@ public class ResetGenerateParseTest
|
|||
final List<ResetFrame> frames = new ArrayList<>();
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
ByteBufferPool.Lease lease = generator.generateReset(streamId, error);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateReset(lease, streamId, error);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -75,13 +77,14 @@ public class ResetGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParseOneByteAtATime() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
ResetGenerator generator = new ResetGenerator(new HeaderGenerator());
|
||||
|
||||
int streamId = 13;
|
||||
int error = 17;
|
||||
|
||||
final List<ResetFrame> frames = new ArrayList<>();
|
||||
ByteBufferPool.Lease lease = generator.generateReset(streamId, error);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateReset(lease, streamId, error);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -26,7 +26,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.generator.SettingsGenerator;
|
||||
import org.eclipse.jetty.http2.parser.ErrorCode;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
|
@ -69,13 +70,14 @@ public class SettingsGenerateParseTest
|
|||
|
||||
private List<SettingsFrame> testGenerateParse(Map<Integer, Integer> settings)
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator());
|
||||
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
final List<SettingsFrame> frames = new ArrayList<>();
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
ByteBufferPool.Lease lease = generator.generateSettings(settings, true);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateSettings(lease, settings, true);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -102,10 +104,11 @@ public class SettingsGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParseInvalidSettings() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator());
|
||||
Map<Integer, Integer> settings1 = new HashMap<>();
|
||||
settings1.put(13, 17);
|
||||
ByteBufferPool.Lease lease = generator.generateSettings(settings1, true);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
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));
|
||||
|
@ -134,7 +137,7 @@ public class SettingsGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParseOneByteAtATime() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
SettingsGenerator generator = new SettingsGenerator(new HeaderGenerator());
|
||||
|
||||
Map<Integer, Integer> settings1 = new HashMap<>();
|
||||
int key = 13;
|
||||
|
@ -142,7 +145,8 @@ public class SettingsGenerateParseTest
|
|||
settings1.put(key, value);
|
||||
|
||||
final List<SettingsFrame> frames = new ArrayList<>();
|
||||
ByteBufferPool.Lease lease = generator.generateSettings(settings1, true);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateSettings(lease, settings1, true);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -22,7 +22,8 @@ import java.nio.ByteBuffer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.http2.generator.HeaderGenerator;
|
||||
import org.eclipse.jetty.http2.generator.WindowUpdateGenerator;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
|
@ -36,7 +37,7 @@ public class WindowUpdateGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParse() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
WindowUpdateGenerator generator = new WindowUpdateGenerator(new HeaderGenerator());
|
||||
|
||||
int streamId = 13;
|
||||
int windowUpdate = 17;
|
||||
|
@ -45,7 +46,8 @@ public class WindowUpdateGenerateParseTest
|
|||
final List<WindowUpdateFrame> frames = new ArrayList<>();
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
ByteBufferPool.Lease lease = generator.generateWindowUpdate(streamId, windowUpdate);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateWindowUpdate(lease, streamId, windowUpdate);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
@ -75,13 +77,14 @@ public class WindowUpdateGenerateParseTest
|
|||
@Test
|
||||
public void testGenerateParseOneByteAtATime() throws Exception
|
||||
{
|
||||
Generator generator = new Generator(byteBufferPool);
|
||||
WindowUpdateGenerator generator = new WindowUpdateGenerator(new HeaderGenerator());
|
||||
|
||||
int streamId = 13;
|
||||
int windowUpdate = 17;
|
||||
|
||||
final List<WindowUpdateFrame> frames = new ArrayList<>();
|
||||
ByteBufferPool.Lease lease = generator.generateWindowUpdate(streamId, windowUpdate);
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateWindowUpdate(lease, streamId, windowUpdate);
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -54,6 +54,17 @@
|
|||
<artifactId>jetty-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-test-helper</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.server;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class ByteBufferCallback implements Callback
|
||||
{
|
||||
private final ByteBufferPool byteBufferPool;
|
||||
private final ByteBuffer buffer;
|
||||
private final Callback callback;
|
||||
|
||||
public ByteBufferCallback(ByteBufferPool byteBufferPool, ByteBuffer buffer, Callback callback)
|
||||
{
|
||||
this.byteBufferPool = byteBufferPool;
|
||||
this.buffer = buffer;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public ByteBuffer getByteBuffer()
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
recycle();
|
||||
callback.succeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
recycle();
|
||||
callback.failed(x);
|
||||
}
|
||||
|
||||
private void recycle()
|
||||
{
|
||||
byteBufferPool.release(buffer);
|
||||
}
|
||||
}
|
|
@ -25,18 +25,22 @@ import org.eclipse.jetty.http2.api.Stream;
|
|||
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.http2.parser.Parser;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.AbstractConnectionFactory;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
public class HTTP2ServerConnectionFactory extends AbstractConnectionFactory
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(HTTP2ServerConnectionFactory.class);
|
||||
private static final String CHANNEL_ATTRIBUTE = HttpChannelOverHTTP2.class.getName();
|
||||
|
||||
private final HttpConfiguration httpConfiguration;
|
||||
|
||||
public HTTP2ServerConnectionFactory(HttpConfiguration httpConfiguration)
|
||||
|
@ -50,7 +54,8 @@ public class HTTP2ServerConnectionFactory extends AbstractConnectionFactory
|
|||
{
|
||||
Session.Listener listener = new HTTPServerSessionListener(connector, httpConfiguration, endPoint);
|
||||
|
||||
HTTP2Session session = new HTTP2ServerSession(listener);
|
||||
Generator generator = new Generator(connector.getByteBufferPool());
|
||||
HTTP2Session session = new HTTP2ServerSession(endPoint, generator, listener);
|
||||
|
||||
Parser parser = new Parser(connector.getByteBufferPool(), session);
|
||||
HTTP2Connection connection = new HTTP2Connection(connector.getByteBufferPool(), connector.getExecutor(),
|
||||
|
@ -67,7 +72,6 @@ public class HTTP2ServerConnectionFactory extends AbstractConnectionFactory
|
|||
|
||||
public HTTPServerSessionListener(Connector connector, HttpConfiguration httpConfiguration, EndPoint endPoint)
|
||||
{
|
||||
|
||||
this.connector = connector;
|
||||
this.httpConfiguration = httpConfiguration;
|
||||
this.endPoint = endPoint;
|
||||
|
@ -81,29 +85,24 @@ public class HTTP2ServerConnectionFactory extends AbstractConnectionFactory
|
|||
HttpTransportOverHTTP2 transport = new HttpTransportOverHTTP2();
|
||||
HttpInputOverHTTP2 input = new HttpInputOverHTTP2();
|
||||
HttpChannelOverHTTP2 channel = new HttpChannelOverHTTP2(connector, httpConfiguration, endPoint, transport, input);
|
||||
// TODO: link channel to stream.
|
||||
stream.setAttribute(CHANNEL_ATTRIBUTE, channel);
|
||||
|
||||
// if (frame.getMetaData().isEmpty())
|
||||
// {
|
||||
// TODO: abort.
|
||||
// return null;
|
||||
// }
|
||||
|
||||
channel.requestStart(frame);
|
||||
channel.requestHeaders(frame);
|
||||
|
||||
return frame.isEndStream() ? null : this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataFrame frame)
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
|
||||
HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(CHANNEL_ATTRIBUTE);
|
||||
channel.requestContent(frame, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Stream stream, Throwable x)
|
||||
{
|
||||
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,22 +18,19 @@
|
|||
|
||||
package org.eclipse.jetty.http2.server;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.eclipse.jetty.http2.HTTP2Session;
|
||||
import org.eclipse.jetty.http2.HTTP2Stream;
|
||||
import org.eclipse.jetty.http2.IStream;
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
|
||||
public class HTTP2ServerSession extends HTTP2Session
|
||||
{
|
||||
private final ConcurrentMap<Integer, IStream> streams = new ConcurrentHashMap<>();
|
||||
|
||||
public HTTP2ServerSession(Listener listener)
|
||||
public HTTP2ServerSession(EndPoint endPoint, Generator generator, Listener listener)
|
||||
{
|
||||
super(listener);
|
||||
super(endPoint, generator, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -41,9 +38,10 @@ public class HTTP2ServerSession extends HTTP2Session
|
|||
{
|
||||
// TODO: handle max concurrent streams
|
||||
// TODO: handle duplicate streams
|
||||
// TODO: handle empty headers
|
||||
|
||||
IStream stream = new HTTP2Stream();
|
||||
IStream existing = streams.putIfAbsent(stream.getId(), stream);
|
||||
IStream stream = new HTTP2Stream(this);
|
||||
IStream existing = putIfAbsent(stream);
|
||||
if (existing == null)
|
||||
{
|
||||
Stream.Listener listener = notifyNewStream(stream, frame);
|
||||
|
|
|
@ -19,11 +19,16 @@
|
|||
package org.eclipse.jetty.http2.server;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.hpack.MetaData;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
|
@ -31,23 +36,83 @@ import org.eclipse.jetty.server.HttpConfiguration;
|
|||
import org.eclipse.jetty.server.HttpInput;
|
||||
import org.eclipse.jetty.server.HttpTransport;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class HttpChannelOverHTTP2 extends HttpChannel<ByteBuffer>
|
||||
public class HttpChannelOverHTTP2 extends HttpChannel<ByteBufferCallback>
|
||||
{
|
||||
public HttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
|
||||
public HttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBufferCallback> input)
|
||||
{
|
||||
super(connector, configuration, endPoint, transport, input);
|
||||
}
|
||||
|
||||
public void requestStart(HeadersFrame frame)
|
||||
public void requestHeaders(HeadersFrame frame)
|
||||
{
|
||||
// TODO: extract method, etc.
|
||||
String method = null;
|
||||
HttpURI uri = new HttpURI("/foo/bar");
|
||||
HttpVersion version = null;
|
||||
MetaData metaData = frame.getMetaData();
|
||||
if (!metaData.isRequest())
|
||||
{
|
||||
badMessage(400, null);
|
||||
return;
|
||||
}
|
||||
|
||||
MetaData.Request requestMetaData = (MetaData.Request)metaData;
|
||||
|
||||
String method = requestMetaData.getMethod();
|
||||
HttpURI uri = new HttpURI(requestMetaData.getPath());
|
||||
HttpVersion version = HttpVersion.HTTP_2_0;
|
||||
|
||||
startRequest(method, uri, version);
|
||||
|
||||
// TODO: See SPDY's
|
||||
HttpScheme scheme = requestMetaData.getScheme();
|
||||
if (scheme != null)
|
||||
{
|
||||
getRequest().setScheme(scheme.asString());
|
||||
}
|
||||
|
||||
parsedHostHeader(requestMetaData.getHost(), requestMetaData.getPort());
|
||||
|
||||
List<HttpField> fields = requestMetaData.getFields();
|
||||
for (int i = 0; i < fields.size(); ++i)
|
||||
{
|
||||
HttpField field = fields.get(i);
|
||||
parsedHeader(field);
|
||||
}
|
||||
|
||||
headerComplete();
|
||||
|
||||
if (frame.isEndStream())
|
||||
{
|
||||
messageComplete();
|
||||
}
|
||||
|
||||
// TODO: pending refactoring of HttpChannel API.
|
||||
// Here we "cheat", knowing that headerComplete() will always return true
|
||||
// and that content() and messageComplete() will always return false.
|
||||
// This is the only place where we process the channel.
|
||||
execute(this);
|
||||
}
|
||||
|
||||
public void requestContent(DataFrame frame, Callback callback)
|
||||
{
|
||||
// We must copy the data since we do not know when its bytes will be consumed.
|
||||
ByteBufferPool byteBufferPool = getByteBufferPool();
|
||||
ByteBuffer original = frame.getData();
|
||||
final ByteBuffer copy = byteBufferPool.acquire(original.remaining(), original.isDirect());
|
||||
BufferUtil.clearToFill(copy);
|
||||
copy.put(original).flip();
|
||||
|
||||
// TODO: pending refactoring of HttpChannel API (see above).
|
||||
content(new ByteBufferCallback(byteBufferPool, copy, callback));
|
||||
|
||||
if (frame.isEndStream())
|
||||
{
|
||||
messageComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean messageComplete()
|
||||
{
|
||||
super.messageComplete();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,27 +22,35 @@ import java.nio.ByteBuffer;
|
|||
|
||||
import org.eclipse.jetty.server.QueuedHttpInput;
|
||||
|
||||
public class HttpInputOverHTTP2 extends QueuedHttpInput<ByteBuffer>
|
||||
public class HttpInputOverHTTP2 extends QueuedHttpInput<ByteBufferCallback>
|
||||
{
|
||||
@Override
|
||||
protected void onContentConsumed(ByteBuffer item)
|
||||
protected int remaining(ByteBufferCallback item)
|
||||
{
|
||||
return item.getByteBuffer().remaining();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int remaining(ByteBuffer item)
|
||||
protected int get(ByteBufferCallback item, byte[] buffer, int offset, int length)
|
||||
{
|
||||
return 0;
|
||||
ByteBuffer byteBuffer = item.getByteBuffer();
|
||||
length = Math.min(byteBuffer.remaining(), length);
|
||||
byteBuffer.get(buffer, offset, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int get(ByteBuffer item, byte[] buffer, int offset, int length)
|
||||
protected void consume(ByteBufferCallback item, int length)
|
||||
{
|
||||
return 0;
|
||||
ByteBuffer byteBuffer = item.getByteBuffer();
|
||||
byteBuffer.position(byteBuffer.position() + length);
|
||||
if (!byteBuffer.hasRemaining())
|
||||
onContentConsumed(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void consume(ByteBuffer item, int length)
|
||||
protected void onContentConsumed(ByteBufferCallback item)
|
||||
{
|
||||
item.succeeded();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,21 +19,80 @@
|
|||
package org.eclipse.jetty.http2.server;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.eclipse.jetty.http.HttpGenerator;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http2.IStream;
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.hpack.MetaData;
|
||||
import org.eclipse.jetty.server.HttpTransport;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class HttpTransportOverHTTP2 implements HttpTransport
|
||||
{
|
||||
private final AtomicBoolean commit = new AtomicBoolean();
|
||||
private final IStream stream = null;
|
||||
private final HeadersFrame request = null;
|
||||
|
||||
@Override
|
||||
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
|
||||
{
|
||||
MetaData.Request metaData = (MetaData.Request)request.getMetaData();
|
||||
boolean isHeadRequest = HttpMethod.HEAD.is(metaData.getMethod());
|
||||
boolean hasContent = BufferUtil.hasContent(content) && !isHeadRequest;
|
||||
|
||||
// TODO: the idea here is this:
|
||||
// CallbackLease lease = new CallbackLease(callback);
|
||||
// commit(lease, ...)
|
||||
// stream.header(lease, frame)
|
||||
// session.frame(lease, frame)
|
||||
// generator.generate(lease, frame)
|
||||
// generateHeader(lease, frame);
|
||||
// bodyGenerator[frame.getType()].generateBody(lease, frame);
|
||||
// stream.content(lease, frame)
|
||||
// ...
|
||||
// flush(lease)
|
||||
//
|
||||
// Problem is that in this way I need to aggregate multiple callbacks for the same lease.
|
||||
// So it'd need another abstraction that is a Lease+Callback
|
||||
|
||||
if (commit.compareAndSet(false, true))
|
||||
{
|
||||
commit(info, !hasContent, !hasContent ? callback : new Callback.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
if (hasContent)
|
||||
{
|
||||
send(content, lastContent, callback);
|
||||
}
|
||||
}
|
||||
|
||||
private void commit(HttpGenerator.ResponseInfo info, boolean endStream, Callback callback)
|
||||
{
|
||||
MetaData metaData = new MetaData.Response(info.getStatus(), info.getHttpFields());
|
||||
HeadersFrame frame = new HeadersFrame(stream.getId(), metaData, null, endStream);
|
||||
stream.headers(frame, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(ByteBuffer content, boolean lastContent, Callback callback)
|
||||
{
|
||||
DataFrame frame = new DataFrame(stream.getId(), content, lastContent);
|
||||
stream.data(frame, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.generator.Generator;
|
||||
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.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HTTP2ServerTest
|
||||
{
|
||||
private Server server;
|
||||
private ServerConnector connector;
|
||||
private String path;
|
||||
private ByteBufferPool byteBufferPool;
|
||||
private Generator generator;
|
||||
|
||||
private void startServer(HttpServlet servlet) throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = new ServerConnector(server, new HTTP2ServerConnectionFactory(new HttpConfiguration()));
|
||||
server.addConnector(connector);
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler(server, "/");
|
||||
path = "/test";
|
||||
context.addServlet(new ServletHolder(servlet), path);
|
||||
|
||||
byteBufferPool = new MappedByteBufferPool();
|
||||
generator = new Generator(byteBufferPool);
|
||||
|
||||
server.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void dispose() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestResponseNoContent() throws Exception
|
||||
{
|
||||
startServer(new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
String host = "localhost";
|
||||
int port = connector.getLocalPort();
|
||||
HttpFields fields = new HttpFields();
|
||||
MetaData.Request metaData = new MetaData.Request(HttpScheme.HTTP, HttpMethod.GET.asString(),
|
||||
host + ":" + port, host, port, path, fields);
|
||||
HeadersFrame request = new HeadersFrame(1, metaData, null, true);
|
||||
Generator.LeaseCallback lease = generator.generate(request, Callback.Adapter.INSTANCE);
|
||||
|
||||
try (SocketChannel client = SocketChannel.open(new InetSocketAddress(host, port)))
|
||||
{
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
client.write(buffer);
|
||||
}
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(2048);
|
||||
client.read(buffer);
|
||||
buffer.flip();
|
||||
|
||||
final AtomicReference<HeadersFrame> frameRef = new AtomicReference<>();
|
||||
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public boolean onHeaders(HeadersFrame frame)
|
||||
{
|
||||
frameRef.set(frame);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
parser.parse(buffer);
|
||||
|
||||
HeadersFrame response = frameRef.get();
|
||||
Assert.assertNotNull(response);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue