| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #include "http/http_parser.h" |
| |
| namespace doris { |
| |
| std::ostream& operator<<(std::ostream& os, const HttpChunkParseCtx& ctx) { |
| os << "HttpChunkParseCtx(" |
| << "state=" << ctx.state << "size=" << ctx.size << "length=" << ctx.length << ")"; |
| return os; |
| } |
| |
| #define CR ((uint8_t)'\r') |
| #define LF ((uint8_t)'\n') |
| |
| 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; |
| } |
| |
| } // namespace doris |