diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java index 9f581897d01..2d7c3a43d1a 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/parser/SettingsBodyParser.java @@ -43,7 +43,7 @@ public class SettingsBodyParser extends BodyParser super(headerParser, listener); } - private void reset() + protected void reset() { state = State.PREPARE; cursor = 0; @@ -157,7 +157,7 @@ public class SettingsBodyParser extends BodyParser return false; } - private boolean onSettings(Map settings) + protected boolean onSettings(Map settings) { SettingsFrame frame = new SettingsFrame(settings, hasFlag(Flags.ACK)); reset(); diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java index 26ccb7d9246..9fefa1c9ce0 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java @@ -24,6 +24,7 @@ import java.util.Map; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http2.ErrorCode; +import org.eclipse.jetty.http2.Flags; import org.eclipse.jetty.http2.HTTP2Cipher; import org.eclipse.jetty.http2.IStream; import org.eclipse.jetty.http2.api.Session; @@ -34,12 +35,14 @@ import org.eclipse.jetty.http2.frames.HeadersFrame; import org.eclipse.jetty.http2.frames.PushPromiseFrame; import org.eclipse.jetty.http2.frames.ResetFrame; import org.eclipse.jetty.http2.frames.SettingsFrame; +import org.eclipse.jetty.http2.parser.SettingsBodyParser; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.NegotiatingServerConnection.CipherDiscriminator; import org.eclipse.jetty.util.B64Code; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.annotation.Name; @@ -76,26 +79,52 @@ public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionF @Override public Connection newConnection(Connector connector, EndPoint endPoint, Object attachment) { - Connection connection = super.newConnection(connector,endPoint,attachment); - + HTTP2ServerConnection connection = (HTTP2ServerConnection)super.newConnection(connector,endPoint,attachment); + if (attachment instanceof MetaData.Request) { MetaData.Request request = (MetaData.Request) attachment; if (LOG.isDebugEnabled()) LOG.debug("{} upgraded {}",this,request.toString()+request.getFields()); - - // TODO work out why _ needs replacing? - byte[] settings = B64Code.decode(request.getFields().getField(HttpHeader.HTTP2_SETTINGS).getValue().replace('_', '=')); + + final byte[] settings = B64Code.decodeRFC4648URL(request.getFields().getField(HttpHeader.HTTP2_SETTINGS).getValue()); + if (LOG.isDebugEnabled()) - LOG.debug("{} settings {}",this, TypeUtil.toHexString(settings)); - - // TODO process the settings frame + LOG.debug("{} settings {}",this,TypeUtil.toHexString(settings)); + + SettingsBodyParser parser = new SettingsBodyParser(null,null) + { + @Override + protected int getStreamId() + { + return 0; + } + + @Override + protected int getBodyLength() + { + return settings.length; + } + @Override + protected boolean onSettings(Map settings) + { + SettingsFrame frame = new SettingsFrame(settings, false); + // TODO something with this frame? + + reset(); + return true; + } + + }; + parser.parse(BufferUtil.toBuffer(settings)); + // TODO use the metadata to push a response } - + return connection; } + private class HTTPServerSessionListener extends ServerSessionListener.Adapter implements Stream.Listener { @@ -170,4 +199,5 @@ public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionF session.close(ErrorCode.PROTOCOL_ERROR.code, reason, Callback.Adapter.INSTANCE); } } + } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java b/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java index f828642e9d1..1fa93de35e5 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/B64Code.java @@ -35,12 +35,12 @@ public class B64Code { private static final char __pad='='; private static final char[] __rfc1421alphabet= - { - 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', - 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', - 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', - 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' - }; + { + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' + }; private static final byte[] __rfc1421nibbles; static @@ -52,6 +52,25 @@ public class B64Code __rfc1421nibbles[(byte)__rfc1421alphabet[b]]=b; __rfc1421nibbles[(byte)__pad]=0; } + + private static final char[] __rfc4648urlAlphabet= + { + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','-','_' + }; + + private static final byte[] __rfc4648urlNibbles; + static + { + __rfc4648urlNibbles=new byte[256]; + for (int i=0;i<256;i++) + __rfc4648urlNibbles[i]=-1; + for (byte b=0;b<64;b++) + __rfc4648urlNibbles[(byte)__rfc4648urlAlphabet[b]]=b; + __rfc4648urlNibbles[(byte)__pad]=0; + } private B64Code() { @@ -430,6 +449,75 @@ public class B64Code return; } + /* ------------------------------------------------------------ */ + public static byte[] decodeRFC4648URL(String encoded) + { + if (encoded==null) + return null; + + ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3); + decodeRFC4648URL(encoded, bout); + return bout.toByteArray(); + } + + /* ------------------------------------------------------------ */ + /** + * Base 64 decode as described in RFC 4648 URL. + *

Unlike {@link #decode(char[])}, extra whitespace is ignored. + * @param encoded String to decode. + * @param bout stream for decoded bytes + * @throws IllegalArgumentException if the input is not a valid + * B64 encoding. + */ + static public void decodeRFC4648URL (String encoded, ByteArrayOutputStream bout) + { + if (encoded==null) + return; + + if (bout == null) + throw new IllegalArgumentException("No outputstream for decoded bytes"); + + int ci=0; + byte nibbles[] = new byte[4]; + int s=0; + + while (ci>>4); + break; + case 3: + bout.write(nibbles[1]<<4|nibbles[2]>>>2); + break; + case 4: + bout.write(nibbles[2]<<6|nibbles[3]); + s=0; + break; + } + + } + + return; + } + public static void encode(int value,Appendable buf) throws IOException {