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.0.2
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2014-2016 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.Grizzly;
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.hpack.DecodingCallback;

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

    private static final String INVALID_CHARACTER_MESSAGE =
            "Invalid character 0x%02x at index '%s' found in header %s [%s: %s]";

    @SuppressWarnings("DuplicateThrows")
    static void decodeRequestHeaders(final Http2Connection http2Connection,
                                     final HttpRequestPacket request)
            throws InvalidCharacterException, IOException {

        try {
            http2Connection.getHeadersDecoder().decode(new DecodingCallback() {

                @Override
                public void onDecoded(CharSequence name, CharSequence value) {
                    validateHeaderCharacters(name, value);
                    if (name.charAt(0) == ':') {
                        processServiceRequestHeader(request, name.toString(), value.toString());
                    } else {
                        processNormalHeader(request, name.toString(), value.toString());
                    }
                }

            });
        } catch (RuntimeException re) {
            throw new InvalidCharacterException(re);
        } finally {
            request.setProtocol(Protocol.HTTP_2_0);
        }
    }

    @SuppressWarnings("DuplicateThrows")
    static void decodeResponseHeaders(final Http2Connection http2Connection,
                                      final HttpResponsePacket response)
            throws InvalidCharacterException, IOException {

        try {
            http2Connection.getHeadersDecoder().decode(new DecodingCallback() {

                @Override
                public void onDecoded(final CharSequence name, final CharSequence value) {
                    validateHeaderCharacters(name, value);
                    if (name.charAt(0) == ':') {
                        processServiceResponseHeader(response, name.toString(), value.toString());
                    } else {
                        processNormalHeader(response, name.toString(), value.toString());
                    }
                }

            });
        } catch (RuntimeException re) {
            throw new InvalidCharacterException(re);
        } finally {
            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);
            }
        }
    }

    private static void validateHeaderCharacters(final CharSequence name, final CharSequence value) {
        assert (name != null);
        assert (value != null);
        int idx = ensureRange(name);
        if (idx != -1) {
            final String msg = String.format(INVALID_CHARACTER_MESSAGE, (int) name.charAt(idx), idx, "name", name, value);
            throw new RuntimeException(msg);
        }
        idx = ensureRange(value);
        if (idx != -1) {
            final String msg = String.format(INVALID_CHARACTER_MESSAGE, (int) name.charAt(idx), idx, "value", name, value);
            throw new RuntimeException(msg);
        }
    }

    private static int ensureRange(final CharSequence cs) {
        for (int i = 0, len = cs.length(); i < len; i++) {
            final char c = cs.charAt(i);
            if (c < 0x20 || c > 0xFF) {
                return i;
            }
        }
        return -1;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy