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

org.http4s.blaze.http_parser.ParserBase Maven / Gradle / Ivy

package org.http4s.blaze.http_parser;


import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.http4s.blaze.http_parser.BaseExceptions.BadRequest;

/**
 * @author Bryce Anderson
 *         Created on 2/4/14
 */
public abstract class ParserBase {

    // Methods for managing the internal buffer

    ParserBase(int initialSize) {
        _internalBuffer = new byte[initialSize];
        clearBuffer();
    }

    private int _bufferPosition = 0;
    private int _bufferLen = 0;
    private byte[] _internalBuffer;

    private boolean _cr;
    private int _segmentByteLimit;
    private int _segmentBytePosition;

    /** for shutting down the parser and its state */
    public void shutdownParser() {
        clearBuffer();
    }

    void reset() {
       clearBuffer();
    }

    final protected void makeRoom(int size) {
        // Resize the internal array
        int nextsize = Math.max(_bufferLen*2, _bufferLen + 2*size);
        this._bufferLen = nextsize;
        byte[] next = new byte[nextsize];

        System.arraycopy(_internalBuffer, 0, next, 0, _bufferPosition);
        _internalBuffer = next;
    }

    final protected void putByte(byte c) {
        if (_internalBuffer.length == _bufferPosition) {
            makeRoom(1);
        }
        _internalBuffer[_bufferPosition++] = c;
    }

    final protected int bufferPosition() {
        return _bufferPosition;
    }

    final protected void clearBuffer() {
        _bufferPosition = 0;
    }

    final protected String getString() {
        return getString(0, _bufferPosition);
    }

    final protected String getString(int end) {
        return getString(0, end);
    }

    final protected String getString(int start, int end) {
        String str = new String(_internalBuffer, start, end, StandardCharsets.US_ASCII);
        return str;
    }

    /** Returns the string in the buffer minus an leading or trailing whitespace or quotes */
    final protected String getTrimmedString() throws BaseExceptions.BadRequest {
        if (_bufferPosition == 0) return "";

        int start = 0;
        boolean quoted = false;
        // Look for start
        while (start < _bufferPosition) {
            final byte ch = _internalBuffer[start];
            if (ch == '"') {
                quoted = true;
                break;
            }
            else if (ch != HttpTokens.SPACE && ch != HttpTokens.TAB) {
                break;
            }
            start++;
        }

        int end = _bufferPosition - 1;  // Position is of next write

        // Look for end
        while(end > start) {
            final byte ch = _internalBuffer[end];
            if (quoted) {
                if (ch == '"') break;
                else if (ch != HttpTokens.SPACE && ch != HttpTokens.TAB) {
                    throw new BaseExceptions.BadRequest("String might not quoted correctly: '" + getString() + "'");
                }
            }
            else if (ch != HttpTokens.SPACE && ch != HttpTokens.TAB) break;
            end--;
        }

        String str = new String(_internalBuffer, start, end + 1, StandardCharsets.US_ASCII);
        return str;
    }

    final protected boolean arrayMatches(final byte[] bytes) {
        if (bytes.length != _bufferPosition) return false;

        for (int i = 0; i < _bufferPosition; i++) {
            if (bytes[i] != _internalBuffer[i])
                return false;
        }

        return true;
    }

    /* ------------------------------------------------------------------- */

    final protected void resetLimit(int limit) {
        _segmentByteLimit = limit;
        _segmentBytePosition = 0;
    }

    // Removes CRs but returns LFs
    final protected byte next(final ByteBuffer buffer) throws BaseExceptions.BadRequest {

        if (!buffer.hasRemaining()) return 0;

        if (_segmentByteLimit == _segmentBytePosition) {
            shutdownParser();
            throw new BadRequest("Request length limit exceeded: " + _segmentByteLimit);
        }

        final byte ch = buffer.get();
        _segmentBytePosition++;

        // If we ended on a CR, make sure we are
        if (_cr) {
            if (ch != HttpTokens.LF) {
                throw new BadRequest("Invalid sequence: LF didn't follow CR: " + ch);
            }
            _cr = false;
            return ch;
        }

        // Make sure its a valid character
        if (ch < HttpTokens.SPACE) {
            if (ch == HttpTokens.CR) {   // Set the flag to check for _cr and just run again
                _cr = true;
                return next(buffer);
            }
            else if (ch == HttpTokens.TAB) {
                return ch;
            }
            else {
                if (ch == HttpTokens.LF) {
                    shutdownParser();
                    throw new BadRequest("LineFeed found without CR");
                }
                else {
                    shutdownParser();
                    throw new BadRequest("Invalid char: " + ch);
                }
            }
        }

        return ch;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy