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