aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexey Efimov <xeno@prnwatch.com>2022-05-04 10:13:58 +0300
committerAlexey Efimov <xeno@prnwatch.com>2022-05-04 10:13:58 +0300
commit6faf680f58ba8341a694dcbadf572d37197ae888 (patch)
treee5fd053607e59e6db7bdec1ea100f46e149a6f97
parentb4c4f96bf4475170a9ac5e4568cc7b05789986c8 (diff)
downloadydb-6faf680f58ba8341a694dcbadf572d37197ae888.tar.gz
add async monitoring http KIKIMR-14742
ref:c51d608f0ae78f08597b88f837491da33a953ef6
-rw-r--r--library/cpp/actors/http/http.cpp61
-rw-r--r--library/cpp/actors/http/http.h55
-rw-r--r--library/cpp/actors/http/http_proxy.cpp22
-rw-r--r--library/cpp/actors/http/http_ut.cpp17
-rw-r--r--library/cpp/lwtrace/mon/mon_lwtrace.cpp6
-rw-r--r--library/cpp/lwtrace/mon/mon_lwtrace.h2
-rw-r--r--library/cpp/monlib/service/monservice.h4
-rw-r--r--ydb/core/blobstorage/pdisk/blobstorage_pdisk_ut_run.cpp3
-rw-r--r--ydb/core/blobstorage/ut_vdisk/lib/astest.h4
-rw-r--r--ydb/core/blobstorage/ut_vdisk/lib/prepare.cpp4
-rw-r--r--ydb/core/driver_lib/run/factories.h2
-rw-r--r--ydb/core/driver_lib/run/run.cpp19
-rw-r--r--ydb/core/mon/CMakeLists.txt2
-rw-r--r--ydb/core/mon/async_http_mon.cpp511
-rw-r--r--ydb/core/mon/async_http_mon.h41
-rw-r--r--ydb/core/mon/mon.cpp597
-rw-r--r--ydb/core/mon/mon.h88
-rw-r--r--ydb/core/mon/mon_impl.h394
-rw-r--r--ydb/core/mon/sync_http_mon.cpp106
-rw-r--r--ydb/core/mon/sync_http_mon.h33
-rw-r--r--ydb/core/mon_alloc/tcmalloc.cpp5
-rw-r--r--ydb/core/protos/config.proto1
-rw-r--r--ydb/core/tablet/tablet_monitoring_proxy.cpp3
-rw-r--r--ydb/core/testlib/actors/test_runtime.cpp4
-rw-r--r--ydb/core/viewer/viewer.cpp3
-rw-r--r--ydb/core/viewer/viewer.h2
-rw-r--r--ydb/services/persqueue_v1/persqueue_new_schemecache_ut.cpp6
-rw-r--r--ydb/services/persqueue_v1/persqueue_ut.cpp3
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();