273 lines
7.8 KiB
C++
273 lines
7.8 KiB
C++
// 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
|