From fcdabeb933892650155ca1803f3cadd5f078a4c9 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Thu, 16 Sep 2021 23:07:26 +0200 Subject: [PATCH] Issue #6728 - QUIC and HTTP/3 - Changed the parsers to emit during parsing, rather than returning the event. Signed-off-by: Simone Bordet --- .../src/main/java/module-info.java | 2 +- .../client/HTTP3ClientConnectionFactory.java | 4 +- .../src/main/java/module-info.java | 4 +- .../http3/internal/ControlConnection.java | 45 +---------- .../jetty/http3/internal/HTTP3Connection.java | 69 +---------------- .../http3/internal/InstructionConnection.java | 13 ++++ .../UnidirectionalStreamConnection.java | 4 +- .../http3/internal/parser/BodyParser.java | 77 +++++++++++++++---- .../internal/parser/CancelPushBodyParser.java | 8 +- .../http3/internal/parser/ControlParser.java | 52 ++++++------- .../http3/internal/parser/DataBodyParser.java | 45 +++++++---- .../internal/parser/GoAwayBodyParser.java | 8 +- .../internal/parser/HeadersBodyParser.java | 55 +++++++------ .../internal/parser/MaxPushIdBodyParser.java | 8 +- .../http3/internal/parser/MessageParser.java | 59 +++++++------- .../http3/internal/parser/ParseException.java | 34 -------- .../parser/PushPromiseBodyParser.java | 8 +- .../internal/parser/SettingsBodyParser.java | 45 +++++++---- .../internal/parser/UnknownBodyParser.java | 8 +- .../http3/internal/DataGenerateParseTest.java | 30 ++++---- .../internal/HeadersGenerateParseTest.java | 26 ++++--- .../internal/SettingsGenerateParseTest.java | 30 ++++---- .../internal/parser/EncodedFieldSection.java | 3 +- .../AbstractHTTP3ServerConnectionFactory.java | 4 +- .../src/main/java/module-info.java | 6 +- .../jetty/quic/common/QuicStreamEndPoint.java | 5 ++ .../src/main/java/module-info.java | 2 +- 27 files changed, 309 insertions(+), 345 deletions(-) delete mode 100644 jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/ParseException.java diff --git a/jetty-http3/http3-client/src/main/java/module-info.java b/jetty-http3/http3-client/src/main/java/module-info.java index b12dfab5bf4..6fa6a7002cc 100644 --- a/jetty-http3/http3-client/src/main/java/module-info.java +++ b/jetty-http3/http3-client/src/main/java/module-info.java @@ -20,7 +20,7 @@ module org.eclipse.jetty.http3.client requires transitive org.eclipse.jetty.http3.qpack; requires transitive org.eclipse.jetty.io; requires transitive org.eclipse.jetty.quic.common; - requires org.eclipse.jetty.quic.client; + requires transitive org.eclipse.jetty.quic.client; requires transitive org.eclipse.jetty.util; requires org.slf4j; } diff --git a/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/HTTP3ClientConnectionFactory.java b/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/HTTP3ClientConnectionFactory.java index 91ab5f80987..4dc9c19675d 100644 --- a/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/HTTP3ClientConnectionFactory.java +++ b/jetty-http3/http3-client/src/main/java/org/eclipse/jetty/http3/client/HTTP3ClientConnectionFactory.java @@ -77,7 +77,7 @@ public class HTTP3ClientConnectionFactory implements ClientConnectionFactory, Pr long streamId = streamEndPoint.getStreamId(); ClientHTTP3Session http3Session = (ClientHTTP3Session)streamEndPoint.getQuicSession().getProtocolSession(); // TODO: Parser may be created internally, if I pass the QuicStreamEndPoint and ClientHTTP3Session. - MessageParser parser = new MessageParser(streamId, http3Session.getQpackDecoder()); - return new HTTP3Connection(streamEndPoint, http3Session.getQuicSession().getExecutor(), http3Session.getQuicSession().getByteBufferPool(), parser, http3Session.getSessionClient()); + MessageParser parser = new MessageParser(http3Session.getSessionClient(), http3Session.getQpackDecoder(), streamId, streamEndPoint::isStreamFinished); + return new HTTP3Connection(streamEndPoint, http3Session.getQuicSession().getExecutor(), http3Session.getQuicSession().getByteBufferPool(), parser); } } diff --git a/jetty-http3/http3-common/src/main/java/module-info.java b/jetty-http3/http3-common/src/main/java/module-info.java index 47b734a3bab..c9ac9cf6815 100644 --- a/jetty-http3/http3-common/src/main/java/module-info.java +++ b/jetty-http3/http3-common/src/main/java/module-info.java @@ -22,9 +22,9 @@ module org.eclipse.jetty.http3.common exports org.eclipse.jetty.http3.internal; requires transitive org.eclipse.jetty.http; - requires org.eclipse.jetty.http3.qpack; + requires transitive org.eclipse.jetty.http3.qpack; requires org.eclipse.jetty.io; - requires org.eclipse.jetty.quic.common; + requires transitive org.eclipse.jetty.quic.common; requires org.eclipse.jetty.util; requires org.slf4j; } diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlConnection.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlConnection.java index 1769bc5433b..6524cec3315 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlConnection.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/ControlConnection.java @@ -16,11 +16,7 @@ package org.eclipse.jetty.http3.internal; import java.nio.ByteBuffer; import java.util.concurrent.Executor; -import org.eclipse.jetty.http3.frames.Frame; -import org.eclipse.jetty.http3.frames.FrameType; -import org.eclipse.jetty.http3.frames.SettingsFrame; import org.eclipse.jetty.http3.internal.parser.ControlParser; -import org.eclipse.jetty.http3.internal.parser.ParserListener; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; @@ -37,16 +33,14 @@ public class ControlConnection extends AbstractConnection implements Connection. private final ByteBufferPool byteBufferPool; private final ControlParser parser; - private final ParserListener listener; private boolean useInputDirectByteBuffers = true; private ByteBuffer buffer; - public ControlConnection(EndPoint endPoint, Executor executor, ByteBufferPool byteBufferPool, ControlParser parser, ParserListener listener) + public ControlConnection(EndPoint endPoint, Executor executor, ByteBufferPool byteBufferPool, ControlParser parser) { super(endPoint, executor); this.byteBufferPool = byteBufferPool; this.parser = parser; - this.listener = listener; } public boolean isUseInputDirectByteBuffers() @@ -90,13 +84,7 @@ public class ControlConnection extends AbstractConnection implements Connection. while (true) { // Parse first in case of bytes from the upgrade. - while (buffer.hasRemaining()) - { - Frame frame = parser.parse(buffer); - if (frame == null) - break; - notifyFrame(frame); - } + parser.parse(buffer); // Then read from the EndPoint. int filled = getEndPoint().fill(buffer); @@ -128,33 +116,4 @@ public class ControlConnection extends AbstractConnection implements Connection. getEndPoint().close(x); } } - - private void notifyFrame(Frame frame) - { - FrameType frameType = frame.getFrameType(); - switch (frameType) - { - case SETTINGS: - { - notifySettings((SettingsFrame)frame); - break; - } - default: - { - throw new UnsupportedOperationException("unsupported frame type " + frameType); - } - } - } - - private void notifySettings(SettingsFrame frame) - { - try - { - listener.onSettings(frame); - } - catch (Throwable x) - { - LOG.info("failure notifying listener {}", listener, x); - } - } } diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3Connection.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3Connection.java index e7c26ed29d9..83de4429e7a 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3Connection.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/HTTP3Connection.java @@ -16,12 +16,7 @@ package org.eclipse.jetty.http3.internal; import java.nio.ByteBuffer; import java.util.concurrent.Executor; -import org.eclipse.jetty.http3.frames.DataFrame; -import org.eclipse.jetty.http3.frames.Frame; -import org.eclipse.jetty.http3.frames.FrameType; -import org.eclipse.jetty.http3.frames.HeadersFrame; import org.eclipse.jetty.http3.internal.parser.MessageParser; -import org.eclipse.jetty.http3.internal.parser.ParserListener; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.quic.common.QuicStreamEndPoint; @@ -34,15 +29,13 @@ public class HTTP3Connection extends AbstractConnection private final ByteBufferPool byteBufferPool; private final MessageParser parser; - private final ParserListener listener; private boolean useInputDirectByteBuffers = true; - public HTTP3Connection(QuicStreamEndPoint endPoint, Executor executor, ByteBufferPool byteBufferPool, MessageParser parser, ParserListener listener) + public HTTP3Connection(QuicStreamEndPoint endPoint, Executor executor, ByteBufferPool byteBufferPool, MessageParser parser) { super(endPoint, executor); this.byteBufferPool = byteBufferPool; this.parser = parser; - this.listener = listener; } public boolean isUseInputDirectByteBuffers() @@ -76,17 +69,7 @@ public class HTTP3Connection extends AbstractConnection if (filled > 0) { - while (buffer.hasRemaining()) - { - Frame frame = parser.parse(buffer); - if (frame == null) - break; - if (frame instanceof HeadersFrame) - frame = ((HeadersFrame)frame).withLast(getEndPoint().isInputShutdown()); - else if (frame instanceof DataFrame) - frame = ((DataFrame)frame).withLast(getEndPoint().isInputShutdown()); - notifyFrame(frame); - } + parser.parse(buffer); } else if (filled == 0) { @@ -110,52 +93,4 @@ public class HTTP3Connection extends AbstractConnection getEndPoint().close(x); } } - - private void notifyFrame(Frame frame) - { - FrameType frameType = frame.getFrameType(); - switch (frameType) - { - case HEADERS: - { - notifyHeaders((HeadersFrame)frame); - break; - } - case DATA: - { - notifyData((DataFrame)frame); - break; - } - default: - { - throw new UnsupportedOperationException("unsupported frame type " + frameType); - } - } - } - - private void notifyHeaders(HeadersFrame frame) - { - try - { - long streamId = ((QuicStreamEndPoint)getEndPoint()).getStreamId(); - listener.onHeaders(streamId, frame); - } - catch (Throwable x) - { - LOG.info("failure notifying listener {}", listener, x); - } - } - - private void notifyData(DataFrame frame) - { - try - { - long streamId = ((QuicStreamEndPoint)getEndPoint()).getStreamId(); - listener.onData(streamId, frame); - } - catch (Throwable x) - { - LOG.info("failure notifying listener {}", listener, x); - } - } } diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionConnection.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionConnection.java index e48788e828e..9d398527a55 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionConnection.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/InstructionConnection.java @@ -1,3 +1,16 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + package org.eclipse.jetty.http3.internal; import java.nio.ByteBuffer; diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/UnidirectionalStreamConnection.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/UnidirectionalStreamConnection.java index d3a55736166..0e44e35c21c 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/UnidirectionalStreamConnection.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/UnidirectionalStreamConnection.java @@ -127,8 +127,8 @@ public class UnidirectionalStreamConnection extends AbstractConnection implement { case ControlConnection.STREAM_TYPE: { - ControlParser parser = new ControlParser(); - ControlConnection newConnection = new ControlConnection(getEndPoint(), getExecutor(), byteBufferPool, parser, listener); + ControlParser parser = new ControlParser(listener); + ControlConnection newConnection = new ControlConnection(getEndPoint(), getExecutor(), byteBufferPool, parser); newConnection.setInputBufferSize(getInputBufferSize()); newConnection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers()); if (LOG.isDebugEnabled()) diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/BodyParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/BodyParser.java index 87875ca91f3..37bcebaa095 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/BodyParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/BodyParser.java @@ -16,7 +16,10 @@ package org.eclipse.jetty.http3.internal.parser; import java.nio.ByteBuffer; import org.eclipse.jetty.http3.ErrorCode; -import org.eclipse.jetty.http3.frames.Frame; +import org.eclipse.jetty.http3.frames.SettingsFrame; +import org.eclipse.jetty.util.BufferUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** *

The base parser for the frame body of HTTP/3 frames.

@@ -27,18 +30,20 @@ import org.eclipse.jetty.http3.frames.Frame; */ public abstract class BodyParser { - private final long streamId; - private final HeaderParser headerParser; + private static final Logger LOG = LoggerFactory.getLogger(BodyParser.class); - protected BodyParser(long streamId, HeaderParser headerParser) + private final HeaderParser headerParser; + private final ParserListener listener; + + protected BodyParser(HeaderParser headerParser, ParserListener listener) { - this.streamId = streamId; this.headerParser = headerParser; + this.listener = listener; } - protected long getStreamId() + protected ParserListener getParserListener() { - return streamId; + return listener; } protected long getBodyLength() @@ -47,18 +52,60 @@ public abstract class BodyParser } /** - *

Parses the frame body bytes in the given {@code buffer}, producing a {@link Frame}.

- *

Only the frame body bytes are consumed, therefore when this method returns, the buffer - * may contain unconsumed bytes, for example for other frames.

+ *

Parses the frame body bytes in the given {@code buffer}.

+ *

Only the frame body bytes are consumed, therefore when this method returns, + * the buffer may contain unconsumed bytes, for example for other frames.

* * @param buffer the buffer to parse - * @return the parsed frame if all the frame body bytes were parsed, or an error frame, - * or null if not enough frame body bytes were present in the buffer + * @return true if all the frame body bytes were parsed; + * false if not enough frame body bytes were present in the buffer */ - public abstract Frame parse(ByteBuffer buffer) throws ParseException; + public abstract boolean parse(ByteBuffer buffer); - protected Frame emptyBody(ByteBuffer buffer) throws ParseException + protected void emptyBody(ByteBuffer buffer) { - throw new ParseException(ErrorCode.PROTOCOL_ERROR.code(), "invalid_frame"); + sessionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code(), "invalid_frame"); + } + + protected void sessionFailure(ByteBuffer buffer, int error, String reason) + { + BufferUtil.clear(buffer); + notifySessionFailure(error, reason); + } + + protected void notifySessionFailure(int error, String reason) + { + try + { + listener.onSessionFailure(error, reason); + } + catch (Throwable x) + { + LOG.info("failure while notifying listener {}", listener, x); + } + } + + protected void notifyStreamFailure(long streamId, int error, String reason) + { + try + { + listener.onStreamFailure(streamId, error, reason); + } + catch (Throwable x) + { + LOG.info("failure while notifying listener {}", listener, x); + } + } + + protected void notifySettings(SettingsFrame frame) + { + try + { + listener.onSettings(frame); + } + catch (Throwable x) + { + LOG.info("failure while notifying listener {}", listener, x); + } } } diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/CancelPushBodyParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/CancelPushBodyParser.java index 5d829246838..3154afc0342 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/CancelPushBodyParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/CancelPushBodyParser.java @@ -15,17 +15,15 @@ package org.eclipse.jetty.http3.internal.parser; import java.nio.ByteBuffer; -import org.eclipse.jetty.http3.frames.Frame; - public class CancelPushBodyParser extends BodyParser { - public CancelPushBodyParser(HeaderParser headerParser) + public CancelPushBodyParser(HeaderParser headerParser, ParserListener listener) { - super(1, headerParser); + super(headerParser, listener); } @Override - public Frame parse(ByteBuffer buffer) + public boolean parse(ByteBuffer buffer) { throw new UnsupportedOperationException(); } diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/ControlParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/ControlParser.java index 176a325f727..7e6260a1e5b 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/ControlParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/ControlParser.java @@ -16,7 +16,6 @@ package org.eclipse.jetty.http3.internal.parser; import java.nio.ByteBuffer; import org.eclipse.jetty.http3.ErrorCode; -import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.frames.FrameType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,14 +34,14 @@ public class ControlParser private final BodyParser unknownBodyParser; private State state = State.HEADER; - public ControlParser() + public ControlParser(ParserListener listener) { this.headerParser = new HeaderParser(); - this.bodyParsers[FrameType.CANCEL_PUSH.type()] = new CancelPushBodyParser(headerParser); - this.bodyParsers[FrameType.SETTINGS.type()] = new SettingsBodyParser(headerParser); - this.bodyParsers[FrameType.GOAWAY.type()] = new GoAwayBodyParser(headerParser); - this.bodyParsers[FrameType.MAX_PUSH_ID.type()] = new MaxPushIdBodyParser(headerParser); - this.unknownBodyParser = new UnknownBodyParser(headerParser); + this.bodyParsers[FrameType.CANCEL_PUSH.type()] = new CancelPushBodyParser(headerParser, listener); + this.bodyParsers[FrameType.SETTINGS.type()] = new SettingsBodyParser(headerParser, listener); + this.bodyParsers[FrameType.GOAWAY.type()] = new GoAwayBodyParser(headerParser, listener); + this.bodyParsers[FrameType.MAX_PUSH_ID.type()] = new MaxPushIdBodyParser(headerParser, listener); + this.unknownBodyParser = new UnknownBodyParser(headerParser, listener); } private void reset() @@ -56,7 +55,7 @@ public class ControlParser * * @param buffer the buffer to parse */ - public Frame parse(ByteBuffer buffer) throws ParseException + public void parse(ByteBuffer buffer) { try { @@ -71,7 +70,7 @@ public class ControlParser state = State.BODY; break; } - return null; + return; } case BODY: { @@ -85,25 +84,26 @@ public class ControlParser // TODO: enforce only control frames, but ignore unknown. if (LOG.isDebugEnabled()) LOG.debug("ignoring unknown frame type {}", Integer.toHexString(frameType)); - Frame frame = unknownBodyParser.parse(buffer); - if (frame == null) - return null; + if (!unknownBodyParser.parse(buffer)) + return; reset(); - break; } else { - Frame frame; if (headerParser.getFrameLength() == 0) - frame = bodyParser.emptyBody(buffer); + { + bodyParser.emptyBody(buffer); + } else - frame = bodyParser.parse(buffer); + { + if (!bodyParser.parse(buffer)) + return; + } if (LOG.isDebugEnabled()) LOG.debug("parsed {} frame body from {}", FrameType.from(frameType), buffer); - if (frame != null) - reset(); - return frame; + reset(); } + break; } default: { @@ -112,22 +112,20 @@ public class ControlParser } } } - catch (ParseException x) - { - if (LOG.isDebugEnabled()) - LOG.debug("parse failed", x); - buffer.clear(); - throw x; - } catch (Throwable x) { if (LOG.isDebugEnabled()) LOG.debug("parse failed", x); buffer.clear(); - throw new ParseException(ErrorCode.INTERNAL_ERROR.code(), "parser_error", true, x); + connectionFailure(buffer, ErrorCode.INTERNAL_ERROR.code(), "parser_error"); } } + private void connectionFailure(ByteBuffer buffer, int error, String reason) + { + unknownBodyParser.sessionFailure(buffer, error, reason); + } + private enum State { HEADER, BODY diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/DataBodyParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/DataBodyParser.java index 9a5f811b26d..3ec4c5df0f6 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/DataBodyParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/DataBodyParser.java @@ -14,9 +14,9 @@ package org.eclipse.jetty.http3.internal.parser; import java.nio.ByteBuffer; +import java.util.function.BooleanSupplier; import org.eclipse.jetty.http3.frames.DataFrame; -import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.util.BufferUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,12 +25,16 @@ public class DataBodyParser extends BodyParser { private static final Logger LOG = LoggerFactory.getLogger(DataBodyParser.class); + private final long streamId; + private final BooleanSupplier isLast; private State state = State.INIT; private long length; - public DataBodyParser(long streamId, HeaderParser headerParser) + public DataBodyParser(HeaderParser headerParser, ParserListener listener, long streamId, BooleanSupplier isLast) { - super(streamId, headerParser); + super(headerParser, listener); + this.streamId = streamId; + this.isLast = isLast; } private void reset() @@ -40,13 +44,13 @@ public class DataBodyParser extends BodyParser } @Override - protected Frame emptyBody(ByteBuffer buffer) + protected void emptyBody(ByteBuffer buffer) { - return onData(BufferUtil.EMPTY_BUFFER, false); + onData(BufferUtil.EMPTY_BUFFER, false); } @Override - public Frame parse(ByteBuffer buffer) + public boolean parse(ByteBuffer buffer) { while (buffer.hasRemaining()) { @@ -72,12 +76,14 @@ public class DataBodyParser extends BodyParser if (length == 0) { reset(); - return onData(slice, false); + onData(slice, false); + return true; } else { // We got partial data, simulate a smaller frame, and stay in DATA state. - return onData(slice, true); + onData(slice, true); + break; } } default: @@ -86,15 +92,28 @@ public class DataBodyParser extends BodyParser } } } - return null; + return false; } - private DataFrame onData(ByteBuffer buffer, boolean fragment) + private void onData(ByteBuffer buffer, boolean fragment) { - DataFrame frame = new DataFrame(buffer, true); + DataFrame frame = new DataFrame(buffer, isLast.getAsBoolean()); if (LOG.isDebugEnabled()) - LOG.debug("notifying synthetic={} {}#{}", fragment, frame, getStreamId()); - return frame; + LOG.debug("notifying synthetic={} {}#{}", fragment, frame, streamId); + notifyData(frame); + } + + private void notifyData(DataFrame frame) + { + ParserListener listener = getParserListener(); + try + { + listener.onData(streamId, frame); + } + catch (Throwable x) + { + LOG.info("failure while notifying listener {}", listener, x); + } } private enum State diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/GoAwayBodyParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/GoAwayBodyParser.java index 7f1e9ba65ea..2ca99ed55e5 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/GoAwayBodyParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/GoAwayBodyParser.java @@ -15,17 +15,15 @@ package org.eclipse.jetty.http3.internal.parser; import java.nio.ByteBuffer; -import org.eclipse.jetty.http3.frames.Frame; - public class GoAwayBodyParser extends BodyParser { - public GoAwayBodyParser(HeaderParser headerParser) + public GoAwayBodyParser(HeaderParser headerParser, ParserListener listener) { - super(1, headerParser); + super(headerParser, listener); } @Override - public Frame parse(ByteBuffer buffer) + public boolean parse(ByteBuffer buffer) { throw new UnsupportedOperationException(); } diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/HeadersBodyParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/HeadersBodyParser.java index 4c2d6f282da..faf50706ed5 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/HeadersBodyParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/HeadersBodyParser.java @@ -16,10 +16,10 @@ package org.eclipse.jetty.http3.internal.parser; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import java.util.function.BooleanSupplier; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http3.ErrorCode; -import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.frames.HeadersFrame; import org.eclipse.jetty.http3.qpack.QpackDecoder; import org.eclipse.jetty.http3.qpack.QpackException; @@ -32,14 +32,17 @@ public class HeadersBodyParser extends BodyParser private static final Logger LOG = LoggerFactory.getLogger(HeadersBodyParser.class); private final List byteBuffers = new ArrayList<>(); + private final long streamId; + private final BooleanSupplier isLast; private final QpackDecoder decoder; private State state = State.INIT; private long length; - private Frame frame; - public HeadersBodyParser(long streamId, HeaderParser headerParser, QpackDecoder decoder) + public HeadersBodyParser(HeaderParser headerParser, ParserListener listener, QpackDecoder decoder, long streamId, BooleanSupplier isLast) { - super(streamId, headerParser); + super(headerParser, listener); + this.streamId = streamId; + this.isLast = isLast; this.decoder = decoder; } @@ -47,11 +50,10 @@ public class HeadersBodyParser extends BodyParser { state = State.INIT; length = 0; - frame = null; } @Override - public Frame parse(ByteBuffer buffer) throws ParseException + public boolean parse(ByteBuffer buffer) { while (buffer.hasRemaining()) { @@ -72,7 +74,7 @@ public class HeadersBodyParser extends BodyParser length -= remaining; ByteBuffer copy = BufferUtil.copy(buffer); byteBuffers.add(copy); - return null; + return false; } else { @@ -106,45 +108,54 @@ public class HeadersBodyParser extends BodyParser } } } - return null; + return false; } - private Frame decode(ByteBuffer encoded) throws ParseException + private boolean decode(ByteBuffer encoded) { try { - // TODO: do a proper reset when the lambda is notified asynchronously. - if (decoder.decode(getStreamId(), encoded, (streamId, metaData) -> this.frame = onHeaders(metaData))) - { - Frame frame = this.frame; - reset(); - return frame; - } - return null; + return decoder.decode(streamId, encoded, (streamId, metaData) -> onHeaders(metaData)); } catch (QpackException.StreamException x) { if (LOG.isDebugEnabled()) LOG.debug("decode failure", x); - throw new ParseException(x.getErrorCode(), x.getMessage()); + notifyStreamFailure(streamId, x.getErrorCode(), x.getMessage()); } catch (QpackException.SessionException x) { if (LOG.isDebugEnabled()) LOG.debug("decode failure", x); - throw new ParseException(x.getErrorCode(), x.getMessage(), true); + notifySessionFailure(x.getErrorCode(), x.getMessage()); } catch (Throwable x) { if (LOG.isDebugEnabled()) LOG.debug("decode failure", x); - throw new ParseException(ErrorCode.INTERNAL_ERROR.code(), "internal_error", true, x); + notifySessionFailure(ErrorCode.INTERNAL_ERROR.code(), "internal_error"); } + return false; } - private Frame onHeaders(MetaData metaData) + private void onHeaders(MetaData metaData) { - return new HeadersFrame(metaData, false); + HeadersFrame frame = new HeadersFrame(metaData, isLast.getAsBoolean()); + reset(); + notifyHeaders(frame); + } + + protected void notifyHeaders(HeadersFrame frame) + { + ParserListener listener = getParserListener(); + try + { + listener.onHeaders(streamId, frame); + } + catch (Throwable x) + { + LOG.info("failure while notifying listener {}", listener, x); + } } private enum State diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MaxPushIdBodyParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MaxPushIdBodyParser.java index ac8bb394253..0ccb5c7ded5 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MaxPushIdBodyParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MaxPushIdBodyParser.java @@ -15,17 +15,15 @@ package org.eclipse.jetty.http3.internal.parser; import java.nio.ByteBuffer; -import org.eclipse.jetty.http3.frames.Frame; - public class MaxPushIdBodyParser extends BodyParser { - public MaxPushIdBodyParser(HeaderParser headerParser) + public MaxPushIdBodyParser(HeaderParser headerParser, ParserListener listener) { - super(1, headerParser); + super(headerParser, listener); } @Override - public Frame parse(ByteBuffer buffer) + public boolean parse(ByteBuffer buffer) { throw new UnsupportedOperationException(); } diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MessageParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MessageParser.java index e6b64fa3d65..10b47fadd9e 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MessageParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/MessageParser.java @@ -14,9 +14,9 @@ package org.eclipse.jetty.http3.internal.parser; import java.nio.ByteBuffer; +import java.util.function.BooleanSupplier; import org.eclipse.jetty.http3.ErrorCode; -import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.frames.FrameType; import org.eclipse.jetty.http3.qpack.QpackDecoder; import org.slf4j.Logger; @@ -36,13 +36,13 @@ public class MessageParser private final BodyParser unknownBodyParser; private State state = State.HEADER; - public MessageParser(long streamId, QpackDecoder decoder) + public MessageParser(ParserListener listener, QpackDecoder decoder, long streamId, BooleanSupplier isLast) { this.headerParser = new HeaderParser(); - this.bodyParsers[FrameType.DATA.type()] = new DataBodyParser(streamId, headerParser); - this.bodyParsers[FrameType.HEADERS.type()] = new HeadersBodyParser(streamId, headerParser, decoder); - this.bodyParsers[FrameType.PUSH_PROMISE.type()] = new PushPromiseBodyParser(headerParser); - this.unknownBodyParser = new UnknownBodyParser(headerParser); + this.bodyParsers[FrameType.DATA.type()] = new DataBodyParser(headerParser, listener, streamId, isLast); + this.bodyParsers[FrameType.HEADERS.type()] = new HeadersBodyParser(headerParser, listener, decoder, streamId, isLast); + this.bodyParsers[FrameType.PUSH_PROMISE.type()] = new PushPromiseBodyParser(headerParser, listener); + this.unknownBodyParser = new UnknownBodyParser(headerParser, listener); } private void reset() @@ -52,12 +52,11 @@ public class MessageParser } /** - *

Parses the given {@code buffer} bytes and returns parsed frames.

+ *

Parses the given {@code buffer} bytes and emit events to a {@link ParserListener}.

* * @param buffer the buffer to parse - * @return a parsed frame, or null if not enough bytes were provided to parse a frame */ - public Frame parse(ByteBuffer buffer) throws ParseException + public void parse(ByteBuffer buffer) { try { @@ -72,7 +71,7 @@ public class MessageParser state = State.BODY; break; } - return null; + return; } case BODY: { @@ -86,27 +85,25 @@ public class MessageParser // Unknown frame types must be ignored. if (LOG.isDebugEnabled()) LOG.debug("Ignoring unknown frame type {}", Integer.toHexString(frameType)); - Frame frame = unknownBodyParser.parse(buffer); - if (frame == null) - return null; - reset(); - break; + if (!unknownBodyParser.parse(buffer)) + return; } else { - Frame frame; if (headerParser.getFrameLength() == 0) - frame = bodyParser.emptyBody(buffer); - else - frame = bodyParser.parse(buffer); - if (frame != null) { - if (LOG.isDebugEnabled()) - LOG.debug("Parsed {} frame body from {}", FrameType.from(frameType), buffer); - reset(); + bodyParser.emptyBody(buffer); } - return frame; + else + { + if (!bodyParser.parse(buffer)) + return; + } + if (LOG.isDebugEnabled()) + LOG.debug("Parsed {} frame body from {}", FrameType.from(frameType), buffer); } + reset(); + break; } default: { @@ -115,22 +112,20 @@ public class MessageParser } } } - catch (ParseException x) - { - if (LOG.isDebugEnabled()) - LOG.debug("parse failed", x); - buffer.clear(); - throw x; - } catch (Throwable x) { if (LOG.isDebugEnabled()) LOG.debug("parse failed", x); buffer.clear(); - throw new ParseException(ErrorCode.INTERNAL_ERROR.code(), "parser_error", true, x); + connectionFailure(buffer, ErrorCode.INTERNAL_ERROR.code(), "parser_error"); } } + private void connectionFailure(ByteBuffer buffer, int error, String reason) + { + unknownBodyParser.sessionFailure(buffer, error, reason); + } + private enum State { HEADER, BODY diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/ParseException.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/ParseException.java deleted file mode 100644 index 12b40d3e12a..00000000000 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/ParseException.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.eclipse.jetty.http3.internal.parser; - -public class ParseException extends Exception -{ - private final int error; - private final boolean fatal; - - public ParseException(int error, String message) - { - this(error, message, false); - } - - public ParseException(int error, String message, boolean fatal) - { - this(error, message, fatal, null); - } - - public ParseException(int error, String message, boolean fatal, Throwable cause) - { - super(message, cause); - this.error = error; - this.fatal = fatal; - } - - public int getErrorCode() - { - return error; - } - - public boolean isFatal() - { - return fatal; - } -} diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/PushPromiseBodyParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/PushPromiseBodyParser.java index ffde5464795..9d8dd6181f5 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/PushPromiseBodyParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/PushPromiseBodyParser.java @@ -15,17 +15,15 @@ package org.eclipse.jetty.http3.internal.parser; import java.nio.ByteBuffer; -import org.eclipse.jetty.http3.frames.Frame; - public class PushPromiseBodyParser extends BodyParser { - public PushPromiseBodyParser(HeaderParser headerParser) + public PushPromiseBodyParser(HeaderParser headerParser, ParserListener listener) { - super(1, headerParser); + super(headerParser, listener); } @Override - public Frame parse(ByteBuffer buffer) + public boolean parse(ByteBuffer buffer) { throw new UnsupportedOperationException(); } diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/SettingsBodyParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/SettingsBodyParser.java index 385e40d0868..673723a0e14 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/SettingsBodyParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/SettingsBodyParser.java @@ -18,7 +18,6 @@ import java.util.LinkedHashMap; import java.util.Map; import org.eclipse.jetty.http3.ErrorCode; -import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.frames.SettingsFrame; import org.eclipse.jetty.http3.internal.VarLenInt; @@ -30,9 +29,9 @@ public class SettingsBodyParser extends BodyParser private long key; private Map settings; - public SettingsBodyParser(HeaderParser headerParser) + public SettingsBodyParser(HeaderParser headerParser, ParserListener listener) { - super(1, headerParser); + super(headerParser, listener); } private void reset() @@ -45,13 +44,13 @@ public class SettingsBodyParser extends BodyParser } @Override - protected Frame emptyBody(ByteBuffer buffer) + protected void emptyBody(ByteBuffer buffer) { - return onSettings(Map.of()); + onSettings(Map.of()); } @Override - public Frame parse(ByteBuffer buffer) throws ParseException + public boolean parse(ByteBuffer buffer) { while (buffer.hasRemaining()) { @@ -73,16 +72,27 @@ public class SettingsBodyParser extends BodyParser })) { if (settings.containsKey(key)) - throw new ParseException(ErrorCode.SETTINGS_ERROR.code(), "settings_duplicate"); + { + sessionFailure(buffer, ErrorCode.SETTINGS_ERROR.code(), "settings_duplicate"); + return true; + } if (SettingsFrame.isReserved(key)) - throw new ParseException(ErrorCode.SETTINGS_ERROR.code(), "settings_reserved"); + { + sessionFailure(buffer, ErrorCode.SETTINGS_ERROR.code(), "settings_reserved"); + return true; + } if (length > 0) + { state = State.VALUE; + } else - throw new ParseException(ErrorCode.FRAME_ERROR.code(), "settings_invalid_format"); + { + sessionFailure(buffer, ErrorCode.FRAME_ERROR.code(), "settings_invalid_format"); + return true; + } break; } - return null; + return false; } case VALUE: { @@ -101,15 +111,17 @@ public class SettingsBodyParser extends BodyParser { Map settings = this.settings; reset(); - return onSettings(settings); + onSettings(settings); + return true; } else { - throw new ParseException(ErrorCode.FRAME_ERROR.code(), "settings_invalid_format"); + sessionFailure(buffer, ErrorCode.FRAME_ERROR.code(), "settings_invalid_format"); + return true; } break; } - return null; + return false; } default: { @@ -117,12 +129,13 @@ public class SettingsBodyParser extends BodyParser } } } - return null; + return false; } - private SettingsFrame onSettings(Map settings) + private void onSettings(Map settings) { - return new SettingsFrame(settings); + SettingsFrame frame = new SettingsFrame(settings); + notifySettings(frame); } private enum State diff --git a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/UnknownBodyParser.java b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/UnknownBodyParser.java index 927c1825890..a84e5cc74dc 100644 --- a/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/UnknownBodyParser.java +++ b/jetty-http3/http3-common/src/main/java/org/eclipse/jetty/http3/internal/parser/UnknownBodyParser.java @@ -15,17 +15,15 @@ package org.eclipse.jetty.http3.internal.parser; import java.nio.ByteBuffer; -import org.eclipse.jetty.http3.frames.Frame; - public class UnknownBodyParser extends BodyParser { - public UnknownBodyParser(HeaderParser headerParser) + public UnknownBodyParser(HeaderParser headerParser, ParserListener listener) { - super(1, headerParser); + super(headerParser, listener); } @Override - public Frame parse(ByteBuffer buffer) + public boolean parse(ByteBuffer buffer) { throw new UnsupportedOperationException(); } diff --git a/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java b/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java index 6171c40a242..a2f2b89aadc 100644 --- a/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java +++ b/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/DataGenerateParseTest.java @@ -19,9 +19,9 @@ import java.util.List; import java.util.Random; import org.eclipse.jetty.http3.frames.DataFrame; -import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.internal.generator.MessageGenerator; import org.eclipse.jetty.http3.internal.parser.MessageParser; +import org.eclipse.jetty.http3.internal.parser.ParserListener; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.NullByteBufferPool; import org.eclipse.jetty.util.BufferUtil; @@ -29,24 +29,25 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; public class DataGenerateParseTest { @Test - public void testGenerateParseEmpty() throws Exception + public void testGenerateParseEmpty() { testGenerateParse(BufferUtil.EMPTY_BUFFER); } @Test - public void testGenerateParse() throws Exception + public void testGenerateParse() { byte[] bytes = new byte[1024]; new Random().nextBytes(bytes); testGenerateParse(ByteBuffer.wrap(bytes)); } - private void testGenerateParse(ByteBuffer byteBuffer) throws Exception + private void testGenerateParse(ByteBuffer byteBuffer) { byte[] inputBytes = new byte[byteBuffer.remaining()]; byteBuffer.get(inputBytes); @@ -55,20 +56,23 @@ public class DataGenerateParseTest ByteBufferPool.Lease lease = new ByteBufferPool.Lease(new NullByteBufferPool()); new MessageGenerator(null, 8192, true).generate(lease, 0, input); - List frames = new ArrayList<>(); - MessageParser parser = new MessageParser(0, null); + List frames = new ArrayList<>(); + MessageParser parser = new MessageParser(new ParserListener() + { + @Override + public void onData(long streamId, DataFrame frame) + { + frames.add(frame); + } + }, null, 13, () -> true); for (ByteBuffer buffer : lease.getByteBuffers()) { - while (buffer.hasRemaining()) - { - Frame frame = parser.parse(buffer); - if (frame != null) - frames.add(frame); - } + parser.parse(buffer); + assertFalse(buffer.hasRemaining()); } assertEquals(1, frames.size()); - DataFrame output = (DataFrame)frames.get(0); + DataFrame output = frames.get(0); byte[] outputBytes = new byte[output.getData().remaining()]; output.getData().get(outputBytes); assertArrayEquals(inputBytes, outputBytes); diff --git a/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java b/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java index 989b3bd291e..d7bb091ca50 100644 --- a/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java +++ b/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/HeadersGenerateParseTest.java @@ -22,10 +22,10 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; -import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.frames.HeadersFrame; import org.eclipse.jetty.http3.internal.generator.MessageGenerator; import org.eclipse.jetty.http3.internal.parser.MessageParser; +import org.eclipse.jetty.http3.internal.parser.ParserListener; import org.eclipse.jetty.http3.qpack.QpackDecoder; import org.eclipse.jetty.http3.qpack.QpackEncoder; import org.eclipse.jetty.io.ByteBufferPool; @@ -33,11 +33,12 @@ import org.eclipse.jetty.io.NullByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; public class HeadersGenerateParseTest { @Test - public void testGenerateParse() throws Exception + public void testGenerateParse() { HttpURI uri = HttpURI.from("http://host:1234/path?a=b"); HttpFields fields = HttpFields.build() @@ -50,20 +51,23 @@ public class HeadersGenerateParseTest new MessageGenerator(encoder, 8192, true).generate(lease, 0, input); QpackDecoder decoder = new QpackDecoder(instructions -> {}, 8192); - List frames = new ArrayList<>(); - MessageParser parser = new MessageParser(0, decoder); + List frames = new ArrayList<>(); + MessageParser parser = new MessageParser(new ParserListener() + { + @Override + public void onHeaders(long streamId, HeadersFrame frame) + { + frames.add(frame); + } + }, decoder, 13, () -> true); for (ByteBuffer buffer : lease.getByteBuffers()) { - while (buffer.hasRemaining()) - { - Frame frame = parser.parse(buffer); - if (frame != null) - frames.add(frame); - } + parser.parse(buffer); + assertFalse(buffer.hasRemaining()); } assertEquals(1, frames.size()); - HeadersFrame output = (HeadersFrame)frames.get(0); + HeadersFrame output = frames.get(0); MetaData.Request inputMetaData = (MetaData.Request)input.getMetaData(); MetaData.Request outputMetaData = (MetaData.Request)output.getMetaData(); diff --git a/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/SettingsGenerateParseTest.java b/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/SettingsGenerateParseTest.java index 2ace0ad6b5b..868d62765f6 100644 --- a/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/SettingsGenerateParseTest.java +++ b/jetty-http3/http3-common/src/test/java/org/eclipse/jetty/http3/internal/SettingsGenerateParseTest.java @@ -18,51 +18,55 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.eclipse.jetty.http3.frames.Frame; import org.eclipse.jetty.http3.frames.SettingsFrame; import org.eclipse.jetty.http3.internal.generator.ControlGenerator; import org.eclipse.jetty.http3.internal.parser.ControlParser; +import org.eclipse.jetty.http3.internal.parser.ParserListener; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.NullByteBufferPool; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; public class SettingsGenerateParseTest { @Test - public void testGenerateParseEmpty() throws Exception + public void testGenerateParseEmpty() { testGenerateParse(Map.of()); } @Test - public void testGenerateParse() throws Exception + public void testGenerateParse() { testGenerateParse(Map.of(13L, 7L, 31L, 29L)); } - private void testGenerateParse(Map settings) throws Exception + private void testGenerateParse(Map settings) { SettingsFrame input = new SettingsFrame(settings); ByteBufferPool.Lease lease = new ByteBufferPool.Lease(new NullByteBufferPool()); new ControlGenerator().generate(lease, 0, input); - List frames = new ArrayList<>(); - ControlParser parser = new ControlParser(); + List frames = new ArrayList<>(); + ControlParser parser = new ControlParser(new ParserListener() + { + @Override + public void onSettings(SettingsFrame frame) + { + frames.add(frame); + } + }); for (ByteBuffer buffer : lease.getByteBuffers()) { - while (buffer.hasRemaining()) - { - Frame frame = parser.parse(buffer); - if (frame != null) - frames.add(frame); - } + parser.parse(buffer); + assertFalse(buffer.hasRemaining()); } assertEquals(1, frames.size()); - SettingsFrame output = (SettingsFrame)frames.get(0); + SettingsFrame output = frames.get(0); assertEquals(input.getSettings(), output.getSettings()); } diff --git a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/parser/EncodedFieldSection.java b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/parser/EncodedFieldSection.java index 0ed2b193427..4da36987bc5 100644 --- a/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/parser/EncodedFieldSection.java +++ b/jetty-http3/http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/parser/EncodedFieldSection.java @@ -130,7 +130,8 @@ public class EncodedFieldSection private EncodedField parseNameReference(ByteBuffer buffer) throws EncodingException { - LOG.info("parseLiteralFieldLineWithNameReference: " + BufferUtil.toDetailString(buffer)); + if (LOG.isDebugEnabled()) + LOG.debug("parseLiteralFieldLineWithNameReference: " + BufferUtil.toDetailString(buffer)); byte firstByte = buffer.get(buffer.position()); boolean allowEncoding = (firstByte & 0x20) != 0; diff --git a/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/AbstractHTTP3ServerConnectionFactory.java b/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/AbstractHTTP3ServerConnectionFactory.java index f3872aafcad..096c7326743 100644 --- a/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/AbstractHTTP3ServerConnectionFactory.java +++ b/jetty-http3/http3-server/src/main/java/org/eclipse/jetty/http3/server/AbstractHTTP3ServerConnectionFactory.java @@ -97,8 +97,8 @@ public abstract class AbstractHTTP3ServerConnectionFactory extends AbstractConne QuicStreamEndPoint streamEndPoint = (QuicStreamEndPoint)endPoint; long streamId = streamEndPoint.getStreamId(); ServerHTTP3Session http3Session = (ServerHTTP3Session)streamEndPoint.getQuicSession().getProtocolSession(); - MessageParser parser = new MessageParser(streamId, http3Session.getQpackDecoder()); - HTTP3Connection connection = new HTTP3Connection(streamEndPoint, connector.getExecutor(), connector.getByteBufferPool(), parser, http3Session.getSessionServer()); + MessageParser parser = new MessageParser(http3Session.getSessionServer(), http3Session.getQpackDecoder(), streamId, streamEndPoint::isStreamFinished); + HTTP3Connection connection = new HTTP3Connection(streamEndPoint, connector.getExecutor(), connector.getByteBufferPool(), parser); return connection; } } diff --git a/jetty-quic/quic-client/src/main/java/module-info.java b/jetty-quic/quic-client/src/main/java/module-info.java index 22dd89436e6..cad24583867 100644 --- a/jetty-quic/quic-client/src/main/java/module-info.java +++ b/jetty-quic/quic-client/src/main/java/module-info.java @@ -15,10 +15,10 @@ module org.eclipse.jetty.quic.client { exports org.eclipse.jetty.quic.client; - requires org.eclipse.jetty.quic.common; + requires transitive org.eclipse.jetty.quic.common; requires org.eclipse.jetty.quic.quiche; requires org.eclipse.jetty.client; - requires org.eclipse.jetty.io; - requires org.eclipse.jetty.util; + requires transitive org.eclipse.jetty.io; + requires transitive org.eclipse.jetty.util; requires org.slf4j; } diff --git a/jetty-quic/quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java b/jetty-quic/quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java index eb273e4518d..58698b682d8 100644 --- a/jetty-quic/quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java +++ b/jetty-quic/quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicStreamEndPoint.java @@ -68,6 +68,11 @@ public class QuicStreamEndPoint extends AbstractEndPoint return session.getRemoteAddress(); } + public boolean isStreamFinished() + { + return session.isFinished(streamId); + } + @Override protected void doShutdownInput() { diff --git a/jetty-quic/quic-server/src/main/java/module-info.java b/jetty-quic/quic-server/src/main/java/module-info.java index 01fb2092373..9eb7c620266 100644 --- a/jetty-quic/quic-server/src/main/java/module-info.java +++ b/jetty-quic/quic-server/src/main/java/module-info.java @@ -18,7 +18,7 @@ module org.eclipse.jetty.quic.server requires transitive org.eclipse.jetty.quic.common; requires org.eclipse.jetty.quic.quiche; requires org.eclipse.jetty.io; - requires org.eclipse.jetty.server; + requires transitive org.eclipse.jetty.server; requires org.eclipse.jetty.util; requires org.slf4j; }