diff options
author | Alexey Efimov <xeno@prnwatch.com> | 2022-05-04 10:13:58 +0300 |
---|---|---|
committer | Alexey Efimov <xeno@prnwatch.com> | 2022-05-04 10:13:58 +0300 |
commit | 6faf680f58ba8341a694dcbadf572d37197ae888 (patch) | |
tree | e5fd053607e59e6db7bdec1ea100f46e149a6f97 | |
parent | b4c4f96bf4475170a9ac5e4568cc7b05789986c8 (diff) | |
download | ydb-6faf680f58ba8341a694dcbadf572d37197ae888.tar.gz |
add async monitoring http KIKIMR-14742
ref:c51d608f0ae78f08597b88f837491da33a953ef6
28 files changed, 1358 insertions, 640 deletions
diff --git a/library/cpp/actors/http/http.cpp b/library/cpp/actors/http/http.cpp index 7125f9d8b0..3d07c870ce 100644 --- a/library/cpp/actors/http/http.cpp +++ b/library/cpp/actors/http/http.cpp @@ -63,6 +63,14 @@ void THttpRequest::Clear() { } template <> +bool THttpParser<THttpRequest, TSocketBuffer>::HaveBody() const { + if (!Body.empty()) { + return true; + } + return (!ContentType.empty() || !ContentLength.empty() || !TransferEncoding.empty()); +} + +template <> void THttpParser<THttpRequest, TSocketBuffer>::Advance(size_t len) { TStringBuf data(Pos(), len); while (!data.empty()) { @@ -98,7 +106,6 @@ void THttpParser<THttpRequest, TSocketBuffer>::Advance(size_t len) { case EParseStage::Header: { if (ProcessData(Header, data, "\r\n", MaxHeaderSize)) { if (Header.empty()) { - Headers = TStringBuf(Headers.data(), data.begin() - Headers.begin()); if (HaveBody()) { Stage = EParseStage::Body; } else { @@ -107,6 +114,7 @@ void THttpParser<THttpRequest, TSocketBuffer>::Advance(size_t len) { } else { ProcessHeader(Header); } + Headers = TStringBuf(Headers.data(), data.data() - Headers.data()); } break; } @@ -116,8 +124,13 @@ void THttpParser<THttpRequest, TSocketBuffer>::Advance(size_t len) { Body = Content; Stage = EParseStage::Done; } - } else if (TransferEncoding == "chunked") { + } else if (TEqNoCase()(TransferEncoding, "chunked")) { Stage = EParseStage::ChunkLength; + } else if (TotalSize.has_value()) { + if (ProcessData(Content, data, GetBodySizeFromTotalSize())) { + Body = Content; + Stage = EParseStage::Done; + } } else { // Invalid body encoding Stage = EParseStage::Error; @@ -189,6 +202,15 @@ THttpParser<THttpRequest, TSocketBuffer>::EParseStage THttpParser<THttpRequest, } template <> +bool THttpParser<THttpResponse, TSocketBuffer>::HaveBody() const { + if (!Body.empty()) { + return true; + } + return (!Status.starts_with("1") && Status != "204" && Status != "304") + && (!ContentType.empty() || !ContentLength.empty() || !TransferEncoding.empty()); +} + +template <> THttpParser<THttpResponse, TSocketBuffer>::EParseStage THttpParser<THttpResponse, TSocketBuffer>::GetInitialStage() { return EParseStage::Protocol; } @@ -237,6 +259,8 @@ void THttpParser<THttpResponse, TSocketBuffer>::Advance(size_t len) { if (Header.empty()) { if (HaveBody() && (ContentLength.empty() || ContentLength != "0")) { Stage = EParseStage::Body; + } else if (TotalSize.has_value() && !data.empty()) { + Stage = EParseStage::Body; } else { Stage = EParseStage::Done; } @@ -252,8 +276,13 @@ void THttpParser<THttpResponse, TSocketBuffer>::Advance(size_t len) { if (ProcessData(Body, data, FromString(ContentLength))) { Stage = EParseStage::Done; } - } else if (TransferEncoding == "chunked") { + } else if (TEqNoCase()(TransferEncoding, "chunked")) { Stage = EParseStage::ChunkLength; + } else if (TotalSize.has_value()) { + if (ProcessData(Content, data, GetBodySizeFromTotalSize())) { + Body = Content; + Stage = EParseStage::Done; + } } else { // Invalid body encoding Stage = EParseStage::Error; @@ -333,9 +362,19 @@ void THttpParser<THttpResponse, TSocketBuffer>::ConnectionClosed() { } THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseString(TStringBuf data) { + THttpParser<THttpResponse, TSocketBuffer> parser(data); + THeadersBuilder headers(parser.Headers); + if (!WorkerName.empty()) { + headers.Set("X-Worker-Name", WorkerName); + } THttpOutgoingResponsePtr response = new THttpOutgoingResponse(this); - response->Append(data); - response->Reparse(); + response->InitResponse(parser.Protocol, parser.Version, parser.Status, parser.Message); + response->Set(headers); + if (parser.HaveBody()) { + response->SetBody(parser.Body); + } else { + response->Set<&THttpResponse::ContentLength>("0"); + } return response; } @@ -601,11 +640,17 @@ void TCookiesBuilder::Set(TStringBuf name, TStringBuf data) { } THeaders::THeaders(TStringBuf headers) { + Parse(headers); +} + +size_t THeaders::Parse(TStringBuf headers) { + auto start = headers.begin(); for (TStringBuf param = headers.NextTok("\r\n"); !param.empty(); param = headers.NextTok("\r\n")) { TStringBuf name = param.NextTok(":"); param.SkipPrefix(" "); Headers[name] = param; } + return headers.begin() - start; } TStringBuf THeaders::operator [](TStringBuf name) const { @@ -636,7 +681,11 @@ TString THeaders::Render() const { } THeadersBuilder::THeadersBuilder() - :THeaders(TStringBuf()) + : THeaders(TStringBuf()) +{} + +THeadersBuilder::THeadersBuilder(TStringBuf headers) + : THeaders(headers) {} THeadersBuilder::THeadersBuilder(const THeadersBuilder& builder) { diff --git a/library/cpp/actors/http/http.h b/library/cpp/actors/http/http.h index 96c5c1ec48..3e5e39e719 100644 --- a/library/cpp/actors/http/http.h +++ b/library/cpp/actors/http/http.h @@ -40,6 +40,17 @@ struct TLessNoCase { } }; +struct TEqNoCase { + bool operator()(TStringBuf l, TStringBuf r) const { + auto ll = l.length(); + auto rl = r.length(); + if (ll != rl) { + return false; + } + return strnicmp(l.data(), r.data(), ll) == 0; + } +}; + struct TUrlParameters { THashMap<TStringBuf, TStringBuf> Parameters; @@ -77,6 +88,7 @@ struct THeaders { TStringBuf operator [](TStringBuf name) const; bool Has(TStringBuf name) const; TStringBuf Get(TStringBuf name) const; // raw + size_t Parse(TStringBuf headers); TString Render() const; }; @@ -84,6 +96,7 @@ struct THeadersBuilder : THeaders { TDeque<std::pair<TString, TString>> Data; THeadersBuilder(); + THeadersBuilder(TStringBuf headers); THeadersBuilder(const THeadersBuilder& builder); void Set(TStringBuf name, TStringBuf data); }; @@ -188,6 +201,7 @@ public: size_t ChunkLength = 0; size_t ContentSize = 0; TString Content; + std::optional<size_t> TotalSize; THttpParser(const THttpParser& src) : HeaderType(src) @@ -285,6 +299,10 @@ public: void Advance(size_t len); void ConnectionClosed(); + size_t GetBodySizeFromTotalSize() const { + return TotalSize.value() - (HeaderType::Headers.end() - BufferType::Data()); + } + void Clear() { BufferType::Clear(); HeaderType::Clear(); @@ -333,9 +351,7 @@ public: return IsReady() || IsError(); } - bool HaveBody() const { - return !HeaderType::ContentType.empty() || !HeaderType::ContentLength.empty() || !HeaderType::TransferEncoding.empty(); - } + bool HaveBody() const; bool EnsureEnoughSpaceAvailable(size_t need = BufferType::BUFFER_MIN_STEP) { bool result = BufferType::EnsureEnoughSpaceAvailable(need); @@ -395,6 +411,16 @@ public: : Stage(GetInitialStage()) , LastSuccessStage(Stage) {} + + THttpParser(TStringBuf data) + : Stage(GetInitialStage()) + , LastSuccessStage(Stage) + { + BufferType::Assign(data.data(), data.size()); + BufferType::Clear(); // reset position to 0 + TotalSize = data.size(); + Advance(data.size()); + } }; template <typename HeaderType, typename BufferType> @@ -440,14 +466,21 @@ public: Y_VERIFY_DEBUG(Stage == ERenderStage::Header); Append(name); Append(": "); + auto data = BufferType::Pos(); Append(value); + auto cit = HeaderType::HeadersLocation.find(name); + if (cit != HeaderType::HeadersLocation.end()) { + (this->*cit->second) = TStringBuf(data, BufferType::Pos()); + } Append("\r\n"); HeaderType::Headers = TStringBuf(HeaderType::Headers.Data(), BufferType::Pos() - HeaderType::Headers.Data()); } void Set(const THeaders& headers) { Y_VERIFY_DEBUG(Stage == ERenderStage::Header); - Append(headers.Render()); + for (const auto& [name, value] : headers.Headers) { + Set(name, value); + } HeaderType::Headers = TStringBuf(HeaderType::Headers.Data(), BufferType::Pos() - HeaderType::Headers.Data()); } @@ -497,6 +530,10 @@ public: Stage = ERenderStage::Done; } + void FinishBody() { + Stage = ERenderStage::Done; + } + bool IsDone() const { return Stage == ERenderStage::Done; } @@ -505,6 +542,10 @@ public: switch (Stage) { case ERenderStage::Header: FinishHeader(); + FinishBody(); + break; + case ERenderStage::Body: + FinishBody(); break; default: break; @@ -599,7 +640,7 @@ public: if (Connection.empty()) { return Version == "1.0"; } else { - return Connection == "close"; + return TEqNoCase()(Connection, "close"); } } @@ -679,14 +720,14 @@ public: bool IsConnectionClose() const { if (!Connection.empty()) { - return Connection == "close"; + return TEqNoCase()(Connection, "close"); } else { return Request->IsConnectionClose(); } } bool IsNeedBody() const { - return Status != "204"; + return GetRequest()->Method != "HEAD" && Status != "204"; } THttpIncomingRequestPtr GetRequest() const { diff --git a/library/cpp/actors/http/http_proxy.cpp b/library/cpp/actors/http/http_proxy.cpp index e0204f6ed0..e347a3e1c2 100644 --- a/library/cpp/actors/http/http_proxy.cpp +++ b/library/cpp/actors/http/http_proxy.cpp @@ -68,13 +68,14 @@ protected: return; } else { if (url.EndsWith('/')) { - url.Trunc(url.size() - 1); - } - size_t pos = url.rfind('/'); - if (pos == TStringBuf::npos) { - break; + url.Chop(1); } else { - url = url.substr(0, pos + 1); + size_t pos = url.rfind('/'); + if (pos == TStringBuf::npos) { + break; + } else { + url = url.substr(0, pos + 1); + } } } } @@ -117,7 +118,8 @@ protected: Connections.erase(event->Get()->ConnectionID); } - void Handle(TEvHttpProxy::TEvRegisterHandler::TPtr event, const NActors::TActorContext&) { + void Handle(TEvHttpProxy::TEvRegisterHandler::TPtr event, const NActors::TActorContext& ctx) { + LOG_DEBUG_S(ctx, HttpLog, "Register handler " << event->Get()->Path << " to " << event->Get()->Handler); Handlers[event->Get()->Path] = event->Get()->Handler; } @@ -207,6 +209,12 @@ protected: } void Handle(NActors::TEvents::TEvPoison::TPtr, const NActors::TActorContext&) { + for (const TActorId& acceptor : Acceptors) { + Send(acceptor, new NActors::TEvents::TEvPoisonPill()); + } + for (const TActorId& connection : Connections) { + Send(connection, new NActors::TEvents::TEvPoisonPill()); + } PassAway(); } diff --git a/library/cpp/actors/http/http_ut.cpp b/library/cpp/actors/http/http_ut.cpp index caa5b3e183..cdb9025ff1 100644 --- a/library/cpp/actors/http/http_ut.cpp +++ b/library/cpp/actors/http/http_ut.cpp @@ -50,7 +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(BasicParsingChunkedBody) { + Y_UNIT_TEST(BasicParsingChunkedBodyRequest) { + NHttp::THttpIncomingRequestPtr request = new NHttp::THttpIncomingRequest(); + EatWholeString(request, "POST /Url HTTP/1.1\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nthis\r\n4\r\n is \r\n5\r\ntest.\r\n0\r\n\r\n"); + UNIT_ASSERT_EQUAL(request->Stage, NHttp::THttpIncomingRequest::EParseStage::Done); + UNIT_ASSERT_EQUAL(request->Method, "POST"); + UNIT_ASSERT_EQUAL(request->URL, "/Url"); + UNIT_ASSERT_EQUAL(request->Connection, "close"); + UNIT_ASSERT_EQUAL(request->Protocol, "HTTP"); + UNIT_ASSERT_EQUAL(request->Version, "1.1"); + UNIT_ASSERT_EQUAL(request->TransferEncoding, "chunked"); + UNIT_ASSERT_EQUAL(request->Body, "this is test."); + } + + Y_UNIT_TEST(BasicParsingChunkedBodyResponse) { NHttp::THttpOutgoingRequestPtr request = nullptr; //new NHttp::THttpOutgoingRequest(); NHttp::THttpIncomingResponsePtr response = new NHttp::THttpIncomingResponse(request); EatWholeString(response, "HTTP/1.1 200 OK\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nthis\r\n4\r\n is \r\n5\r\ntest.\r\n0\r\n\r\n"); @@ -83,7 +96,7 @@ Y_UNIT_TEST_SUITE(HttpProxy) { UNIT_ASSERT_EQUAL(response->Body, "this\r\n is test."); } - Y_UNIT_TEST(CreateRepsonseWithCompressedBody) { + Y_UNIT_TEST(CreateResponseWithCompressedBody) { NHttp::THttpIncomingRequestPtr request = nullptr; NHttp::THttpOutgoingResponsePtr response = new NHttp::THttpOutgoingResponse(request, "HTTP", "1.1", "200", "OK"); response->Set<&NHttp::THttpResponse::ContentEncoding>("gzip"); diff --git a/library/cpp/lwtrace/mon/mon_lwtrace.cpp b/library/cpp/lwtrace/mon/mon_lwtrace.cpp index a61ee9ce22..5e79716834 100644 --- a/library/cpp/lwtrace/mon/mon_lwtrace.cpp +++ b/library/cpp/lwtrace/mon/mon_lwtrace.cpp @@ -4671,12 +4671,12 @@ private: } }; -void RegisterPages(NMonitoring::TMonService2* mon, bool allowUnsafe) { +void RegisterPages(NMonitoring::TIndexMonPage* index, bool allowUnsafe) { THolder<NLwTraceMonPage::TLWTraceMonPage> p = MakeHolder<NLwTraceMonPage::TLWTraceMonPage>(allowUnsafe); - mon->Register(p.Release()); + index->Register(p.Release()); #define WWW_STATIC_FILE(file, type) \ - mon->Register(new TResourceMonPage(file, file, NMonitoring::TResourceMonPage::type)); + index->Register(new TResourceMonPage(file, file, NMonitoring::TResourceMonPage::type)); WWW_STATIC_FILE("lwtrace/mon/static/common.css", CSS); WWW_STATIC_FILE("lwtrace/mon/static/common.js", JAVASCRIPT); WWW_STATIC_FILE("lwtrace/mon/static/css/bootstrap.min.css", CSS); diff --git a/library/cpp/lwtrace/mon/mon_lwtrace.h b/library/cpp/lwtrace/mon/mon_lwtrace.h index 8030f6ea61..f196adeabd 100644 --- a/library/cpp/lwtrace/mon/mon_lwtrace.h +++ b/library/cpp/lwtrace/mon/mon_lwtrace.h @@ -19,7 +19,7 @@ public: void Output(TStringStream& ss); }; -void RegisterPages(NMonitoring::TMonService2* mon, bool allowUnsafe = false); +void RegisterPages(NMonitoring::TIndexMonPage* index, bool allowUnsafe = false); NLWTrace::TProbeRegistry& ProbeRegistry(); // This is not safe to use this function before main() NLWTrace::TManager& TraceManager(bool allowUnsafe = false); TDashboardRegistry& DashboardRegistry(); diff --git a/library/cpp/monlib/service/monservice.h b/library/cpp/monlib/service/monservice.h index 8f5e52fcdb..3116a76765 100644 --- a/library/cpp/monlib/service/monservice.h +++ b/library/cpp/monlib/service/monservice.h @@ -68,6 +68,10 @@ namespace NMonitoring { IMonPage* FindPage(const TString& relativePath); TIndexMonPage* FindIndexPage(const TString& relativePath); void SortPages(); + + TIndexMonPage* GetRoot() { + return IndexMonPage.Get(); + } }; } diff --git a/ydb/core/blobstorage/pdisk/blobstorage_pdisk_ut_run.cpp b/ydb/core/blobstorage/pdisk/blobstorage_pdisk_ut_run.cpp index 5935d13229..81f223a73c 100644 --- a/ydb/core/blobstorage/pdisk/blobstorage_pdisk_ut_run.cpp +++ b/ydb/core/blobstorage/pdisk/blobstorage_pdisk_ut_run.cpp @@ -4,6 +4,7 @@ #include "blobstorage_pdisk_ut_base_test.h" #include <ydb/core/base/appdata.h> +#include <ydb/core/mon/sync_http_mon.h> #include <ydb/core/blobstorage/crypto/default.h> #include <ydb/library/pdisk_io/aio.h> @@ -138,7 +139,7 @@ void Run(TVector<IActor*> tests, TTestRunConfig runCfg) { if (IsMonitoringEnabled) { // Monitoring startup - monitoring.Reset(new NActors::TMon({ + monitoring.Reset(new NActors::TSyncHttpMon({ .Port = pm.GetPort(8081), .Title = "TestYard monitoring" })); diff --git a/ydb/core/blobstorage/ut_vdisk/lib/astest.h b/ydb/core/blobstorage/ut_vdisk/lib/astest.h index 2cf254eff2..a61c2ac8cd 100644 --- a/ydb/core/blobstorage/ut_vdisk/lib/astest.h +++ b/ydb/core/blobstorage/ut_vdisk/lib/astest.h @@ -6,7 +6,7 @@ #include <library/cpp/actors/core/executor_pool_basic.h> #include <library/cpp/actors/core/executor_pool_io.h> #include <library/cpp/actors/core/scheduler_basic.h> -#include <ydb/core/mon/mon.h> +#include <ydb/core/mon/sync_http_mon.h> #include <library/cpp/actors/interconnect/interconnect.h> #include <library/cpp/actors/protos/services_common.pb.h> #include <ydb/core/base/appdata.h> @@ -106,7 +106,7 @@ inline void TTestWithActorSystem::Run(NActors::IActor *testActor) { if (!MonPort) { MonPort = pm.GetPort(MonPort); } - Monitoring.reset(new NActors::TMon({ + Monitoring.reset(new NActors::TSyncHttpMon({ .Port = MonPort, .Title = "at" })); diff --git a/ydb/core/blobstorage/ut_vdisk/lib/prepare.cpp b/ydb/core/blobstorage/ut_vdisk/lib/prepare.cpp index b2c020c3d2..9d4a4678ab 100644 --- a/ydb/core/blobstorage/ut_vdisk/lib/prepare.cpp +++ b/ydb/core/blobstorage/ut_vdisk/lib/prepare.cpp @@ -6,6 +6,8 @@ #include <ydb/core/blobstorage/vdisk/vdisk_actor.h> #include <ydb/core/blobstorage/pdisk/blobstorage_pdisk_tools.h> +#include <ydb/core/mon/sync_http_mon.h> + #include <ydb/core/scheme/scheme_type_registry.h> #include <library/cpp/actors/core/executor_pool_basic.h> @@ -359,7 +361,7 @@ void TConfiguration::Prepare(IVDiskSetup *vdiskSetup, bool newPDisks, bool runRe ////////////////////////////////////////////////////////////////////////////// ///////////////////////// MONITORING SETTINGS ///////////////////////////////// - Monitoring.reset(new NActors::TMon({ + Monitoring.reset(new NActors::TSyncHttpMon({ .Port = 8088, .Title = "at" })); diff --git a/ydb/core/driver_lib/run/factories.h b/ydb/core/driver_lib/run/factories.h index 08c885fc93..e5d12f2494 100644 --- a/ydb/core/driver_lib/run/factories.h +++ b/ydb/core/driver_lib/run/factories.h @@ -55,7 +55,7 @@ struct TModuleFactories { /// Factory for pdisk's aio engines std::shared_ptr<NPDisk::IIoContextFactory> IoContextFactory; - std::function<NActors::TMon* (NActors::TMon::TConfig)> MonitoringFactory; + std::function<NActors::TMon* (NActors::TMon::TConfig, const NKikimrConfig::TAppConfig& appConfig)> MonitoringFactory; std::shared_ptr<NSQS::IAuthFactory> SqsAuthFactory; std::shared_ptr<NHttpProxy::IAuthFactory> DataStreamsAuthFactory; diff --git a/ydb/core/driver_lib/run/run.cpp b/ydb/core/driver_lib/run/run.cpp index d39be96ca5..359c2098c8 100644 --- a/ydb/core/driver_lib/run/run.cpp +++ b/ydb/core/driver_lib/run/run.cpp @@ -31,7 +31,8 @@ #include <ydb/core/formats/clickhouse_block.h> #include <ydb/core/grpc_services/grpc_request_proxy.h> #include <ydb/core/grpc_services/grpc_mon.h> -#include <ydb/core/mon/mon.h> +#include <ydb/core/mon/sync_http_mon.h> +#include <ydb/core/mon/async_http_mon.h> #include <ydb/core/mon/crossref.h> #include <ydb/core/mon_alloc/profiler.h> @@ -372,9 +373,13 @@ void TKikimrRunner::InitializeMonitoring(const TKikimrRunConfig& runConfig, bool } if (ModuleFactories && ModuleFactories->MonitoringFactory) { - Monitoring = ModuleFactories->MonitoringFactory(std::move(monConfig)); + Monitoring = ModuleFactories->MonitoringFactory(std::move(monConfig), appConfig); } else { - Monitoring = new NActors::TMon(std::move(monConfig)); + if (appConfig.GetFeatureFlags().GetEnableAsyncHttpMon()) { + Monitoring = new NActors::TAsyncHttpMon(std::move(monConfig)); + } else { + Monitoring = new NActors::TSyncHttpMon(std::move(monConfig)); + } } if (Monitoring) { Monitoring->RegisterCountersPage("counters", "Counters", Counters); @@ -1383,10 +1388,6 @@ void RegisterBaseTagForMemoryProfiling(TActorSystem* as) { void TKikimrRunner::KikimrStart() { - if (!!Monitoring) { - Monitoring->Start(); - } - if (!!PollerThreads) { PollerThreads->Start(); } @@ -1397,6 +1398,10 @@ void TKikimrRunner::KikimrStart() { ActorSystem->Start(); } + if (!!Monitoring) { + Monitoring->Start(ActorSystem.Get()); + } + for (auto& server : GRpcServers) { if (server.second) { server.second->Start(); diff --git a/ydb/core/mon/CMakeLists.txt b/ydb/core/mon/CMakeLists.txt index 3127cb92e7..6ad51ae7b1 100644 --- a/ydb/core/mon/CMakeLists.txt +++ b/ydb/core/mon/CMakeLists.txt @@ -18,6 +18,8 @@ target_link_libraries(ydb-core-mon PUBLIC ydb-library-aclib ) target_sources(ydb-core-mon PRIVATE + ${CMAKE_SOURCE_DIR}/ydb/core/mon/async_http_mon.cpp ${CMAKE_SOURCE_DIR}/ydb/core/mon/mon.cpp + ${CMAKE_SOURCE_DIR}/ydb/core/mon/sync_http_mon.cpp ${CMAKE_SOURCE_DIR}/ydb/core/mon/crossref.cpp ) diff --git a/ydb/core/mon/async_http_mon.cpp b/ydb/core/mon/async_http_mon.cpp new file mode 100644 index 0000000000..e6c49866bc --- /dev/null +++ b/ydb/core/mon/async_http_mon.cpp @@ -0,0 +1,511 @@ +#include "async_http_mon.h" +#include <library/cpp/actors/core/actor_bootstrapped.h> +#include <library/cpp/actors/http/http_proxy.h> +#include <ydb/core/base/appdata.h> +#include <ydb/core/base/ticket_parser.h> + +#include <library/cpp/lwtrace/all.h> +#include <library/cpp/lwtrace/mon/mon_lwtrace.h> +#include <library/cpp/actors/core/probes.h> +#include <ydb/core/base/monitoring_provider.h> + +#include <library/cpp/monlib/service/pages/version_mon_page.h> +#include <library/cpp/monlib/service/pages/mon_page.h> +#include <library/cpp/monlib/dynamic_counters/counters.h> +#include <library/cpp/monlib/dynamic_counters/page.h> + +#include <util/system/hostname.h> + +#include "mon_impl.h" + +namespace NActors { + +// compatibility layer +class THttpMonRequest : public NMonitoring::IMonHttpRequest { +public: + NHttp::THttpIncomingRequestPtr Request; + TStringStream& Response; + NMonitoring::IMonPage* Page; + TString PathInfo; + mutable std::unique_ptr<THttpHeaders> Headers; + mutable std::unique_ptr<TCgiParameters> Params; + mutable std::unique_ptr<TCgiParameters> PostParams; + + THttpMonRequest(NHttp::THttpIncomingRequestPtr request, TStringStream& response, NMonitoring::IMonPage* page, const TString& pathInfo) + : Request(request) + , Response(response) + , Page(page) + , PathInfo(pathInfo) + { + } + + static TString GetPathFromUrl(TStringBuf url) { + return TString(url.Before('?')); + } + + static TString GetPathInfoFromUrl(NMonitoring::IMonPage* page, TStringBuf url) { + TString path = GetPageFullPath(page); + url.SkipPrefix(path); + return GetPathFromUrl(url); + } + + virtual IOutputStream& Output() override { + return Response; + } + + virtual HTTP_METHOD GetMethod() const override { + if (Request->Method == "GET") { + return HTTP_METHOD_GET; + } + if (Request->Method == "OPTIONS") { + return HTTP_METHOD_OPTIONS; + } + if (Request->Method == "POST") { + return HTTP_METHOD_POST; + } + if (Request->Method == "HEAD") { + return HTTP_METHOD_HEAD; + } + if (Request->Method == "PUT") { + return HTTP_METHOD_PUT; + } + return HTTP_METHOD_UNDEFINED; + } + + virtual TStringBuf GetPath() const override { + return GetPathFromUrl(Request->URL); + } + + virtual TStringBuf GetPathInfo() const override { + return PathInfo; + } + + virtual TStringBuf GetUri() const override { + return Request->URL; + } + + virtual const TCgiParameters& GetParams() const override { + if (!Params) { + Params = std::make_unique<TCgiParameters>(Request->URL.After('?')); + } + return *Params; + } + + virtual const TCgiParameters& GetPostParams() const override { + if (!PostParams) { + PostParams = std::make_unique<TCgiParameters>(Request->Body); + } + return *PostParams; + } + + virtual TStringBuf GetPostContent() const override { + return Request->Body; + } + + virtual const THttpHeaders& GetHeaders() const override { + if (!Headers) { + TString strHeaders(Request->Headers); + TStringInput headers(strHeaders); + Headers = std::make_unique<THttpHeaders>(&headers); + } + return *Headers; + } + + virtual TStringBuf GetHeader(TStringBuf name) const override { + auto header = GetHeaders().FindHeader(name); + if (header) { + return header->Value(); + } + return {}; + } + + virtual TStringBuf GetCookie(TStringBuf name) const override { + NHttp::TCookies cookies(GetHeader("Cookie")); + return cookies.Get(name); + } + + virtual TString GetRemoteAddr() const override { + return Request->Address.ToString(); + } + + virtual TString GetServiceTitle() const override { + return {}; + } + + virtual NMonitoring::IMonPage* GetPage() const override { + return Page; + } + + virtual IMonHttpRequest* MakeChild(NMonitoring::IMonPage* page, const TString& pathInfo) const override { + return new THttpMonRequest(Request, Response, page, pathInfo); + } +}; + +// container for legacy requests +class THttpMonRequestContainer : public TStringStream, public THttpMonRequest { +public: + THttpMonRequestContainer(NHttp::THttpIncomingRequestPtr request, NMonitoring::IMonPage* index) + : THttpMonRequest(request, *this, index, GetPathInfoFromUrl(index, request->URL)) + { + } +}; + +// handles actor communication +class THttpMonLegacyActorRequest : public TActorBootstrapped<THttpMonLegacyActorRequest> { +public: + NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr Event; + THttpMonRequestContainer Container; + TActorMonPage* ActorMonPage; + + THttpMonLegacyActorRequest(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, TActorMonPage* actorMonPage) + : Event(std::move(event)) + , Container(Event->Get()->Request, actorMonPage) + , ActorMonPage(actorMonPage) + {} + + void Bootstrap() { + if (Event->Get()->Request->Method == "OPTIONS") { + return ReplyOptionsAndPassAway(); + } + Become(&THttpMonLegacyActorRequest::StateFunc); + Schedule(TDuration::Seconds(600), new TEvents::TEvWakeup()); + if (ActorMonPage->Authorizer) { + NActors::IEventHandle* handle = ActorMonPage->Authorizer(SelfId(), Container); + if (handle) { + TActivationContext::Send(handle); + return; + } + } + SendRequest(); + } + + void ReplyWith(NHttp::THttpOutgoingResponsePtr response) { + Send(Event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); + } + + void ReplyOptionsAndPassAway() { + NHttp::THttpIncomingRequestPtr request = Event->Get()->Request; + TString url(request->URL.Before('?')); + TString type = mimetypeByExt(url.data()); + if (type.empty()) { + type = "application/json"; + } + NHttp::THeaders headers(request->Headers); + TString origin = TString(headers["Origin"]); + if (origin.empty()) { + origin = "*"; + } + TStringBuilder response; + response << "HTTP/1.1 204 No Content\r\n" + "Access-Control-Allow-Origin: " << origin << "\r\n" + "Access-Control-Allow-Credentials: true\r\n" + "Access-Control-Allow-Headers: Content-Type,Authorization,Origin,Accept\r\n" + "Access-Control-Allow-Methods: OPTIONS, GET, POST\r\n" + "Content-Type: " + type + "\r\n" + "Connection: keep-alive\r\n\r\n"; + ReplyWith(request->CreateResponseString(response)); + PassAway(); + } + + void ReplyUnathorizedAndPassAway(const TString& error = {}) { + NHttp::THttpIncomingRequestPtr request = Event->Get()->Request; + NHttp::THeaders headers(request->Headers); + TStringBuilder response; + TStringBuilder body; + body << "<html><body><h1>401 Unauthorized</h1>"; + if (!error.empty()) { + body << "<p>" << error << "</p>"; + } + body << "</body></html>"; + TString origin = TString(headers["Origin"]); + if (origin.empty()) { + origin = "*"; + } + response << "HTTP/1.1 401 Unauthorized\r\n"; + response << "Access-Control-Allow-Origin: " << origin << "\r\n"; + response << "Access-Control-Allow-Credentials: true\r\n"; + response << "Access-Control-Allow-Headers: Content-Type,Authorization,Origin,Accept\r\n"; + response << "Access-Control-Allow-Methods: OPTIONS, GET, POST\r\n"; + response << "Content-Type: text/html\r\n"; + response << "Content-Length: " << body.Size() << "\r\n"; + response << "\r\n"; + response << body; + ReplyWith(request->CreateResponseString(response)); + PassAway(); + } + + void ReplyForbiddenAndPassAway(const TString& error = {}) { + NHttp::THttpIncomingRequestPtr request = Event->Get()->Request; + TStringBuilder response; + TStringBuilder body; + body << "<html><body><h1>403 Forbidden</h1>"; + if (!error.empty()) { + body << "<p>" << error << "</p>"; + } + body << "</body></html>"; + response << "HTTP/1.1 403 Forbidden\r\n"; + response << "Content-Type: text/html\r\n"; + response << "Content-Length: " << body.Size() << "\r\n"; + response << "\r\n"; + response << body; + ReplyWith(request->CreateResponseString(response)); + PassAway(); + } + + void SendRequest(const NKikimr::TEvTicketParser::TEvAuthorizeTicketResult* authorizeResult = {}) { + NHttp::THttpIncomingRequestPtr request = Event->Get()->Request; + if (ActorMonPage->Authorizer) { + TString user = authorizeResult ? authorizeResult->Token->GetUserSID() : "anonymous"; + LOG_NOTICE_S(*TlsActivationContext, NActorsServices::HTTP, + request->Address.ToString() + << " " << user + << " " << request->Method + << " " << request->URL); + } + TString serializedToken = authorizeResult ? authorizeResult->SerializedToken : ""; + Send(ActorMonPage->TargetActorId, new NMon::TEvHttpInfo( + Container, serializedToken), IEventHandle::FlagTrackDelivery); + } + + void HandleWakeup(TEvents::TEvWakeup::TPtr&) { + NHttp::THttpIncomingRequestPtr request = Event->Get()->Request; + ReplyWith(request->CreateResponseGatewayTimeout( + TStringBuilder() << "Timeout requesting actor " << ActorMonPage->TargetActorId)); + PassAway(); + } + + void HandleUndelivered(TEvents::TEvUndelivered::TPtr&) { + NHttp::THttpIncomingRequestPtr request = Event->Get()->Request; + ReplyWith(request->CreateResponseServiceUnavailable( + TStringBuilder() << "Actor " << ActorMonPage->TargetActorId << " is not available")); + PassAway(); + } + + void HandleResponse(NMon::IEvHttpInfoRes::TPtr& ev) { + if (ev->Get()->GetContentType() == NMon::IEvHttpInfoRes::Html) { + THtmlResultMonPage resultPage(ActorMonPage->Path, ActorMonPage->Title, ActorMonPage->Host, ActorMonPage->PreTag, *(ev->Get())); + resultPage.Parent = ActorMonPage->Parent; + resultPage.Output(Container); + } else { + ev->Get()->Output(Container); + } + ReplyWith(Event->Get()->Request->CreateResponseString(Container.Str())); + PassAway(); + } + + void Handle(NKikimr::TEvTicketParser::TEvAuthorizeTicketResult::TPtr& ev) { + const NKikimr::TEvTicketParser::TEvAuthorizeTicketResult& result(*ev->Get()); + if (result.Error) { + return ReplyUnathorizedAndPassAway(result.Error.Message); + } + bool found = false; + for (const TString& sid : ActorMonPage->AllowedSIDs) { + if (result.Token->IsExist(sid)) { + found = true; + break; + } + } + if (found || ActorMonPage->AllowedSIDs.empty()) { + SendRequest(&result); + } else { + return ReplyForbiddenAndPassAway("SID is not allowed"); + } + } + + STATEFN(StateFunc) { + switch (ev->GetTypeRewrite()) { + hFunc(TEvents::TEvWakeup, HandleWakeup); + hFunc(TEvents::TEvUndelivered, HandleUndelivered); + hFunc(NMon::IEvHttpInfoRes, HandleResponse); + hFunc(NKikimr::TEvTicketParser::TEvAuthorizeTicketResult, Handle); + } + } +}; + +// handles all indexes and static data in synchronous way +class THttpMonLegacyIndexRequest : public TActorBootstrapped<THttpMonLegacyIndexRequest> { +public: + NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr Event; + THttpMonRequestContainer Container; + + THttpMonLegacyIndexRequest(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, NMonitoring::IMonPage* index) + : Event(std::move(event)) + , Container(Event->Get()->Request, index) + {} + + void Bootstrap() { + ProcessRequest(); + } + + void ProcessRequest() { + Container.Page->Output(Container); + NHttp::THttpOutgoingResponsePtr response = Event->Get()->Request->CreateResponseString(Container.Str()); + Send(Event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); + PassAway(); + } +}; + +// receives all requests for one actor page and converts them to request-actors +class THttpMonServiceLegacyActor : public TActorBootstrapped<THttpMonServiceLegacyActor> { +public: + THttpMonServiceLegacyActor(TActorMonPage* actorMonPage) + : ActorMonPage(actorMonPage) + { + } + + void Bootstrap() { + Become(&THttpMonServiceLegacyActor::StateWork); + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr& ev) { + Register(new THttpMonLegacyActorRequest(std::move(ev), ActorMonPage)); + } + + STATEFN(StateWork) { + switch (ev->GetTypeRewrite()) { + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + } + } + + TActorMonPage* ActorMonPage; +}; + +// receives everyhing not related to actor communcation, converts them to request-actors +class THttpMonServiceLegacyIndex : public TActorBootstrapped<THttpMonServiceLegacyIndex> { +public: + THttpMonServiceLegacyIndex(TIntrusivePtr<NMonitoring::TIndexMonPage> indexMonPage, const TString& redirectRoot = {}) + : IndexMonPage(std::move(indexMonPage)) + , RedirectRoot(redirectRoot) + { + } + + void Bootstrap() { + Become(&THttpMonServiceLegacyIndex::StateWork); + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr& ev) { + if (ev->Get()->Request->URL == "/" && RedirectRoot) { + TStringBuilder response; + response << "HTTP/1.1 302 Found\r\nLocation: " << RedirectRoot << "\r\n\r\n"; + Send(ev->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(ev->Get()->Request->CreateResponseString(response))); + } else { + Register(new THttpMonLegacyIndexRequest(std::move(ev), IndexMonPage.Get())); + } + } + + STATEFN(StateWork) { + switch (ev->GetTypeRewrite()) { + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + } + } + + TIntrusivePtr<NMonitoring::TIndexMonPage> IndexMonPage; + TString RedirectRoot; +}; + +TAsyncHttpMon::TAsyncHttpMon(TConfig config) + : Config(std::move(config)) + , IndexMonPage(new NMonitoring::TIndexMonPage("", Config.Title)) +{ +} + +void TAsyncHttpMon::Start(TActorSystem* actorSystem) { + if (actorSystem) { + ActorSystem = actorSystem; + Register(new TIndexRedirectMonPage(IndexMonPage)); + Register(new NMonitoring::TVersionMonPage); + Register(new NMonitoring::TTablesorterCssMonPage); + Register(new NMonitoring::TTablesorterJsMonPage); + NLwTraceMonPage::RegisterPages(IndexMonPage.Get()); + NLwTraceMonPage::ProbeRegistry().AddProbesList(LWTRACE_GET_PROBES(ACTORLIB_PROVIDER)); + NLwTraceMonPage::ProbeRegistry().AddProbesList(LWTRACE_GET_PROBES(MONITORING_PROVIDER)); + HttpProxyActorId = ActorSystem->Register( + NHttp::CreateHttpProxy(), + TMailboxType::ReadAsFilled, + ActorSystem->AppData<NKikimr::TAppData>()->UserPoolId); + HttpMonServiceActorId = ActorSystem->Register( + new THttpMonServiceLegacyIndex(IndexMonPage, Config.RedirectMainPageTo), + TMailboxType::ReadAsFilled, + ActorSystem->AppData<NKikimr::TAppData>()->UserPoolId); + TStringBuilder workerName; + workerName << FQDNHostName() << ":" << Config.Port; + ActorSystem->Send(HttpProxyActorId, new NHttp::TEvHttpProxy::TEvAddListeningPort(Config.Port, workerName)); + ActorSystem->Send(HttpProxyActorId, new NHttp::TEvHttpProxy::TEvRegisterHandler("/", HttpMonServiceActorId)); + for (NMonitoring::IMonPage* page : ActorMonPages) { + RegisterActorMonPage(page); + } + ActorMonPages.clear(); + } +} + +void TAsyncHttpMon::Stop() { + IndexMonPage->ClearPages(); // it's required to avoid loop-reference + if (ActorSystem) { + for (const TActorId& actorId : ActorServices) { + ActorSystem->Send(actorId, new TEvents::TEvPoisonPill); + } + ActorSystem->Send(HttpMonServiceActorId, new TEvents::TEvPoisonPill); + ActorSystem->Send(HttpProxyActorId, new TEvents::TEvPoisonPill); + } +} + +void TAsyncHttpMon::Register(NMonitoring::IMonPage* page) { + IndexMonPage->Register(page); +} + +NMonitoring::TIndexMonPage* TAsyncHttpMon::RegisterIndexPage(const TString& path, const TString& title) { + auto page = IndexMonPage->RegisterIndexPage(path, title); + IndexMonPage->SortPages(); + return page; +} + +void TAsyncHttpMon::RegisterActorMonPage(NMonitoring::IMonPage* page) { + if (ActorSystem) { + TActorMonPage* actorMonPage = reinterpret_cast<TActorMonPage*>(page); + auto actorId = ActorSystem->Register( + new THttpMonServiceLegacyActor(actorMonPage), + TMailboxType::ReadAsFilled, + ActorSystem->AppData<NKikimr::TAppData>()->UserPoolId); + ActorSystem->Send(HttpProxyActorId, new NHttp::TEvHttpProxy::TEvRegisterHandler(GetPageFullPath(actorMonPage), actorId)); + ActorServices.push_back(actorId); + } +} + +NMonitoring::IMonPage* TAsyncHttpMon::RegisterActorPage(TRegisterActorPageFields fields) { + TActorMonPage* page = new TActorMonPage( + fields.RelPath, + fields.Title, + Config.Host, + fields.PreTag, + fields.ActorSystem, + fields.ActorId, + fields.AllowedSIDs ? fields.AllowedSIDs : Config.AllowedSIDs, + fields.UseAuth ? Config.Authorizer : TRequestAuthorizer()); + if (fields.Index) { + fields.Index->Register(page); + fields.Index->SortPages(); + } else { + Register(page); + } + + if (ActorSystem && HttpProxyActorId) { + RegisterActorMonPage(page); + } else { + ActorMonPages.push_back(page); + } + + return page; +} + +NMonitoring::IMonPage* TAsyncHttpMon::RegisterCountersPage(const TString& path, const TString& title, TIntrusivePtr<NMonitoring::TDynamicCounters> counters) { + TDynamicCountersPage* page = new TDynamicCountersPage(path, title, counters); + page->SetUnknownGroupPolicy(EUnknownGroupPolicy::Ignore); + Register(page); + return page; +} + +NMonitoring::IMonPage* TAsyncHttpMon::FindPage(const TString& relPath) { + return IndexMonPage->FindPage(relPath); +} + +} diff --git a/ydb/core/mon/async_http_mon.h b/ydb/core/mon/async_http_mon.h new file mode 100644 index 0000000000..c312bc1030 --- /dev/null +++ b/ydb/core/mon/async_http_mon.h @@ -0,0 +1,41 @@ +#pragma once + +#include <library/cpp/monlib/service/monservice.h> +#include <library/cpp/monlib/dynamic_counters/counters.h> +#include <library/cpp/monlib/service/pages/index_mon_page.h> +#include <library/cpp/monlib/service/pages/tablesorter/css_mon_page.h> +#include <library/cpp/monlib/service/pages/tablesorter/js_mon_page.h> + +#include <library/cpp/actors/core/mon.h> +#include <library/cpp/actors/http/http.h> + +#include "mon.h" + +namespace NActors { + +class TAsyncHttpMon : public TMon { +public: + TAsyncHttpMon(TConfig config); + + void Start(TActorSystem* actorSystem) override; + void Stop() override; + + void Register(NMonitoring::IMonPage* page) override; + NMonitoring::TIndexMonPage* RegisterIndexPage(const TString& path, const TString& title) override; + NMonitoring::IMonPage* RegisterActorPage(TRegisterActorPageFields fields) override; + NMonitoring::IMonPage* RegisterCountersPage(const TString& path, const TString& title, TIntrusivePtr<NMonitoring::TDynamicCounters> counters) override; + NMonitoring::IMonPage* FindPage(const TString& relPath) override; + +protected: + TConfig Config; + TIntrusivePtr<NMonitoring::TIndexMonPage> IndexMonPage; + TActorSystem* ActorSystem = {}; + TActorId HttpProxyActorId; + TActorId HttpMonServiceActorId; + std::vector<NMonitoring::IMonPage*> ActorMonPages; + std::vector<TActorId> ActorServices; + + void RegisterActorMonPage(NMonitoring::IMonPage* page); +}; + +} // NActors diff --git a/ydb/core/mon/mon.cpp b/ydb/core/mon/mon.cpp index 166ac90144..5268f93372 100644 --- a/ydb/core/mon/mon.cpp +++ b/ydb/core/mon/mon.cpp @@ -1,550 +1,65 @@ #include "mon.h" -#include <library/cpp/actors/core/actorsystem.h> -#include <library/cpp/actors/core/hfunc.h> -#include <library/cpp/actors/core/mon.h> -#include <library/cpp/actors/core/probes.h> -#include <library/cpp/lwtrace/mon/mon_lwtrace.h> -#include <library/cpp/mime/types/mime.h> -#include <library/cpp/monlib/service/pages/version_mon_page.h> -#include <library/cpp/monlib/service/pages/mon_page.h> -#include <library/cpp/monlib/dynamic_counters/counters.h> -#include <library/cpp/monlib/dynamic_counters/page.h> -#include <library/cpp/threading/future/future.h> -#include <library/cpp/string_utils/url/url.h> -#include <util/system/event.h> #include <ydb/core/base/appdata.h> -#include <ydb/core/base/monitoring_provider.h> #include <ydb/core/base/ticket_parser.h> namespace NActors { - using namespace NMonitoring; - - using THttpResponsePtr = THolder<NMon::IEvHttpInfoRes>; - - //////////////////////////////////////////////////////////////////////////////// - // MON REQUEST - //////////////////////////////////////////////////////////////////////////////// - class TMonRequest : public NActors::TActor<TMonRequest> { - public: - static constexpr NKikimrServices::TActivity::EType ActorActivityType() { - return NKikimrServices::TActivity::ACTORLIB_COMMON; - } - - TMonRequest(const TActorId &targetActorId, IMonHttpRequest& request, - NThreading::TPromise<THttpResponsePtr> result, const TVector<TString> &sids, TMon::TRequestAuthorizer authorizer) - : TActor(&TMonRequest::StateFunc) - , TargetActorId(targetActorId) - , Request(request) - , Result(result) - , AllowedSIDs(sids) - , Authorizer(authorizer) - { - } - - ~TMonRequest() { - if (!Result.HasValue()) { - Result.SetValue(nullptr); - } - } - - STFUNC(StateFunc) { - switch (ev->GetTypeRewrite()) { - HFunc(TEvents::TEvBootstrap, HandleBootstrap); - HFunc(TEvents::TEvPoisonPill, HandlePoisonPill); - HFunc(TEvents::TEvWakeup, HandleWakeup); - HFunc(TEvents::TEvUndelivered, HandleUndelivered); - HFunc(NMon::IEvHttpInfoRes, HandleInfoRes); - HFunc(NKikimr::TEvTicketParser::TEvAuthorizeTicketResult, Handle); - } - } - - void HandleBootstrap(TEvents::TEvBootstrap::TPtr &, const TActorContext &ctx) { - if (Request.GetMethod() == HTTP_METHOD_OPTIONS) { - return ReplyOptionsResultAndDie(ctx); - } - ctx.Schedule(TDuration::Seconds(600), new TEvents::TEvWakeup()); - if (Authorizer) { - NActors::IEventHandle* handle = Authorizer(SelfId(), Request); - if (handle) { - ctx.Send(handle); - return; - } - } - SendRequest(ctx); - } - - void HandlePoisonPill(TEvents::TEvPoisonPill::TPtr &, const TActorContext &ctx) { - Die(ctx); - } - - void HandleWakeup(TEvents::TEvWakeup::TPtr &, const TActorContext &ctx) { - Result.SetValue(nullptr); - Die(ctx); - } - - void HandleUndelivered(TEvents::TEvUndelivered::TPtr &, const TActorContext &ctx) { - ReplyActorUnavailableAndDie(ctx); - } - - void HandleInfoRes(NMon::IEvHttpInfoRes::TPtr &ev, const NActors::TActorContext &ctx) { - Result.SetValue(THolder<NMon::IEvHttpInfoRes>(ev->Release().Release())); - Die(ctx); - } - - void Handle(NKikimr::TEvTicketParser::TEvAuthorizeTicketResult::TPtr &ev, const TActorContext &ctx) { - const NKikimr::TEvTicketParser::TEvAuthorizeTicketResult &result(*ev->Get()); - if (result.Error) { - ReplyUnathorizedAndDie(ctx, result.Error.Message); - return; - } - bool found = false; - for (const TString& sid : AllowedSIDs) { - if (result.Token->IsExist(sid)) { - found = true; - break;; - } - } - if (found || AllowedSIDs.empty()) { - User = result.Token->GetUserSID(); - SendRequest(ctx, result.SerializedToken); - } else { - ReplyForbiddenAndDie(ctx, TStringBuilder() << "SID is not allowed"); - } - } - - TString GetUser() const { - return User ? User : "anonymous"; - } - - static TString GetMethod(HTTP_METHOD method) { - switch (method) { - case HTTP_METHOD_UNDEFINED: return "UNDEFINED"; - case HTTP_METHOD_OPTIONS: return "OPTIONS"; - case HTTP_METHOD_GET: return "GET"; - case HTTP_METHOD_HEAD: return "HEAD"; - case HTTP_METHOD_POST: return "POST"; - case HTTP_METHOD_PUT: return "PUT"; - case HTTP_METHOD_DELETE: return "DELETE"; - case HTTP_METHOD_TRACE: return "TRACE"; - case HTTP_METHOD_CONNECT: return "CONNECT"; - case HTTP_METHOD_EXTENSION: return "EXTENSION"; - default: return "UNKNOWN"; - } - } - - void SendRequest(const TActorContext &ctx, const TString& serializedToken = TString()) { - if (Authorizer) { - LOG_WARN_S(ctx, NActorsServices::HTTP, - Request.GetRemoteAddr() - << " " << GetUser() - << " " << GetMethod(Request.GetMethod()) - << " " << Request.GetUri()); - } - ctx.Send(TargetActorId, new NMon::TEvHttpInfo(Request, serializedToken), IEventHandle::FlagTrackDelivery); - } - - void ReplyOptionsResultAndDie(const TActorContext &ctx) { - TString url(Request.GetPathInfo()); - TString type = mimetypeByExt(url.data()); - if (type.empty()) { - type = "application/json"; - } - TString origin = TString(Request.GetHeader("Origin")); - if (origin.empty()) { - origin = "*"; - } - TStringBuilder response; - response << "HTTP/1.1 204 No Content\r\n" - "Access-Control-Allow-Origin: " << origin << "\r\n" - "Access-Control-Allow-Credentials: true\r\n" - "Access-Control-Allow-Headers: Content-Type,Authorization,Origin,Accept\r\n" - "Access-Control-Allow-Methods: OPTIONS, GET, POST\r\n" - "Content-Type: " + type + "\r\n" - "Connection: Keep-Alive\r\n\r\n"; - Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response, 0, NMon::IEvHttpInfoRes::EContentType::Custom)); - Die(ctx); - } - - void ReplyUnathorizedAndDie(const TActorContext &ctx, const TString& error = TString()) { - TStringStream response; - TStringStream body; - body << "<html><body><h1>401 Unauthorized</h1>"; - if (!error.empty()) { - body << "<p>" << error << "</p>"; - } - body << "</body></html>"; - TString origin = TString(Request.GetHeader("Origin")); - if (origin.empty()) { - origin = "*"; - } - response << "HTTP/1.1 401 Unauthorized\r\n"; - response << "Access-Control-Allow-Origin: " << origin << "\r\n"; - response << "Access-Control-Allow-Credentials: true\r\n"; - response << "Access-Control-Allow-Headers: Content-Type,Authorization,Origin,Accept\r\n"; - response << "Access-Control-Allow-Methods: OPTIONS, GET, POST\r\n"; - response << "Content-Type: text/html\r\n"; - response << "Content-Length: " << body.Size() << "\r\n"; - response << "\r\n"; - response << body.Str(); - Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response.Str(), 0, NMon::IEvHttpInfoRes::EContentType::Custom)); - Die(ctx); - } - - void ReplyForbiddenAndDie(const TActorContext &ctx, const TString& error = TString()) { - TStringStream response; - TStringStream body; - body << "<html><body><h1>403 Forbidden</h1>"; - if (!error.empty()) { - body << "<p>" << error << "</p>"; - } - body << "</body></html>"; - response << "HTTP/1.1 403 Forbidden\r\n"; - response << "Content-Type: text/html\r\n"; - response << "Content-Length: " << body.Size() << "\r\n"; - response << "\r\n"; - response << body.Str(); - Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response.Str(), 0, NMon::IEvHttpInfoRes::EContentType::Custom)); - Die(ctx); - } - - void ReplyActorUnavailableAndDie(const TActorContext &ctx, const TString& error = TString()) { - TStringStream response; - TStringStream body; - body << "<html><body><h1>503 Actor Unavailable</h1>"; - if (!error.empty()) { - body << "<p>" << error << "</p>"; - } - body << "</body></html>"; - response << "HTTP/1.1 503 Actor Unavailable\r\n"; - response << "Content-Type: text/html\r\n"; - response << "Content-Length: " << body.Size() << "\r\n"; - response << "\r\n"; - response << body.Str(); - Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response.Str(), 0, NMon::IEvHttpInfoRes::EContentType::Custom)); - Die(ctx); - } - - virtual TAutoPtr<NActors::IEventHandle> AfterRegister(const NActors::TActorId &self, const TActorId& parentId) override { - Y_UNUSED(parentId); - return new NActors::IEventHandle(self, self, new TEvents::TEvBootstrap(), 0); - } - - protected: - TActorId TargetActorId; - IMonHttpRequest& Request; - NThreading::TPromise<THttpResponsePtr> Result; - const TVector<TString> &AllowedSIDs; - TMon::TRequestAuthorizer Authorizer; - TString User; - }; - - - //////////////////////////////////////////////////////////////////////////////// - // HTML results page - //////////////////////////////////////////////////////////////////////////////// - class THtmlResultMonPage: public THtmlMonPage { - public: - THtmlResultMonPage(const TString &path, const TString &title, const TString &host, bool preTag, - const NMon::IEvHttpInfoRes &result) - : THtmlMonPage(path, title, true) - , Host(host) - , PreTag(preTag) - , Result(result) - { - } - - void Output(NMonitoring::IMonHttpRequest& request) override { - IOutputStream& out = request.Output(); - - out << HTTPOKHTML; - - out << "<!DOCTYPE html>\n"; - out << "<html>"; - out << "<head>"; - if (Title) { - if (Host) { - out << "<title>" << Title << " - " << Host << "</title>\n"; - } else { - out << "<title>" << Title << "</title>\n"; - } - } - - out << "<link rel='stylesheet' href='https://yastatic.net/bootstrap/3.3.1/css/bootstrap.min.css'>\n"; - out << "<script language='javascript' type='text/javascript' src='https://yastatic.net/jquery/2.1.3/jquery.min.js'></script>\n"; - out << "<script language='javascript' type='text/javascript' src='https://yastatic.net/bootstrap/3.3.1/js/bootstrap.min.js'></script>\n"; - - if (OutputTableSorterJsCss) { - out << "<link rel='stylesheet' href='/jquery.tablesorter.css'>\n"; - out << "<script language='javascript' type='text/javascript' src='/jquery.tablesorter.js'></script>\n"; - } - - out << "<style type=\"text/css\">\n"; - out << ".table-nonfluid { width: auto; }\n"; - out << ".narrow-line50 {line-height: 50%}\n"; - out << ".narrow-line60 {line-height: 60%}\n"; - out << ".narrow-line70 {line-height: 70%}\n"; - out << ".narrow-line80 {line-height: 80%}\n"; - out << ".narrow-line90 {line-height: 90%}\n"; - out << "</style>\n"; - out << "</head>"; - out << "<body>"; - - OutputNavBar(out); - - out << "<div class='container'>"; - if (Title) { - out << "<h2>" << Title << "</h2>"; - } - OutputContent(request); - out << "</div>"; - out << "</body>"; - } - - void OutputContent(NMonitoring::IMonHttpRequest &request) override { - if (PreTag) { - request.Output() << "<pre>\n"; - } - Result.Output(request.Output()); - if (PreTag) { - request.Output() << "</pre>\n"; - } - } - - private: - TString Host; - bool PreTag; - const NMon::IEvHttpInfoRes &Result; - }; - - - //////////////////////////////////////////////////////////////////////////////// - // INDEX PAGE - // Redirects index page to fixed url - //////////////////////////////////////////////////////////////////////////////// - class TIndexRedirectMonPage: public IMonPage { - public: - TIndexRedirectMonPage(TIntrusivePtr<TIndexMonPage> indexMonPage, const TString& path = "internal") - : IMonPage(path) - , IndexMonPage(std::move(indexMonPage)) - { - } - - void Output(IMonHttpRequest& request) override { - IndexMonPage->OutputIndexPage(request); - } - - TIntrusivePtr<TIndexMonPage> IndexMonPage; - }; - - - //////////////////////////////////////////////////////////////////////////////// - // ACTOR MONITORING PAGE - // Encapsulates a request to an actor - //////////////////////////////////////////////////////////////////////////////// - class TActorMonPage: public IMonPage { - public: - TActorMonPage(const TString &path, const TString &title, const TString &host, bool preTag, - TActorSystem *actorSystem, const TActorId &actorId, const TVector<TString> &sids, - TMon::TRequestAuthorizer authorizer) - : IMonPage(path, title) - , Host(host) - , PreTag(preTag) - , ActorSystem(actorSystem) - , TargetActorId(actorId) - , AllowedSIDs(sids) - , Authorizer(std::move(authorizer)) - { - } - - void Output(IMonHttpRequest &request) override { - auto promise = NThreading::NewPromise<THttpResponsePtr>(); - auto future = promise.GetFuture(); - - ActorSystem->Register(new TMonRequest(TargetActorId, request, promise, AllowedSIDs, Authorizer)); - - THttpResponsePtr result = future.ExtractValue(TDuration::Max()); - - if (result) { - Output(request, *result); - } else { - TStringStream out; - out << "Error: timeout. We were not able to receive response from '" - << Title << "'.\n"; - Output(request, NMon::TEvHttpInfoRes(out.Str())); - } - } - - private: - void Output(IMonHttpRequest &request, const NMon::IEvHttpInfoRes &result) const { - if (result.GetContentType() == NMon::IEvHttpInfoRes::Html) { - THtmlResultMonPage resultPage(Path, Title, Host, PreTag, result); - resultPage.Parent = this->Parent; - resultPage.Output(request); - } else { - result.Output(request.Output()); - } - } - - private: - TString Host; - bool PreTag; - TActorSystem *ActorSystem; - TActorId TargetActorId; - const TVector<TString> AllowedSIDs; - TMon::TRequestAuthorizer Authorizer; - }; - - - //////////////////////////////////////////////////////////////////////////////// - // TMON CLASS - //////////////////////////////////////////////////////////////////////////////// - TMon::TMon(TMon::TConfig config) - : TBase(config.Port, config.Address, config.Threads, config.Title) - , Config(std::move(config)) - { - } - - TMon::~TMon() { - Stop(); - } - - void TMon::Start() { - TBase::Register(new TIndexRedirectMonPage(IndexMonPage)); - TBase::Register(new NMonitoring::TVersionMonPage); - TBase::Register(new NMonitoring::TTablesorterCssMonPage); - TBase::Register(new NMonitoring::TTablesorterJsMonPage); - - NLwTraceMonPage::RegisterPages((TBase*)this); - NLwTraceMonPage::ProbeRegistry().AddProbesList(LWTRACE_GET_PROBES(ACTORLIB_PROVIDER)); - NLwTraceMonPage::ProbeRegistry().AddProbesList(LWTRACE_GET_PROBES(MONITORING_PROVIDER)); - TBase::Start(); - } - - void TMon::Stop() { - IndexMonPage->ClearPages(); // it's required to avoid loop-reference - TBase::Stop(); - } - - void TMon::Register(NMonitoring::IMonPage *page) { - TBase::Register(page); - TBase::SortPages(); - } - - TIndexMonPage *TMon::RegisterIndexPage(const TString &path, const TString &title) { - auto page = TBase::RegisterIndexPage(path, title); - TBase::SortPages(); - return page; - } - - IMonPage *TMon::RegisterActorPage(TIndexMonPage *index, const TString &relPath, - const TString &title, bool preTag, TActorSystem *actorSystem, const TActorId &actorId, bool useAuth) { - return RegisterActorPage({ - .Title = title, - .RelPath = relPath, - .ActorSystem = actorSystem, - .Index = index, - .PreTag = preTag, - .ActorId = actorId, - .UseAuth = useAuth, - }); - } - - IMonPage* TMon::RegisterActorPage(TMon::TRegisterActorPageFields fields) { - IMonPage *page = new TActorMonPage( - fields.RelPath, - fields.Title, - Config.Host, - fields.PreTag, - fields.ActorSystem, - fields.ActorId, - fields.AllowedSIDs ? fields.AllowedSIDs : Config.AllowedSIDs, - fields.UseAuth ? Config.Authorizer : TRequestAuthorizer()); - if (fields.Index) { - fields.Index->Register(page); - fields.Index->SortPages(); - } else { - Register(page); - } - - return page; - } - - IMonPage *TMon::RegisterCountersPage(const TString &path, const TString &title, TIntrusivePtr<TDynamicCounters> counters) { - TDynamicCountersPage* page = new TDynamicCountersPage(path, title, counters); - page->SetUnknownGroupPolicy(EUnknownGroupPolicy::Ignore); - Register(page); - return page; - } - - IMonPage *TMon::FindPage(const TString &relPath) { - return TBase::FindPage(relPath); - } - - TIndexMonPage *TMon::FindIndexPage(const TString &relPath) { - return TBase::FindIndexPage(relPath); - } - - void TMon::OutputIndexPage(IOutputStream& out) { - if (Config.RedirectMainPageTo) { - // XXX manual http response construction - out << "HTTP/1.1 302 Found\r\n" - << "Location: " << Config.RedirectMainPageTo << "\r\n" - << "Connection: Close\r\n\r\n"; - } else { - NMonitoring::TMonService2::OutputIndexPage(out); - } - } - - void TMon::SetAllowedSIDs(const TVector<TString>& sids) { - Config.AllowedSIDs = sids; - } - - ui16 TMon::GetListenPort() { - return Options().Port; - } - - NActors::IEventHandle* TMon::DefaultAuthorizer(const NActors::TActorId& owner, NMonitoring::IMonHttpRequest& request) { - TStringBuf ydbSessionId = request.GetCookie("ydb_session_id"); - TStringBuf authorization = request.GetHeader("Authorization"); - if (!authorization.empty()) { - return new NActors::IEventHandle( - NKikimr::MakeTicketParserID(), - owner, - new NKikimr::TEvTicketParser::TEvAuthorizeTicket({ - .Ticket = TString(authorization) - }), - IEventHandle::FlagTrackDelivery - ); - } else if (!ydbSessionId.empty()) { - return new NActors::IEventHandle( - NKikimr::MakeTicketParserID(), - owner, - new NKikimr::TEvTicketParser::TEvAuthorizeTicket({ - .Ticket = TString("Login ") + TString(ydbSessionId) - }), - IEventHandle::FlagTrackDelivery - ); - } else if (NKikimr::AppData()->EnforceUserTokenRequirement && NKikimr::AppData()->DefaultUserSIDs.empty()) { - return new NActors::IEventHandle( - owner, - owner, - new NKikimr::TEvTicketParser::TEvAuthorizeTicketResult(TString(), { - .Message = "No security credentials were provided", - .Retryable = false - }) - ); - } else if (!NKikimr::AppData()->DefaultUserSIDs.empty()) { - TIntrusivePtr<NACLib::TUserToken> token = new NACLib::TUserToken(NKikimr::AppData()->DefaultUserSIDs); - return new NActors::IEventHandle( - owner, - owner, - new NKikimr::TEvTicketParser::TEvAuthorizeTicketResult(TString(), token, token->SerializeAsString()) - ); - } else { - return nullptr; - } +using namespace NMonitoring; + +IMonPage* TMon::RegisterActorPage(TIndexMonPage* index, const TString& relPath, + const TString& title, bool preTag, TActorSystem* actorSystem, const TActorId& actorId, bool useAuth) { + return RegisterActorPage({ + .Title = title, + .RelPath = relPath, + .ActorSystem = actorSystem, + .Index = index, + .PreTag = preTag, + .ActorId = actorId, + .UseAuth = useAuth, + }); +} + +NActors::IEventHandle* TMon::DefaultAuthorizer(const NActors::TActorId& owner, NMonitoring::IMonHttpRequest& request) { + TStringBuf ydbSessionId = request.GetCookie("ydb_session_id"); + TStringBuf authorization = request.GetHeader("Authorization"); + if (!authorization.empty()) { + return new NActors::IEventHandle( + NKikimr::MakeTicketParserID(), + owner, + new NKikimr::TEvTicketParser::TEvAuthorizeTicket({ + .Ticket = TString(authorization) + }), + IEventHandle::FlagTrackDelivery + ); + } else if (!ydbSessionId.empty()) { + return new NActors::IEventHandle( + NKikimr::MakeTicketParserID(), + owner, + new NKikimr::TEvTicketParser::TEvAuthorizeTicket({ + .Ticket = TString("Login ") + TString(ydbSessionId) + }), + IEventHandle::FlagTrackDelivery + ); + } else if (NKikimr::AppData()->EnforceUserTokenRequirement && NKikimr::AppData()->DefaultUserSIDs.empty()) { + return new NActors::IEventHandle( + owner, + owner, + new NKikimr::TEvTicketParser::TEvAuthorizeTicketResult(TString(), { + .Message = "No security credentials were provided", + .Retryable = false + }) + ); + } else if (!NKikimr::AppData()->DefaultUserSIDs.empty()) { + TIntrusivePtr<NACLib::TUserToken> token = new NACLib::TUserToken(NKikimr::AppData()->DefaultUserSIDs); + return new NActors::IEventHandle( + owner, + owner, + new NKikimr::TEvTicketParser::TEvAuthorizeTicketResult(TString(), token, token->SerializeAsString()) + ); + } else { + return nullptr; } +} -} // NActors +} diff --git a/ydb/core/mon/mon.h b/ydb/core/mon/mon.h index 1cb5dbecd0..976c2ee748 100644 --- a/ydb/core/mon/mon.h +++ b/ydb/core/mon/mon.h @@ -5,62 +5,54 @@ #include <library/cpp/monlib/service/pages/tablesorter/css_mon_page.h> #include <library/cpp/monlib/service/pages/tablesorter/js_mon_page.h> +#include <library/cpp/actors/core/actor.h> #include <library/cpp/actors/core/mon.h> namespace NActors { - class TActorSystem; - struct TActorId; +class TActorSystem; +struct TActorId; - class TMon : public NMonitoring::TMonService2 { - public: - using TRequestAuthorizer = std::function<IEventHandle*(const NActors::TActorId& owner, NMonitoring::IMonHttpRequest& request)>; +class TMon { +public: + using TRequestAuthorizer = std::function<IEventHandle*(const NActors::TActorId& owner, NMonitoring::IMonHttpRequest& request)>; - struct TConfig { - ui16 Port = 0; - TString Address; - ui32 Threads = 10; - TString Title; - TString Host; - TRequestAuthorizer Authorizer = DefaultAuthorizer; - TVector<TString> AllowedSIDs; - TString RedirectMainPageTo; - }; + static NActors::IEventHandle* DefaultAuthorizer(const NActors::TActorId& owner, NMonitoring::IMonHttpRequest& request); - TMon(TConfig config); - virtual ~TMon(); - void Start(); - void Stop(); - - void Register(NMonitoring::IMonPage *page); - NMonitoring::TIndexMonPage *RegisterIndexPage(const TString &path, const TString &title); - - struct TRegisterActorPageFields { - TString Title; - TString RelPath; - TActorSystem* ActorSystem; - NMonitoring::TIndexMonPage* Index; - bool PreTag = false; - TActorId ActorId; - bool UseAuth = true; - TVector<TString> AllowedSIDs; - }; - - NMonitoring::IMonPage* RegisterActorPage(TRegisterActorPageFields fields); - NMonitoring::IMonPage *RegisterActorPage(NMonitoring::TIndexMonPage *index, const TString &relPath, - const TString &title, bool preTag, TActorSystem *actorSystem, const TActorId &actorId, bool useAuth = true); - NMonitoring::IMonPage *RegisterCountersPage(const TString &path, const TString &title, TIntrusivePtr<NMonitoring::TDynamicCounters> counters); - NMonitoring::IMonPage *FindPage(const TString &relPath); - NMonitoring::TIndexMonPage *FindIndexPage(const TString &relPath); - void OutputIndexPage(IOutputStream& out) override; - void SetAllowedSIDs(const TVector<TString>& sids); // sets allowed users/groups for this web interface - ui16 GetListenPort(); - - static NActors::IEventHandle* DefaultAuthorizer(const NActors::TActorId& owner, NMonitoring::IMonHttpRequest& request); + struct TConfig { + ui16 Port = 0; + TString Address; + ui32 Threads = 10; + TString Title; + TString Host; + TRequestAuthorizer Authorizer = DefaultAuthorizer; + TVector<TString> AllowedSIDs; + TString RedirectMainPageTo; + }; - protected: - typedef NMonitoring::TMonService2 TBase; - TConfig Config; + virtual ~TMon() = default; + virtual void Start(TActorSystem* actorSystem = {}) = 0; + virtual void Stop() = 0; + virtual void Register(NMonitoring::IMonPage* page) = 0; + + virtual NMonitoring::TIndexMonPage* RegisterIndexPage(const TString& path, const TString& title) = 0; + + struct TRegisterActorPageFields { + TString Title; + TString RelPath; + TActorSystem* ActorSystem; + NMonitoring::TIndexMonPage* Index; + bool PreTag = false; + TActorId ActorId; + bool UseAuth = true; + TVector<TString> AllowedSIDs; }; + virtual NMonitoring::IMonPage* RegisterActorPage(TRegisterActorPageFields fields) = 0; + NMonitoring::IMonPage* RegisterActorPage(NMonitoring::TIndexMonPage* index, const TString& relPath, + const TString& title, bool preTag, TActorSystem* actorSystem, const TActorId& actorId, bool useAuth = true); + virtual NMonitoring::IMonPage* RegisterCountersPage(const TString& path, const TString& title, TIntrusivePtr<NMonitoring::TDynamicCounters> counters) = 0; + virtual NMonitoring::IMonPage* FindPage(const TString& relPath) = 0; +}; + } // NActors diff --git a/ydb/core/mon/mon_impl.h b/ydb/core/mon/mon_impl.h new file mode 100644 index 0000000000..495fc47a9b --- /dev/null +++ b/ydb/core/mon/mon_impl.h @@ -0,0 +1,394 @@ +#pragma once + +#include "mon.h" +#include <ydb/core/protos/services.pb.h> +#include <library/cpp/monlib/dynamic_counters/counters.h> +#include <library/cpp/monlib/dynamic_counters/page.h> + +namespace NActors { + +using namespace NMonitoring; +using THttpResponsePtr = THolder<NMon::IEvHttpInfoRes>; + +//////////////////////////////////////////////////////////////////////////////// +// MON REQUEST +//////////////////////////////////////////////////////////////////////////////// +class TMonRequest : public NActors::TActor<TMonRequest> { +public: + static constexpr NKikimrServices::TActivity::EType ActorActivityType() { + return NKikimrServices::TActivity::ACTORLIB_COMMON; + } + + TMonRequest(const TActorId &targetActorId, IMonHttpRequest& request, + NThreading::TPromise<THttpResponsePtr> result, const TVector<TString> &sids, TMon::TRequestAuthorizer authorizer) + : TActor(&TMonRequest::StateFunc) + , TargetActorId(targetActorId) + , Request(request) + , Result(result) + , AllowedSIDs(sids) + , Authorizer(authorizer) + { + } + + ~TMonRequest() { + if (!Result.HasValue()) { + Result.SetValue(nullptr); + } + } + + STFUNC(StateFunc) { + switch (ev->GetTypeRewrite()) { + HFunc(TEvents::TEvBootstrap, HandleBootstrap); + HFunc(TEvents::TEvPoisonPill, HandlePoisonPill); + HFunc(TEvents::TEvWakeup, HandleWakeup); + HFunc(TEvents::TEvUndelivered, HandleUndelivered); + HFunc(NMon::IEvHttpInfoRes, HandleInfoRes); + HFunc(NKikimr::TEvTicketParser::TEvAuthorizeTicketResult, Handle); + } + } + + void HandleBootstrap(TEvents::TEvBootstrap::TPtr &, const TActorContext &ctx) { + if (Request.GetMethod() == HTTP_METHOD_OPTIONS) { + return ReplyOptionsResultAndDie(ctx); + } + ctx.Schedule(TDuration::Seconds(600), new TEvents::TEvWakeup()); + if (Authorizer) { + NActors::IEventHandle* handle = Authorizer(SelfId(), Request); + if (handle) { + ctx.Send(handle); + return; + } + } + SendRequest(ctx); + } + + void HandlePoisonPill(TEvents::TEvPoisonPill::TPtr &, const TActorContext &ctx) { + Die(ctx); + } + + void HandleWakeup(TEvents::TEvWakeup::TPtr &, const TActorContext &ctx) { + Result.SetValue(nullptr); + Die(ctx); + } + + void HandleUndelivered(TEvents::TEvUndelivered::TPtr &, const TActorContext &ctx) { + ReplyActorUnavailableAndDie(ctx); + } + + void HandleInfoRes(NMon::IEvHttpInfoRes::TPtr &ev, const NActors::TActorContext &ctx) { + Result.SetValue(THolder<NMon::IEvHttpInfoRes>(ev->Release().Release())); + Die(ctx); + } + + void Handle(NKikimr::TEvTicketParser::TEvAuthorizeTicketResult::TPtr &ev, const TActorContext &ctx) { + const NKikimr::TEvTicketParser::TEvAuthorizeTicketResult &result(*ev->Get()); + if (result.Error) { + ReplyUnathorizedAndDie(ctx, result.Error.Message); + return; + } + bool found = false; + for (const TString& sid : AllowedSIDs) { + if (result.Token->IsExist(sid)) { + found = true; + break;; + } + } + if (found || AllowedSIDs.empty()) { + User = result.Token->GetUserSID(); + SendRequest(ctx, result.SerializedToken); + } else { + ReplyForbiddenAndDie(ctx, TStringBuilder() << "SID is not allowed"); + } + } + + TString GetUser() const { + return User ? User : "anonymous"; + } + + static TString GetMethod(HTTP_METHOD method) { + switch (method) { + case HTTP_METHOD_UNDEFINED: return "UNDEFINED"; + case HTTP_METHOD_OPTIONS: return "OPTIONS"; + case HTTP_METHOD_GET: return "GET"; + case HTTP_METHOD_HEAD: return "HEAD"; + case HTTP_METHOD_POST: return "POST"; + case HTTP_METHOD_PUT: return "PUT"; + case HTTP_METHOD_DELETE: return "DELETE"; + case HTTP_METHOD_TRACE: return "TRACE"; + case HTTP_METHOD_CONNECT: return "CONNECT"; + case HTTP_METHOD_EXTENSION: return "EXTENSION"; + default: return "UNKNOWN"; + } + } + + void SendRequest(const TActorContext &ctx, const TString& serializedToken = TString()) { + if (Authorizer) { + LOG_WARN_S(ctx, NActorsServices::HTTP, + Request.GetRemoteAddr() + << " " << GetUser() + << " " << GetMethod(Request.GetMethod()) + << " " << Request.GetUri()); + } + ctx.Send(TargetActorId, new NMon::TEvHttpInfo(Request, serializedToken), IEventHandle::FlagTrackDelivery); + } + + void ReplyOptionsResultAndDie(const TActorContext &ctx) { + TString url(Request.GetPathInfo()); + TString type = mimetypeByExt(url.data()); + if (type.empty()) { + type = "application/json"; + } + TString origin = TString(Request.GetHeader("Origin")); + if (origin.empty()) { + origin = "*"; + } + TStringBuilder response; + response << "HTTP/1.1 204 No Content\r\n" + "Access-Control-Allow-Origin: " << origin << "\r\n" + "Access-Control-Allow-Credentials: true\r\n" + "Access-Control-Allow-Headers: Content-Type,Authorization,Origin,Accept\r\n" + "Access-Control-Allow-Methods: OPTIONS, GET, POST\r\n" + "Content-Type: " + type + "\r\n" + "Connection: Keep-Alive\r\n\r\n"; + Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response, 0, NMon::IEvHttpInfoRes::EContentType::Custom)); + Die(ctx); + } + + void ReplyUnathorizedAndDie(const TActorContext &ctx, const TString& error = TString()) { + TStringStream response; + TStringStream body; + body << "<html><body><h1>401 Unauthorized</h1>"; + if (!error.empty()) { + body << "<p>" << error << "</p>"; + } + body << "</body></html>"; + TString origin = TString(Request.GetHeader("Origin")); + if (origin.empty()) { + origin = "*"; + } + response << "HTTP/1.1 401 Unauthorized\r\n"; + response << "Access-Control-Allow-Origin: " << origin << "\r\n"; + response << "Access-Control-Allow-Credentials: true\r\n"; + response << "Access-Control-Allow-Headers: Content-Type,Authorization,Origin,Accept\r\n"; + response << "Access-Control-Allow-Methods: OPTIONS, GET, POST\r\n"; + response << "Content-Type: text/html\r\n"; + response << "Content-Length: " << body.Size() << "\r\n"; + response << "\r\n"; + response << body.Str(); + Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response.Str(), 0, NMon::IEvHttpInfoRes::EContentType::Custom)); + Die(ctx); + } + + void ReplyForbiddenAndDie(const TActorContext &ctx, const TString& error = TString()) { + TStringStream response; + TStringStream body; + body << "<html><body><h1>403 Forbidden</h1>"; + if (!error.empty()) { + body << "<p>" << error << "</p>"; + } + body << "</body></html>"; + response << "HTTP/1.1 403 Forbidden\r\n"; + response << "Content-Type: text/html\r\n"; + response << "Content-Length: " << body.Size() << "\r\n"; + response << "\r\n"; + response << body.Str(); + Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response.Str(), 0, NMon::IEvHttpInfoRes::EContentType::Custom)); + Die(ctx); + } + + void ReplyActorUnavailableAndDie(const TActorContext &ctx, const TString& error = TString()) { + TStringStream response; + TStringStream body; + body << "<html><body><h1>503 Actor Unavailable</h1>"; + if (!error.empty()) { + body << "<p>" << error << "</p>"; + } + body << "</body></html>"; + response << "HTTP/1.1 503 Actor Unavailable\r\n"; + response << "Content-Type: text/html\r\n"; + response << "Content-Length: " << body.Size() << "\r\n"; + response << "\r\n"; + response << body.Str(); + Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response.Str(), 0, NMon::IEvHttpInfoRes::EContentType::Custom)); + Die(ctx); + } + + virtual TAutoPtr<NActors::IEventHandle> AfterRegister(const NActors::TActorId &self, const TActorId& parentId) override { + Y_UNUSED(parentId); + return new NActors::IEventHandle(self, self, new TEvents::TEvBootstrap(), 0); + } + +protected: + TActorId TargetActorId; + IMonHttpRequest& Request; + NThreading::TPromise<THttpResponsePtr> Result; + const TVector<TString> &AllowedSIDs; + TMon::TRequestAuthorizer Authorizer; + TString User; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// HTML results page +//////////////////////////////////////////////////////////////////////////////// +class THtmlResultMonPage: public THtmlMonPage { +public: + THtmlResultMonPage(const TString &path, const TString &title, const TString &host, bool preTag, + const NMon::IEvHttpInfoRes &result) + : THtmlMonPage(path, title, true) + , Host(host) + , PreTag(preTag) + , Result(result) + { + } + + void Output(NMonitoring::IMonHttpRequest& request) override { + IOutputStream& out = request.Output(); + + out << HTTPOKHTML; + + out << "<!DOCTYPE html>\n"; + out << "<html>"; + out << "<head>"; + if (Title) { + if (Host) { + out << "<title>" << Title << " - " << Host << "</title>\n"; + } else { + out << "<title>" << Title << "</title>\n"; + } + } + + out << "<link rel='stylesheet' href='https://yastatic.net/bootstrap/3.3.1/css/bootstrap.min.css'>\n"; + out << "<script language='javascript' type='text/javascript' src='https://yastatic.net/jquery/2.1.3/jquery.min.js'></script>\n"; + out << "<script language='javascript' type='text/javascript' src='https://yastatic.net/bootstrap/3.3.1/js/bootstrap.min.js'></script>\n"; + + if (OutputTableSorterJsCss) { + out << "<link rel='stylesheet' href='/jquery.tablesorter.css'>\n"; + out << "<script language='javascript' type='text/javascript' src='/jquery.tablesorter.js'></script>\n"; + } + + out << "<style type=\"text/css\">\n"; + out << ".table-nonfluid { width: auto; }\n"; + out << ".narrow-line50 {line-height: 50%}\n"; + out << ".narrow-line60 {line-height: 60%}\n"; + out << ".narrow-line70 {line-height: 70%}\n"; + out << ".narrow-line80 {line-height: 80%}\n"; + out << ".narrow-line90 {line-height: 90%}\n"; + out << "</style>\n"; + out << "</head>"; + out << "<body>"; + + OutputNavBar(out); + + out << "<div class='container'>"; + if (Title) { + out << "<h2>" << Title << "</h2>"; + } + OutputContent(request); + out << "</div>"; + out << "</body>"; + } + + void OutputContent(NMonitoring::IMonHttpRequest &request) override { + if (PreTag) { + request.Output() << "<pre>\n"; + } + Result.Output(request.Output()); + if (PreTag) { + request.Output() << "</pre>\n"; + } + } + +private: + TString Host; + bool PreTag; + const NMon::IEvHttpInfoRes &Result; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// INDEX PAGE +// Redirects index page to fixed url +//////////////////////////////////////////////////////////////////////////////// +class TIndexRedirectMonPage: public IMonPage { +public: + TIndexRedirectMonPage(TIntrusivePtr<TIndexMonPage> indexMonPage, const TString& path = "internal") + : IMonPage(path) + , IndexMonPage(std::move(indexMonPage)) + { + } + + void Output(IMonHttpRequest& request) override { + IndexMonPage->Output(request); + } + + TIntrusivePtr<TIndexMonPage> IndexMonPage; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// ACTOR MONITORING PAGE +// Encapsulates a request to an actor +//////////////////////////////////////////////////////////////////////////////// +class TActorMonPage: public IMonPage { +public: + TActorMonPage(const TString &path, const TString &title, const TString &host, bool preTag, + TActorSystem *actorSystem, const TActorId &actorId, const TVector<TString> &sids, + TMon::TRequestAuthorizer authorizer) + : IMonPage(path, title) + , Host(host) + , PreTag(preTag) + , ActorSystem(actorSystem) + , TargetActorId(actorId) + , AllowedSIDs(sids) + , Authorizer(std::move(authorizer)) + { + } + + void Output(IMonHttpRequest &request) override { + auto promise = NThreading::NewPromise<THttpResponsePtr>(); + auto future = promise.GetFuture(); + + ActorSystem->Register(new TMonRequest(TargetActorId, request, promise, AllowedSIDs, Authorizer)); + + THttpResponsePtr result = future.ExtractValue(TDuration::Max()); + + if (result) { + Output(request, *result); + } else { + TStringStream out; + out << "Error: timeout. We were not able to receive response from '" + << Title << "'.\n"; + Output(request, NMon::TEvHttpInfoRes(out.Str())); + } + } + + void Output(IMonHttpRequest &request, const NMon::IEvHttpInfoRes &result) const { + if (result.GetContentType() == NMon::IEvHttpInfoRes::Html) { + THtmlResultMonPage resultPage(Path, Title, Host, PreTag, result); + resultPage.Parent = this->Parent; + resultPage.Output(request); + } else { + result.Output(request.Output()); + } + } + + TString Host; + bool PreTag; + TActorSystem *ActorSystem; + TActorId TargetActorId; + const TVector<TString> AllowedSIDs; + TMon::TRequestAuthorizer Authorizer; +}; + +inline TString GetPageFullPath(const NMonitoring::IMonPage* page) { + TStringBuilder path; + if (page->Parent) { + path << GetPageFullPath(page->Parent); + path << '/'; + } + path << page->Path; + return path; +} + +} diff --git a/ydb/core/mon/sync_http_mon.cpp b/ydb/core/mon/sync_http_mon.cpp new file mode 100644 index 0000000000..74ea7dadec --- /dev/null +++ b/ydb/core/mon/sync_http_mon.cpp @@ -0,0 +1,106 @@ +#include "sync_http_mon.h" + +#include <library/cpp/actors/core/actorsystem.h> +#include <library/cpp/actors/core/hfunc.h> +#include <library/cpp/actors/core/mon.h> +#include <library/cpp/actors/core/probes.h> +#include <library/cpp/lwtrace/mon/mon_lwtrace.h> +#include <library/cpp/mime/types/mime.h> +#include <library/cpp/monlib/service/pages/version_mon_page.h> +#include <library/cpp/monlib/service/pages/mon_page.h> +#include <library/cpp/monlib/dynamic_counters/counters.h> +#include <library/cpp/monlib/dynamic_counters/page.h> +#include <library/cpp/threading/future/future.h> +#include <library/cpp/string_utils/url/url.h> +#include <util/system/event.h> +#include <ydb/core/base/appdata.h> +#include <ydb/core/base/monitoring_provider.h> +#include <ydb/core/base/ticket_parser.h> + +#include "mon_impl.h" + +namespace NActors { + + //////////////////////////////////////////////////////////////////////////////// + // TMON CLASS + //////////////////////////////////////////////////////////////////////////////// + TSyncHttpMon::TSyncHttpMon(TSyncHttpMon::TConfig config) + : TBase(config.Port, config.Address, config.Threads, config.Title) + , Config(std::move(config)) + { + } + + TSyncHttpMon::~TSyncHttpMon() { + Stop(); + } + + void TSyncHttpMon::Start(TActorSystem*) { + TBase::Register(new TIndexRedirectMonPage(IndexMonPage)); + TBase::Register(new NMonitoring::TVersionMonPage); + TBase::Register(new NMonitoring::TTablesorterCssMonPage); + TBase::Register(new NMonitoring::TTablesorterJsMonPage); + + NLwTraceMonPage::RegisterPages(IndexMonPage.Get()); + NLwTraceMonPage::ProbeRegistry().AddProbesList(LWTRACE_GET_PROBES(ACTORLIB_PROVIDER)); + NLwTraceMonPage::ProbeRegistry().AddProbesList(LWTRACE_GET_PROBES(MONITORING_PROVIDER)); + TBase::Start(); + } + + void TSyncHttpMon::Stop() { + IndexMonPage->ClearPages(); // it's required to avoid loop-reference + TBase::Stop(); + } + + void TSyncHttpMon::Register(NMonitoring::IMonPage* page) { + TBase::Register(page); + TBase::SortPages(); + } + + TIndexMonPage* TSyncHttpMon::RegisterIndexPage(const TString& path, const TString& title) { + auto page = TBase::RegisterIndexPage(path, title); + TBase::SortPages(); + return page; + } + + IMonPage* TSyncHttpMon::RegisterActorPage(TMon::TRegisterActorPageFields fields) { + IMonPage* page = new TActorMonPage( + fields.RelPath, + fields.Title, + Config.Host, + fields.PreTag, + fields.ActorSystem, + fields.ActorId, + fields.AllowedSIDs ? fields.AllowedSIDs : Config.AllowedSIDs, + fields.UseAuth ? Config.Authorizer : TRequestAuthorizer()); + if (fields.Index) { + fields.Index->Register(page); + fields.Index->SortPages(); + } else { + Register(page); + } + + return page; + } + + IMonPage* TSyncHttpMon::RegisterCountersPage(const TString &path, const TString &title, TIntrusivePtr<TDynamicCounters> counters) { + TDynamicCountersPage* page = new TDynamicCountersPage(path, title, counters); + page->SetUnknownGroupPolicy(EUnknownGroupPolicy::Ignore); + Register(page); + return page; + } + + void TSyncHttpMon::OutputIndexPage(IOutputStream& out) { + if (Config.RedirectMainPageTo) { + // XXX manual http response construction + out << "HTTP/1.1 302 Found\r\n" + << "Location: " << Config.RedirectMainPageTo << "\r\n" + << "Connection: Close\r\n\r\n"; + } else { + NMonitoring::TMonService2::OutputIndexPage(out); + } + } + + IMonPage* TSyncHttpMon::FindPage(const TString& relPath) { + return TBase::FindPage(relPath); + } +} // NActors diff --git a/ydb/core/mon/sync_http_mon.h b/ydb/core/mon/sync_http_mon.h new file mode 100644 index 0000000000..717461e87f --- /dev/null +++ b/ydb/core/mon/sync_http_mon.h @@ -0,0 +1,33 @@ +#pragma once + +#include <library/cpp/monlib/service/monservice.h> +#include <library/cpp/monlib/dynamic_counters/counters.h> +#include <library/cpp/monlib/service/pages/tablesorter/css_mon_page.h> +#include <library/cpp/monlib/service/pages/tablesorter/js_mon_page.h> + +#include <library/cpp/actors/core/mon.h> + +#include "mon.h" + +namespace NActors { + +class TSyncHttpMon : public TMon, public NMonitoring::TMonService2 { +public: + TSyncHttpMon(TConfig config); + virtual ~TSyncHttpMon(); + void Start(TActorSystem* actorSystem = {}) override; + void Stop() override; + + void Register(NMonitoring::IMonPage *page) override; + NMonitoring::TIndexMonPage* RegisterIndexPage(const TString& path, const TString& title) override; + NMonitoring::IMonPage* RegisterActorPage(TRegisterActorPageFields fields) override; + NMonitoring::IMonPage* RegisterCountersPage(const TString& path, const TString& title, TIntrusivePtr<NMonitoring::TDynamicCounters> counters) override; + void OutputIndexPage(IOutputStream& out) override; + NMonitoring::IMonPage* FindPage(const TString& relPath) override; + +protected: + typedef NMonitoring::TMonService2 TBase; + TConfig Config; +}; + +} // NActors diff --git a/ydb/core/mon_alloc/tcmalloc.cpp b/ydb/core/mon_alloc/tcmalloc.cpp index 6c8689205f..ab84c83141 100644 --- a/ydb/core/mon_alloc/tcmalloc.cpp +++ b/ydb/core/mon_alloc/tcmalloc.cpp @@ -659,10 +659,7 @@ public: } void RegisterPages(TMon* mon, TActorSystem* actorSystem, TActorId actorId) override { - auto* indexPage = mon->FindIndexPage("memory"); - if (!indexPage) { - return; - } + auto* indexPage = mon->RegisterIndexPage("memory", "Memory"); mon->RegisterActorPage(indexPage, "heap", "Heap", false, actorSystem, actorId); mon->RegisterActorPage(indexPage, "peakheap", "Peak heap", false, actorSystem, actorId); mon->RegisterActorPage(indexPage, "fragmentation", "Fragmentation", false, actorSystem, actorId); diff --git a/ydb/core/protos/config.proto b/ydb/core/protos/config.proto index 0f5f61344f..ff170cd69a 100644 --- a/ydb/core/protos/config.proto +++ b/ydb/core/protos/config.proto @@ -695,6 +695,7 @@ message TFeatureFlags { optional bool EnableImplicitScanQueryInScripts = 61 [default = true]; optional bool EnablePredicateExtractForScanQueries = 62 [default = true]; optional bool AllowVDiskDefrag = 63 [default = true]; + optional bool EnableAsyncHttpMon = 64 [default = false]; } diff --git a/ydb/core/tablet/tablet_monitoring_proxy.cpp b/ydb/core/tablet/tablet_monitoring_proxy.cpp index ab421f1228..7f1ba12829 100644 --- a/ydb/core/tablet/tablet_monitoring_proxy.cpp +++ b/ydb/core/tablet/tablet_monitoring_proxy.cpp @@ -126,7 +126,8 @@ public: } void Handle(NMon::TEvRemoteJsonInfoRes::TPtr &ev, const TActorContext &ctx) { - ctx.Send(Sender, new NMon::TEvHttpInfoRes(NMonitoring::HTTPOKJSON + ev->Get()->Json, 0, NMon::IEvHttpInfoRes::EContentType::Custom)); + static const char HTTPOKJSON[] = "HTTP/1.1 200 Ok\r\nContent-Type: application/json\r\n\r\n"; + ctx.Send(Sender, new NMon::TEvHttpInfoRes(HTTPOKJSON + ev->Get()->Json, 0, NMon::IEvHttpInfoRes::EContentType::Custom)); Detach(ctx); } diff --git a/ydb/core/testlib/actors/test_runtime.cpp b/ydb/core/testlib/actors/test_runtime.cpp index 5c542ae28d..dec58164fb 100644 --- a/ydb/core/testlib/actors/test_runtime.cpp +++ b/ydb/core/testlib/actors/test_runtime.cpp @@ -3,7 +3,7 @@ #include <ydb/core/base/appdata.h> #include <ydb/core/base/blobstorage.h> #include <ydb/core/base/counters.h> -#include <ydb/core/mon/mon.h> +#include <ydb/core/mon/sync_http_mon.h> #include <ydb/core/mon_alloc/profiler.h> #include <ydb/core/tablet/tablet_impl.h> @@ -148,7 +148,7 @@ namespace NActors { if (NeedMonitoring && !SingleSysEnv) { ui16 port = GetPortManager().GetPort(); - node->Mon.Reset(new NActors::TMon({ + node->Mon.Reset(new NActors::TSyncHttpMon({ .Port = port, .Threads = 10, .Title = "KIKIMR monitoring" diff --git a/ydb/core/viewer/viewer.cpp b/ydb/core/viewer/viewer.cpp index 6aff691c0a..2378bf0ae9 100644 --- a/ydb/core/viewer/viewer.cpp +++ b/ydb/core/viewer/viewer.cpp @@ -170,7 +170,8 @@ public: .UseAuth = false, }); auto whiteboardServiceId = NNodeWhiteboard::MakeNodeWhiteboardServiceId(ctx.SelfID.NodeId()); - ctx.Send(whiteboardServiceId, new NNodeWhiteboard::TEvWhiteboard::TEvSystemStateAddEndpoint("http-mon", Sprintf(":%d", mon->GetListenPort()))); + ctx.Send(whiteboardServiceId, new NNodeWhiteboard::TEvWhiteboard::TEvSystemStateAddEndpoint( + "http-mon", Sprintf(":%d", KikimrRunConfig.AppConfig.GetMonitoringConfig().GetMonitoringPort()))); AllowOrigin = KikimrRunConfig.AppConfig.GetMonitoringConfig().GetAllowOrigin(); diff --git a/ydb/core/viewer/viewer.h b/ydb/core/viewer/viewer.h index ae426b2e35..7412a51540 100644 --- a/ydb/core/viewer/viewer.h +++ b/ydb/core/viewer/viewer.h @@ -13,7 +13,7 @@ namespace NKikimr { namespace NViewer { -inline TActorId MakeViewerID(ui32 node = 0) { +inline TActorId MakeViewerID(ui32 node) { char x[12] = {'v','i','e','w','e','r'}; return TActorId(node, TStringBuf(x, 12)); } diff --git a/ydb/services/persqueue_v1/persqueue_new_schemecache_ut.cpp b/ydb/services/persqueue_v1/persqueue_new_schemecache_ut.cpp index 89fac2d310..db11350c10 100644 --- a/ydb/services/persqueue_v1/persqueue_new_schemecache_ut.cpp +++ b/ydb/services/persqueue_v1/persqueue_new_schemecache_ut.cpp @@ -5,7 +5,7 @@ #include <ydb/core/testlib/test_pq_client.h> #include <ydb/core/persqueue/cluster_tracker.h> - +#include <ydb/core/mon/sync_http_mon.h> #include <ydb/core/tablet/tablet_counters_aggregator.h> #include <ydb/library/aclib/aclib.h> @@ -14,7 +14,7 @@ #include <ydb/library/persqueue/topic_parser/topic_parser.h> #include <library/cpp/testing/unittest/tests_data.h> -#include <library/cpp/testing/unittest/registar.h> +#include <library/cpp/testing/unittest/registar.h> #include <library/cpp/json/json_reader.h> #include <util/string/join.h> @@ -331,7 +331,7 @@ namespace NKikimr::NPersQueueTests { const auto monPort = TPortManager().GetPort(); auto Counters = server.CleverServer->GetGRpcServerRootCounters(); - NActors::TMon Monitoring({monPort, "localhost", 3, "root", "localhost", {}, {}, {}}); + NActors::TSyncHttpMon Monitoring({monPort, "localhost", 3, "root", "localhost", {}, {}, {}}); Monitoring.RegisterCountersPage("counters", "Counters", Counters); Monitoring.Start(); diff --git a/ydb/services/persqueue_v1/persqueue_ut.cpp b/ydb/services/persqueue_v1/persqueue_ut.cpp index 96c47a0c72..d8b5d0be84 100644 --- a/ydb/services/persqueue_v1/persqueue_ut.cpp +++ b/ydb/services/persqueue_v1/persqueue_ut.cpp @@ -6,6 +6,7 @@ #include <ydb/services/persqueue_v1/ut/persqueue_test_fixture.h> #include <ydb/core/base/appdata.h> +#include <ydb/core/mon/sync_http_mon.h> #include <ydb/core/testlib/test_pq_client.h> #include <ydb/core/protos/grpc_pq_old.pb.h> #include <ydb/core/persqueue/cluster_tracker.h> @@ -1610,7 +1611,7 @@ Y_UNIT_TEST_SUITE(TPersQueueTest) { const auto monPort = TPortManager().GetPort(); auto Counters = server.CleverServer->GetGRpcServerRootCounters(); - NActors::TMon Monitoring({monPort, "localhost", 3, "root", "localhost", {}, {}, {}}); + NActors::TSyncHttpMon Monitoring({monPort, "localhost", 3, "root", "localhost", {}, {}, {}}); Monitoring.RegisterCountersPage("counters", "Counters", Counters); Monitoring.Start(); |