src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java
branchhttp-client-branch
changeset 56104 3420c1bdd254
parent 56094 881f0ecb513f
child 56128 249a863b0aca
--- a/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java	Mon Feb 12 17:32:52 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java	Mon Feb 12 18:45:17 2018 +0000
@@ -27,6 +27,7 @@
 
 import java.io.EOFException;
 import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
 import java.net.URI;
@@ -36,6 +37,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.ArrayList;
 import java.util.Objects;
@@ -575,7 +577,7 @@
      * identifiers; those initiated by the server MUST use even-numbered
      * stream identifiers.
      */
-    private static final boolean isSeverInitiatedStream(int streamid) {
+    private static final boolean isServerInitiatedStream(int streamid) {
         return (streamid & 0x1) == 0;
     }
 
@@ -620,12 +622,17 @@
                 if (frame instanceof HeaderFrame) {
                     // always decode the headers as they may affect
                     // connection-level HPACK decoding state
-                    HeaderDecoder decoder = new HeaderDecoder();
-                    decodeHeaders((HeaderFrame) frame, decoder);
+                    DecodingCallback decoder = new ValidatingHeadersConsumer();
+                    try {
+                        decodeHeaders((HeaderFrame) frame, decoder);
+                    } catch (UncheckedIOException e) {
+                        protocolError(ResetFrame.PROTOCOL_ERROR, e.getMessage());
+                        return;
+                    }
                 }
 
                 if (!(frame instanceof ResetFrame)) {
-                    if (isSeverInitiatedStream(streamid)) {
+                    if (isServerInitiatedStream(streamid)) {
                         if (streamid < nextPushStream) {
                             // trailing data on a cancelled push promise stream,
                             // reset will already have been sent, ignore
@@ -642,10 +649,20 @@
             }
             if (frame instanceof PushPromiseFrame) {
                 PushPromiseFrame pp = (PushPromiseFrame)frame;
-                handlePushPromise(stream, pp);
+                try {
+                    handlePushPromise(stream, pp);
+                } catch (UncheckedIOException e) {
+                    protocolError(ResetFrame.PROTOCOL_ERROR, e.getMessage());
+                    return;
+                }
             } else if (frame instanceof HeaderFrame) {
                 // decode headers (or continuation)
-                decodeHeaders((HeaderFrame) frame, stream.rspHeadersConsumer());
+                try {
+                    decodeHeaders((HeaderFrame) frame, stream.rspHeadersConsumer());
+                } catch (UncheckedIOException e) {
+                    protocolError(ResetFrame.PROTOCOL_ERROR, e.getMessage());
+                    return;
+                }
                 stream.incoming(frame);
             } else {
                 stream.incoming(frame);
@@ -1139,7 +1156,8 @@
                     + connection.getConnectionFlow() + ")";
     }
 
-    static class HeaderDecoder implements DecodingCallback {
+    static class HeaderDecoder extends ValidatingHeadersConsumer {
+
         HttpHeadersImpl headers;
 
         HeaderDecoder() {
@@ -1148,7 +1166,10 @@
 
         @Override
         public void onDecoded(CharSequence name, CharSequence value) {
-            headers.addHeader(name.toString(), value.toString());
+            String n = name.toString();
+            String v = value.toString();
+            super.onDecoded(n, v);
+            headers.addHeader(n, v);
         }
 
         HttpHeadersImpl headers() {
@@ -1156,6 +1177,39 @@
         }
     }
 
+    /*
+     * Checks RFC 7540 rules (relaxed) compliance regarding pseudo-headers.
+     */
+    static class ValidatingHeadersConsumer implements DecodingCallback {
+
+        private static final Set<String> PSEUDO_HEADERS =
+                Set.of(":authority", ":method", ":path", ":scheme", ":status");
+
+        @Override
+        public void onDecoded(CharSequence name, CharSequence value)
+                throws UncheckedIOException
+        {
+            String n = name.toString();
+            if (n.startsWith(":")) {
+                if (!PSEUDO_HEADERS.contains(n)) {
+                    throw newException("Unexpected pseudo-header '%s'", n);
+                }
+            } else if (!Utils.isValidName(n)) {
+                throw newException("Bad header name '%s'", n);
+            }
+            String v = value.toString();
+            if (!Utils.isValidValue(v)) {
+                throw newException("Bad header value '%s'", v);
+            }
+        }
+
+        private UncheckedIOException newException(String message, String header)
+        {
+            return new UncheckedIOException(
+                    new IOException(String.format(message, header)));
+        }
+    }
+
     static final class ConnectionWindowUpdateSender extends WindowUpdateSender {
 
         final int initialWindowSize;