HttpParser::ParseState HttpParser::http_parse_chunked()

in be/src/http/http_parser.cpp [31:270]


HttpParser::ParseState HttpParser::http_parse_chunked(const uint8_t** buf, const int64_t buf_len,
                                                      HttpChunkParseCtx* ctx) {
    enum ChunkParseState {
        SW_CHUNK_START = 0,
        SW_CHUNK_SIZE,
        SW_CHUNK_EXTENSION,
        SW_CHUNK_EXTENSION_ALMOST_DONE,
        SW_CHUNK_DATA,
        SW_AFTER_DATA,
        SW_AFTER_DATA_ALMOST_DONE,
        SW_LAST_CHUNK_EXTENSION,
        SW_LAST_CHUNK_EXTENSION_ALMOST_DONE,
        SW_TRAILER,
        SW_TRAILER_ALMOST_DONE,
        SW_TRAILER_HEADER,
        SW_TRAILER_HEADER_ALMOST_DONE
    } state;

    // from last state
    state = (ChunkParseState)ctx->state;
    ParseState rc = PARSE_AGAIN;
    if (state == SW_CHUNK_DATA && ctx->size == 0) {
        state = SW_AFTER_DATA;
    }

    const uint8_t* pos = *buf;
    const uint8_t* end = *buf + buf_len;
    for (; pos < end; pos++) {
        uint8_t ch = *pos;
        uint8_t c = ch;
        bool break_loop = false;
        switch (state) {
        case SW_CHUNK_START:
            if (ch >= '0' && ch <= '9') {
                state = SW_CHUNK_SIZE;
                ctx->size = ch - '0';
                break;
            }
            c = ch | 0x20;
            if (c >= 'a' && c <= 'f') {
                state = SW_CHUNK_SIZE;
                ctx->size = c - 'a' + 10;
                break;
            }
            // Invalid state
            return PARSE_ERROR;
        case SW_CHUNK_SIZE:
            if (ch >= '0' && ch <= '9') {
                ctx->size = ctx->size * 16 + (ch - '0');
                break;
            }
            c = ch | 0x20;
            if (c >= 'a' && c <= 'f') {
                ctx->size = ctx->size * 16 + (c - 'a' + 10);
                break;
            }
            // handle last check
            if (ctx->size == 0) {
                switch (ch) {
                case CR:
                    state = SW_LAST_CHUNK_EXTENSION_ALMOST_DONE;
                    break;
                case LF:
                    state = SW_TRAILER;
                    break;
                case ';':
                case ' ':
                case '\t':
                    state = SW_LAST_CHUNK_EXTENSION;
                    break;
                default:
                    // Invalid state
                    return PARSE_ERROR;
                }
                break;
            }
            // Check if size over
            switch (ch) {
            case CR:
                state = SW_CHUNK_EXTENSION_ALMOST_DONE;
                break;
            case LF:
                state = SW_CHUNK_DATA;
                break;
            case ';':
            case ' ':
            case '\t':
                state = SW_CHUNK_EXTENSION;
                break;
            default:
                return PARSE_ERROR;
            }
            break;
        case SW_CHUNK_EXTENSION:
            switch (ch) {
            case CR:
                state = SW_CHUNK_EXTENSION_ALMOST_DONE;
                break;
            case LF:
                state = SW_CHUNK_DATA;
                break;
            default:
                // just swallow this character
                break;
            }
            break;
        case SW_CHUNK_EXTENSION_ALMOST_DONE:
            if (ch == LF) {
                state = SW_CHUNK_DATA;
                break;
            }
            return PARSE_ERROR;
        case SW_CHUNK_DATA:
            rc = PARSE_OK;
            break_loop = true;
            break;
        case SW_AFTER_DATA:
            switch (ch) {
            case CR:
                state = SW_AFTER_DATA_ALMOST_DONE;
                break;
            case LF:
                // Next chunk
                state = SW_CHUNK_START;
                break;
            default:
                // just swallow
                break;
            }
            break;
        case SW_AFTER_DATA_ALMOST_DONE:
            if (ch == LF) {
                state = SW_CHUNK_START;
                break;
            }
            return PARSE_ERROR;
        case SW_LAST_CHUNK_EXTENSION:
            switch (ch) {
            case CR:
                state = SW_LAST_CHUNK_EXTENSION_ALMOST_DONE;
                break;
            case LF:
                state = SW_TRAILER;
                break;
            default:
                // Just swallow
                break;
            }
            break;
        case SW_LAST_CHUNK_EXTENSION_ALMOST_DONE:
            if (ch == LF) {
                state = SW_TRAILER;
                break;
            }
            return PARSE_ERROR;
        case SW_TRAILER:
            switch (ch) {
            case CR:
                state = SW_TRAILER_ALMOST_DONE;
                break;
            case LF:
                // Over
                ctx->state = 0;
                *buf = pos + 1;
                return PARSE_DONE;
            default:
                state = SW_TRAILER_HEADER;
            }
            break;
        case SW_TRAILER_ALMOST_DONE:
            if (ch == LF) {
                ctx->state = 0;
                *buf = pos + 1;
                return PARSE_DONE;
            }
            return PARSE_ERROR;
        case SW_TRAILER_HEADER:
            switch (ch) {
            case CR:
                state = SW_TRAILER_HEADER_ALMOST_DONE;
                break;
            case LF:
                state = SW_TRAILER;
                break;
            default:
                break;
            }
            break;
        case SW_TRAILER_HEADER_ALMOST_DONE:
            if (ch == LF) {
                state = SW_TRAILER;
                break;
            }
            return PARSE_ERROR;
        }
        if (break_loop) {
            break;
        }
    }

    ctx->state = state;
    *buf = pos;

    // Set length have
    switch (state) {
    case SW_CHUNK_START:
        ctx->length = 3 /* "0" LF LF */;
        break;
    case SW_CHUNK_SIZE:
        ctx->length = 1                            /* LF */
                      + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */
                                   : 1 /* LF */);
        break;
    case SW_CHUNK_EXTENSION:
    case SW_CHUNK_EXTENSION_ALMOST_DONE:
        ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */;
        break;
    case SW_CHUNK_DATA:
        ctx->length = ctx->size + 4 /* LF "0" LF LF */;
        break;
    case SW_AFTER_DATA:
    case SW_AFTER_DATA_ALMOST_DONE:
        ctx->length = 4 /* LF "0" LF LF */;
        break;
    case SW_LAST_CHUNK_EXTENSION:
    case SW_LAST_CHUNK_EXTENSION_ALMOST_DONE:
        ctx->length = 2 /* LF LF */;
        break;
    case SW_TRAILER:
    case SW_TRAILER_ALMOST_DONE:
        ctx->length = 1 /* LF */;
        break;
    case SW_TRAILER_HEADER:
    case SW_TRAILER_HEADER_ALMOST_DONE:
        ctx->length = 2 /* LF LF */;
        break;
    }

    return rc;
}