You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2015/06/01 11:18:47 UTC

svn commit: r1682843 - /tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java

Author: markt
Date: Mon Jun  1 09:18:47 2015
New Revision: 1682843

URL: http://svn.apache.org/r1682843
Log:
Writing unit tests means we need an HTTP/2 client. Start to extract the HTTP/2 parsing code into a separate class so it can be shared by the test class and the server. This is a work in progress.

Added:
    tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java   (with props)

Added: tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java?rev=1682843&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java (added)
+++ tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java Mon Jun  1 09:18:47 2015
@@ -0,0 +1,173 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.coyote.http2;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+class Http2Parser {
+
+    private static final Log log = LogFactory.getLog(Http2Parser.class);
+    private static final StringManager sm = StringManager.getManager(Http2Parser.class);
+
+    private static final byte[] CLIENT_PREFACE_START =
+            "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n ".getBytes(StandardCharsets.ISO_8859_1);
+
+    private final Input input;
+    private final byte[] frameHeaderBuffer = new byte[9];
+
+    private volatile boolean readPreface = false;
+
+    Http2Parser(Input input) {
+        this.input = input;
+    }
+
+
+    /**
+     * Read and process a single frame. Once the start of a frame is read, the
+     * remainder will be read using blocking IO.
+     *
+     * @param block Should this method block until a frame is available is no
+     *              frame is available immediately?
+     *
+     * @throws IOException If an IO error occurs while trying to read a frame
+     */
+    public void readFrame(boolean block) throws IOException {
+        input.fill(frameHeaderBuffer, block);
+
+        // TODO: This is incomplete
+    }
+
+
+    /**
+     * Read and validate the connection preface from input using blocking IO.
+     *
+     * @return <code>true</code> if a valid preface was read, otherwise false.
+     */
+    public boolean readConnectionPreface() {
+        if (readPreface) {
+            return true;
+        }
+
+        byte[] data = new byte[CLIENT_PREFACE_START.length];
+        try {
+            input.fill(data, true);
+        } catch (IOException ioe) {
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("http2Parser.preface.io"), ioe);
+            }
+            return false;
+        }
+
+        for (int i = 0; i < CLIENT_PREFACE_START.length; i++) {
+            if (CLIENT_PREFACE_START[i] != data[i]) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("http2Parser.preface.invalid",
+                            new String(data, StandardCharsets.ISO_8859_1)));
+                }
+                return false;
+            }
+        }
+
+        readPreface = true;
+        return true;
+    }
+
+
+    boolean readHttpUpgradeResponse() throws IOException {
+        // Only used by test code so safe to keep this just a little larger than
+        // we are expecting.
+        ByteBuffer data = ByteBuffer.allocate(128);
+        byte[] singleByte = new byte[1];
+        // Looking for \r\n\r\n
+        int seen = 0;
+        while (seen < 4) {
+            input.fill(singleByte, true);
+            switch (seen) {
+            case 0:
+            case 2: {
+                if (singleByte[0] == '\r') {
+                    seen++;
+                } else {
+                    seen = 0;
+                }
+                break;
+            }
+            case 1:
+            case 3: {
+                if (singleByte[0] == '\n') {
+                    seen++;
+                } else {
+                    seen = 0;
+                }
+                break;
+            }
+            }
+            data.put(singleByte[0]);
+        }
+
+        String response = new String(data.array(), data.arrayOffset(),
+                data.arrayOffset() + data.position(), StandardCharsets.ISO_8859_1);
+
+        String[] responseLines = response.split("\r\n");
+
+        if (responseLines.length < 3) {
+            return false;
+        }
+        if (!responseLines[0].startsWith("HTTP/1.1 101")) {
+            return false;
+        }
+        // TODO: There may be other headers.
+        if (!responseLines[1].equals("Connection: Upgrade")) {
+            return false;
+        }
+        if (!responseLines[2].startsWith("Upgrade: h2c")) {
+            return false;
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Interface that must be implemented by the source of data for the parser.
+     */
+    static interface Input {
+
+        /**
+         * Fill the given array with data unless non-blocking is requested and
+         * no data is available. If any data is available then the buffer will
+         * be filled with blocking I/O.
+         *
+         * @param data  Buffer to fill
+         * @param block Should the first read into the provided buffer be a
+         *              blocking read or not.
+         *
+         * @return <code>true</code> if the buffer was filled otherwise
+         *         <code>false</code>
+         *
+         * @throws IOException If an I/O occurred while obtaining data with
+         *                     which to fill the buffer
+         */
+        boolean fill(byte[] data, boolean block) throws IOException;
+    }
+}

Propchange: tomcat/trunk/java/org/apache/coyote/http2/Http2Parser.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org