diff options
author | xenoxeno <xeno@ydb.tech> | 2022-11-17 13:36:18 +0300 |
---|---|---|
committer | xenoxeno <xeno@ydb.tech> | 2022-11-17 13:36:18 +0300 |
commit | 5d34ce409febec222f18d3aae7983a3c658967f4 (patch) | |
tree | 6cf8f293a0d6de3e2e8e8922955205c945992f60 | |
parent | 53b9f0e219ca25b30531930fffc826f272fae1e3 (diff) | |
download | ydb-5d34ce409febec222f18d3aae7983a3c658967f4.tar.gz |
fix node crash on bad content length parsing
-rw-r--r-- | library/cpp/actors/http/http.cpp | 28 |
1 files changed, 20 insertions, 8 deletions
diff --git a/library/cpp/actors/http/http.cpp b/library/cpp/actors/http/http.cpp index 7057689b52..f5d7f5cf5c 100644 --- a/library/cpp/actors/http/http.cpp +++ b/library/cpp/actors/http/http.cpp @@ -22,6 +22,10 @@ inline TStringBuf operator +=(TStringBuf& l, TStringBuf r) { return l = l + r; } +static bool is_not_number(TStringBuf v) { + return v.empty() || std::find_if_not(v.begin(), v.end(), [](unsigned char c) { return std::isdigit(c); }) != v.end(); +} + namespace NHttp { template <> TStringBuf THttpRequest::GetName<&THttpRequest::Host>() { return "Host"; } @@ -126,13 +130,16 @@ void THttpParser<THttpRequest, TSocketBuffer>::Advance(size_t len) { [[fallthrough]]; } case EParseStage::Body: { - if (!ContentLength.empty()) { - if (ProcessData(Content, data, FromString(ContentLength))) { + if (TEqNoCase()(TransferEncoding, "chunked")) { + Stage = EParseStage::ChunkLength; + } else if (!ContentLength.empty()) { + if (is_not_number(ContentLength)) { + // Invalid content length + Stage = EParseStage::Error; + } else if (ProcessData(Content, data, FromStringWithDefault(ContentLength, 0))) { Body = Content; Stage = EParseStage::Done; } - } else if (TEqNoCase()(TransferEncoding, "chunked")) { - Stage = EParseStage::ChunkLength; } else if (TotalSize.has_value()) { if (ProcessData(Content, data, GetBodySizeFromTotalSize())) { Body = Content; @@ -282,16 +289,19 @@ void THttpParser<THttpResponse, TSocketBuffer>::Advance(size_t len) { [[fallthrough]]; } case EParseStage::Body: { - if (!ContentLength.empty()) { - if (ProcessData(Body, data, FromString(ContentLength))) { + if (TEqNoCase()(TransferEncoding, "chunked")) { + Stage = EParseStage::ChunkLength; + } else if (!ContentLength.empty()) { + if (is_not_number(ContentLength)) { + // Invalid content length + Stage = EParseStage::Error; + } else if (ProcessData(Body, data, FromStringWithDefault(ContentLength, 0))) { Stage = EParseStage::Done; if (Body && ContentEncoding == "deflate") { Content = DecompressDeflate(Body); Body = Content; } } - } else if (TEqNoCase()(TransferEncoding, "chunked")) { - Stage = EParseStage::ChunkLength; } else if (TotalSize.has_value()) { if (ProcessData(Content, data, GetBodySizeFromTotalSize())) { Body = Content; @@ -401,9 +411,11 @@ THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseString(TStringBuf d } } } + headers.Erase("Transfer-Encoding"); // we erase transfer-encoding because we convert body to content-length response->Set(headers); response->SetBody(parser.Body); } else { + headers.Erase("Transfer-Encoding"); // we erase transfer-encoding because we convert body to content-length response->Set(headers); if (!response->ContentLength) { response->Set<&THttpResponse::ContentLength>("0"); |