From 68211df2bd2640fb87c12ff0beeb959b28e2e8a4 Mon Sep 17 00:00:00 2001 From: jepett0 <111313089+jepett0@users.noreply.github.com> Date: Mon, 5 Feb 2024 20:17:52 +0300 Subject: Handle bad http headers in requests to YDB (#1542) Header without a colon no longer crashes YDB --- ydb/library/actors/http/http.cpp | 10 +++++---- ydb/library/actors/http/http.h | 10 ++++++--- ydb/library/actors/http/http_ut.cpp | 42 +++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/ydb/library/actors/http/http.cpp b/ydb/library/actors/http/http.cpp index 9da46e412bb..8a8e28fd394 100644 --- a/ydb/library/actors/http/http.cpp +++ b/ydb/library/actors/http/http.cpp @@ -119,8 +119,9 @@ void THttpParser::Advance(size_t len) { } else { Stage = EParseStage::Done; } - } else { - ProcessHeader(Header); + } else if (!ProcessHeader(Header)) { + Stage = EParseStage::Error; + break; } Headers = TStringBuf(Headers.data(), data.data() - Headers.data()); } @@ -278,8 +279,9 @@ void THttpParser::Advance(size_t len) { } else { Stage = EParseStage::Done; } - } else { - ProcessHeader(Header); + } else if (!ProcessHeader(Header)) { + Stage = EParseStage::Error; + break; } Headers = TStringBuf(Headers.data(), data.data() - Headers.data()); } diff --git a/ydb/library/actors/http/http.h b/ydb/library/actors/http/http.h index d96ab062e89..47ba11cdcc1 100644 --- a/ydb/library/actors/http/http.h +++ b/ydb/library/actors/http/http.h @@ -291,16 +291,20 @@ public: return target.size() == size; } - void ProcessHeader(TStringBuf& header) { - TStringBuf name = header.NextTok(':'); + bool ProcessHeader(TStringBuf& header) { + TStringBuf name; + TStringBuf value; + if (!header.TrySplit(':', name, value)) { + return false; + } TrimBegin(name, ' '); - TStringBuf value = header; Trim(value, ' '); auto cit = HeaderType::HeadersLocation.find(name); if (cit != HeaderType::HeadersLocation.end()) { this->*cit->second = value; } header.Clear(); + return true; } size_t ParseHex(TStringBuf value) { diff --git a/ydb/library/actors/http/http_ut.cpp b/ydb/library/actors/http/http_ut.cpp index 6c92f3b938f..6a10b9d5192 100644 --- a/ydb/library/actors/http/http_ut.cpp +++ b/ydb/library/actors/http/http_ut.cpp @@ -50,6 +50,20 @@ Y_UNIT_TEST_SUITE(HttpProxy) { UNIT_ASSERT_EQUAL(request->Headers, "Host: test\r\nSome-Header: 32344\r\n\r\n"); } + Y_UNIT_TEST(HeaderParsingError_Request) { + NHttp::THttpIncomingRequestPtr request = new NHttp::THttpIncomingRequest(); + EatWholeString(request, "GET /test HTTP/1.1\r\nHost: test\r\nHeader-Without-A-Colon\r\n\r\n"); + UNIT_ASSERT_C(request->IsError(), static_cast(request->Stage)); + UNIT_ASSERT_EQUAL_C(request->GetErrorText(), "Invalid http header", static_cast(request->LastSuccessStage)); + } + + Y_UNIT_TEST(HeaderParsingError_Response) { + NHttp::THttpIncomingResponsePtr response = new NHttp::THttpIncomingResponse(nullptr); + EatWholeString(response, "HTTP/1.1 200 OK\r\nConnection: close\r\nHeader-Without-A-Colon\r\n\r\n"); + UNIT_ASSERT_C(response->IsError(), static_cast(response->Stage)); + UNIT_ASSERT_EQUAL_C(response->GetErrorText(), "Invalid http header", static_cast(response->LastSuccessStage)); + } + Y_UNIT_TEST(GetWithSpecifiedContentType) { NHttp::THttpIncomingRequestPtr request = new NHttp::THttpIncomingRequest(); EatWholeString(request, "GET /test HTTP/1.1\r\nHost: test\r\nContent-Type: application/json\r\nSome-Header: 32344\r\n\r\n"); @@ -506,4 +520,32 @@ CRA/5XcX13GJwHHj6LCoc3sL7mt8qV9HKY2AOZ88mpObzISZxgPpdKCfjsrdm63V UNIT_ASSERT_EQUAL(response->Response->Status, "400"); UNIT_ASSERT_EQUAL(response->Response->Body, "Invalid http header"); } + + Y_UNIT_TEST(HeaderWithoutAColon) { + NActors::TTestActorRuntimeBase actorSystem; + actorSystem.SetUseRealInterconnect(); + TPortManager portManager; + TIpPort port = portManager.GetTcpPort(); + TAutoPtr handle; + actorSystem.Initialize(); + + NActors::IActor* proxy = NHttp::CreateHttpProxy(); + NActors::TActorId proxyId = actorSystem.Register(proxy); + actorSystem.Send(new NActors::IEventHandle(proxyId, actorSystem.AllocateEdgeActor(), new NHttp::TEvHttpProxy::TEvAddListeningPort(port)), 0, true); + actorSystem.GrabEdgeEvent(handle); + + NActors::TActorId serverId = actorSystem.AllocateEdgeActor(); + actorSystem.Send(new NActors::IEventHandle(proxyId, serverId, new NHttp::TEvHttpProxy::TEvRegisterHandler("/test", serverId)), 0, true); + + NActors::TActorId clientId = actorSystem.AllocateEdgeActor(); + NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestGet("http://[::1]:" + ToString(port) + "/test"); + httpRequest->Append("Header-Without-A-Colon\r\n"); + httpRequest->FinishHeader(); + actorSystem.Send(new NActors::IEventHandle(proxyId, clientId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)), 0, true); + + NHttp::TEvHttpProxy::TEvHttpIncomingResponse* response = actorSystem.GrabEdgeEvent(handle); + + UNIT_ASSERT_EQUAL(response->Response->Status, "400"); + UNIT_ASSERT_EQUAL(response->Response->Body, "Invalid http header"); + } } -- cgit v1.3