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

org.glassfish.grizzly.spdy.SpdyDecoderUtils Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013 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.spdy;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Cacheable;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.http.HttpHeader;
import org.glassfish.grizzly.http.HttpResponsePacket;
import org.glassfish.grizzly.http.Protocol;
import org.glassfish.grizzly.http.util.Ascii;
import org.glassfish.grizzly.http.util.BufferChunk;
import org.glassfish.grizzly.http.util.ByteChunk;
import org.glassfish.grizzly.http.util.DataChunk;
import org.glassfish.grizzly.http.util.Header;
import org.glassfish.grizzly.http.util.HttpCodecUtils;
import org.glassfish.grizzly.http.util.MimeHeaders;
import org.glassfish.grizzly.utils.Charsets;

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

    static void processSynStreamHeadersArray(final SpdyRequest spdyRequest,
            final Buffer decoded) {
        
        final byte[] headersArray = decoded.array();
        int position = decoded.arrayOffset() + decoded.position();

        final int headersCount = getInt(headersArray, position);
        position += 4;

        for (int i = 0; i < headersCount; i++) {
            final boolean isServiceHeader = (headersArray[position + 4] == ':');

            if (isServiceHeader) {
                position = processServiceSynStreamHeader(spdyRequest,
                        headersArray, position);
            } else {
                position = processNormalHeader(spdyRequest,
                        headersArray, position);
            }
        }
    }

    static void processSynStreamHeadersBuffer(final SpdyRequest spdyRequest,
            final Buffer headersBuffer) {

        int position = headersBuffer.position();
        final int headersCount = headersBuffer.getInt(position);
        position += 4;

        for (int i = 0; i < headersCount; i++) {
            final boolean isServiceHeader = (headersBuffer.get(position + 4) == ':');

            if (isServiceHeader) {
                position = processServiceSynStreamHeader(spdyRequest,
                        headersBuffer, position);
            } else {
                position = processNormalHeader(spdyRequest,
                        headersBuffer, position);
            }
        }
    }
    
    static void processUSynStreamHeadersArray(final SpdyRequest spdyRequest,
            final Buffer decoded) {
        
        final byte[] headersArray = decoded.array();
        int position = decoded.arrayOffset() + decoded.position();

        final int headersCount = getInt(headersArray, position);
        position += 4;

        final SpdyResponse spdyResponse = (SpdyResponse) spdyRequest.getResponse();
        
        for (int i = 0; i < headersCount; i++) {
            final boolean isServiceHeader = (headersArray[position + 4] == ':');

            if (isServiceHeader) {
                position = processServiceUSynStreamHeader(spdyRequest,
                        headersArray, position);
            } else {
                position = processNormalHeader(spdyResponse,
                        headersArray, position);
            }
        }
    }

    static void processUSynStreamHeadersBuffer(final SpdyRequest spdyRequest,
            final Buffer headersBuffer) {

        int position = headersBuffer.position();
        final int headersCount = headersBuffer.getInt(position);
        position += 4;

        final SpdyResponse spdyResponse = (SpdyResponse) spdyRequest.getResponse();

        for (int i = 0; i < headersCount; i++) {
            final boolean isServiceHeader = (headersBuffer.get(position + 4) == ':');

            if (isServiceHeader) {
                position = processServiceUSynStreamHeader(spdyRequest,
                        headersBuffer, position);
            } else {
                position = processNormalHeader(spdyResponse,
                        headersBuffer, position);
            }
        }
    }

    private static int processServiceSynStreamHeader(final SpdyRequest spdyRequest,
                                                     final byte[] headersArray,
                                                     final int position) {

        final int nameSize = getInt(headersArray, position);
        final int valueSize = getInt(headersArray, position + nameSize + 4);

        final int nameStart = position + 5; // Skip headerNameSize and following ':'
        final int valueStart = position + nameSize + 8;
        final int valueEnd = valueStart + valueSize;

        switch (nameSize - 1) {
            case 4: {
                if (checkArraysContent(headersArray, nameStart,
                        Constants.HOST_HEADER_BYTES, 1)) {
                    spdyRequest.getHeaders().addValue(Header.Host)
                            .setBytes(headersArray, valueStart, valueEnd);

                    return valueEnd;
                } else if (checkArraysContent(headersArray, nameStart,
                        Constants.PATH_HEADER_BYTES, 1)) {

                    int questionIdx = -1;
                    for (int i = 0; i < valueSize; i++) {
                        if (headersArray[valueStart + i] == '?') {
                            questionIdx = i + valueStart;
                            break;
                        }
                    }

                    if (questionIdx == -1) {
                        spdyRequest.getRequestURIRef().init(headersArray, valueStart, valueEnd);
                    } else {
                        spdyRequest.getRequestURIRef().init(headersArray, valueStart, questionIdx);
                        if (questionIdx < valueEnd - 1) {
                            spdyRequest.getQueryStringDC()
                                    .setBytes(headersArray, questionIdx + 1, valueEnd);
                        }
                    }

                    return valueEnd;
                }

                break;
            } case 6: {
                if (checkArraysContent(headersArray, nameStart,
                        Constants.METHOD_HEADER_BYTES, 1)) {
                    spdyRequest.getMethodDC().setBytes(
                            headersArray, valueStart, valueEnd);

                    return valueEnd;
                } else if (checkArraysContent(headersArray, nameStart,
                        Constants.SCHEMA_HEADER_BYTES, 1)) {

                    spdyRequest.setSecure(valueSize == 5); // support http and https only
                    return valueEnd;
                }
                
                break;
            } case 7: {
                if (checkArraysContent(headersArray, nameStart,
                                Constants.VERSION_HEADER_BYTES, 1)) {
                    spdyRequest.getProtocolDC().setBytes(
                            headersArray, valueStart, valueEnd);

                    return valueEnd;
                }
            }
        }

        LOGGER.log(Level.FINE, "Skipping unknown service header[{0}={1}",
                new Object[]{new String(headersArray, nameStart - 1, nameSize, Charsets.ASCII_CHARSET),
                    new String(headersArray, valueStart, valueSize, Charsets.ASCII_CHARSET)});

        return valueEnd;
    }
    
    private static int processServiceSynStreamHeader(final SpdyRequest spdyRequest,
                                              final Buffer buffer, final int position) {

        final int nameSize = buffer.getInt(position);
        final int valueSize = buffer.getInt(position + nameSize + 4);

        final int nameStart = position + 5; // Skip headerNameSize and following ':'
        final int valueStart = position + nameSize + 8;
        final int valueEnd = valueStart + valueSize;

        switch (nameSize - 1) {
            case 4: {
                if (checkBufferContent(buffer, nameStart,
                        Constants.HOST_HEADER_BYTES, 1)) {
                    spdyRequest.getHeaders().addValue(Header.Host)
                            .setBuffer(buffer, valueStart, valueEnd);

                    return valueEnd;
                } else if (checkBufferContent(buffer, nameStart,
                        Constants.PATH_HEADER_BYTES, 1)) {

                    int questionIdx = -1;
                    for (int i = 0; i < valueSize; i++) {
                        if (buffer.get(valueStart + i) == '?') {
                            questionIdx = i + valueStart;
                            break;
                        }
                    }

                    if (questionIdx == -1) {
                        spdyRequest.getRequestURIRef().init(buffer, valueStart, valueEnd);
                    } else {
                        spdyRequest.getRequestURIRef().getOriginalRequestURIBC()
                                .setBuffer(buffer, valueStart, questionIdx);
                        if (questionIdx < valueEnd - 1) {
                            spdyRequest.getQueryStringDC()
                                    .setBuffer(buffer, questionIdx + 1, valueEnd);
                        }
                    }

                    return valueEnd;
                }

                break;
            }
            case 6: {
                if (checkBufferContent(buffer, nameStart,
                        Constants.METHOD_HEADER_BYTES, 1)) {
                    spdyRequest.getMethodDC().setBuffer(
                            buffer, valueStart, valueEnd);

                    return valueEnd;
                } else if (checkBufferContent(buffer, nameStart,
                        Constants.SCHEMA_HEADER_BYTES, 1)) {

                    spdyRequest.setSecure(valueSize == 5); // support http and https only
                    return valueEnd;
                }
                
                break;
            }
            case 7: {
                if (checkBufferContent(buffer, nameStart,
                        Constants.VERSION_HEADER_BYTES, 1)) {
                    spdyRequest.getProtocolDC().setBuffer(
                            buffer, valueStart, valueEnd);

                    return valueEnd;
                }
            }
        }

        LOGGER.log(Level.FINE, "Skipping unknown service header[{0}={1}",
                new Object[] {
                    buffer.toStringContent(Charsets.ASCII_CHARSET, nameStart - 1, nameSize),
                    buffer.toStringContent(Charsets.ASCII_CHARSET, valueStart, valueSize)});

        return valueEnd;
    }
    
    private static int processServiceUSynStreamHeader(final SpdyRequest spdyRequest,
            final byte[] headersArray, final int position) {

        final int nameSize = getInt(headersArray, position);
        final int valueSize = getInt(headersArray, position + nameSize + 4);

        final int nameStart = position + 5; // Skip headerNameSize and following ':'
        final int valueStart = position + nameSize + 8;
        final int valueEnd = valueStart + valueSize;

        switch (nameSize - 1) {
            case 4: {
                if (checkArraysContent(headersArray, nameStart,
                        Constants.HOST_HEADER_BYTES, 1)) {
                    spdyRequest.getHeaders().addValue(Header.Host)
                            .setBytes(headersArray, valueStart, valueEnd);

                    return valueEnd;
                } else if (checkArraysContent(headersArray, nameStart,
                        Constants.PATH_HEADER_BYTES, 1)) {

                    int questionIdx = -1;
                    for (int i = 0; i < valueSize; i++) {
                        if (headersArray[valueStart + i] == '?') {
                            questionIdx = i + valueStart;
                            break;
                        }
                    }

                    if (questionIdx == -1) {
                        spdyRequest.getRequestURIRef().init(headersArray, valueStart, valueEnd);
                    } else {
                        spdyRequest.getRequestURIRef().init(headersArray, valueStart, questionIdx);
                        if (questionIdx < valueEnd - 1) {
                            spdyRequest.getQueryStringDC()
                                    .setBytes(headersArray, questionIdx + 1, valueEnd);
                        }
                    }

                    return valueEnd;
                }

                break;
            } case 6: {
                if (checkArraysContent(headersArray, nameStart,
                        Constants.SCHEMA_HEADER_BYTES, 1)) {

                    spdyRequest.setSecure(valueSize == 5); // support http and https only
                    return valueEnd;
                } else if (checkArraysContent(headersArray, nameStart,
                        Constants.STATUS_HEADER_BYTES, 1)) { // support :status for unidirectional requests
                    if (valueEnd < 3) {
                        throw new IllegalStateException("Unknown status code: " +
                                new String(headersArray, valueStart, valueEnd - valueStart, Charsets.UTF8_CHARSET));
                    }

                    final HttpResponsePacket spdyResponse =
                            spdyRequest.getResponse();
                    
                    spdyResponse.setStatus(Ascii.parseInt(headersArray,
                                                          valueStart,
                                                          3));
                    
                    final int reasonPhraseIdx =
                            HttpCodecUtils.skipSpaces(headersArray,
                            valueStart + 3, valueEnd, valueEnd);
                    
                    if (reasonPhraseIdx != -1) {
                        int reasonPhraseEnd = skipLastSpaces(headersArray,
                                valueStart + 3, valueEnd) + 1;
                        if (reasonPhraseEnd == 0) {
                            reasonPhraseEnd = valueEnd;
                        }
                        
                        spdyResponse.getReasonPhraseRawDC().setBytes(
                                headersArray, reasonPhraseIdx, reasonPhraseEnd);
                    }
                    
                    return valueEnd;
                }
                
                break;
            } case 7: {
                if (checkArraysContent(headersArray, nameStart,
                                Constants.VERSION_HEADER_BYTES, 1)) {
                    spdyRequest.getProtocolDC().setBytes(
                            headersArray, valueStart, valueEnd);
                    spdyRequest.getResponse().getProtocolDC().setBytes(
                            headersArray, valueStart, valueEnd);
                    
                    return valueEnd;
                }
            }
        }

        LOGGER.log(Level.FINE, "Skipping unknown service header[{0}={1}",
                new Object[]{new String(headersArray, nameStart - 1, nameSize, Charsets.ASCII_CHARSET),
                    new String(headersArray, valueStart, valueSize, Charsets.ASCII_CHARSET)});

        return valueEnd;
    }
    
    private static int processServiceUSynStreamHeader(final SpdyRequest spdyRequest,
                                              final Buffer buffer, final int position) {

        final int nameSize = buffer.getInt(position);
        final int valueSize = buffer.getInt(position + nameSize + 4);

        final int nameStart = position + 5; // Skip headerNameSize and following ':'
        final int valueStart = position + nameSize + 8;
        final int valueEnd = valueStart + valueSize;

        switch (nameSize - 1) {
            case 4: {
                if (checkBufferContent(buffer, nameStart,
                        Constants.HOST_HEADER_BYTES, 1)) {
                    spdyRequest.getHeaders().addValue(Header.Host)
                            .setBuffer(buffer, valueStart, valueEnd);

                    return valueEnd;
                } else if (checkBufferContent(buffer, nameStart,
                        Constants.PATH_HEADER_BYTES, 1)) {

                    int questionIdx = -1;
                    for (int i = 0; i < valueSize; i++) {
                        if (buffer.get(valueStart + i) == '?') {
                            questionIdx = i + valueStart;
                            break;
                        }
                    }

                    if (questionIdx == -1) {
                        spdyRequest.getRequestURIRef().init(buffer, valueStart, valueEnd);
                    } else {
                        spdyRequest.getRequestURIRef().getOriginalRequestURIBC()
                                .setBuffer(buffer, valueStart, questionIdx);
                        if (questionIdx < valueEnd - 1) {
                            spdyRequest.getQueryStringDC()
                                    .setBuffer(buffer, questionIdx + 1, valueEnd);
                        }
                    }

                    return valueEnd;
                }

                break;
            }
            case 6: {
                if (checkBufferContent(buffer, nameStart,
                        Constants.SCHEMA_HEADER_BYTES, 1)) {

                    spdyRequest.setSecure(valueSize == 5); // support http and https only
                    return valueEnd;
                } else if (checkBufferContent(buffer, nameStart,
                        Constants.STATUS_HEADER_BYTES, 1)) { // support :status for unidirectional requests
                    if (valueEnd < 3) {
                        throw new IllegalStateException("Unknown status code: " +
                                buffer.toStringContent(Charsets.ASCII_CHARSET, valueEnd, (valueEnd - valueStart)));
                    }

                    final HttpResponsePacket spdyResponse =
                            spdyRequest.getResponse();
                    
                    spdyResponse.setStatus(
                            Ascii.parseInt(buffer, valueStart, 3));

                    final int reasonPhraseIdx =
                            HttpCodecUtils.skipSpaces(buffer, valueStart + 3, valueEnd);

                    if (reasonPhraseIdx != -1) {
                        int reasonPhraseEnd = skipLastSpaces(buffer,
                                valueStart + 3, valueEnd) + 1;
                        if (reasonPhraseEnd == 0) {
                            reasonPhraseEnd = valueEnd;
                        }

                        spdyResponse.getReasonPhraseRawDC().setBuffer(
                                buffer, reasonPhraseIdx, reasonPhraseEnd);
                    }

                    return valueEnd;
                }
                
                break;
            }
            case 7: {
                if (checkBufferContent(buffer, nameStart,
                        Constants.VERSION_HEADER_BYTES, 1)) {
                    spdyRequest.getProtocolDC().setBuffer(
                            buffer, valueStart, valueEnd);
                    spdyRequest.getResponse().getProtocolDC().setBuffer(
                            buffer, valueStart, valueEnd);
                    return valueEnd;
                }
            }
        }

        LOGGER.log(Level.FINE, "Skipping unknown service header[{0}={1}",
                new Object[] {
                    buffer.toStringContent(Charsets.ASCII_CHARSET, nameStart - 1, nameSize),
                    buffer.toStringContent(Charsets.ASCII_CHARSET, valueStart, valueSize)});

        return valueEnd;
    }
    
    private static int processServiceSynReplyHeader(final SpdyResponse spdyResponse,
                                                    final byte[] headersArray,
                                                    final int position,
                                                    final InitialLineParsingState state) {

        final int nameSize = getInt(headersArray, position);
        final int valueSize = getInt(headersArray, position + nameSize + 4);

        final int nameStart = position + 5; // Skip headerNameSize and following ':'
        final int valueStart = position + nameSize + 8;
        final int valueEnd = valueStart + valueSize;

        switch (nameSize - 1) {
            case 6: {
                if (checkArraysContent(headersArray, nameStart,
                        Constants.STATUS_HEADER_BYTES, 1)) {
                    if (valueEnd < 3) {
                        throw new IllegalStateException("Unknown status code: " +
                                new String(headersArray, valueStart, valueEnd - valueStart, Charsets.UTF8_CHARSET));
                    }

                    
                    spdyResponse.setStatus(Ascii.parseInt(headersArray,
                                                          valueStart,
                                                          3));
                    state.statusCodeParsed();

                    final int reasonPhraseIdx =
                            HttpCodecUtils.skipSpaces(headersArray,
                            valueStart + 3, valueEnd, valueEnd);
                    
                    if (reasonPhraseIdx != -1) {
                        int reasonPhraseEnd = skipLastSpaces(headersArray,
                                valueStart + 3, valueEnd) + 1;
                        if (reasonPhraseEnd == 0) {
                            reasonPhraseEnd = valueEnd;
                        }
                        
                        spdyResponse.getReasonPhraseRawDC().setBytes(
                                headersArray, reasonPhraseIdx, reasonPhraseEnd);
                    }
                    state.reasonPhraseParsed();
                    
                    return valueEnd;
                }

                break;
            }

            case 7: {
                if (checkArraysContent(headersArray, nameStart,
                        Constants.VERSION_HEADER_BYTES, 1)) {
                    spdyResponse.setProtocol(Protocol.valueOf(headersArray,
                            valueStart, valueEnd - valueStart));
                    state.protocolParsed();

                    return valueEnd;
                }
            }
        }

        LOGGER.log(Level.FINE, "Skipping unknown service header[{0}={1}",
                new Object[]{new String(headersArray, position, nameSize, Charsets.ASCII_CHARSET),
                    new String(headersArray, valueStart, valueSize, Charsets.ASCII_CHARSET)});

        return valueEnd;
    }

    private static int processServiceSynReplyHeader(final SpdyResponse spdyResponse,
                                                    final Buffer buffer,
                                                    int position,
                                                    final InitialLineParsingState state) {

        final int nameSize = buffer.getInt(position);
        final int valueSize = buffer.getInt(position + nameSize + 4);

        final int nameStart = position + 5; // Skip headerNameSize and following ':'
        final int valueStart = position + nameSize + 8;
        final int valueEnd = valueStart + valueSize;

        switch (nameSize - 1) {
            case 6: {
                if (checkBufferContent(buffer, nameStart,
                        Constants.STATUS_HEADER_BYTES, 1)) {
                    if (valueEnd < 3) {
                        throw new IllegalStateException("Unknown status code: " +
                                buffer.toStringContent(Charsets.ASCII_CHARSET, valueEnd, (valueEnd - valueStart)));
                    }

                    spdyResponse.setStatus(Ascii.parseInt(buffer, valueStart, 3));
                    state.statusCodeParsed();

                    final int reasonPhraseIdx =
                            HttpCodecUtils.skipSpaces(buffer, valueStart + 3, valueEnd);

                    if (reasonPhraseIdx != -1) {
                        int reasonPhraseEnd = skipLastSpaces(buffer,
                                valueStart + 3, valueEnd) + 1;
                        if (reasonPhraseEnd == 0) {
                            reasonPhraseEnd = valueEnd;
                        }

                        spdyResponse.getReasonPhraseRawDC().setBuffer(
                                buffer, reasonPhraseIdx, reasonPhraseEnd);
                    }
                    state.reasonPhraseParsed();

                    return valueEnd;
                }

                break;
            }

            case 7: {
                if (checkBufferContent(buffer, nameStart,
                        Constants.VERSION_HEADER_BYTES, 1)) {
                    spdyResponse.setProtocol(Protocol.valueOf(buffer,
                            valueStart, valueEnd - valueStart));
                    state.protocolParsed();
                    return valueEnd;
                }
            }
        }

        LOGGER.log(Level.FINE, "Skipping unknown service header[{0}={1}",
                new Object[]{buffer.toStringContent(Charsets.ASCII_CHARSET, position, nameSize),
                             buffer.toStringContent(Charsets.ASCII_CHARSET, valueStart, valueSize)});

        return valueEnd;
    }
    
    static void processSynReplyHeadersArray(final SpdyResponse spdyResponse,
                                            final Buffer decoded,
                                            final FilterChainContext ctx,
                                            final SpdyHandlerFilter handlerFilter) {
        final byte[] headersArray = decoded.array();
        int position = decoded.arrayOffset() + decoded.position();

        final int headersCount = getInt(headersArray, position);
        position += 4;

        InitialLineParsingState state = InitialLineParsingState.create();

        for (int i = 0; i < headersCount && !spdyResponse.isSkipRemainder(); i++) {
            final boolean isServiceHeader = (headersArray[position + 4] == ':');

            if (isServiceHeader) {
                position = processServiceSynReplyHeader(spdyResponse, headersArray, position, state);
                if (state != null && state.isParseComplete()) {
                    handlerFilter.onInitialLineParsed(spdyResponse, ctx);
                    state.recycle();
                    state = null;
                }
            } else {
                position = processNormalHeader(spdyResponse, headersArray, position);
            }
        }
        handlerFilter.onHttpHeadersParsed(spdyResponse, ctx);
    }

    static void processSynReplyHeadersBuffer(final SpdyResponse spdyResponse,
                                             final Buffer decoded,
                                             final FilterChainContext ctx,
                                             final SpdyHandlerFilter handlerFilter) {
        int position = decoded.position();

        final int headersCount = decoded.getInt(position);
        position += 4;

        InitialLineParsingState state = InitialLineParsingState.create();

        for (int i = 0; i < headersCount && !spdyResponse.isSkipRemainder(); i++) {
            final boolean isServiceHeader = (decoded.get(position + 4) == ':');

            if (isServiceHeader) {
                position = processServiceSynReplyHeader(spdyResponse, decoded, position, state);
                if (state != null && state.isParseComplete()) {
                    handlerFilter.onInitialLineParsed(spdyResponse, ctx);
                    state = null;
                }
            } else {
                position = processNormalHeader(spdyResponse, decoded, position);
            }
        }
        handlerFilter.onHttpHeadersParsed(spdyResponse, ctx);
    }
    
    
    private static int processNormalHeader(final HttpHeader httpHeader,
            final byte[] headersArray, int position) {

        final MimeHeaders mimeHeaders = httpHeader.getHeaders();

        final int headerNameSize = getInt(headersArray, position);
        position += 4;

        final int headerNamePosition = position;
        
        final DataChunk valueChunk =
                mimeHeaders.addValue(headersArray, headerNamePosition, headerNameSize);

        position += headerNameSize;

        final int headerValueSize = getInt(headersArray, position);
        position += 4;

        for (int i = 0; i < headerValueSize; i++) {
            final byte b = headersArray[position + i];
            if (b == 0) {
                headersArray[position + i] = ',';
            }
        }

        final int end = position + headerValueSize;

        valueChunk.setBytes(headersArray, position, end);
        
        finalizeKnownHeader(httpHeader, headersArray,
                headerNamePosition, headerNameSize,
                position, end - position);
        
        return end;
    }

    private static int processNormalHeader(final HttpHeader httpHeader,
                                           final Buffer headerBuffer,
                                           int position) {

        final MimeHeaders mimeHeaders = httpHeader.getHeaders();

        final int headerNameSize = headerBuffer.getInt(position);
        position += 4;

        final int headerNamePosition = position;

        final DataChunk valueChunk =
                mimeHeaders.addValue(headerBuffer, headerNamePosition, headerNameSize);

        position += headerNameSize;

        final int headerValueSize = headerBuffer.getInt(position);
        position += 4;

        for (int i = 0; i < headerValueSize; i++) {
            final byte b = headerBuffer.get(position + i);
            if (b == 0) {
                headerBuffer.put(position + i, (byte) ',');
            }
        }

        final int end = position + headerValueSize;

        valueChunk.setBuffer(headerBuffer, position, end);

        finalizeKnownHeader(httpHeader, headerBuffer,
                headerNamePosition, headerNameSize,
                position, end - position);

        return end;
    }
    
    private static void finalizeKnownHeader(final HttpHeader httpHeader,
            final byte[] array,
            final int nameStart, final int nameLen,
            final int valueStart, final int valueLen) {
        
        final int nameEnd = nameStart + nameLen;
        
        if (nameLen == Header.ContentLength.getLowerCaseBytes().length) {
            if (httpHeader.getContentLength() == -1
                    && ByteChunk.equalsIgnoreCaseLowerCase(array, nameStart, nameEnd,
                    Header.ContentLength.getLowerCaseBytes())) {
                httpHeader.setContentLengthLong(Ascii.parseLong(
                        array, valueStart, valueLen));
            }
        } else if (nameLen == Header.Upgrade.getLowerCaseBytes().length) {
            if (ByteChunk.equalsIgnoreCaseLowerCase(array, nameStart, nameEnd,
                    Header.Upgrade.getLowerCaseBytes())) {
                httpHeader.getUpgradeDC().setBytes(array, valueStart,
                        valueStart + valueLen);
            }
        } else if (nameLen == Header.Expect.getLowerCaseBytes().length) {
            if (ByteChunk.equalsIgnoreCaseLowerCase(array, nameStart, nameEnd,
                    Header.Expect.getLowerCaseBytes())) {
                ((SpdyRequest) httpHeader).requiresAcknowledgement(true);
            }
        }
    }

    private static void finalizeKnownHeader(final HttpHeader httpHeader,
                                            final Buffer buffer,
                                            final int nameStart, final int nameLen,
                                            final int valueStart, final int valueLen) {

        final int nameEnd = nameStart + nameLen;

        if (nameLen == Header.ContentLength.getLowerCaseBytes().length) {
            if (httpHeader.getContentLength() == -1
                    && BufferChunk.equalsIgnoreCaseLowerCase(buffer, nameStart, nameEnd,
                    Header.ContentLength.getLowerCaseBytes())) {
                httpHeader.setContentLengthLong(Ascii.parseLong(
                        buffer, valueStart, valueLen));
            }
        } else if (nameLen == Header.Upgrade.getLowerCaseBytes().length) {
            if (BufferChunk.equalsIgnoreCaseLowerCase(buffer,
                                                      nameStart,
                                                      nameEnd,
                                                      Header.Upgrade.getLowerCaseBytes())) {
                httpHeader.getUpgradeDC().setBuffer(buffer, valueStart,
                        valueStart + valueLen);
            }
        } else if (nameLen == Header.Expect.getLowerCaseBytes().length) {
            if (BufferChunk.equalsIgnoreCaseLowerCase(buffer,
                                                      nameStart,
                                                      nameEnd,
                                                      Header.Expect.getLowerCaseBytes())) {
                ((SpdyRequest) httpHeader).requiresAcknowledgement(true);
            }
        }
    }
    
    private static int getInt(final byte[] array, final int position) {
        return ((array[position] & 0xFF) << 24) +
                ((array[position + 1] & 0xFF) << 16) +
                ((array[position + 2] & 0xFF) << 8) +
                (array[position + 3] & 0xFF);
    }

    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;
    }

    private static boolean checkBufferContent(final Buffer toTest, final int pos,
            final byte[] control, final int pos2) {
        for (int i = 0, len = control.length - pos2; i < len; i++) {
            if (toTest.get(pos + i) != control[pos2 + i]) {
                return false;
            }
        }

        return true;
    }
    
    private static int skipLastSpaces(byte[] array, int start, int end) {
        for (int i = end - 1; i >= start; i--) {
            if (HttpCodecUtils.isNotSpaceAndTab(array[i])) {
                return i;
            }
        }
        
        return -1;
    }

    private static int skipLastSpaces(Buffer buffer, int start, int end) {
        for (int i = end - 1; i >= start; i--) {
            if (HttpCodecUtils.isNotSpaceAndTab(buffer.get(i))) {
                return i;
            }
        }

        return -1;
    }


    // ---------------------------------------------------------- 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 protocolParsed() {
            parseState |= (1 << 2);
        }

        void reasonPhraseParsed() {
            parseState |= (1 << 1);
        }

        void statusCodeParsed() {
            parseState |= 1;
        }

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


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


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

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy