Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #7625 - HTTP/3 error against www.google.com #7637

Merged
merged 1 commit into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ public QpackDecoder getQpackDecoder()
return decoder;
}

public QpackEncoder getQpackEncoder()
{
return encoder;
}

public HTTP3SessionClient getSessionClient()
{
return session;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,24 @@ protected GoAwayFrame newGoAwayFrame(boolean graceful)
return GoAwayFrame.CLIENT_GRACEFUL;
return super.newGoAwayFrame(graceful);
}

@Override
protected void onSettingMaxTableCapacity(long value)
{
getProtocolSession().getQpackEncoder().setCapacity((int)value);
}

@Override
protected void onSettingMaxFieldSectionSize(long value)
{
getProtocolSession().getQpackDecoder().setMaxHeaderSize((int)value);
}

@Override
protected void onSettingMaxBlockedStreams(long value)
{
ClientHTTP3Session session = getProtocolSession();
session.getQpackDecoder().setMaxBlockedStreams((int)value);
session.getQpackEncoder().setMaxBlockedStreams((int)value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@

public class SettingsFrame extends Frame
{
public static final long MAX_TABLE_CAPACITY = 0x01;
public static final long MAX_FIELD_SECTION_SIZE = 0x06;
public static final long MAX_BLOCKED_STREAMS = 0x07;

public static boolean isReserved(long key)
{
return key >= 0 && key <= 5;
if (key == MAX_TABLE_CAPACITY ||
key == MAX_FIELD_SECTION_SIZE ||
key == MAX_BLOCKED_STREAMS)
return false;
// Other HTTP/2 settings are reserved and must not be sent/received.
return key >= 0x00 && key <= 0x05;
}

private final Map<Long, Long> settings;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,32 @@ public void onSettings(SettingsFrame frame)
{
if (LOG.isDebugEnabled())
LOG.debug("received {} on {}", frame, this);

frame.getSettings().forEach((key, value) ->
{
if (key == SettingsFrame.MAX_TABLE_CAPACITY)
onSettingMaxTableCapacity(value);
else if (key == SettingsFrame.MAX_FIELD_SECTION_SIZE)
onSettingMaxFieldSectionSize(value);
else if (key == SettingsFrame.MAX_BLOCKED_STREAMS)
onSettingMaxBlockedStreams(value);
});

notifySettings(frame);
}

protected void onSettingMaxTableCapacity(long value)
{
}

protected void onSettingMaxFieldSectionSize(long value)
{
}

protected void onSettingMaxBlockedStreams(long value)
{
}

private void notifySettings(SettingsFrame frame)
{
try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ public class QpackDecoder implements Dumpable
private final List<EncodedFieldSection> _encodedFieldSections = new ArrayList<>();
private final NBitIntegerParser _integerDecoder = new NBitIntegerParser();
private final InstructionHandler _instructionHandler = new InstructionHandler();
private final int _maxHeaderSize;
private int _maxHeaderSize;
private int _maxBlockedStreams;

private static class MetaDataNotification
{
Expand Down Expand Up @@ -87,6 +88,27 @@ QpackContext getQpackContext()
return _context;
}

public int getMaxHeaderSize()
{
return _maxHeaderSize;
}

public void setMaxHeaderSize(int maxHeaderSize)
{
_maxHeaderSize = maxHeaderSize;
}

public int getMaxBlockedStreams()
{
// TODO: implement logic about blocked streams by calling this method.
return _maxBlockedStreams;
}

public void setMaxBlockedStreams(int maxBlockedStreams)
{
_maxBlockedStreams = maxBlockedStreams;
}

public interface Handler
{
void onMetaData(long streamId, MetaData metadata);
Expand All @@ -110,7 +132,8 @@ public boolean decode(long streamId, ByteBuffer buffer, Handler handler) throws
LOG.debug("Decoding: streamId={}, buffer={}", streamId, BufferUtil.toDetailString(buffer));

// If the buffer is big, don't even think about decoding it
if (buffer.remaining() > _maxHeaderSize)
int maxHeaderSize = getMaxHeaderSize();
if (buffer.remaining() > maxHeaderSize)
throw new QpackException.SessionException(QPACK_DECOMPRESSION_FAILED, "header_too_large");

_integerDecoder.setPrefix(8);
Expand Down Expand Up @@ -139,7 +162,7 @@ public boolean decode(long streamId, ByteBuffer buffer, Handler handler) throws
// Decode it straight away if we can, otherwise add it to the list of EncodedFieldSections.
if (requiredInsertCount <= insertCount)
{
MetaData metaData = encodedFieldSection.decode(_context, _maxHeaderSize);
MetaData metaData = encodedFieldSection.decode(_context, maxHeaderSize);
if (LOG.isDebugEnabled())
LOG.debug("Decoded: streamId={}, metadata={}", streamId, metaData);
_metaDataNotifications.add(new MetaDataNotification(streamId, metaData, handler));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public class QpackEncoder implements Dumpable
private final List<Instruction> _instructions = new ArrayList<>();
private final Instruction.Handler _handler;
private final QpackContext _context;
private final int _maxBlockedStreams;
private int _maxBlockedStreams;
private final Map<Long, StreamInfo> _streamInfoMap = new HashMap<>();
private final EncoderInstructionParser _parser;
private final InstructionHandler _instructionHandler = new InstructionHandler();
Expand All @@ -106,6 +106,21 @@ public QpackEncoder(Instruction.Handler handler, int maxBlockedStreams)
_parser = new EncoderInstructionParser(_instructionHandler);
}

public int getMaxBlockedStreams()
{
return _maxBlockedStreams;
}

public void setMaxBlockedStreams(int maxBlockedStreams)
{
_maxBlockedStreams = maxBlockedStreams;
}

public int getCapacity()
{
return _context.getDynamicTable().getCapacity();
}

/**
* Set the capacity of the DynamicTable and send a instruction to set the capacity on the remote Decoder.
*
Expand Down Expand Up @@ -411,7 +426,7 @@ private boolean referenceEntry(Entry entry, StreamInfo streamInfo)
return true;
}

if (_blockedStreams < _maxBlockedStreams)
if (_blockedStreams < getMaxBlockedStreams())
{
_blockedStreams++;
sectionInfo.block();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,26 @@ protected GoAwayFrame newGoAwayFrame(boolean graceful)
return super.newGoAwayFrame(graceful);
}

@Override
protected void onSettingMaxTableCapacity(long value)
{
getProtocolSession().getQpackEncoder().setCapacity((int)value);
}

@Override
protected void onSettingMaxFieldSectionSize(long value)
{
getProtocolSession().getQpackDecoder().setMaxHeaderSize((int)value);
}

@Override
protected void onSettingMaxBlockedStreams(long value)
{
ServerHTTP3Session session = getProtocolSession();
session.getQpackDecoder().setMaxBlockedStreams((int)value);
session.getQpackEncoder().setMaxBlockedStreams((int)value);
}

private void notifyAccept()
{
Server.Listener listener = getListener();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public QpackDecoder getQpackDecoder()
return decoder;
}

public QpackEncoder getQpackEncoder()
{
return encoder;
}

public HTTP3SessionServer getSessionServer()
{
return session;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package org.eclipse.jetty.http3.tests;

import java.nio.ByteBuffer;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
Expand All @@ -34,13 +35,15 @@
import org.eclipse.jetty.http3.internal.HTTP3ErrorCode;
import org.eclipse.jetty.http3.internal.HTTP3Session;
import org.eclipse.jetty.http3.server.AbstractHTTP3ServerConnectionFactory;
import org.eclipse.jetty.http3.server.internal.HTTP3SessionServer;
import org.eclipse.jetty.quic.client.ClientQuicSession;
import org.eclipse.jetty.quic.common.QuicSession;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand Down Expand Up @@ -90,6 +93,59 @@ public void onSettings(Session session, SettingsFrame frame)
assertTrue(clientSettingsLatch.await(5, TimeUnit.SECONDS));
}

@Test
public void testSettings() throws Exception
{
Map.Entry<Long, Long> maxTableCapacity = new AbstractMap.SimpleEntry<>(SettingsFrame.MAX_TABLE_CAPACITY, 1024L);
Map.Entry<Long, Long> maxHeaderSize = new AbstractMap.SimpleEntry<>(SettingsFrame.MAX_FIELD_SECTION_SIZE, 2048L);
Map.Entry<Long, Long> maxBlockedStreams = new AbstractMap.SimpleEntry<>(SettingsFrame.MAX_BLOCKED_STREAMS, 16L);
CountDownLatch settingsLatch = new CountDownLatch(2);
AtomicReference<HTTP3SessionServer> serverSessionRef = new AtomicReference<>();
start(new Session.Server.Listener()
{
@Override
public Map<Long, Long> onPreface(Session session)
{
serverSessionRef.set((HTTP3SessionServer)session);
return Map.ofEntries(maxTableCapacity, maxHeaderSize, maxBlockedStreams);
}

@Override
public void onSettings(Session session, SettingsFrame frame)
{
settingsLatch.countDown();
}
});

HTTP3SessionClient clientSession = (HTTP3SessionClient)newSession(new Session.Client.Listener()
{
@Override
public Map<Long, Long> onPreface(Session session)
{
return Map.ofEntries(maxTableCapacity, maxHeaderSize, maxBlockedStreams);
}

@Override
public void onSettings(Session session, SettingsFrame frame)
{
settingsLatch.countDown();
}
});

assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));

HTTP3SessionServer serverSession = serverSessionRef.get();
assertEquals(maxTableCapacity.getValue(), serverSession.getProtocolSession().getQpackEncoder().getCapacity());
assertEquals(maxBlockedStreams.getValue(), serverSession.getProtocolSession().getQpackEncoder().getMaxBlockedStreams());
assertEquals(maxBlockedStreams.getValue(), serverSession.getProtocolSession().getQpackDecoder().getMaxBlockedStreams());
assertEquals(maxHeaderSize.getValue(), serverSession.getProtocolSession().getQpackDecoder().getMaxHeaderSize());

assertEquals(maxTableCapacity.getValue(), clientSession.getProtocolSession().getQpackEncoder().getCapacity());
assertEquals(maxBlockedStreams.getValue(), clientSession.getProtocolSession().getQpackEncoder().getMaxBlockedStreams());
assertEquals(maxBlockedStreams.getValue(), clientSession.getProtocolSession().getQpackDecoder().getMaxBlockedStreams());
assertEquals(maxHeaderSize.getValue(), clientSession.getProtocolSession().getQpackDecoder().getMaxHeaderSize());
}

@Test
public void testGETThenResponseWithoutContent() throws Exception
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ public void testExternalServer() throws Exception
client.start();
try
{
HostPort hostPort = new HostPort("google.com:443");
// HostPort hostPort = new HostPort("nghttp2.org:4433");
HostPort hostPort = new HostPort("quic.tech:8443");
// HostPort hostPort = new HostPort("quic.tech:8443");
// HostPort hostPort = new HostPort("h2o.examp1e.net:443");
// HostPort hostPort = new HostPort("test.privateoctopus.com:4433");
Session.Client session = client.connect(new InetSocketAddress(hostPort.getHost(), hostPort.getPort()), new Session.Client.Listener() {})
Expand Down