All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.grizzly.http2.DecoderUtils Maven / Gradle / Ivy

There is a newer version: 4.1.0-M1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2014-2015 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.grizzly.http2;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.grizzly.Cacheable;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.http.HttpHeader;
import org.glassfish.grizzly.http.HttpRequestPacket;
import org.glassfish.grizzly.http.HttpResponsePacket;
import org.glassfish.grizzly.http.Protocol;
import org.glassfish.grizzly.http.util.DataChunk;
import org.glassfish.grizzly.http.util.Header;
import org.glassfish.grizzly.http.util.MimeHeaders;
import org.glassfish.grizzly.http2.compression.HeaderListener;

/**
 * Http2Frames -> HTTP Packet decoder utils.
 * 
 * @author Grizzly team
 */
class DecoderUtils {
    private final static Logger LOGGER = Grizzly.logger(DecoderUtils.class);

    static void decodeRequestHeaders(final Http2Connection http2Connection,
            final HttpRequestPacket request) throws IOException {
        
        http2Connection.getHeadersDecoder().decode(new HeaderListener() {

            @Override
            public void onDecodedHeader(String name, String value) {
                if (name.charAt(0) == ':') {
                    processServiceRequestHeader(request, name, value);
                } else {
                    processNormalHeader(request, name, value);
                }
            }
            
//            public void addHeader(final byte[] name, final byte[] value,
//                    final boolean isSensitive) {
//                if (name[0] == ':') {
//                    processServiceRequestHeader(request, name, value);
//                } else {
//                    processNormalHeader(request, name, value);
//                }
//            }
//
//            public void emitHeader(byte[] name, byte[] value, boolean isSensitive) {
//                addHeader(name, value, isSensitive);
//            }
//
//            public void emitHeader(byte[] name, byte[] value) {
//                addHeader(name, value, false);
//            }
        });
        
        request.setProtocol(Protocol.HTTP_2_0);
    }

    static void decodeResponseHeaders(final Http2Connection http2Connection,
            final HttpResponsePacket response) throws IOException {
        
        http2Connection.getHeadersDecoder().decode(new HeaderListener() {

            @Override
            public void onDecodedHeader(String name, String value) {
                if (name.charAt(0) == ':') {
                    processServiceResponseHeader(response, name, value);
                } else {
                    processNormalHeader(response, name, value);
                }
            }
            
//            @Override
//            public void addHeader(final byte[] name, final byte[] value,
//                    final boolean isSensitive) {
//                if (name[0] == ':') {
//                    processServiceResponseHeader(response, name, value);
//                } else {
//                    processNormalHeader(response, name, value);
//                }
//            }
//            
//            public void emitHeader(byte[] name, byte[] value, boolean isSensitive) {
//                addHeader(name, value, isSensitive);
//            }
//            
//            public void emitHeader(byte[] name, byte[] value) {
//                addHeader(name, value, false);
//            }
        });
        
        response.setProtocol(Protocol.HTTP_2_0);
    }

    private static void processServiceRequestHeader(
            final HttpRequestPacket request,
            final String name, final String value) {

        final int valueLen = value.length();
        
        switch (name) {
            case Constants.PATH_HEADER: {
                int questionIdx = value.indexOf('?');

                if (questionIdx == -1) {
                    request.getRequestURIRef().init(value);
                } else {
                    request.getRequestURIRef().init(value.substring(0, questionIdx));
                    if (questionIdx < valueLen - 1) {
                        request.getQueryStringDC().setString(value.substring(questionIdx + 1));
                    }
                }
                
                return;
            }
            case Constants.METHOD_HEADER: {
                request.getMethodDC().setString(value);
                return;
            }
            case Constants.SCHEMA_HEADER: {
                request.setSecure(valueLen == 5); // support http and https only
                return;
            }
            case Constants.AUTHORITY_HEADER: {
                request.getHeaders().addValue(Header.Host)
                        .setString(value);
                return;
            }
        }
        
        LOGGER.log(Level.FINE, "Skipping unknown service header[{0}={1}",
                new Object[]{name, value});
    }    
    
    private static void processServiceResponseHeader(
            final HttpResponsePacket response,
            final String name, final String value) {

        final int valueLen = value.length();
        switch (name) {
            case Constants.STATUS_HEADER: {
                if ((valueLen) != 3) {
                    throw new IllegalStateException("Unexpected status code: " + value);
                }
                
                response.setStatus(Integer.parseInt(value));
            }
        }
        
        LOGGER.log(Level.FINE, "Skipping unknown service header[{0}={1}",
                new Object[]{name, value});
    }
    
    private static void processNormalHeader(final HttpHeader httpHeader,
            final String name, final String value) {

        final MimeHeaders mimeHeaders = httpHeader.getHeaders();

        final DataChunk valueChunk =
                mimeHeaders.addValue(name);

        valueChunk.setString(value.replace('\u0000', ','));
        
        finalizeKnownHeader(httpHeader, name, value);
    }

    private static void finalizeKnownHeader(final HttpHeader httpHeader,
            final String name, final String value) {
        
        switch (name) {
            case "content-length": {
                httpHeader.setContentLengthLong(Long.parseLong(value));
                return;
            }
            
            case "upgrade": {
                httpHeader.getUpgradeDC().setString(value);
                return;
            }
            
            case "expect": {
                ((Http2Request) httpHeader).requiresAcknowledgement(true);
                return;
            }
        }
    }    
    
//    private static void processServiceRequestHeader(
//            final HttpRequestPacket request,
//            final byte[] name, final byte[] value) {
//
//        final int valueLen = value.length;
//        
//        switch (name.length - 1) {
//            case 4: {
//                if (checkArraysContent(name, 1,
//                        Constants.PATH_HEADER_BYTES, 1)) {
//
//                    int questionIdx = -1;
//                    
//                    for (int i = 0; i < valueLen; i++) {
//                        if (value[i] == '?') {
//                            questionIdx = i;
//                            break;
//                        }
//                    }
//
//                    if (questionIdx == -1) {
//                        request.getRequestURIRef().init(value, 0, valueLen);
//                    } else {
//                        request.getRequestURIRef().init(value, 0, questionIdx);
//                        if (questionIdx < valueLen - 1) {
//                            request.getQueryStringDC()
//                                    .setBytes(value, questionIdx + 1, valueLen);
//                        }
//                    }
//
//                    return;
//                }
//
//                break;
//            } case 6: {
//                if (checkArraysContent(name, 1,
//                        Constants.METHOD_HEADER_BYTES, 1)) {
//                    request.getMethodDC().setBytes(value);
//
//                    return;
//                } else if (checkArraysContent(name, 1,
//                        Constants.SCHEMA_HEADER_BYTES, 1)) {
//
//                    request.setSecure(valueLen == 5); // support http and https only
//                    return;
//                }
//                
//                break;
//            } case 9: {
//                if (checkArraysContent(name, 1,
//                        Constants.AUTHORITY_HEADER_BYTES, 1)) {
//                    request.getHeaders().addValue(Header.Host)
//                            .setBytes(value, 0, valueLen);
//
//                    return;
//                }
//            }
//        }
//
//        LOGGER.log(Level.FINE, "Skipping unknown service header[{0}={1}",
//                new Object[]{new String(name, Charsets.ASCII_CHARSET),
//                    new String(value, Charsets.ASCII_CHARSET)});
//    }    
//    
//    private static void processServiceResponseHeader(
//            final HttpResponsePacket response,
//            final byte[] name, final byte[] value) {
//
//        switch (name.length - 1) {
//            case 6: {
//                if (checkArraysContent(name, 1,
//                        Constants.STATUS_HEADER_BYTES, 1)) {
//                    if ((value.length) != 3) {
//                        throw new IllegalStateException("Unexpected status code: " +
//                                new String(value, Charsets.UTF8_CHARSET));
//                    }
//
//                    
//                    response.setStatus(Ascii.parseInt(value, 0, 3));
//                }
//
//                break;
//            }
//        }
//
//        LOGGER.log(Level.FINE, "Skipping unknown service header[{0}={1}",
//                new Object[]{new String(name, Charsets.ASCII_CHARSET),
//                    new String(value, Charsets.ASCII_CHARSET)});
//    }
//    
//    private static void processNormalHeader(final HttpHeader httpHeader,
//            final byte[] header, final byte[] value) {
//
//        final MimeHeaders mimeHeaders = httpHeader.getHeaders();
//
//        final DataChunk valueChunk =
//                mimeHeaders.addValue(header, 0, header.length);
//
//        final int valueLen = value.length;
//        
//        for (int i = 0; i < valueLen; i++) {
//            final byte b = value[i];
//            if (b == 0) {
//                value[i] = ',';
//            }
//        }
//
//        valueChunk.setBytes(value, 0, valueLen);
//        
//        finalizeKnownHeader(httpHeader, header, value);
//    }
//
//    private static void finalizeKnownHeader(final HttpHeader httpHeader,
//            final byte[] name, final byte[] value) {
//        
//        final int nameLen = name.length;
//        
//        if (nameLen == Header.ContentLength.getLowerCaseBytes().length) {
//            if (httpHeader.getContentLength() == -1
//                    && ByteChunk.equalsIgnoreCaseLowerCase(name, 0, nameLen,
//                    Header.ContentLength.getLowerCaseBytes())) {
//                httpHeader.setContentLengthLong(Ascii.parseLong(
//                        value, 0, value.length));
//            }
//        } else if (nameLen == Header.Upgrade.getLowerCaseBytes().length) {
//            if (ByteChunk.equalsIgnoreCaseLowerCase(name, 0, nameLen,
//                    Header.Upgrade.getLowerCaseBytes())) {
//                httpHeader.getUpgradeDC().setBytes(value);
//            }
//        } else if (nameLen == Header.Expect.getLowerCaseBytes().length) {
//            if (ByteChunk.equalsIgnoreCaseLowerCase(name, 0, nameLen,
//                    Header.Expect.getLowerCaseBytes())) {
//                ((Http2Request) httpHeader).requiresAcknowledgement(true);
//            }
//        }
//    }

    private static boolean checkArraysContent(final byte[] b1, final int pos1,
            final byte[] control, final int pos2) {
        for (int i = 0, len = control.length - pos2; i < len; i++) {
            if (b1[pos1 + i] != control[pos2 + i]) {
                return false;
            }
        }

        return true;
    }

    // ---------------------------------------------------------- Nested Classes


    private static final class InitialLineParsingState implements Cacheable {

        private static final ThreadCache.CachedTypeIndex CACHE_IDX =
                ThreadCache.obtainIndex(InitialLineParsingState.class, 8);

        private byte parseState;


        // --------------------------------------------- Package Private Methods


        static InitialLineParsingState create() {
            InitialLineParsingState state = ThreadCache.getFromCache(CACHE_IDX);
            if (state == null) {
                state = new InitialLineParsingState();
            }
            return state;
        }


        void statusCodeParsed() {
            parseState |= 1;
        }

        boolean isParseComplete() {
            return parseState == 0x1;
        }


        // ---------------------------------------------- Methods from Cacheable


        @Override
        public void recycle() {
            parseState = 0;
            ThreadCache.putToCache(CACHE_IDX, this);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy