diff options
author | Alexey Efimov <xeno@prnwatch.com> | 2022-02-10 16:49:41 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:49:41 +0300 |
commit | 26e0e4fb5e5cd6b4d7f4c21f9fcd7978891bf946 (patch) | |
tree | d34555f21d4d9f94f84d460e55b77d7eb41a953c /library | |
parent | ca3252a147a429eac4ba8221857493c58dcd09b5 (diff) | |
download | ydb-26e0e4fb5e5cd6b4d7f4c21f9fcd7978891bf946.tar.gz |
Restoring authorship annotation for Alexey Efimov <xeno@prnwatch.com>. Commit 1 of 2.
Diffstat (limited to 'library')
52 files changed, 4810 insertions, 4810 deletions
diff --git a/library/cpp/actors/core/actor.h b/library/cpp/actors/core/actor.h index ed29bd14b9..dcdaf07e93 100644 --- a/library/cpp/actors/core/actor.h +++ b/library/cpp/actors/core/actor.h @@ -239,7 +239,7 @@ namespace NActors { INTERCONNECT_POLLER = 285, INTERCONNECT_SESSION_KILLER = 286, ACTOR_SYSTEM_SCHEDULER_ACTOR = 312, - ACTOR_FUTURE_CALLBACK = 337, + ACTOR_FUTURE_CALLBACK = 337, INTERCONNECT_MONACTOR = 362, INTERCONNECT_LOAD_ACTOR = 376, INTERCONNECT_LOAD_RESPONDER = 377, diff --git a/library/cpp/actors/core/actorsystem.h b/library/cpp/actors/core/actorsystem.h index 40499d7586..48482dc66b 100644 --- a/library/cpp/actors/core/actorsystem.h +++ b/library/cpp/actors/core/actorsystem.h @@ -120,10 +120,10 @@ namespace NActors { return TString(); } - virtual ui32 GetThreads() const { - return 1; - } - + virtual ui32 GetThreads() const { + return 1; + } + // generic virtual TAffinity* Affinity() const = 0; diff --git a/library/cpp/actors/core/event_local.h b/library/cpp/actors/core/event_local.h index 2845aa94dd..a7870e10af 100644 --- a/library/cpp/actors/core/event_local.h +++ b/library/cpp/actors/core/event_local.h @@ -66,7 +66,7 @@ namespace NActors { static IEventBase* Load(NActors::TEventSerializedData*) { return new TEv(); } - + static IEventBase* Load(const TString&) { return new TEv(); } diff --git a/library/cpp/actors/core/event_pb.h b/library/cpp/actors/core/event_pb.h index d7546b901a..d104517e5d 100644 --- a/library/cpp/actors/core/event_pb.h +++ b/library/cpp/actors/core/event_pb.h @@ -157,7 +157,7 @@ namespace NActors { TString ToString() const override { return Record.ShortDebugString(); } - + bool IsSerializable() const override { return true; } @@ -422,72 +422,72 @@ namespace NActors { return TypeName<TEv>() + " { " + TBase::Record.ShortDebugString() + " }"; } }; - - template <typename TEv, typename TRecord, ui32 TEventType> + + template <typename TEv, typename TRecord, ui32 TEventType> class TEventPreSerializedPB: public TEventPB<TEv, TRecord, TEventType> { - protected: - using TBase = TEventPB<TEv, TRecord, TEventType>; - using TSelf = TEventPreSerializedPB<TEv, TRecord, TEventType>; - using TBase::Record; - - public: - TString PreSerializedData; // already serialized PB data (using message::SerializeToString) - - TEventPreSerializedPB() = default; - - explicit TEventPreSerializedPB(const TRecord& rec) - : TBase(rec) + protected: + using TBase = TEventPB<TEv, TRecord, TEventType>; + using TSelf = TEventPreSerializedPB<TEv, TRecord, TEventType>; + using TBase::Record; + + public: + TString PreSerializedData; // already serialized PB data (using message::SerializeToString) + + TEventPreSerializedPB() = default; + + explicit TEventPreSerializedPB(const TRecord& rec) + : TBase(rec) { } - - explicit TEventPreSerializedPB(TRecord&& rec) - : TBase(std::move(rec)) + + explicit TEventPreSerializedPB(TRecord&& rec) + : TBase(std::move(rec)) { } - - // when remote event received locally this method will merge preserialized data - const TRecord& GetRecord() { - TRecord& base(TBase::Record); - if (!PreSerializedData.empty()) { - TRecord copy; + + // when remote event received locally this method will merge preserialized data + const TRecord& GetRecord() { + TRecord& base(TBase::Record); + if (!PreSerializedData.empty()) { + TRecord copy; Y_PROTOBUF_SUPPRESS_NODISCARD copy.ParseFromString(PreSerializedData); - copy.MergeFrom(base); - base.Swap(©); - PreSerializedData.clear(); - } + copy.MergeFrom(base); + base.Swap(©); + PreSerializedData.clear(); + } return TBase::Record; - } - - const TRecord& GetRecord() const { - return const_cast<TSelf*>(this)->GetRecord(); - } - - TRecord* MutableRecord() { + } + + const TRecord& GetRecord() const { + return const_cast<TSelf*>(this)->GetRecord(); + } + + TRecord* MutableRecord() { GetRecord(); // Make sure PreSerializedData is parsed - return &(TBase::Record); - } - - TString ToString() const override { + return &(TBase::Record); + } + + TString ToString() const override { return GetRecord().ShortDebugString(); - } - + } + bool SerializeToArcadiaStream(TChunkSerializer* chunker) const override { return chunker->WriteString(&PreSerializedData) && TBase::SerializeToArcadiaStream(chunker); - } - - ui32 CalculateSerializedSize() const override { - return PreSerializedData.size() + TBase::CalculateSerializedSize(); - } - - size_t GetCachedByteSize() const { - return PreSerializedData.size() + TBase::GetCachedByteSize(); - } + } + + ui32 CalculateSerializedSize() const override { + return PreSerializedData.size() + TBase::CalculateSerializedSize(); + } + + size_t GetCachedByteSize() const { + return PreSerializedData.size() + TBase::GetCachedByteSize(); + } ui32 CalculateSerializedSizeCached() const override { return GetCachedByteSize(); } - }; - + }; + inline TActorId ActorIdFromProto(const NActorsProto::TActorId& actorId) { return TActorId(actorId.GetRawX1(), actorId.GetRawX2()); } diff --git a/library/cpp/actors/core/executor_pool_base.cpp b/library/cpp/actors/core/executor_pool_base.cpp index c3b9999168..9d50d702e6 100644 --- a/library/cpp/actors/core/executor_pool_base.cpp +++ b/library/cpp/actors/core/executor_pool_base.cpp @@ -161,8 +161,8 @@ namespace NActors { bool TExecutorPoolBaseMailboxed::Cleanup() { return MailboxTable->Cleanup(); } - - ui32 TExecutorPoolBase::GetThreads() const { - return PoolThreads; - } + + ui32 TExecutorPoolBase::GetThreads() const { + return PoolThreads; + } } diff --git a/library/cpp/actors/core/executor_pool_base.h b/library/cpp/actors/core/executor_pool_base.h index c84ce1af77..7f19188ae1 100644 --- a/library/cpp/actors/core/executor_pool_base.h +++ b/library/cpp/actors/core/executor_pool_base.h @@ -42,7 +42,7 @@ namespace NActors { ~TExecutorPoolBase(); void ScheduleActivation(ui32 activation) override; TAffinity* Affinity() const override; - ui32 GetThreads() const override; + ui32 GetThreads() const override; }; void DoActorInit(TActorSystem*, IActor*, const TActorId&, const TActorId&); diff --git a/library/cpp/actors/core/interconnect.h b/library/cpp/actors/core/interconnect.h index 679a4b8cc6..d71fe02c48 100644 --- a/library/cpp/actors/core/interconnect.h +++ b/library/cpp/actors/core/interconnect.h @@ -60,7 +60,7 @@ namespace NActors { // protobuf-parser ctor explicit TNodeLocation(const NActorsInterconnect::TNodeLocation& location); - + // serialized protobuf ctor static constexpr struct TFromSerialized {} FromSerialized {}; TNodeLocation(TFromSerialized, const TString& s); @@ -114,7 +114,7 @@ namespace NActors { friend bool operator > (const TNodeLocation& x, const TNodeLocation& y) { return x.Compare(y) > 0; } friend bool operator >=(const TNodeLocation& x, const TNodeLocation& y) { return x.Compare(y) >= 0; } }; - + struct TEvInterconnect { enum EEv { EvForward = EventSpaceBegin(TEvents::ES_INTERCONNECT), @@ -177,7 +177,7 @@ namespace NActors { struct TEvListNodes: public TEventLocal<TEvListNodes, EvListNodes> { }; - + struct TNodeInfo { ui32 NodeId; TString Address; @@ -185,10 +185,10 @@ namespace NActors { TString ResolveHost; ui16 Port; TNodeLocation Location; - - TNodeInfo() = default; - TNodeInfo(const TNodeInfo&) = default; - TNodeInfo& operator =(const TNodeInfo&) = default; + + TNodeInfo() = default; + TNodeInfo(const TNodeInfo&) = default; + TNodeInfo& operator =(const TNodeInfo&) = default; TNodeInfo(ui32 nodeId, const TString& address, const TString& host, @@ -203,12 +203,12 @@ namespace NActors { , Location(location) { } - + operator ui32() const { return NodeId; } }; - + struct TEvNodesInfo: public TEventLocal<TEvNodesInfo, EvNodesInfo> { TVector<TNodeInfo> Nodes; @@ -218,8 +218,8 @@ namespace NActors { return &x; } return nullptr; - } - }; + } + }; struct TEvDisconnect; @@ -251,5 +251,5 @@ namespace NActors { struct TEvPoisonSession : TEventLocal<TEvPoisonSession, EvPoisonSession> {}; struct TEvTerminate : TEventLocal<TEvTerminate, EvTerminate> {}; - }; + }; } diff --git a/library/cpp/actors/core/log.cpp b/library/cpp/actors/core/log.cpp index 5f63b5af58..312baa767a 100644 --- a/library/cpp/actors/core/log.cpp +++ b/library/cpp/actors/core/log.cpp @@ -11,7 +11,7 @@ static_assert(int(NActors::NLog::PRI_WARN) == int(::TLOG_WARNING), "expect int(N static_assert(int(NActors::NLog::PRI_NOTICE) == int(::TLOG_NOTICE), "expect int(NActors::NLog::PRI_NOTICE) == int(::TLOG_NOTICE)"); static_assert(int(NActors::NLog::PRI_INFO) == int(::TLOG_INFO), "expect int(NActors::NLog::PRI_INFO) == int(::TLOG_INFO)"); static_assert(int(NActors::NLog::PRI_DEBUG) == int(::TLOG_DEBUG), "expect int(NActors::NLog::PRI_DEBUG) == int(::TLOG_DEBUG)"); -static_assert(int(NActors::NLog::PRI_TRACE) == int(::TLOG_RESOURCES), "expect int(NActors::NLog::PRI_TRACE) == int(::TLOG_RESOURCES)"); +static_assert(int(NActors::NLog::PRI_TRACE) == int(::TLOG_RESOURCES), "expect int(NActors::NLog::PRI_TRACE) == int(::TLOG_RESOURCES)"); namespace { struct TRecordWithNewline { @@ -441,7 +441,7 @@ namespace NActors { str << "Change priority" << Endl; } UL() { - for (int p = NLog::PRI_EMERG; p <= NLog::PRI_TRACE; ++p) { + for (int p = NLog::PRI_EMERG; p <= NLog::PRI_TRACE; ++p) { LI() { str << "<a href='logger?c=" << component << "&p=" << p << "'>" << NLog::PriorityToString(NLog::EPrio(p)) << "</a>"; @@ -452,7 +452,7 @@ namespace NActors { str << "Change sampling priority" << Endl; } UL() { - for (int p = NLog::PRI_EMERG; p <= NLog::PRI_TRACE; ++p) { + for (int p = NLog::PRI_EMERG; p <= NLog::PRI_TRACE; ++p) { LI() { str << "<a href='logger?c=" << component << "&sp=" << p << "'>" << NLog::PriorityToString(NLog::EPrio(p)) << "</a>"; @@ -515,7 +515,7 @@ namespace NActors { } } TABLEBODY() { - for (int p = NLog::PRI_EMERG; p <= NLog::PRI_TRACE; ++p) { + for (int p = NLog::PRI_EMERG; p <= NLog::PRI_TRACE; ++p) { TABLER() { TABLED() { str << "<a href = 'logger?c=-1&p=" << p << "'>" @@ -537,7 +537,7 @@ namespace NActors { } } TABLEBODY() { - for (int p = NLog::PRI_EMERG; p <= NLog::PRI_TRACE; ++p) { + for (int p = NLog::PRI_EMERG; p <= NLog::PRI_TRACE; ++p) { TABLER() { TABLED() { str << "<a href = 'logger?c=-1&sp=" << p << "'>" diff --git a/library/cpp/actors/core/mon.h b/library/cpp/actors/core/mon.h index c450f2338e..d17b231107 100644 --- a/library/cpp/actors/core/mon.h +++ b/library/cpp/actors/core/mon.h @@ -12,7 +12,7 @@ namespace NActors { HttpInfoRes, RemoteHttpInfo, RemoteHttpInfoRes, - RemoteJsonInfoRes, + RemoteJsonInfoRes, RemoteBinaryInfoRes, End }; @@ -28,14 +28,14 @@ namespace NActors { } TEvHttpInfo(const NMonitoring::IMonHttpRequest& request, const TString& userToken) - : Request(request) - , UserToken(userToken) - , SubRequestId(0) - { - } - + : Request(request) + , UserToken(userToken) + , SubRequestId(0) + { + } + const NMonitoring::IMonHttpRequest& Request; - TString UserToken; // built and serialized + TString UserToken; // built and serialized // SubRequestId != 0 means that we assemble reply from multiple parts and SubRequestId contains this part id int SubRequestId; }; @@ -168,22 +168,22 @@ namespace NActors { struct TEvRemoteJsonInfoRes: public NActors::TEventBase<TEvRemoteJsonInfoRes, RemoteJsonInfoRes> { TEvRemoteJsonInfoRes() { } - + TEvRemoteJsonInfoRes(const TString& json) - : Json(json) + : Json(json) { } - + TString Json; - + TString ToStringHeader() const override { - return "TEvRemoteJsonInfoRes"; - } - + return "TEvRemoteJsonInfoRes"; + } + bool SerializeToArcadiaStream(TChunkSerializer *serializer) const override { return serializer->WriteString(&Json); - } - + } + ui32 CalculateSerializedSize() const override { return Json.size(); } @@ -194,9 +194,9 @@ namespace NActors { static IEventBase* Load(TEventSerializedData* bufs) { return new TEvRemoteJsonInfoRes(bufs->GetString()); - } - }; - + } + }; + struct TEvRemoteBinaryInfoRes: public NActors::TEventBase<TEvRemoteBinaryInfoRes, RemoteBinaryInfoRes> { TEvRemoteBinaryInfoRes() { } diff --git a/library/cpp/actors/core/process_stats.cpp b/library/cpp/actors/core/process_stats.cpp index 0e1dbd0031..688a9de6b3 100644 --- a/library/cpp/actors/core/process_stats.cpp +++ b/library/cpp/actors/core/process_stats.cpp @@ -1,7 +1,7 @@ #include "actorsystem.h" #include "actor_bootstrapped.h" #include "hfunc.h" -#include "process_stats.h" +#include "process_stats.h" #include <library/cpp/monlib/dynamic_counters/counters.h> #include <library/cpp/monlib/metrics/metric_registry.h> @@ -44,8 +44,8 @@ namespace NActors { bool TProcStat::Fill(pid_t pid) { try { - TString strPid(ToString(pid)); - TFileInput proc("/proc/" + strPid + "/status"); + TString strPid(ToString(pid)); + TFileInput proc("/proc/" + strPid + "/status"); TString str; while (proc.ReadLine(str)) { if (ExtractVal(str, "VmRSS:", Rss)) @@ -60,7 +60,7 @@ namespace NActors { float tickPerMillisec = TicksPerMillisec(); - TFileInput procStat("/proc/" + strPid + "/stat"); + TFileInput procStat("/proc/" + strPid + "/stat"); procStat.ReadLine(str); if (!str.empty()) { sscanf(str.data(), @@ -78,7 +78,7 @@ namespace NActors { Uptime = SystemUptime - TDuration::MilliSeconds(StartTime / TicksPerMillisec()); } - TFileInput statm("/proc/" + strPid + "/statm"); + TFileInput statm("/proc/" + strPid + "/statm"); statm.ReadLine(str); TVector<TString> fields; StringSplitter(str).Split(' ').SkipEmpty().Collect(&fields); @@ -91,27 +91,27 @@ namespace NActors { FileRss = shared * PageSize; AnonRss = (resident - shared) * PageSize; } - - TFileInput cgroup("/proc/" + strPid + "/cgroup"); - TString line; - TString memoryCGroup; - while (cgroup.ReadLine(line) > 0) { - StringSplitter(line).Split(':').Collect(&fields); - if (fields.size() > 2 && fields[1] == "memory") { - memoryCGroup = fields[2]; - break; - } - } - if (!memoryCGroup.empty()) { - TFileInput limit("/sys/fs/cgroup/memory" + memoryCGroup + "/memory.limit_in_bytes"); - if (limit.ReadLine(line) > 0) { - CGroupMemLim = FromString<ui64>(line); - if (CGroupMemLim > (1ULL << 40)) { - CGroupMemLim = 0; - } - } - } - + + TFileInput cgroup("/proc/" + strPid + "/cgroup"); + TString line; + TString memoryCGroup; + while (cgroup.ReadLine(line) > 0) { + StringSplitter(line).Split(':').Collect(&fields); + if (fields.size() > 2 && fields[1] == "memory") { + memoryCGroup = fields[2]; + break; + } + } + if (!memoryCGroup.empty()) { + TFileInput limit("/sys/fs/cgroup/memory" + memoryCGroup + "/memory.limit_in_bytes"); + if (limit.ReadLine(line) > 0) { + CGroupMemLim = FromString<ui64>(line); + if (CGroupMemLim > (1ULL << 40)) { + CGroupMemLim = 0; + } + } + } + } catch (...) { return false; } @@ -191,7 +191,7 @@ namespace { VmSize = ProcStatGroup->GetCounter("Process/VmSize", false); AnonRssSize = ProcStatGroup->GetCounter("Process/AnonRssSize", false); FileRssSize = ProcStatGroup->GetCounter("Process/FileRssSize", false); - CGroupMemLimit = ProcStatGroup->GetCounter("Process/CGroupMemLimit", false); + CGroupMemLimit = ProcStatGroup->GetCounter("Process/CGroupMemLimit", false); UserTime = ProcStatGroup->GetCounter("Process/UserTime", true); SysTime = ProcStatGroup->GetCounter("Process/SystemTime", true); MinorPageFaults = ProcStatGroup->GetCounter("Process/MinorPageFaults", true); @@ -205,9 +205,9 @@ namespace { *VmSize = procStat.Vsize; *AnonRssSize = procStat.AnonRss; *FileRssSize = procStat.FileRss; - if (procStat.CGroupMemLim) { - *CGroupMemLimit = procStat.CGroupMemLim; - } + if (procStat.CGroupMemLim) { + *CGroupMemLimit = procStat.CGroupMemLim; + } *UserTime = procStat.Utime; *SysTime = procStat.Stime; *MinorPageFaults = procStat.MinFlt; @@ -222,7 +222,7 @@ namespace { NMonitoring::TDynamicCounters::TCounterPtr VmSize; NMonitoring::TDynamicCounters::TCounterPtr AnonRssSize; NMonitoring::TDynamicCounters::TCounterPtr FileRssSize; - NMonitoring::TDynamicCounters::TCounterPtr CGroupMemLimit; + NMonitoring::TDynamicCounters::TCounterPtr CGroupMemLimit; NMonitoring::TDynamicCounters::TCounterPtr UserTime; NMonitoring::TDynamicCounters::TCounterPtr SysTime; NMonitoring::TDynamicCounters::TCounterPtr MinorPageFaults; @@ -242,7 +242,7 @@ namespace { VmSize = registry.IntGauge({{"sensor", "process.VmSize"}}); AnonRssSize = registry.IntGauge({{"sensor", "process.AnonRssSize"}}); FileRssSize = registry.IntGauge({{"sensor", "process.FileRssSize"}}); - CGroupMemLimit = registry.IntGauge({{"sensor", "process.CGroupMemLimit"}}); + CGroupMemLimit = registry.IntGauge({{"sensor", "process.CGroupMemLimit"}}); UptimeSeconds = registry.IntGauge({{"sensor", "process.UptimeSeconds"}}); NumThreads = registry.IntGauge({{"sensor", "process.NumThreads"}}); SystemUptimeSeconds = registry.IntGauge({{"sensor", "system.UptimeSeconds"}}); @@ -257,7 +257,7 @@ namespace { VmSize->Set(procStat.Vsize); AnonRssSize->Set(procStat.AnonRss); FileRssSize->Set(procStat.FileRss); - CGroupMemLimit->Set(procStat.CGroupMemLim); + CGroupMemLimit->Set(procStat.CGroupMemLim); UptimeSeconds->Set(procStat.Uptime.Seconds()); NumThreads->Set(procStat.NumThreads); SystemUptimeSeconds->Set(procStat.SystemUptime.Seconds()); @@ -282,7 +282,7 @@ namespace { NMonitoring::TIntGauge* VmSize; NMonitoring::TIntGauge* AnonRssSize; NMonitoring::TIntGauge* FileRssSize; - NMonitoring::TIntGauge* CGroupMemLimit; + NMonitoring::TIntGauge* CGroupMemLimit; NMonitoring::TRate* UserTime; NMonitoring::TRate* SysTime; NMonitoring::TRate* MinorPageFaults; diff --git a/library/cpp/actors/core/process_stats.h b/library/cpp/actors/core/process_stats.h index 66346d0b5a..7b329d6bb4 100644 --- a/library/cpp/actors/core/process_stats.h +++ b/library/cpp/actors/core/process_stats.h @@ -10,57 +10,57 @@ namespace NMonitoring { } namespace NActors { - struct TProcStat { - ui64 Rss; - ui64 VolCtxSwtch; - ui64 NonvolCtxSwtch; - - int Pid; - char State; - int Ppid; - int Pgrp; - int Session; - int TtyNr; - int TPgid; - unsigned Flags; - unsigned long MinFlt; - unsigned long CMinFlt; - unsigned long MajFlt; - unsigned long CMajFlt; - unsigned long Utime; - unsigned long Stime; - long CUtime; - long CStime; - long Priority; - long Nice; - long NumThreads; - long ItRealValue; - // StartTime is measured from system boot - unsigned long long StartTime; - unsigned long Vsize; - long RssPages; - unsigned long RssLim; - ui64 FileRss; - ui64 AnonRss; - ui64 CGroupMemLim = 0; - - TDuration Uptime; - TDuration SystemUptime; - // ... - - TProcStat() { - Zero(*this); - Y_UNUSED(PageSize); - } - - bool Fill(pid_t pid); - - private: - long PageSize = 0; - - long ObtainPageSize(); - }; - + struct TProcStat { + ui64 Rss; + ui64 VolCtxSwtch; + ui64 NonvolCtxSwtch; + + int Pid; + char State; + int Ppid; + int Pgrp; + int Session; + int TtyNr; + int TPgid; + unsigned Flags; + unsigned long MinFlt; + unsigned long CMinFlt; + unsigned long MajFlt; + unsigned long CMajFlt; + unsigned long Utime; + unsigned long Stime; + long CUtime; + long CStime; + long Priority; + long Nice; + long NumThreads; + long ItRealValue; + // StartTime is measured from system boot + unsigned long long StartTime; + unsigned long Vsize; + long RssPages; + unsigned long RssLim; + ui64 FileRss; + ui64 AnonRss; + ui64 CGroupMemLim = 0; + + TDuration Uptime; + TDuration SystemUptime; + // ... + + TProcStat() { + Zero(*this); + Y_UNUSED(PageSize); + } + + bool Fill(pid_t pid); + + private: + long PageSize = 0; + + long ObtainPageSize(); + }; + IActor* CreateProcStatCollector(ui32 intervalSec, NMonitoring::TDynamicCounterPtr counters); IActor* CreateProcStatCollector(TDuration interval, NMonitoring::TMetricRegistry& registry); } diff --git a/library/cpp/actors/helpers/future_callback.h b/library/cpp/actors/helpers/future_callback.h index 8ca0d99fda..ecaf25d144 100644 --- a/library/cpp/actors/helpers/future_callback.h +++ b/library/cpp/actors/helpers/future_callback.h @@ -1,33 +1,33 @@ -#pragma once - +#pragma once + #include <library/cpp/actors/core/actor.h> #include <library/cpp/actors/core/hfunc.h> - -namespace NActors { - -template <typename EventType> -struct TActorFutureCallback : TActor<TActorFutureCallback<EventType>> { - using TCallback = std::function<void(TAutoPtr<TEventHandle<EventType>>&)>; - using TBase = TActor<TActorFutureCallback<EventType>>; - TCallback Callback; - - static constexpr IActor::EActivityType ActorActivityType() { - return IActor::ACTOR_FUTURE_CALLBACK; - } - - TActorFutureCallback(TCallback&& callback) - : TBase(&TActorFutureCallback::StateWaitForEvent) - , Callback(std::move(callback)) - {} - - STRICT_STFUNC(StateWaitForEvent, - HFunc(EventType, Handle) - ) - - void Handle(typename EventType::TPtr ev, const TActorContext& ctx) { - Callback(ev); - TBase::Die(ctx); - } -}; - -} // NActors + +namespace NActors { + +template <typename EventType> +struct TActorFutureCallback : TActor<TActorFutureCallback<EventType>> { + using TCallback = std::function<void(TAutoPtr<TEventHandle<EventType>>&)>; + using TBase = TActor<TActorFutureCallback<EventType>>; + TCallback Callback; + + static constexpr IActor::EActivityType ActorActivityType() { + return IActor::ACTOR_FUTURE_CALLBACK; + } + + TActorFutureCallback(TCallback&& callback) + : TBase(&TActorFutureCallback::StateWaitForEvent) + , Callback(std::move(callback)) + {} + + STRICT_STFUNC(StateWaitForEvent, + HFunc(EventType, Handle) + ) + + void Handle(typename EventType::TPtr ev, const TActorContext& ctx) { + Callback(ev); + TBase::Die(ctx); + } +}; + +} // NActors diff --git a/library/cpp/actors/helpers/ya.make b/library/cpp/actors/helpers/ya.make index d8771179de..e7302cea09 100644 --- a/library/cpp/actors/helpers/ya.make +++ b/library/cpp/actors/helpers/ya.make @@ -7,7 +7,7 @@ SRCS( activeactors.h flow_controlled_queue.cpp flow_controlled_queue.h - future_callback.h + future_callback.h mon_histogram_helper.h selfping_actor.cpp ) diff --git a/library/cpp/actors/http/http.cpp b/library/cpp/actors/http/http.cpp index 7125f9d8b0..90fdd161ed 100644 --- a/library/cpp/actors/http/http.cpp +++ b/library/cpp/actors/http/http.cpp @@ -1,116 +1,116 @@ -#include "http.h" +#include "http.h" #include <library/cpp/string_utils/quote/quote.h> - -inline TStringBuf operator +(TStringBuf l, TStringBuf r) { - if (l.empty()) { - return r; - } - if (r.empty()) { - return l; - } - if (l.end() == r.begin()) { - return TStringBuf(l.data(), l.size() + r.size()); - } - if (r.end() == l.begin()) { - return TStringBuf(r.data(), l.size() + r.size()); - } - Y_FAIL("oops"); - return TStringBuf(); -} - -inline TStringBuf operator +=(TStringBuf& l, TStringBuf r) { - return l = l + r; -} - -namespace NHttp { - -template <> TStringBuf THttpRequest::GetName<&THttpRequest::Host>() { return "Host"; } -template <> TStringBuf THttpRequest::GetName<&THttpRequest::Accept>() { return "Accept"; } -template <> TStringBuf THttpRequest::GetName<&THttpRequest::Connection>() { return "Connection"; } -template <> TStringBuf THttpRequest::GetName<&THttpRequest::ContentType>() { return "Content-Type"; } -template <> TStringBuf THttpRequest::GetName<&THttpRequest::ContentLength>() { return "Content-Length"; } -template <> TStringBuf THttpRequest::GetName<&THttpRequest::TransferEncoding>() { return "Transfer-Encoding"; } - -const TMap<TStringBuf, TStringBuf THttpRequest::*, TLessNoCase> THttpRequest::HeadersLocation = { - { THttpRequest::GetName<&THttpRequest::Host>(), &THttpRequest::Host }, - { THttpRequest::GetName<&THttpRequest::Accept>(), &THttpRequest::Accept }, - { THttpRequest::GetName<&THttpRequest::Connection>(), &THttpRequest::Connection }, - { THttpRequest::GetName<&THttpRequest::ContentType>(), &THttpRequest::ContentType }, - { THttpRequest::GetName<&THttpRequest::ContentLength>(), &THttpRequest::ContentLength }, - { THttpRequest::GetName<&THttpRequest::TransferEncoding>(), &THttpRequest::TransferEncoding }, -}; - -template <> TStringBuf THttpResponse::GetName<&THttpResponse::Connection>() { return "Connection"; } -template <> TStringBuf THttpResponse::GetName<&THttpResponse::ContentType>() { return "Content-Type"; } -template <> TStringBuf THttpResponse::GetName<&THttpResponse::ContentLength>() { return "Content-Length"; } -template <> TStringBuf THttpResponse::GetName<&THttpResponse::TransferEncoding>() { return "Transfer-Encoding"; } -template <> TStringBuf THttpResponse::GetName<&THttpResponse::LastModified>() { return "Last-Modified"; } + +inline TStringBuf operator +(TStringBuf l, TStringBuf r) { + if (l.empty()) { + return r; + } + if (r.empty()) { + return l; + } + if (l.end() == r.begin()) { + return TStringBuf(l.data(), l.size() + r.size()); + } + if (r.end() == l.begin()) { + return TStringBuf(r.data(), l.size() + r.size()); + } + Y_FAIL("oops"); + return TStringBuf(); +} + +inline TStringBuf operator +=(TStringBuf& l, TStringBuf r) { + return l = l + r; +} + +namespace NHttp { + +template <> TStringBuf THttpRequest::GetName<&THttpRequest::Host>() { return "Host"; } +template <> TStringBuf THttpRequest::GetName<&THttpRequest::Accept>() { return "Accept"; } +template <> TStringBuf THttpRequest::GetName<&THttpRequest::Connection>() { return "Connection"; } +template <> TStringBuf THttpRequest::GetName<&THttpRequest::ContentType>() { return "Content-Type"; } +template <> TStringBuf THttpRequest::GetName<&THttpRequest::ContentLength>() { return "Content-Length"; } +template <> TStringBuf THttpRequest::GetName<&THttpRequest::TransferEncoding>() { return "Transfer-Encoding"; } + +const TMap<TStringBuf, TStringBuf THttpRequest::*, TLessNoCase> THttpRequest::HeadersLocation = { + { THttpRequest::GetName<&THttpRequest::Host>(), &THttpRequest::Host }, + { THttpRequest::GetName<&THttpRequest::Accept>(), &THttpRequest::Accept }, + { THttpRequest::GetName<&THttpRequest::Connection>(), &THttpRequest::Connection }, + { THttpRequest::GetName<&THttpRequest::ContentType>(), &THttpRequest::ContentType }, + { THttpRequest::GetName<&THttpRequest::ContentLength>(), &THttpRequest::ContentLength }, + { THttpRequest::GetName<&THttpRequest::TransferEncoding>(), &THttpRequest::TransferEncoding }, +}; + +template <> TStringBuf THttpResponse::GetName<&THttpResponse::Connection>() { return "Connection"; } +template <> TStringBuf THttpResponse::GetName<&THttpResponse::ContentType>() { return "Content-Type"; } +template <> TStringBuf THttpResponse::GetName<&THttpResponse::ContentLength>() { return "Content-Length"; } +template <> TStringBuf THttpResponse::GetName<&THttpResponse::TransferEncoding>() { return "Transfer-Encoding"; } +template <> TStringBuf THttpResponse::GetName<&THttpResponse::LastModified>() { return "Last-Modified"; } template <> TStringBuf THttpResponse::GetName<&THttpResponse::ContentEncoding>() { return "Content-Encoding"; } - -const TMap<TStringBuf, TStringBuf THttpResponse::*, TLessNoCase> THttpResponse::HeadersLocation = { - { THttpResponse::GetName<&THttpResponse::Connection>(), &THttpResponse::Connection }, - { THttpResponse::GetName<&THttpResponse::ContentType>(), &THttpResponse::ContentType }, - { THttpResponse::GetName<&THttpResponse::ContentLength>(), &THttpResponse::ContentLength }, - { THttpResponse::GetName<&THttpResponse::TransferEncoding>(), &THttpResponse::TransferEncoding }, - { THttpResponse::GetName<&THttpResponse::LastModified>(), &THttpResponse::LastModified }, + +const TMap<TStringBuf, TStringBuf THttpResponse::*, TLessNoCase> THttpResponse::HeadersLocation = { + { THttpResponse::GetName<&THttpResponse::Connection>(), &THttpResponse::Connection }, + { THttpResponse::GetName<&THttpResponse::ContentType>(), &THttpResponse::ContentType }, + { THttpResponse::GetName<&THttpResponse::ContentLength>(), &THttpResponse::ContentLength }, + { THttpResponse::GetName<&THttpResponse::TransferEncoding>(), &THttpResponse::TransferEncoding }, + { THttpResponse::GetName<&THttpResponse::LastModified>(), &THttpResponse::LastModified }, { THttpResponse::GetName<&THttpResponse::ContentEncoding>(), &THttpResponse::ContentEncoding } -}; - -void THttpRequest::Clear() { - // a dirty little trick - this->~THttpRequest(); // basically, do nothing - new (this) THttpRequest(); // reset all fields -} - -template <> -void THttpParser<THttpRequest, TSocketBuffer>::Advance(size_t len) { - TStringBuf data(Pos(), len); - while (!data.empty()) { - if (Stage != EParseStage::Error) { - LastSuccessStage = Stage; - } - switch (Stage) { - case EParseStage::Method: { - if (ProcessData(Method, data, ' ', MaxMethodSize)) { - Stage = EParseStage::URL; - } - break; - } - case EParseStage::URL: { - if (ProcessData(URL, data, ' ', MaxURLSize)) { - Stage = EParseStage::Protocol; - } - break; - } - case EParseStage::Protocol: { - if (ProcessData(Protocol, data, '/', MaxProtocolSize)) { - Stage = EParseStage::Version; - } - break; - } - case EParseStage::Version: { - if (ProcessData(Version, data, "\r\n", MaxVersionSize)) { - Stage = EParseStage::Header; - Headers = data; - } - break; - } - 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 { - Stage = EParseStage::Done; - } - } else { - ProcessHeader(Header); - } - } - break; - } - case EParseStage::Body: { +}; + +void THttpRequest::Clear() { + // a dirty little trick + this->~THttpRequest(); // basically, do nothing + new (this) THttpRequest(); // reset all fields +} + +template <> +void THttpParser<THttpRequest, TSocketBuffer>::Advance(size_t len) { + TStringBuf data(Pos(), len); + while (!data.empty()) { + if (Stage != EParseStage::Error) { + LastSuccessStage = Stage; + } + switch (Stage) { + case EParseStage::Method: { + if (ProcessData(Method, data, ' ', MaxMethodSize)) { + Stage = EParseStage::URL; + } + break; + } + case EParseStage::URL: { + if (ProcessData(URL, data, ' ', MaxURLSize)) { + Stage = EParseStage::Protocol; + } + break; + } + case EParseStage::Protocol: { + if (ProcessData(Protocol, data, '/', MaxProtocolSize)) { + Stage = EParseStage::Version; + } + break; + } + case EParseStage::Version: { + if (ProcessData(Version, data, "\r\n", MaxVersionSize)) { + Stage = EParseStage::Header; + Headers = data; + } + break; + } + 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 { + Stage = EParseStage::Done; + } + } else { + ProcessHeader(Header); + } + } + break; + } + case EParseStage::Body: { if (!ContentLength.empty()) { if (ProcessData(Content, data, FromString(ContentLength))) { Body = Content; @@ -121,9 +121,9 @@ void THttpParser<THttpRequest, TSocketBuffer>::Advance(size_t len) { } else { // Invalid body encoding Stage = EParseStage::Error; - } - break; - } + } + break; + } case EParseStage::ChunkLength: { if (ProcessData(Line, data, "\r\n", MaxChunkLengthSize)) { if (!Line.empty()) { @@ -170,484 +170,484 @@ void THttpParser<THttpRequest, TSocketBuffer>::Advance(size_t len) { break; } - case EParseStage::Done: - case EParseStage::Error: { - data.Clear(); - break; - } - default: - Y_FAIL("Invalid processing sequence"); - break; - } - } - TSocketBuffer::Advance(len); -} - -template <> -THttpParser<THttpRequest, TSocketBuffer>::EParseStage THttpParser<THttpRequest, TSocketBuffer>::GetInitialStage() { - return EParseStage::Method; -} - -template <> -THttpParser<THttpResponse, TSocketBuffer>::EParseStage THttpParser<THttpResponse, TSocketBuffer>::GetInitialStage() { - return EParseStage::Protocol; -} - -void THttpResponse::Clear() { - // a dirty little trick - this->~THttpResponse(); // basically, do nothing - new (this) THttpResponse(); // reset all fields -} - -template <> -void THttpParser<THttpResponse, TSocketBuffer>::Advance(size_t len) { - TStringBuf data(Pos(), len); - while (!data.empty()) { - if (Stage != EParseStage::Error) { - LastSuccessStage = Stage; - } - switch (Stage) { - case EParseStage::Protocol: { - if (ProcessData(Protocol, data, '/', MaxProtocolSize)) { - Stage = EParseStage::Version; - } - break; - } - case EParseStage::Version: { - if (ProcessData(Version, data, ' ', MaxVersionSize)) { - Stage = EParseStage::Status; - } - break; - } - case EParseStage::Status: { - if (ProcessData(Status, data, ' ', MaxStatusSize)) { - Stage = EParseStage::Message; - } - break; - } - case EParseStage::Message: { - if (ProcessData(Message, data, "\r\n", MaxMessageSize)) { - Stage = EParseStage::Header; - Headers = TStringBuf(data.data(), size_t(0)); - } - break; - } - case EParseStage::Header: { - if (ProcessData(Header, data, "\r\n", MaxHeaderSize)) { - if (Header.empty()) { - if (HaveBody() && (ContentLength.empty() || ContentLength != "0")) { - Stage = EParseStage::Body; - } else { - Stage = EParseStage::Done; - } - } else { - ProcessHeader(Header); - } - Headers = TStringBuf(Headers.data(), data.data() - Headers.data()); - } - break; - } - case EParseStage::Body: { - if (!ContentLength.empty()) { - if (ProcessData(Body, data, FromString(ContentLength))) { - Stage = EParseStage::Done; - } - } else if (TransferEncoding == "chunked") { - Stage = EParseStage::ChunkLength; - } else { - // Invalid body encoding - Stage = EParseStage::Error; - } - break; - } - case EParseStage::ChunkLength: { - if (ProcessData(Line, data, "\r\n", MaxChunkLengthSize)) { - if (!Line.empty()) { - ChunkLength = ParseHex(Line); - if (ChunkLength <= MaxChunkSize) { - ContentSize = Content.size() + ChunkLength; - if (ContentSize <= MaxChunkContentSize) { - Stage = EParseStage::ChunkData; - Line.Clear(); - } else { - // Invalid chunk content length - Stage = EParseStage::Error; - } - } else { - // Invalid chunk length - Stage = EParseStage::Error; - } - } else { - // Invalid body encoding - Stage = EParseStage::Error; - } - } - break; - } - case EParseStage::ChunkData: { - if (!IsError()) { - if (ProcessData(Content, data, ContentSize)) { - if (ProcessData(Line, data, 2)) { - if (Line == "\r\n") { - if (ChunkLength == 0) { - Body = Content; - Stage = EParseStage::Done; - } else { - Stage = EParseStage::ChunkLength; - } - Line.Clear(); - } else { - // Invalid body encoding - Stage = EParseStage::Error; - } - } - } - } - break; - } - case EParseStage::Done: - case EParseStage::Error: - data.Clear(); - break; - default: - // Invalid processing sequence - Stage = EParseStage::Error; - break; - } - } - TSocketBuffer::Advance(len); -} - -template <> -void THttpParser<THttpResponse, TSocketBuffer>::ConnectionClosed() { - if (Stage == EParseStage::Done) { - return; - } - if (Stage == EParseStage::Body) { - // ? - Stage = EParseStage::Done; - } else { - LastSuccessStage = Stage; - Stage = EParseStage::Error; - } -} - -THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseString(TStringBuf data) { - THttpOutgoingResponsePtr response = new THttpOutgoingResponse(this); - response->Append(data); - response->Reparse(); - return response; -} - -THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseOK(TStringBuf body, TStringBuf contentType, TInstant lastModified) { - return CreateResponse("200", "OK", contentType, body, lastModified); -} - -THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseBadRequest(TStringBuf html, TStringBuf contentType) { - if (html.empty() && IsError()) { - contentType = "text/plain"; - html = GetErrorText(); - } - return CreateResponse("400", "Bad Request", contentType, html); -} - -THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseNotFound(TStringBuf html, TStringBuf contentType) { - return CreateResponse("404", "Not Found", contentType, html); -} - -THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseServiceUnavailable(TStringBuf html, TStringBuf contentType) { - return CreateResponse("503", "Service Unavailable", contentType, html); -} - -THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseGatewayTimeout(TStringBuf html, TStringBuf contentType) { - return CreateResponse("504", "Gateway Timeout", contentType, html); -} - -THttpIncomingResponse::THttpIncomingResponse(THttpOutgoingRequestPtr request) - : Request(request) -{} - -THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponse(TStringBuf status, TStringBuf message, TStringBuf contentType, TStringBuf body, TInstant lastModified) { - TStringBuf version = Version; - if (version != "1.0" && version != "1.1") { - version = "1.1"; - } - THttpOutgoingResponsePtr response = new THttpOutgoingResponse(this, "HTTP", version, status, message); - response->Set<&THttpResponse::Connection>(GetConnection()); - if (!WorkerName.empty()) { - response->Set("X-Worker-Name", WorkerName); - } - if (!contentType.empty() && !body.empty()) { - response->Set<&THttpResponse::ContentType>(contentType); - } - if (lastModified) { - response->Set<&THttpResponse::LastModified>(lastModified.FormatGmTime("%a, %d %b %Y %H:%M:%S GMT")); - } - if (response->IsNeedBody() || !body.empty()) { - if (Method == "HEAD") { - response->Set<&THttpResponse::ContentLength>(ToString(body.size())); - } else { - response->Set<&THttpResponse::Body>(body); - } - } - return response; -} - -THttpIncomingRequestPtr THttpIncomingRequest::Duplicate() { - THttpIncomingRequestPtr request = new THttpIncomingRequest(*this); - request->Reparse(); - request->Timer.Reset(); - return request; -} - -THttpIncomingResponsePtr THttpIncomingResponse::Duplicate(THttpOutgoingRequestPtr request) { - THttpIncomingResponsePtr response = new THttpIncomingResponse(*this); - response->Reparse(); - response->Request = request; - return response; -} - -THttpOutgoingResponsePtr THttpOutgoingResponse::Duplicate(THttpIncomingRequestPtr request) { - THttpOutgoingResponsePtr response = new THttpOutgoingResponse(*this); - response->Reparse(); - response->Request = request; - return response; -} - - -THttpOutgoingResponsePtr THttpIncomingResponse::Reverse(THttpIncomingRequestPtr request) { - THttpOutgoingResponsePtr response = new THttpOutgoingResponse(request); - response->Assign(Data(), Size()); - response->Reparse(); - return response; -} - -THttpOutgoingRequest::THttpOutgoingRequest(TStringBuf method, TStringBuf scheme, TStringBuf host, TStringBuf uri, TStringBuf protocol, TStringBuf version) { - Secure = (scheme == "https"); - TString urie = UrlEscapeRet(uri); - InitRequest(method, urie, protocol, version); - if (host) { - Set<&THttpRequest::Host>(host); - } -} - -THttpOutgoingRequest::THttpOutgoingRequest(TStringBuf method, TStringBuf url, TStringBuf protocol, TStringBuf version) { - TStringBuf scheme, host, uri; - if (!CrackURL(url, scheme, host, uri)) { - Y_FAIL("Invalid URL specified"); - } - if (!scheme.empty() && scheme != "http" && scheme != "https") { - Y_FAIL("Invalid URL specified"); - } - Secure = (scheme == "https"); - TString urie = UrlEscapeRet(uri); - InitRequest(method, urie, protocol, version); - if (host) { - Set<&THttpRequest::Host>(host); - } -} - -THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequestString(const TString& data) { - THttpOutgoingRequestPtr request = new THttpOutgoingRequest(); - request->Assign(data.data(), data.size()); - request->Reparse(); - return request; -} - -THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequestGet(TStringBuf url) { - return CreateRequest("GET", url); -} - -THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequestGet(TStringBuf host, TStringBuf uri) { - return CreateHttpRequest("GET", host, uri); -} - -THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequestPost(TStringBuf url, TStringBuf contentType, TStringBuf body) { - return CreateRequest("POST", url, contentType, body); -} - -THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequestPost(TStringBuf host, TStringBuf uri, TStringBuf contentType, TStringBuf body) { - return CreateHttpRequest("POST", host, uri, contentType, body); -} - -THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequest(TStringBuf method, TStringBuf url, TStringBuf contentType, TStringBuf body) { - THttpOutgoingRequestPtr request = new THttpOutgoingRequest(method, url, "HTTP", "1.1"); - request->Set<&THttpRequest::Accept>("*/*"); - if (!contentType.empty()) { - request->Set<&THttpRequest::ContentType>(contentType); - request->Set<&THttpRequest::Body>(body); - } - return request; -} - -THttpOutgoingRequestPtr THttpOutgoingRequest::CreateHttpRequest(TStringBuf method, TStringBuf host, TStringBuf uri, TStringBuf contentType, TStringBuf body) { - THttpOutgoingRequestPtr request = new THttpOutgoingRequest(method, "http", host, uri, "HTTP", "1.1"); - request->Set<&THttpRequest::Accept>("*/*"); - if (!contentType.empty()) { - request->Set<&THttpRequest::ContentType>(contentType); - request->Set<&THttpRequest::Body>(body); - } - return request; -} - -THttpOutgoingRequestPtr THttpOutgoingRequest::Duplicate() { - THttpOutgoingRequestPtr request = new THttpOutgoingRequest(*this); - request->Reparse(); - return request; -} - -THttpOutgoingResponse::THttpOutgoingResponse(THttpIncomingRequestPtr request) - : Request(request) -{} - -THttpOutgoingResponse::THttpOutgoingResponse(THttpIncomingRequestPtr request, TStringBuf protocol, TStringBuf version, TStringBuf status, TStringBuf message) - : Request(request) -{ - InitResponse(protocol, version, status, message); -} - -const size_t THttpConfig::BUFFER_MIN_STEP; -const TDuration THttpConfig::CONNECTION_TIMEOUT; - -TUrlParameters::TUrlParameters(TStringBuf url) { - TStringBuf base; - TStringBuf params; - if (url.TrySplit('?', base, params)) { - for (TStringBuf param = params.NextTok('&'); !param.empty(); param = params.NextTok('&')) { - TStringBuf name = param.NextTok('='); - Parameters[name] = param; - } - } -} - -TString TUrlParameters::operator [](TStringBuf name) const { - TString value(Get(name)); - CGIUnescape(value); - return value; -} - -bool TUrlParameters::Has(TStringBuf name) const { - return Parameters.count(name) != 0; -} - -TStringBuf TUrlParameters::Get(TStringBuf name) const { - auto it = Parameters.find(name); - if (it != Parameters.end()) { - return it->second; - } - return TStringBuf(); -} - -TString TUrlParameters::Render() const { - TStringBuilder parameters; - for (const std::pair<TStringBuf, TStringBuf> parameter : Parameters) { - if (parameters.empty()) { - parameters << '?'; - } else { - parameters << '&'; - } - parameters << parameter.first; - parameters << '='; - parameters << parameter.second; - } - return parameters; -} - -TCookies::TCookies(TStringBuf cookie) { - for (TStringBuf param = cookie.NextTok(';'); !param.empty(); param = cookie.NextTok(';')) { - param.SkipPrefix(" "); - TStringBuf name = param.NextTok('='); - Cookies[name] = param; - } -} - -TStringBuf TCookies::operator [](TStringBuf name) const { - return Get(name); -} - -bool TCookies::Has(TStringBuf name) const { - return Cookies.count(name) != 0; -} - -TStringBuf TCookies::Get(TStringBuf name) const { - auto it = Cookies.find(name); - if (it != Cookies.end()) { - return it->second; - } - return TStringBuf(); -} - -TString TCookies::Render() const { - TStringBuilder cookies; - for (const std::pair<TStringBuf, TStringBuf> cookie : Cookies) { - if (!cookies.empty()) { - cookies << ' '; - } - cookies << cookie.first; - cookies << '='; - cookies << cookie.second; - cookies << ';'; - } - return cookies; -} - -TCookiesBuilder::TCookiesBuilder() - :TCookies(TStringBuf()) -{} - -void TCookiesBuilder::Set(TStringBuf name, TStringBuf data) { - Data.emplace_back(name, data); - Cookies[Data.back().first] = Data.back().second; -} - -THeaders::THeaders(TStringBuf headers) { - for (TStringBuf param = headers.NextTok("\r\n"); !param.empty(); param = headers.NextTok("\r\n")) { - TStringBuf name = param.NextTok(":"); - param.SkipPrefix(" "); - Headers[name] = param; - } -} - -TStringBuf THeaders::operator [](TStringBuf name) const { - return Get(name); -} - -bool THeaders::Has(TStringBuf name) const { - return Headers.count(name) != 0; -} - -TStringBuf THeaders::Get(TStringBuf name) const { - auto it = Headers.find(name); - if (it != Headers.end()) { - return it->second; - } - return TStringBuf(); -} - -TString THeaders::Render() const { - TStringBuilder headers; - for (const std::pair<TStringBuf, TStringBuf> header : Headers) { - headers << header.first; - headers << ": "; - headers << header.second; - headers << "\r\n"; - } - return headers; -} - -THeadersBuilder::THeadersBuilder() - :THeaders(TStringBuf()) -{} - -THeadersBuilder::THeadersBuilder(const THeadersBuilder& builder) { - for (const auto& pr : builder.Headers) { - Set(pr.first, pr.second); - } -} - -void THeadersBuilder::Set(TStringBuf name, TStringBuf data) { - Data.emplace_back(name, data); - Headers[Data.back().first] = Data.back().second; -} - -} + case EParseStage::Done: + case EParseStage::Error: { + data.Clear(); + break; + } + default: + Y_FAIL("Invalid processing sequence"); + break; + } + } + TSocketBuffer::Advance(len); +} + +template <> +THttpParser<THttpRequest, TSocketBuffer>::EParseStage THttpParser<THttpRequest, TSocketBuffer>::GetInitialStage() { + return EParseStage::Method; +} + +template <> +THttpParser<THttpResponse, TSocketBuffer>::EParseStage THttpParser<THttpResponse, TSocketBuffer>::GetInitialStage() { + return EParseStage::Protocol; +} + +void THttpResponse::Clear() { + // a dirty little trick + this->~THttpResponse(); // basically, do nothing + new (this) THttpResponse(); // reset all fields +} + +template <> +void THttpParser<THttpResponse, TSocketBuffer>::Advance(size_t len) { + TStringBuf data(Pos(), len); + while (!data.empty()) { + if (Stage != EParseStage::Error) { + LastSuccessStage = Stage; + } + switch (Stage) { + case EParseStage::Protocol: { + if (ProcessData(Protocol, data, '/', MaxProtocolSize)) { + Stage = EParseStage::Version; + } + break; + } + case EParseStage::Version: { + if (ProcessData(Version, data, ' ', MaxVersionSize)) { + Stage = EParseStage::Status; + } + break; + } + case EParseStage::Status: { + if (ProcessData(Status, data, ' ', MaxStatusSize)) { + Stage = EParseStage::Message; + } + break; + } + case EParseStage::Message: { + if (ProcessData(Message, data, "\r\n", MaxMessageSize)) { + Stage = EParseStage::Header; + Headers = TStringBuf(data.data(), size_t(0)); + } + break; + } + case EParseStage::Header: { + if (ProcessData(Header, data, "\r\n", MaxHeaderSize)) { + if (Header.empty()) { + if (HaveBody() && (ContentLength.empty() || ContentLength != "0")) { + Stage = EParseStage::Body; + } else { + Stage = EParseStage::Done; + } + } else { + ProcessHeader(Header); + } + Headers = TStringBuf(Headers.data(), data.data() - Headers.data()); + } + break; + } + case EParseStage::Body: { + if (!ContentLength.empty()) { + if (ProcessData(Body, data, FromString(ContentLength))) { + Stage = EParseStage::Done; + } + } else if (TransferEncoding == "chunked") { + Stage = EParseStage::ChunkLength; + } else { + // Invalid body encoding + Stage = EParseStage::Error; + } + break; + } + case EParseStage::ChunkLength: { + if (ProcessData(Line, data, "\r\n", MaxChunkLengthSize)) { + if (!Line.empty()) { + ChunkLength = ParseHex(Line); + if (ChunkLength <= MaxChunkSize) { + ContentSize = Content.size() + ChunkLength; + if (ContentSize <= MaxChunkContentSize) { + Stage = EParseStage::ChunkData; + Line.Clear(); + } else { + // Invalid chunk content length + Stage = EParseStage::Error; + } + } else { + // Invalid chunk length + Stage = EParseStage::Error; + } + } else { + // Invalid body encoding + Stage = EParseStage::Error; + } + } + break; + } + case EParseStage::ChunkData: { + if (!IsError()) { + if (ProcessData(Content, data, ContentSize)) { + if (ProcessData(Line, data, 2)) { + if (Line == "\r\n") { + if (ChunkLength == 0) { + Body = Content; + Stage = EParseStage::Done; + } else { + Stage = EParseStage::ChunkLength; + } + Line.Clear(); + } else { + // Invalid body encoding + Stage = EParseStage::Error; + } + } + } + } + break; + } + case EParseStage::Done: + case EParseStage::Error: + data.Clear(); + break; + default: + // Invalid processing sequence + Stage = EParseStage::Error; + break; + } + } + TSocketBuffer::Advance(len); +} + +template <> +void THttpParser<THttpResponse, TSocketBuffer>::ConnectionClosed() { + if (Stage == EParseStage::Done) { + return; + } + if (Stage == EParseStage::Body) { + // ? + Stage = EParseStage::Done; + } else { + LastSuccessStage = Stage; + Stage = EParseStage::Error; + } +} + +THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseString(TStringBuf data) { + THttpOutgoingResponsePtr response = new THttpOutgoingResponse(this); + response->Append(data); + response->Reparse(); + return response; +} + +THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseOK(TStringBuf body, TStringBuf contentType, TInstant lastModified) { + return CreateResponse("200", "OK", contentType, body, lastModified); +} + +THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseBadRequest(TStringBuf html, TStringBuf contentType) { + if (html.empty() && IsError()) { + contentType = "text/plain"; + html = GetErrorText(); + } + return CreateResponse("400", "Bad Request", contentType, html); +} + +THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseNotFound(TStringBuf html, TStringBuf contentType) { + return CreateResponse("404", "Not Found", contentType, html); +} + +THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseServiceUnavailable(TStringBuf html, TStringBuf contentType) { + return CreateResponse("503", "Service Unavailable", contentType, html); +} + +THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponseGatewayTimeout(TStringBuf html, TStringBuf contentType) { + return CreateResponse("504", "Gateway Timeout", contentType, html); +} + +THttpIncomingResponse::THttpIncomingResponse(THttpOutgoingRequestPtr request) + : Request(request) +{} + +THttpOutgoingResponsePtr THttpIncomingRequest::CreateResponse(TStringBuf status, TStringBuf message, TStringBuf contentType, TStringBuf body, TInstant lastModified) { + TStringBuf version = Version; + if (version != "1.0" && version != "1.1") { + version = "1.1"; + } + THttpOutgoingResponsePtr response = new THttpOutgoingResponse(this, "HTTP", version, status, message); + response->Set<&THttpResponse::Connection>(GetConnection()); + if (!WorkerName.empty()) { + response->Set("X-Worker-Name", WorkerName); + } + if (!contentType.empty() && !body.empty()) { + response->Set<&THttpResponse::ContentType>(contentType); + } + if (lastModified) { + response->Set<&THttpResponse::LastModified>(lastModified.FormatGmTime("%a, %d %b %Y %H:%M:%S GMT")); + } + if (response->IsNeedBody() || !body.empty()) { + if (Method == "HEAD") { + response->Set<&THttpResponse::ContentLength>(ToString(body.size())); + } else { + response->Set<&THttpResponse::Body>(body); + } + } + return response; +} + +THttpIncomingRequestPtr THttpIncomingRequest::Duplicate() { + THttpIncomingRequestPtr request = new THttpIncomingRequest(*this); + request->Reparse(); + request->Timer.Reset(); + return request; +} + +THttpIncomingResponsePtr THttpIncomingResponse::Duplicate(THttpOutgoingRequestPtr request) { + THttpIncomingResponsePtr response = new THttpIncomingResponse(*this); + response->Reparse(); + response->Request = request; + return response; +} + +THttpOutgoingResponsePtr THttpOutgoingResponse::Duplicate(THttpIncomingRequestPtr request) { + THttpOutgoingResponsePtr response = new THttpOutgoingResponse(*this); + response->Reparse(); + response->Request = request; + return response; +} + + +THttpOutgoingResponsePtr THttpIncomingResponse::Reverse(THttpIncomingRequestPtr request) { + THttpOutgoingResponsePtr response = new THttpOutgoingResponse(request); + response->Assign(Data(), Size()); + response->Reparse(); + return response; +} + +THttpOutgoingRequest::THttpOutgoingRequest(TStringBuf method, TStringBuf scheme, TStringBuf host, TStringBuf uri, TStringBuf protocol, TStringBuf version) { + Secure = (scheme == "https"); + TString urie = UrlEscapeRet(uri); + InitRequest(method, urie, protocol, version); + if (host) { + Set<&THttpRequest::Host>(host); + } +} + +THttpOutgoingRequest::THttpOutgoingRequest(TStringBuf method, TStringBuf url, TStringBuf protocol, TStringBuf version) { + TStringBuf scheme, host, uri; + if (!CrackURL(url, scheme, host, uri)) { + Y_FAIL("Invalid URL specified"); + } + if (!scheme.empty() && scheme != "http" && scheme != "https") { + Y_FAIL("Invalid URL specified"); + } + Secure = (scheme == "https"); + TString urie = UrlEscapeRet(uri); + InitRequest(method, urie, protocol, version); + if (host) { + Set<&THttpRequest::Host>(host); + } +} + +THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequestString(const TString& data) { + THttpOutgoingRequestPtr request = new THttpOutgoingRequest(); + request->Assign(data.data(), data.size()); + request->Reparse(); + return request; +} + +THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequestGet(TStringBuf url) { + return CreateRequest("GET", url); +} + +THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequestGet(TStringBuf host, TStringBuf uri) { + return CreateHttpRequest("GET", host, uri); +} + +THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequestPost(TStringBuf url, TStringBuf contentType, TStringBuf body) { + return CreateRequest("POST", url, contentType, body); +} + +THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequestPost(TStringBuf host, TStringBuf uri, TStringBuf contentType, TStringBuf body) { + return CreateHttpRequest("POST", host, uri, contentType, body); +} + +THttpOutgoingRequestPtr THttpOutgoingRequest::CreateRequest(TStringBuf method, TStringBuf url, TStringBuf contentType, TStringBuf body) { + THttpOutgoingRequestPtr request = new THttpOutgoingRequest(method, url, "HTTP", "1.1"); + request->Set<&THttpRequest::Accept>("*/*"); + if (!contentType.empty()) { + request->Set<&THttpRequest::ContentType>(contentType); + request->Set<&THttpRequest::Body>(body); + } + return request; +} + +THttpOutgoingRequestPtr THttpOutgoingRequest::CreateHttpRequest(TStringBuf method, TStringBuf host, TStringBuf uri, TStringBuf contentType, TStringBuf body) { + THttpOutgoingRequestPtr request = new THttpOutgoingRequest(method, "http", host, uri, "HTTP", "1.1"); + request->Set<&THttpRequest::Accept>("*/*"); + if (!contentType.empty()) { + request->Set<&THttpRequest::ContentType>(contentType); + request->Set<&THttpRequest::Body>(body); + } + return request; +} + +THttpOutgoingRequestPtr THttpOutgoingRequest::Duplicate() { + THttpOutgoingRequestPtr request = new THttpOutgoingRequest(*this); + request->Reparse(); + return request; +} + +THttpOutgoingResponse::THttpOutgoingResponse(THttpIncomingRequestPtr request) + : Request(request) +{} + +THttpOutgoingResponse::THttpOutgoingResponse(THttpIncomingRequestPtr request, TStringBuf protocol, TStringBuf version, TStringBuf status, TStringBuf message) + : Request(request) +{ + InitResponse(protocol, version, status, message); +} + +const size_t THttpConfig::BUFFER_MIN_STEP; +const TDuration THttpConfig::CONNECTION_TIMEOUT; + +TUrlParameters::TUrlParameters(TStringBuf url) { + TStringBuf base; + TStringBuf params; + if (url.TrySplit('?', base, params)) { + for (TStringBuf param = params.NextTok('&'); !param.empty(); param = params.NextTok('&')) { + TStringBuf name = param.NextTok('='); + Parameters[name] = param; + } + } +} + +TString TUrlParameters::operator [](TStringBuf name) const { + TString value(Get(name)); + CGIUnescape(value); + return value; +} + +bool TUrlParameters::Has(TStringBuf name) const { + return Parameters.count(name) != 0; +} + +TStringBuf TUrlParameters::Get(TStringBuf name) const { + auto it = Parameters.find(name); + if (it != Parameters.end()) { + return it->second; + } + return TStringBuf(); +} + +TString TUrlParameters::Render() const { + TStringBuilder parameters; + for (const std::pair<TStringBuf, TStringBuf> parameter : Parameters) { + if (parameters.empty()) { + parameters << '?'; + } else { + parameters << '&'; + } + parameters << parameter.first; + parameters << '='; + parameters << parameter.second; + } + return parameters; +} + +TCookies::TCookies(TStringBuf cookie) { + for (TStringBuf param = cookie.NextTok(';'); !param.empty(); param = cookie.NextTok(';')) { + param.SkipPrefix(" "); + TStringBuf name = param.NextTok('='); + Cookies[name] = param; + } +} + +TStringBuf TCookies::operator [](TStringBuf name) const { + return Get(name); +} + +bool TCookies::Has(TStringBuf name) const { + return Cookies.count(name) != 0; +} + +TStringBuf TCookies::Get(TStringBuf name) const { + auto it = Cookies.find(name); + if (it != Cookies.end()) { + return it->second; + } + return TStringBuf(); +} + +TString TCookies::Render() const { + TStringBuilder cookies; + for (const std::pair<TStringBuf, TStringBuf> cookie : Cookies) { + if (!cookies.empty()) { + cookies << ' '; + } + cookies << cookie.first; + cookies << '='; + cookies << cookie.second; + cookies << ';'; + } + return cookies; +} + +TCookiesBuilder::TCookiesBuilder() + :TCookies(TStringBuf()) +{} + +void TCookiesBuilder::Set(TStringBuf name, TStringBuf data) { + Data.emplace_back(name, data); + Cookies[Data.back().first] = Data.back().second; +} + +THeaders::THeaders(TStringBuf headers) { + for (TStringBuf param = headers.NextTok("\r\n"); !param.empty(); param = headers.NextTok("\r\n")) { + TStringBuf name = param.NextTok(":"); + param.SkipPrefix(" "); + Headers[name] = param; + } +} + +TStringBuf THeaders::operator [](TStringBuf name) const { + return Get(name); +} + +bool THeaders::Has(TStringBuf name) const { + return Headers.count(name) != 0; +} + +TStringBuf THeaders::Get(TStringBuf name) const { + auto it = Headers.find(name); + if (it != Headers.end()) { + return it->second; + } + return TStringBuf(); +} + +TString THeaders::Render() const { + TStringBuilder headers; + for (const std::pair<TStringBuf, TStringBuf> header : Headers) { + headers << header.first; + headers << ": "; + headers << header.second; + headers << "\r\n"; + } + return headers; +} + +THeadersBuilder::THeadersBuilder() + :THeaders(TStringBuf()) +{} + +THeadersBuilder::THeadersBuilder(const THeadersBuilder& builder) { + for (const auto& pr : builder.Headers) { + Set(pr.first, pr.second); + } +} + +void THeadersBuilder::Set(TStringBuf name, TStringBuf data) { + Data.emplace_back(name, data); + Headers[Data.back().first] = Data.back().second; +} + +} diff --git a/library/cpp/actors/http/http.h b/library/cpp/actors/http/http.h index 96c5c1ec48..a11d6158ee 100644 --- a/library/cpp/actors/http/http.h +++ b/library/cpp/actors/http/http.h @@ -1,703 +1,703 @@ -#pragma once -#include <util/datetime/base.h> -#include <util/string/builder.h> -#include <util/system/thread.h> -#include <util/system/hp_timer.h> -#include <util/generic/hash_set.h> -#include <util/generic/buffer.h> -#include <util/generic/intrlist.h> -#include "http_config.h" - -// TODO(xenoxeno): hide in implementation -template <typename Type> -struct THash<TIntrusivePtr<Type>> { - size_t operator ()(const TIntrusivePtr<Type>& ptr) const { return reinterpret_cast<size_t>(ptr.Get()); } -}; - -template<> -inline void Out<TSockAddrInet6>(IOutputStream& o, const TSockAddrInet6& x) { - o << x.ToString(); -} - -namespace NHttp { - -bool IsIPv6(const TString& host); -bool CrackURL(TStringBuf url, TStringBuf& scheme, TStringBuf& host, TStringBuf& uri); -void CrackAddress(const TString& address, TString& hostname, TIpPort& port); -void TrimBegin(TStringBuf& target, char delim); -void TrimEnd(TStringBuf& target, char delim); -void Trim(TStringBuf& target, char delim); -void TrimEnd(TString& target, char delim); - -struct TLessNoCase { - bool operator()(TStringBuf l, TStringBuf r) const { - auto ll = l.length(); - auto rl = r.length(); - if (ll != rl) { - return ll < rl; - } - return strnicmp(l.data(), r.data(), ll) < 0; - } -}; - -struct TUrlParameters { - THashMap<TStringBuf, TStringBuf> Parameters; - - TUrlParameters(TStringBuf url); - TString operator [](TStringBuf name) const; - bool Has(TStringBuf name) const; - TStringBuf Get(TStringBuf name) const; // raw - TString Render() const; -}; - -struct TCookies { - THashMap<TStringBuf, TStringBuf> Cookies; - - TCookies(TStringBuf cookie); - TCookies(const TCookies&) = delete; - TStringBuf operator [](TStringBuf name) const; - bool Has(TStringBuf name) const; - TStringBuf Get(TStringBuf name) const; // raw - TString Render() const; -}; - -struct TCookiesBuilder : TCookies { +#pragma once +#include <util/datetime/base.h> +#include <util/string/builder.h> +#include <util/system/thread.h> +#include <util/system/hp_timer.h> +#include <util/generic/hash_set.h> +#include <util/generic/buffer.h> +#include <util/generic/intrlist.h> +#include "http_config.h" + +// TODO(xenoxeno): hide in implementation +template <typename Type> +struct THash<TIntrusivePtr<Type>> { + size_t operator ()(const TIntrusivePtr<Type>& ptr) const { return reinterpret_cast<size_t>(ptr.Get()); } +}; + +template<> +inline void Out<TSockAddrInet6>(IOutputStream& o, const TSockAddrInet6& x) { + o << x.ToString(); +} + +namespace NHttp { + +bool IsIPv6(const TString& host); +bool CrackURL(TStringBuf url, TStringBuf& scheme, TStringBuf& host, TStringBuf& uri); +void CrackAddress(const TString& address, TString& hostname, TIpPort& port); +void TrimBegin(TStringBuf& target, char delim); +void TrimEnd(TStringBuf& target, char delim); +void Trim(TStringBuf& target, char delim); +void TrimEnd(TString& target, char delim); + +struct TLessNoCase { + bool operator()(TStringBuf l, TStringBuf r) const { + auto ll = l.length(); + auto rl = r.length(); + if (ll != rl) { + return ll < rl; + } + return strnicmp(l.data(), r.data(), ll) < 0; + } +}; + +struct TUrlParameters { + THashMap<TStringBuf, TStringBuf> Parameters; + + TUrlParameters(TStringBuf url); + TString operator [](TStringBuf name) const; + bool Has(TStringBuf name) const; + TStringBuf Get(TStringBuf name) const; // raw + TString Render() const; +}; + +struct TCookies { + THashMap<TStringBuf, TStringBuf> Cookies; + + TCookies(TStringBuf cookie); + TCookies(const TCookies&) = delete; + TStringBuf operator [](TStringBuf name) const; + bool Has(TStringBuf name) const; + TStringBuf Get(TStringBuf name) const; // raw + TString Render() const; +}; + +struct TCookiesBuilder : TCookies { TDeque<std::pair<TString, TString>> Data; - - TCookiesBuilder(); - void Set(TStringBuf name, TStringBuf data); -}; - -struct THeaders { - TMap<TStringBuf, TStringBuf, TLessNoCase> Headers; - - THeaders() = default; - THeaders(TStringBuf headers); - THeaders(const THeaders&) = delete; - TStringBuf operator [](TStringBuf name) const; - bool Has(TStringBuf name) const; - TStringBuf Get(TStringBuf name) const; // raw - TString Render() const; -}; - -struct THeadersBuilder : THeaders { - TDeque<std::pair<TString, TString>> Data; - - THeadersBuilder(); - THeadersBuilder(const THeadersBuilder& builder); - void Set(TStringBuf name, TStringBuf data); -}; - -class TSocketBuffer : public TBuffer, public THttpConfig { -public: - TSocketBuffer() - : TBuffer(BUFFER_SIZE) - {} - - bool EnsureEnoughSpaceAvailable(size_t need) { - size_t avail = Avail(); - if (avail < need) { - Reserve(Capacity() + std::max(need, BUFFER_MIN_STEP)); - return false; - } - return true; - } -}; - -class THttpRequest { -public: - TStringBuf Method; - TStringBuf URL; - TStringBuf Protocol; - TStringBuf Version; - TStringBuf Headers; - - TStringBuf Host; - TStringBuf Accept; - TStringBuf Connection; - TStringBuf ContentType; - TStringBuf ContentLength; - TStringBuf TransferEncoding; - - TStringBuf Body; - - static const TMap<TStringBuf, TStringBuf THttpRequest::*, TLessNoCase> HeadersLocation; - - template <TStringBuf THttpRequest::* Header> - static TStringBuf GetName(); - void Clear(); -}; - -class THttpResponse { -public: - TStringBuf Protocol; - TStringBuf Version; - TStringBuf Status; - TStringBuf Message; - TStringBuf Headers; - - TStringBuf Connection; - TStringBuf ContentType; - TStringBuf ContentLength; - TStringBuf TransferEncoding; - TStringBuf LastModified; + + TCookiesBuilder(); + void Set(TStringBuf name, TStringBuf data); +}; + +struct THeaders { + TMap<TStringBuf, TStringBuf, TLessNoCase> Headers; + + THeaders() = default; + THeaders(TStringBuf headers); + THeaders(const THeaders&) = delete; + TStringBuf operator [](TStringBuf name) const; + bool Has(TStringBuf name) const; + TStringBuf Get(TStringBuf name) const; // raw + TString Render() const; +}; + +struct THeadersBuilder : THeaders { + TDeque<std::pair<TString, TString>> Data; + + THeadersBuilder(); + THeadersBuilder(const THeadersBuilder& builder); + void Set(TStringBuf name, TStringBuf data); +}; + +class TSocketBuffer : public TBuffer, public THttpConfig { +public: + TSocketBuffer() + : TBuffer(BUFFER_SIZE) + {} + + bool EnsureEnoughSpaceAvailable(size_t need) { + size_t avail = Avail(); + if (avail < need) { + Reserve(Capacity() + std::max(need, BUFFER_MIN_STEP)); + return false; + } + return true; + } +}; + +class THttpRequest { +public: + TStringBuf Method; + TStringBuf URL; + TStringBuf Protocol; + TStringBuf Version; + TStringBuf Headers; + + TStringBuf Host; + TStringBuf Accept; + TStringBuf Connection; + TStringBuf ContentType; + TStringBuf ContentLength; + TStringBuf TransferEncoding; + + TStringBuf Body; + + static const TMap<TStringBuf, TStringBuf THttpRequest::*, TLessNoCase> HeadersLocation; + + template <TStringBuf THttpRequest::* Header> + static TStringBuf GetName(); + void Clear(); +}; + +class THttpResponse { +public: + TStringBuf Protocol; + TStringBuf Version; + TStringBuf Status; + TStringBuf Message; + TStringBuf Headers; + + TStringBuf Connection; + TStringBuf ContentType; + TStringBuf ContentLength; + TStringBuf TransferEncoding; + TStringBuf LastModified; TStringBuf ContentEncoding; - - TStringBuf Body; - - static const TMap<TStringBuf, TStringBuf THttpResponse::*, TLessNoCase> HeadersLocation; - - template <TStringBuf THttpResponse::* Header> - static TStringBuf GetName(); - void Clear(); -}; - -template <typename HeaderType, typename BufferType> -class THttpParser : public HeaderType, public BufferType { -public: - enum class EParseStage : ui8 { - Method, - URL, - Protocol, - Version, - Status, - Message, - Header, - Body, - ChunkLength, - ChunkData, - Done, - Error, - }; - - static constexpr size_t MaxMethodSize = 6; - static constexpr size_t MaxURLSize = 1024; - static constexpr size_t MaxProtocolSize = 4; - static constexpr size_t MaxVersionSize = 4; - static constexpr size_t MaxStatusSize = 3; - static constexpr size_t MaxMessageSize = 1024; + + TStringBuf Body; + + static const TMap<TStringBuf, TStringBuf THttpResponse::*, TLessNoCase> HeadersLocation; + + template <TStringBuf THttpResponse::* Header> + static TStringBuf GetName(); + void Clear(); +}; + +template <typename HeaderType, typename BufferType> +class THttpParser : public HeaderType, public BufferType { +public: + enum class EParseStage : ui8 { + Method, + URL, + Protocol, + Version, + Status, + Message, + Header, + Body, + ChunkLength, + ChunkData, + Done, + Error, + }; + + static constexpr size_t MaxMethodSize = 6; + static constexpr size_t MaxURLSize = 1024; + static constexpr size_t MaxProtocolSize = 4; + static constexpr size_t MaxVersionSize = 4; + static constexpr size_t MaxStatusSize = 3; + static constexpr size_t MaxMessageSize = 1024; static constexpr size_t MaxHeaderSize = 8192; - static constexpr size_t MaxChunkLengthSize = 8; - static constexpr size_t MaxChunkSize = 256 * 1024 * 1024; - static constexpr size_t MaxChunkContentSize = 1 * 1024 * 1024 * 1024; - - EParseStage Stage; - EParseStage LastSuccessStage; - TStringBuf Line; - TStringBuf& Header = Line; - size_t ChunkLength = 0; - size_t ContentSize = 0; - TString Content; - - THttpParser(const THttpParser& src) - : HeaderType(src) - , BufferType(src) - , Stage(src.Stage) - , LastSuccessStage(src.LastSuccessStage) - , Line() - , Header(Line) - , ChunkLength(src.ChunkLength) - , ContentSize(src.ContentSize) - , Content(src.Content) - {} - - template <typename StringType> - bool ProcessData(StringType& target, TStringBuf& source, char delim, size_t maxLen) { - TStringBuf maxSource(source.substr(0, maxLen + 1 - target.size())); - size_t pos = maxSource.find(delim); - target += maxSource.substr(0, pos); - source.Skip(pos); - if (target.size() > maxLen) { - Stage = EParseStage::Error; - return false; - } - if (!source.empty() && *source.begin() == delim) { - source.Skip(1); - } - return pos != TStringBuf::npos; - } - - template <typename StringType> - bool ProcessData(StringType& target, TStringBuf& source, TStringBuf delim, size_t maxLen) { - if (delim.empty()) { - return false; - } - if (delim.size() == 1) { - return ProcessData(target, source, delim[0], maxLen); - } + static constexpr size_t MaxChunkLengthSize = 8; + static constexpr size_t MaxChunkSize = 256 * 1024 * 1024; + static constexpr size_t MaxChunkContentSize = 1 * 1024 * 1024 * 1024; + + EParseStage Stage; + EParseStage LastSuccessStage; + TStringBuf Line; + TStringBuf& Header = Line; + size_t ChunkLength = 0; + size_t ContentSize = 0; + TString Content; + + THttpParser(const THttpParser& src) + : HeaderType(src) + , BufferType(src) + , Stage(src.Stage) + , LastSuccessStage(src.LastSuccessStage) + , Line() + , Header(Line) + , ChunkLength(src.ChunkLength) + , ContentSize(src.ContentSize) + , Content(src.Content) + {} + + template <typename StringType> + bool ProcessData(StringType& target, TStringBuf& source, char delim, size_t maxLen) { + TStringBuf maxSource(source.substr(0, maxLen + 1 - target.size())); + size_t pos = maxSource.find(delim); + target += maxSource.substr(0, pos); + source.Skip(pos); + if (target.size() > maxLen) { + Stage = EParseStage::Error; + return false; + } + if (!source.empty() && *source.begin() == delim) { + source.Skip(1); + } + return pos != TStringBuf::npos; + } + + template <typename StringType> + bool ProcessData(StringType& target, TStringBuf& source, TStringBuf delim, size_t maxLen) { + if (delim.empty()) { + return false; + } + if (delim.size() == 1) { + return ProcessData(target, source, delim[0], maxLen); + } if (ProcessData(target, source, delim.back(), maxLen + 1)) { - for (signed i = delim.size() - 2; i >= 0; --i) { - TrimEnd(target, delim[i]); - } - return true; - } - return false; - } - - template <typename StringType> - bool ProcessData(StringType& target, TStringBuf& source, size_t size) { - TStringBuf maxSource(source.substr(0, size - target.size())); - target += maxSource; - source.Skip(maxSource.size()); - if (target.size() > size && !source.empty()) { - Stage = EParseStage::Error; - return false; - } - return target.size() == size; - } - - void ProcessHeader(TStringBuf& header) { - TStringBuf name = header.NextTok(':'); - TrimBegin(name, ' '); - TStringBuf value = header; - Trim(value, ' '); - auto cit = HeaderType::HeadersLocation.find(name); - if (cit != HeaderType::HeadersLocation.end()) { - this->*cit->second = value; - } - header.Clear(); - } - - size_t ParseHex(TStringBuf value) { - size_t result = 0; - for (char ch : value) { - if (ch >= '0' && ch <= '9') { - result *= 16; - result += ch - '0'; - } else if (ch >= 'a' && ch <= 'f') { - result *= 16; - result += 10 + ch - 'a'; - } else if (ch >= 'A' && ch <= 'F') { - result *= 16; - result += 10 + ch - 'A'; - } else if (ch == ';') { - break; - } else if (isspace(ch)) { - continue; - } else { - Stage = EParseStage::Error; - return 0; - } - } - return result; - } - - void Advance(size_t len); - void ConnectionClosed(); - - void Clear() { - BufferType::Clear(); - HeaderType::Clear(); - Stage = GetInitialStage(); - Line.Clear(); - Content.clear(); - } - - bool IsReady() const { - return Stage == EParseStage::Done; - } - - bool IsError() const { - return Stage == EParseStage::Error; - } - - TStringBuf GetErrorText() const { - switch (LastSuccessStage) { - case EParseStage::Method: - return "Invalid http method"; - case EParseStage::URL: - return "Invalid url"; - case EParseStage::Protocol: - return "Invalid http protocol"; - case EParseStage::Version: - return "Invalid http version"; - case EParseStage::Status: - return "Invalid http status"; - case EParseStage::Message: - return "Invalid http message"; - case EParseStage::Header: - return "Invalid http header"; - case EParseStage::Body: - return "Invalid content body"; - case EParseStage::ChunkLength: - case EParseStage::ChunkData: - return "Broken chunked data"; - case EParseStage::Done: - return "Everything is fine"; - case EParseStage::Error: - return "Error on error"; // wat? ...because we don't want to include default label here - } - } - - bool IsDone() const { - return IsReady() || IsError(); - } - - bool HaveBody() const { - return !HeaderType::ContentType.empty() || !HeaderType::ContentLength.empty() || !HeaderType::TransferEncoding.empty(); - } - - bool EnsureEnoughSpaceAvailable(size_t need = BufferType::BUFFER_MIN_STEP) { - bool result = BufferType::EnsureEnoughSpaceAvailable(need); - if (!result && !BufferType::Empty()) { - Reparse(); - } - return true; - } - - void Reparse() { - size_t size = BufferType::Size(); - Clear(); - Advance(size); - } - - TStringBuf GetRawData() const { - return TStringBuf(BufferType::Data(), BufferType::Size()); - } - - TString GetObfuscatedData() const { - THeaders headers(HeaderType::Headers); - TStringBuf authorization(headers["Authorization"]); - TStringBuf cookie(headers["Cookie"]); - TStringBuf x_ydb_auth_ticket(headers["x-ydb-auth-ticket"]); - TStringBuf x_yacloud_subjecttoken(headers["x-yacloud-subjecttoken"]); - TString data(GetRawData()); - if (!authorization.empty()) { - auto pos = data.find(authorization); - if (pos != TString::npos) { - data.replace(pos, authorization.size(), TString("<obfuscated>")); - } - } - if (!cookie.empty()) { - auto pos = data.find(cookie); - if (pos != TString::npos) { - data.replace(pos, cookie.size(), TString("<obfuscated>")); - } - } - if (!x_ydb_auth_ticket.empty()) { - auto pos = data.find(x_ydb_auth_ticket); - if (pos != TString::npos) { - data.replace(pos, x_ydb_auth_ticket.size(), TString("<obfuscated>")); - } - } - if (!x_yacloud_subjecttoken.empty()) { - auto pos = data.find(x_yacloud_subjecttoken); - if (pos != TString::npos) { - data.replace(pos, x_yacloud_subjecttoken.size(), TString("<obfuscated>")); - } - } - return data; - } - - static EParseStage GetInitialStage(); - - THttpParser() - : Stage(GetInitialStage()) - , LastSuccessStage(Stage) - {} -}; - -template <typename HeaderType, typename BufferType> -class THttpRenderer : public HeaderType, public BufferType { -public: - enum class ERenderStage { - Init, - Header, - Body, - Done, - Error, - }; - - ERenderStage Stage = ERenderStage::Init; - - void Append(TStringBuf text) { - EnsureEnoughSpaceAvailable(text.size()); - BufferType::Append(text.data(), text.size()); - } - - void Append(char c) { - EnsureEnoughSpaceAvailable(sizeof(c)); - BufferType::Append(c); - } - - template <TStringBuf HeaderType::* string> - void AppendParsedValue(TStringBuf value) { - Append(value); - static_cast<HeaderType*>(this)->*string = TStringBuf(BufferType::Pos() - value.size(), value.size()); - } - - template <TStringBuf HeaderType::* name> - void Set(TStringBuf value) { - Y_VERIFY_DEBUG(Stage == ERenderStage::Header); - Append(HeaderType::template GetName<name>()); - Append(": "); - AppendParsedValue<name>(value); - Append("\r\n"); - HeaderType::Headers = TStringBuf(HeaderType::Headers.Data(), BufferType::Pos() - HeaderType::Headers.Data()); - } - - void Set(TStringBuf name, TStringBuf value) { - Y_VERIFY_DEBUG(Stage == ERenderStage::Header); - Append(name); - Append(": "); - Append(value); - 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()); - HeaderType::Headers = TStringBuf(HeaderType::Headers.Data(), BufferType::Pos() - HeaderType::Headers.Data()); - } - - //THttpRenderer(TStringBuf method, TStringBuf url, TStringBuf protocol, TStringBuf version); // request - void InitRequest(TStringBuf method, TStringBuf url, TStringBuf protocol, TStringBuf version) { - Y_VERIFY_DEBUG(Stage == ERenderStage::Init); - AppendParsedValue<&THttpRequest::Method>(method); - Append(' '); - AppendParsedValue<&THttpRequest::URL>(url); - Append(' '); - AppendParsedValue<&THttpRequest::Protocol>(protocol); - Append('/'); - AppendParsedValue<&THttpRequest::Version>(version); - Append("\r\n"); - Stage = ERenderStage::Header; - HeaderType::Headers = TStringBuf(BufferType::Pos(), size_t(0)); - } - - //THttpRenderer(TStringBuf protocol, TStringBuf version, TStringBuf status, TStringBuf message); // response - void InitResponse(TStringBuf protocol, TStringBuf version, TStringBuf status, TStringBuf message) { - Y_VERIFY_DEBUG(Stage == ERenderStage::Init); - AppendParsedValue<&THttpResponse::Protocol>(protocol); - Append('/'); - AppendParsedValue<&THttpResponse::Version>(version); - Append(' '); - AppendParsedValue<&THttpResponse::Status>(status); - Append(' '); - AppendParsedValue<&THttpResponse::Message>(message); - Append("\r\n"); - Stage = ERenderStage::Header; - HeaderType::Headers = TStringBuf(BufferType::Pos(), size_t(0)); - } - - void FinishHeader() { - Append("\r\n"); - HeaderType::Headers = TStringBuf(HeaderType::Headers.Data(), BufferType::Pos() - HeaderType::Headers.Data()); - Stage = ERenderStage::Body; - } - - void SetBody(TStringBuf body) { - Y_VERIFY_DEBUG(Stage == ERenderStage::Header); - if (HeaderType::ContentLength.empty()) { - Set<&HeaderType::ContentLength>(ToString(body.size())); - } - FinishHeader(); - AppendParsedValue<&HeaderType::Body>(body); - Stage = ERenderStage::Done; - } - - bool IsDone() const { - return Stage == ERenderStage::Done; - } - - void Finish() { - switch (Stage) { - case ERenderStage::Header: - FinishHeader(); - break; - default: - break; - } - } - - bool EnsureEnoughSpaceAvailable(size_t need = BufferType::BUFFER_MIN_STEP) { - bool result = BufferType::EnsureEnoughSpaceAvailable(need); - if (!result && !BufferType::Empty()) { - Reparse(); - } - return true; - } - - void Clear() { - BufferType::Clear(); - HeaderType::Clear(); - } - - void Reparse() { - // move-magic - size_t size = BufferType::Size(); - THttpParser<HeaderType, BufferType> parser; - // move the buffer to parser - static_cast<BufferType&>(parser) = std::move(static_cast<BufferType&>(*this)); - // reparse - parser.Clear(); - parser.Advance(size); - // move buffer and result back - static_cast<HeaderType&>(*this) = std::move(static_cast<HeaderType&>(parser)); - static_cast<BufferType&>(*this) = std::move(static_cast<BufferType&>(parser)); - switch (parser.Stage) { - case THttpParser<HeaderType, BufferType>::EParseStage::Method: - case THttpParser<HeaderType, BufferType>::EParseStage::URL: - case THttpParser<HeaderType, BufferType>::EParseStage::Protocol: - case THttpParser<HeaderType, BufferType>::EParseStage::Version: - case THttpParser<HeaderType, BufferType>::EParseStage::Status: - case THttpParser<HeaderType, BufferType>::EParseStage::Message: - Stage = ERenderStage::Init; - break; - case THttpParser<HeaderType, BufferType>::EParseStage::Header: - Stage = ERenderStage::Header; + for (signed i = delim.size() - 2; i >= 0; --i) { + TrimEnd(target, delim[i]); + } + return true; + } + return false; + } + + template <typename StringType> + bool ProcessData(StringType& target, TStringBuf& source, size_t size) { + TStringBuf maxSource(source.substr(0, size - target.size())); + target += maxSource; + source.Skip(maxSource.size()); + if (target.size() > size && !source.empty()) { + Stage = EParseStage::Error; + return false; + } + return target.size() == size; + } + + void ProcessHeader(TStringBuf& header) { + TStringBuf name = header.NextTok(':'); + TrimBegin(name, ' '); + TStringBuf value = header; + Trim(value, ' '); + auto cit = HeaderType::HeadersLocation.find(name); + if (cit != HeaderType::HeadersLocation.end()) { + this->*cit->second = value; + } + header.Clear(); + } + + size_t ParseHex(TStringBuf value) { + size_t result = 0; + for (char ch : value) { + if (ch >= '0' && ch <= '9') { + result *= 16; + result += ch - '0'; + } else if (ch >= 'a' && ch <= 'f') { + result *= 16; + result += 10 + ch - 'a'; + } else if (ch >= 'A' && ch <= 'F') { + result *= 16; + result += 10 + ch - 'A'; + } else if (ch == ';') { + break; + } else if (isspace(ch)) { + continue; + } else { + Stage = EParseStage::Error; + return 0; + } + } + return result; + } + + void Advance(size_t len); + void ConnectionClosed(); + + void Clear() { + BufferType::Clear(); + HeaderType::Clear(); + Stage = GetInitialStage(); + Line.Clear(); + Content.clear(); + } + + bool IsReady() const { + return Stage == EParseStage::Done; + } + + bool IsError() const { + return Stage == EParseStage::Error; + } + + TStringBuf GetErrorText() const { + switch (LastSuccessStage) { + case EParseStage::Method: + return "Invalid http method"; + case EParseStage::URL: + return "Invalid url"; + case EParseStage::Protocol: + return "Invalid http protocol"; + case EParseStage::Version: + return "Invalid http version"; + case EParseStage::Status: + return "Invalid http status"; + case EParseStage::Message: + return "Invalid http message"; + case EParseStage::Header: + return "Invalid http header"; + case EParseStage::Body: + return "Invalid content body"; + case EParseStage::ChunkLength: + case EParseStage::ChunkData: + return "Broken chunked data"; + case EParseStage::Done: + return "Everything is fine"; + case EParseStage::Error: + return "Error on error"; // wat? ...because we don't want to include default label here + } + } + + bool IsDone() const { + return IsReady() || IsError(); + } + + bool HaveBody() const { + return !HeaderType::ContentType.empty() || !HeaderType::ContentLength.empty() || !HeaderType::TransferEncoding.empty(); + } + + bool EnsureEnoughSpaceAvailable(size_t need = BufferType::BUFFER_MIN_STEP) { + bool result = BufferType::EnsureEnoughSpaceAvailable(need); + if (!result && !BufferType::Empty()) { + Reparse(); + } + return true; + } + + void Reparse() { + size_t size = BufferType::Size(); + Clear(); + Advance(size); + } + + TStringBuf GetRawData() const { + return TStringBuf(BufferType::Data(), BufferType::Size()); + } + + TString GetObfuscatedData() const { + THeaders headers(HeaderType::Headers); + TStringBuf authorization(headers["Authorization"]); + TStringBuf cookie(headers["Cookie"]); + TStringBuf x_ydb_auth_ticket(headers["x-ydb-auth-ticket"]); + TStringBuf x_yacloud_subjecttoken(headers["x-yacloud-subjecttoken"]); + TString data(GetRawData()); + if (!authorization.empty()) { + auto pos = data.find(authorization); + if (pos != TString::npos) { + data.replace(pos, authorization.size(), TString("<obfuscated>")); + } + } + if (!cookie.empty()) { + auto pos = data.find(cookie); + if (pos != TString::npos) { + data.replace(pos, cookie.size(), TString("<obfuscated>")); + } + } + if (!x_ydb_auth_ticket.empty()) { + auto pos = data.find(x_ydb_auth_ticket); + if (pos != TString::npos) { + data.replace(pos, x_ydb_auth_ticket.size(), TString("<obfuscated>")); + } + } + if (!x_yacloud_subjecttoken.empty()) { + auto pos = data.find(x_yacloud_subjecttoken); + if (pos != TString::npos) { + data.replace(pos, x_yacloud_subjecttoken.size(), TString("<obfuscated>")); + } + } + return data; + } + + static EParseStage GetInitialStage(); + + THttpParser() + : Stage(GetInitialStage()) + , LastSuccessStage(Stage) + {} +}; + +template <typename HeaderType, typename BufferType> +class THttpRenderer : public HeaderType, public BufferType { +public: + enum class ERenderStage { + Init, + Header, + Body, + Done, + Error, + }; + + ERenderStage Stage = ERenderStage::Init; + + void Append(TStringBuf text) { + EnsureEnoughSpaceAvailable(text.size()); + BufferType::Append(text.data(), text.size()); + } + + void Append(char c) { + EnsureEnoughSpaceAvailable(sizeof(c)); + BufferType::Append(c); + } + + template <TStringBuf HeaderType::* string> + void AppendParsedValue(TStringBuf value) { + Append(value); + static_cast<HeaderType*>(this)->*string = TStringBuf(BufferType::Pos() - value.size(), value.size()); + } + + template <TStringBuf HeaderType::* name> + void Set(TStringBuf value) { + Y_VERIFY_DEBUG(Stage == ERenderStage::Header); + Append(HeaderType::template GetName<name>()); + Append(": "); + AppendParsedValue<name>(value); + Append("\r\n"); + HeaderType::Headers = TStringBuf(HeaderType::Headers.Data(), BufferType::Pos() - HeaderType::Headers.Data()); + } + + void Set(TStringBuf name, TStringBuf value) { + Y_VERIFY_DEBUG(Stage == ERenderStage::Header); + Append(name); + Append(": "); + Append(value); + 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()); + HeaderType::Headers = TStringBuf(HeaderType::Headers.Data(), BufferType::Pos() - HeaderType::Headers.Data()); + } + + //THttpRenderer(TStringBuf method, TStringBuf url, TStringBuf protocol, TStringBuf version); // request + void InitRequest(TStringBuf method, TStringBuf url, TStringBuf protocol, TStringBuf version) { + Y_VERIFY_DEBUG(Stage == ERenderStage::Init); + AppendParsedValue<&THttpRequest::Method>(method); + Append(' '); + AppendParsedValue<&THttpRequest::URL>(url); + Append(' '); + AppendParsedValue<&THttpRequest::Protocol>(protocol); + Append('/'); + AppendParsedValue<&THttpRequest::Version>(version); + Append("\r\n"); + Stage = ERenderStage::Header; + HeaderType::Headers = TStringBuf(BufferType::Pos(), size_t(0)); + } + + //THttpRenderer(TStringBuf protocol, TStringBuf version, TStringBuf status, TStringBuf message); // response + void InitResponse(TStringBuf protocol, TStringBuf version, TStringBuf status, TStringBuf message) { + Y_VERIFY_DEBUG(Stage == ERenderStage::Init); + AppendParsedValue<&THttpResponse::Protocol>(protocol); + Append('/'); + AppendParsedValue<&THttpResponse::Version>(version); + Append(' '); + AppendParsedValue<&THttpResponse::Status>(status); + Append(' '); + AppendParsedValue<&THttpResponse::Message>(message); + Append("\r\n"); + Stage = ERenderStage::Header; + HeaderType::Headers = TStringBuf(BufferType::Pos(), size_t(0)); + } + + void FinishHeader() { + Append("\r\n"); + HeaderType::Headers = TStringBuf(HeaderType::Headers.Data(), BufferType::Pos() - HeaderType::Headers.Data()); + Stage = ERenderStage::Body; + } + + void SetBody(TStringBuf body) { + Y_VERIFY_DEBUG(Stage == ERenderStage::Header); + if (HeaderType::ContentLength.empty()) { + Set<&HeaderType::ContentLength>(ToString(body.size())); + } + FinishHeader(); + AppendParsedValue<&HeaderType::Body>(body); + Stage = ERenderStage::Done; + } + + bool IsDone() const { + return Stage == ERenderStage::Done; + } + + void Finish() { + switch (Stage) { + case ERenderStage::Header: + FinishHeader(); break; - case THttpParser<HeaderType, BufferType>::EParseStage::Body: - case THttpParser<HeaderType, BufferType>::EParseStage::ChunkLength: - case THttpParser<HeaderType, BufferType>::EParseStage::ChunkData: - Stage = ERenderStage::Body; - break; - case THttpParser<HeaderType, BufferType>::EParseStage::Done: - Stage = ERenderStage::Done; - break; - case THttpParser<HeaderType, BufferType>::EParseStage::Error: - Stage = ERenderStage::Error; - break; - } - Y_VERIFY(size == BufferType::Size()); - } - - TStringBuf GetRawData() const { - return TStringBuf(BufferType::Data(), BufferType::Size()); - } -}; - -template <> -template <> -inline void THttpRenderer<THttpResponse, TSocketBuffer>::Set<&THttpResponse::Body>(TStringBuf value) { - SetBody(value); -} - -template <> -template <> -inline void THttpRenderer<THttpRequest, TSocketBuffer>::Set<&THttpRequest::Body>(TStringBuf value) { - SetBody(value); -} - -class THttpIncomingRequest; -using THttpIncomingRequestPtr = TIntrusivePtr<THttpIncomingRequest>; - -class THttpOutgoingResponse; -using THttpOutgoingResponsePtr = TIntrusivePtr<THttpOutgoingResponse>; - -class THttpIncomingRequest : - public THttpParser<THttpRequest, TSocketBuffer>, - public TRefCounted<THttpIncomingRequest, TAtomicCounter> { -public: - THttpConfig::SocketAddressType Address; - TString WorkerName; - THPTimer Timer; - bool Secure = false; - - bool IsConnectionClose() const { - if (Connection.empty()) { - return Version == "1.0"; - } else { - return Connection == "close"; - } - } - - TStringBuf GetConnection() const { - if (!Connection.empty()) { - return Connection; - } - return Version == "1.0" ? "close" : "keep-alive"; - } - - THttpOutgoingResponsePtr CreateResponseOK(TStringBuf body, TStringBuf contentType = "text/html", TInstant lastModified = TInstant()); - THttpOutgoingResponsePtr CreateResponseString(TStringBuf data); - THttpOutgoingResponsePtr CreateResponseBadRequest(TStringBuf html = TStringBuf(), TStringBuf contentType = "text/html"); // 400 - THttpOutgoingResponsePtr CreateResponseNotFound(TStringBuf html = TStringBuf(), TStringBuf contentType = "text/html"); // 404 - THttpOutgoingResponsePtr CreateResponseServiceUnavailable(TStringBuf html = TStringBuf(), TStringBuf contentType = "text/html"); // 503 - THttpOutgoingResponsePtr CreateResponseGatewayTimeout(TStringBuf html = TStringBuf(), TStringBuf contentType = "text/html"); // 504 - THttpOutgoingResponsePtr CreateResponse( - TStringBuf status, - TStringBuf message, - TStringBuf contentType = TStringBuf(), - TStringBuf body = TStringBuf(), - TInstant lastModified = TInstant()); - - THttpIncomingRequestPtr Duplicate(); -}; - -class THttpIncomingResponse; -using THttpIncomingResponsePtr = TIntrusivePtr<THttpIncomingResponse>; - -class THttpOutgoingRequest; -using THttpOutgoingRequestPtr = TIntrusivePtr<THttpOutgoingRequest>; - -class THttpIncomingResponse : - public THttpParser<THttpResponse, TSocketBuffer>, - public TRefCounted<THttpIncomingResponse, TAtomicCounter> { -public: - THttpIncomingResponse(THttpOutgoingRequestPtr request); - - THttpOutgoingRequestPtr GetRequest() const { - return Request; - } - - THttpIncomingResponsePtr Duplicate(THttpOutgoingRequestPtr request); - THttpOutgoingResponsePtr Reverse(THttpIncomingRequestPtr request); - -protected: - THttpOutgoingRequestPtr Request; -}; - -class THttpOutgoingRequest : - public THttpRenderer<THttpRequest, TSocketBuffer>, - public TRefCounted<THttpOutgoingRequest, TAtomicCounter> { -public: - THPTimer Timer; + default: + break; + } + } + + bool EnsureEnoughSpaceAvailable(size_t need = BufferType::BUFFER_MIN_STEP) { + bool result = BufferType::EnsureEnoughSpaceAvailable(need); + if (!result && !BufferType::Empty()) { + Reparse(); + } + return true; + } + + void Clear() { + BufferType::Clear(); + HeaderType::Clear(); + } + + void Reparse() { + // move-magic + size_t size = BufferType::Size(); + THttpParser<HeaderType, BufferType> parser; + // move the buffer to parser + static_cast<BufferType&>(parser) = std::move(static_cast<BufferType&>(*this)); + // reparse + parser.Clear(); + parser.Advance(size); + // move buffer and result back + static_cast<HeaderType&>(*this) = std::move(static_cast<HeaderType&>(parser)); + static_cast<BufferType&>(*this) = std::move(static_cast<BufferType&>(parser)); + switch (parser.Stage) { + case THttpParser<HeaderType, BufferType>::EParseStage::Method: + case THttpParser<HeaderType, BufferType>::EParseStage::URL: + case THttpParser<HeaderType, BufferType>::EParseStage::Protocol: + case THttpParser<HeaderType, BufferType>::EParseStage::Version: + case THttpParser<HeaderType, BufferType>::EParseStage::Status: + case THttpParser<HeaderType, BufferType>::EParseStage::Message: + Stage = ERenderStage::Init; + break; + case THttpParser<HeaderType, BufferType>::EParseStage::Header: + Stage = ERenderStage::Header; + break; + case THttpParser<HeaderType, BufferType>::EParseStage::Body: + case THttpParser<HeaderType, BufferType>::EParseStage::ChunkLength: + case THttpParser<HeaderType, BufferType>::EParseStage::ChunkData: + Stage = ERenderStage::Body; + break; + case THttpParser<HeaderType, BufferType>::EParseStage::Done: + Stage = ERenderStage::Done; + break; + case THttpParser<HeaderType, BufferType>::EParseStage::Error: + Stage = ERenderStage::Error; + break; + } + Y_VERIFY(size == BufferType::Size()); + } + + TStringBuf GetRawData() const { + return TStringBuf(BufferType::Data(), BufferType::Size()); + } +}; + +template <> +template <> +inline void THttpRenderer<THttpResponse, TSocketBuffer>::Set<&THttpResponse::Body>(TStringBuf value) { + SetBody(value); +} + +template <> +template <> +inline void THttpRenderer<THttpRequest, TSocketBuffer>::Set<&THttpRequest::Body>(TStringBuf value) { + SetBody(value); +} + +class THttpIncomingRequest; +using THttpIncomingRequestPtr = TIntrusivePtr<THttpIncomingRequest>; + +class THttpOutgoingResponse; +using THttpOutgoingResponsePtr = TIntrusivePtr<THttpOutgoingResponse>; + +class THttpIncomingRequest : + public THttpParser<THttpRequest, TSocketBuffer>, + public TRefCounted<THttpIncomingRequest, TAtomicCounter> { +public: + THttpConfig::SocketAddressType Address; + TString WorkerName; + THPTimer Timer; bool Secure = false; - - THttpOutgoingRequest() = default; - THttpOutgoingRequest(TStringBuf method, TStringBuf url, TStringBuf protocol, TStringBuf version); - THttpOutgoingRequest(TStringBuf method, TStringBuf scheme, TStringBuf host, TStringBuf uri, TStringBuf protocol, TStringBuf version); - static THttpOutgoingRequestPtr CreateRequestString(TStringBuf data); - static THttpOutgoingRequestPtr CreateRequestString(const TString& data); - static THttpOutgoingRequestPtr CreateRequestGet(TStringBuf url); - static THttpOutgoingRequestPtr CreateRequestGet(TStringBuf host, TStringBuf uri); // http only - static THttpOutgoingRequestPtr CreateRequestPost(TStringBuf url, TStringBuf contentType = {}, TStringBuf body = {}); - static THttpOutgoingRequestPtr CreateRequestPost(TStringBuf host, TStringBuf uri, TStringBuf contentType, TStringBuf body); // http only - static THttpOutgoingRequestPtr CreateRequest(TStringBuf method, TStringBuf url, TStringBuf contentType = TStringBuf(), TStringBuf body = TStringBuf()); - static THttpOutgoingRequestPtr CreateHttpRequest(TStringBuf method, TStringBuf host, TStringBuf uri, TStringBuf contentType = TStringBuf(), TStringBuf body = TStringBuf()); - THttpOutgoingRequestPtr Duplicate(); -}; - -class THttpOutgoingResponse : - public THttpRenderer<THttpResponse, TSocketBuffer>, - public TRefCounted<THttpOutgoingResponse, TAtomicCounter> { -public: - THttpOutgoingResponse(THttpIncomingRequestPtr request); - THttpOutgoingResponse(THttpIncomingRequestPtr request, TStringBuf protocol, TStringBuf version, TStringBuf status, TStringBuf message); - - bool IsConnectionClose() const { - if (!Connection.empty()) { - return Connection == "close"; - } else { - return Request->IsConnectionClose(); - } - } - - bool IsNeedBody() const { - return Status != "204"; - } - - THttpIncomingRequestPtr GetRequest() const { - return Request; - } - - THttpOutgoingResponsePtr Duplicate(THttpIncomingRequestPtr request); - -// it's temporary accessible for cleanup -//protected: - THttpIncomingRequestPtr Request; -}; - -} + + bool IsConnectionClose() const { + if (Connection.empty()) { + return Version == "1.0"; + } else { + return Connection == "close"; + } + } + + TStringBuf GetConnection() const { + if (!Connection.empty()) { + return Connection; + } + return Version == "1.0" ? "close" : "keep-alive"; + } + + THttpOutgoingResponsePtr CreateResponseOK(TStringBuf body, TStringBuf contentType = "text/html", TInstant lastModified = TInstant()); + THttpOutgoingResponsePtr CreateResponseString(TStringBuf data); + THttpOutgoingResponsePtr CreateResponseBadRequest(TStringBuf html = TStringBuf(), TStringBuf contentType = "text/html"); // 400 + THttpOutgoingResponsePtr CreateResponseNotFound(TStringBuf html = TStringBuf(), TStringBuf contentType = "text/html"); // 404 + THttpOutgoingResponsePtr CreateResponseServiceUnavailable(TStringBuf html = TStringBuf(), TStringBuf contentType = "text/html"); // 503 + THttpOutgoingResponsePtr CreateResponseGatewayTimeout(TStringBuf html = TStringBuf(), TStringBuf contentType = "text/html"); // 504 + THttpOutgoingResponsePtr CreateResponse( + TStringBuf status, + TStringBuf message, + TStringBuf contentType = TStringBuf(), + TStringBuf body = TStringBuf(), + TInstant lastModified = TInstant()); + + THttpIncomingRequestPtr Duplicate(); +}; + +class THttpIncomingResponse; +using THttpIncomingResponsePtr = TIntrusivePtr<THttpIncomingResponse>; + +class THttpOutgoingRequest; +using THttpOutgoingRequestPtr = TIntrusivePtr<THttpOutgoingRequest>; + +class THttpIncomingResponse : + public THttpParser<THttpResponse, TSocketBuffer>, + public TRefCounted<THttpIncomingResponse, TAtomicCounter> { +public: + THttpIncomingResponse(THttpOutgoingRequestPtr request); + + THttpOutgoingRequestPtr GetRequest() const { + return Request; + } + + THttpIncomingResponsePtr Duplicate(THttpOutgoingRequestPtr request); + THttpOutgoingResponsePtr Reverse(THttpIncomingRequestPtr request); + +protected: + THttpOutgoingRequestPtr Request; +}; + +class THttpOutgoingRequest : + public THttpRenderer<THttpRequest, TSocketBuffer>, + public TRefCounted<THttpOutgoingRequest, TAtomicCounter> { +public: + THPTimer Timer; + bool Secure = false; + + THttpOutgoingRequest() = default; + THttpOutgoingRequest(TStringBuf method, TStringBuf url, TStringBuf protocol, TStringBuf version); + THttpOutgoingRequest(TStringBuf method, TStringBuf scheme, TStringBuf host, TStringBuf uri, TStringBuf protocol, TStringBuf version); + static THttpOutgoingRequestPtr CreateRequestString(TStringBuf data); + static THttpOutgoingRequestPtr CreateRequestString(const TString& data); + static THttpOutgoingRequestPtr CreateRequestGet(TStringBuf url); + static THttpOutgoingRequestPtr CreateRequestGet(TStringBuf host, TStringBuf uri); // http only + static THttpOutgoingRequestPtr CreateRequestPost(TStringBuf url, TStringBuf contentType = {}, TStringBuf body = {}); + static THttpOutgoingRequestPtr CreateRequestPost(TStringBuf host, TStringBuf uri, TStringBuf contentType, TStringBuf body); // http only + static THttpOutgoingRequestPtr CreateRequest(TStringBuf method, TStringBuf url, TStringBuf contentType = TStringBuf(), TStringBuf body = TStringBuf()); + static THttpOutgoingRequestPtr CreateHttpRequest(TStringBuf method, TStringBuf host, TStringBuf uri, TStringBuf contentType = TStringBuf(), TStringBuf body = TStringBuf()); + THttpOutgoingRequestPtr Duplicate(); +}; + +class THttpOutgoingResponse : + public THttpRenderer<THttpResponse, TSocketBuffer>, + public TRefCounted<THttpOutgoingResponse, TAtomicCounter> { +public: + THttpOutgoingResponse(THttpIncomingRequestPtr request); + THttpOutgoingResponse(THttpIncomingRequestPtr request, TStringBuf protocol, TStringBuf version, TStringBuf status, TStringBuf message); + + bool IsConnectionClose() const { + if (!Connection.empty()) { + return Connection == "close"; + } else { + return Request->IsConnectionClose(); + } + } + + bool IsNeedBody() const { + return Status != "204"; + } + + THttpIncomingRequestPtr GetRequest() const { + return Request; + } + + THttpOutgoingResponsePtr Duplicate(THttpIncomingRequestPtr request); + +// it's temporary accessible for cleanup +//protected: + THttpIncomingRequestPtr Request; +}; + +} diff --git a/library/cpp/actors/http/http_cache.cpp b/library/cpp/actors/http/http_cache.cpp index 27c4eeb6f3..8fe6783260 100644 --- a/library/cpp/actors/http/http_cache.cpp +++ b/library/cpp/actors/http/http_cache.cpp @@ -1,4 +1,4 @@ -#include "http.h" +#include "http.h" #include "http_proxy.h" #include "http_cache.h" #include <library/cpp/actors/core/actor_bootstrapped.h> @@ -6,594 +6,594 @@ #include <library/cpp/actors/core/log.h> #include <library/cpp/actors/core/scheduler_basic.h> #include <library/cpp/actors/http/http.h> -#include <library/cpp/digest/md5/md5.h> -#include <util/digest/multi.h> -#include <util/generic/queue.h> +#include <library/cpp/digest/md5/md5.h> +#include <util/digest/multi.h> +#include <util/generic/queue.h> #include <util/string/cast.h> - -namespace NHttp { - -class THttpOutgoingCacheActor : public NActors::TActorBootstrapped<THttpOutgoingCacheActor>, THttpConfig { -public: - using TBase = NActors::TActorBootstrapped<THttpOutgoingCacheActor>; + +namespace NHttp { + +class THttpOutgoingCacheActor : public NActors::TActorBootstrapped<THttpOutgoingCacheActor>, THttpConfig { +public: + using TBase = NActors::TActorBootstrapped<THttpOutgoingCacheActor>; NActors::TActorId HttpProxyId; - TGetCachePolicy GetCachePolicy; - static constexpr TDuration RefreshTimeout = TDuration::Seconds(1); - - struct TCacheKey { - TString Host; - TString URL; - TString Headers; - - operator size_t() const { - return MultiHash(Host, URL, Headers); - } - - TString GetId() const { - return MD5::Calc(Host + ':' + URL + ':' + Headers); - } - }; - - struct TCacheRecord { - TInstant RefreshTime; - TInstant DeathTime; - TCachePolicy CachePolicy; - NHttp::THttpOutgoingRequestPtr Request; - NHttp::THttpOutgoingRequestPtr OutgoingRequest; - TDuration Timeout; - NHttp::THttpIncomingResponsePtr Response; - TString Error; - TVector<NHttp::TEvHttpProxy::TEvHttpOutgoingRequest::TPtr> Waiters; - - TCacheRecord(const TCachePolicy cachePolicy) - : CachePolicy(cachePolicy) - {} - - bool IsValid() const { - return Response != nullptr || !Error.empty(); - } - - void UpdateResponse(NHttp::THttpIncomingResponsePtr response, const TString& error, TInstant now) { - if (error.empty() || Response == nullptr || !CachePolicy.KeepOnError) { - Response = response; - Error = error; - } - RefreshTime = now + CachePolicy.TimeToRefresh; - if (CachePolicy.PaceToRefresh) { - RefreshTime += TDuration::MilliSeconds(RandomNumber<ui64>() % CachePolicy.PaceToRefresh.MilliSeconds()); - } - } - - TString GetName() const { - return TStringBuilder() << (Request->Secure ? "https://" : "http://") << Request->Host << Request->URL; - } - }; - - struct TRefreshRecord { - TCacheKey Key; - TInstant RefreshTime; - - bool operator <(const TRefreshRecord& b) const { - return RefreshTime > b.RefreshTime; - } - }; - - THashMap<TCacheKey, TCacheRecord> Cache; - TPriorityQueue<TRefreshRecord> RefreshQueue; - THashMap<THttpOutgoingRequest*, TCacheKey> OutgoingRequests; - - THttpOutgoingCacheActor(const NActors::TActorId& httpProxyId, TGetCachePolicy getCachePolicy) - : HttpProxyId(httpProxyId) - , GetCachePolicy(std::move(getCachePolicy)) - {} - - void Bootstrap(const NActors::TActorContext&) { - // - Become(&THttpOutgoingCacheActor::StateWork, RefreshTimeout, new NActors::TEvents::TEvWakeup()); - } - - static TString GetCacheHeadersKey(const NHttp::THttpOutgoingRequest* request, const TCachePolicy& policy) { - TStringBuilder key; - if (!policy.HeadersToCacheKey.empty()) { - NHttp::THeaders headers(request->Headers); - for (const TString& header : policy.HeadersToCacheKey) { - key << headers[header]; - } - } - return key; - } - - static TCacheKey GetCacheKey(const NHttp::THttpOutgoingRequest* request, const TCachePolicy& policy) { - return { ToString(request->Host), ToString(request->URL), GetCacheHeadersKey(request, policy) }; - } - - void Handle(NHttp::TEvHttpProxy::TEvHttpOutgoingResponse::TPtr event, const NActors::TActorContext& ctx) { - ctx.Send(event->Forward(HttpProxyId)); - } - - void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) { - ctx.Send(event->Forward(HttpProxyId)); - } - - void Handle(NHttp::TEvHttpProxy::TEvAddListeningPort::TPtr event, const NActors::TActorContext& ctx) { - ctx.Send(event->Forward(HttpProxyId)); - } - - void Handle(NHttp::TEvHttpProxy::TEvRegisterHandler::TPtr event, const NActors::TActorContext& ctx) { - ctx.Send(event->Forward(HttpProxyId)); - } - - void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) { - NHttp::THttpOutgoingRequestPtr request(event->Get()->Request); - NHttp::THttpIncomingResponsePtr response(event->Get()->Response); - auto itRequests = OutgoingRequests.find(request.Get()); - if (itRequests == OutgoingRequests.end()) { - LOG_ERROR_S(ctx, HttpLog, "Cache received response to unknown request " << request->Host << request->URL); - return; - } - auto key = itRequests->second; - OutgoingRequests.erase(itRequests); - auto it = Cache.find(key); - if (it == Cache.end()) { - LOG_ERROR_S(ctx, HttpLog, "Cache received response to unknown cache key " << request->Host << request->URL); - return; - } - TCacheRecord& cacheRecord = it->second; - cacheRecord.OutgoingRequest.Reset(); - for (auto& waiter : cacheRecord.Waiters) { - NHttp::THttpIncomingResponsePtr response2; - TString error2; - if (response != nullptr) { - response2 = response->Duplicate(waiter->Get()->Request); - } - if (!event->Get()->Error.empty()) { - error2 = event->Get()->Error; - } - ctx.Send(waiter->Sender, new NHttp::TEvHttpProxy::TEvHttpIncomingResponse(waiter->Get()->Request, response2, error2)); - } - cacheRecord.Waiters.clear(); - TString error; - if (event->Get()->Error.empty()) { - if (event->Get()->Response != nullptr && event->Get()->Response->Status != "200") { - error = event->Get()->Response->Message; - } - } else { - error = event->Get()->Error; - } - if (!error.empty()) { - LOG_WARN_S(ctx, HttpLog, "Error from " << cacheRecord.GetName() << ": " << error); - } - LOG_DEBUG_S(ctx, HttpLog, "OutgoingUpdate " << cacheRecord.GetName()); - cacheRecord.UpdateResponse(response, event->Get()->Error, ctx.Now()); - RefreshQueue.push({it->first, it->second.RefreshTime}); - LOG_DEBUG_S(ctx, HttpLog, "OutgoingSchedule " << cacheRecord.GetName() << " at " << cacheRecord.RefreshTime << " until " << cacheRecord.DeathTime); - } - - void Handle(NHttp::TEvHttpProxy::TEvHttpOutgoingRequest::TPtr event, const NActors::TActorContext& ctx) { - const NHttp::THttpOutgoingRequest* request = event->Get()->Request.Get(); - auto policy = GetCachePolicy(request); - if (policy.TimeToExpire == TDuration()) { - ctx.Send(event->Forward(HttpProxyId)); - return; - } - auto key = GetCacheKey(request, policy); - auto it = Cache.find(key); - if (it != Cache.end()) { - if (it->second.IsValid()) { - LOG_DEBUG_S(ctx, HttpLog, "OutgoingRespond " - << it->second.GetName() - << " (" - << ((it->second.Response != nullptr) ? ToString(it->second.Response->Size()) : TString("error")) - << ")"); - NHttp::THttpIncomingResponsePtr response = it->second.Response; - if (response != nullptr) { - response = response->Duplicate(event->Get()->Request); - } - ctx.Send(event->Sender, - new NHttp::TEvHttpProxy::TEvHttpIncomingResponse(event->Get()->Request, - response, - it->second.Error)); - it->second.DeathTime = ctx.Now() + it->second.CachePolicy.TimeToExpire; // prolong active cache items - return; - } - } else { - it = Cache.emplace(key, policy).first; - it->second.Request = event->Get()->Request; - it->second.Timeout = event->Get()->Timeout; - it->second.OutgoingRequest = it->second.Request->Duplicate(); - OutgoingRequests[it->second.OutgoingRequest.Get()] = key; - LOG_DEBUG_S(ctx, HttpLog, "OutgoingInitiate " << it->second.GetName()); - ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(it->second.OutgoingRequest, it->second.Timeout)); - } - it->second.DeathTime = ctx.Now() + it->second.CachePolicy.TimeToExpire; - it->second.Waiters.emplace_back(std::move(event)); - } - - void HandleRefresh(const NActors::TActorContext& ctx) { - while (!RefreshQueue.empty() && RefreshQueue.top().RefreshTime <= ctx.Now()) { - TRefreshRecord rrec = RefreshQueue.top(); - RefreshQueue.pop(); - auto it = Cache.find(rrec.Key); - if (it != Cache.end()) { - if (it->second.DeathTime > ctx.Now()) { - LOG_DEBUG_S(ctx, HttpLog, "OutgoingRefresh " << it->second.GetName()); - it->second.OutgoingRequest = it->second.Request->Duplicate(); - OutgoingRequests[it->second.OutgoingRequest.Get()] = it->first; - ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(it->second.OutgoingRequest, it->second.Timeout)); - } else { - LOG_DEBUG_S(ctx, HttpLog, "OutgoingForget " << it->second.GetName()); - if (it->second.OutgoingRequest) { - OutgoingRequests.erase(it->second.OutgoingRequest.Get()); - } - Cache.erase(it); - } - } - } - ctx.Schedule(RefreshTimeout, new NActors::TEvents::TEvWakeup()); - } - - STFUNC(StateWork) { - switch (ev->GetTypeRewrite()) { - HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, Handle); - HFunc(NHttp::TEvHttpProxy::TEvHttpOutgoingRequest, Handle); - HFunc(NHttp::TEvHttpProxy::TEvAddListeningPort, Handle); - HFunc(NHttp::TEvHttpProxy::TEvRegisterHandler, Handle); - HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); - HFunc(NHttp::TEvHttpProxy::TEvHttpOutgoingResponse, Handle); - CFunc(NActors::TEvents::TSystem::Wakeup, HandleRefresh); - } - } -}; - -const TDuration THttpOutgoingCacheActor::RefreshTimeout; - -class THttpIncomingCacheActor : public NActors::TActorBootstrapped<THttpIncomingCacheActor>, THttpConfig { -public: - using TBase = NActors::TActorBootstrapped<THttpIncomingCacheActor>; - NActors::TActorId HttpProxyId; - TGetCachePolicy GetCachePolicy; - static constexpr TDuration RefreshTimeout = TDuration::Seconds(1); - THashMap<TString, TActorId> Handlers; - - struct TCacheKey { - TString Host; - TString URL; - TString Headers; - - operator size_t() const { - return MultiHash(Host, URL, Headers); - } - - TString GetId() const { - return MD5::Calc(Host + ':' + URL + ':' + Headers); - } - }; - - struct TCacheRecord { - TInstant RefreshTime; - TInstant DeathTime; - TCachePolicy CachePolicy; - TString CacheId; - NHttp::THttpIncomingRequestPtr Request; - TDuration Timeout; - NHttp::THttpOutgoingResponsePtr Response; - TVector<NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr> Waiters; - ui32 Retries = 0; - bool Enqueued = false; - - TCacheRecord(const TCachePolicy cachePolicy) - : CachePolicy(cachePolicy) - {} - - bool IsValid() const { - return Response != nullptr; - } - - void InitRequest(NHttp::THttpIncomingRequestPtr request) { - Request = request; - if (CachePolicy.TimeToExpire) { - DeathTime = NActors::TlsActivationContext->Now() + CachePolicy.TimeToExpire; - } - } - - void UpdateResponse(NHttp::THttpOutgoingResponsePtr response, const TString& error, TInstant now) { - if (error.empty() || !CachePolicy.KeepOnError) { - Response = response; - } - Retries = 0; - if (CachePolicy.TimeToRefresh) { - RefreshTime = now + CachePolicy.TimeToRefresh; - if (CachePolicy.PaceToRefresh) { - RefreshTime += TDuration::MilliSeconds(RandomNumber<ui64>() % CachePolicy.PaceToRefresh.MilliSeconds()); - } - } - } - - void UpdateExpireTime() { - if (CachePolicy.TimeToExpire) { - DeathTime = NActors::TlsActivationContext->Now() + CachePolicy.TimeToExpire; - } - } - - TString GetName() const { - return TStringBuilder() << (Request->Secure ? "https://" : "http://") << Request->Host << Request->URL - << " (" << CacheId << ")"; - } - }; - - struct TRefreshRecord { - TCacheKey Key; - TInstant RefreshTime; - - bool operator <(const TRefreshRecord& b) const { - return RefreshTime > b.RefreshTime; - } - }; - - THashMap<TCacheKey, TCacheRecord> Cache; - TPriorityQueue<TRefreshRecord> RefreshQueue; - THashMap<THttpIncomingRequest*, TCacheKey> IncomingRequests; - - THttpIncomingCacheActor(const NActors::TActorId& httpProxyId, TGetCachePolicy getCachePolicy) - : HttpProxyId(httpProxyId) - , GetCachePolicy(std::move(getCachePolicy)) - {} - - void Bootstrap(const NActors::TActorContext&) { - // - Become(&THttpIncomingCacheActor::StateWork, RefreshTimeout, new NActors::TEvents::TEvWakeup()); - } - - static TString GetCacheHeadersKey(const NHttp::THttpIncomingRequest* request, const TCachePolicy& policy) { - TStringBuilder key; - if (!policy.HeadersToCacheKey.empty()) { - NHttp::THeaders headers(request->Headers); - for (const TString& header : policy.HeadersToCacheKey) { - key << headers[header]; - } - } - return key; - } - - static TCacheKey GetCacheKey(const NHttp::THttpIncomingRequest* request, const TCachePolicy& policy) { - return { ToString(request->Host), ToString(request->URL), GetCacheHeadersKey(request, policy) }; - } - - TActorId GetRequestHandler(NHttp::THttpIncomingRequestPtr request) { - TStringBuf url = request->URL.Before('?'); - THashMap<TString, TActorId>::iterator it; - while (!url.empty()) { - it = Handlers.find(url); - if (it != Handlers.end()) { - return it->second; - } else { - if (url.EndsWith('/')) { - url.Trunc(url.size() - 1); - } - size_t pos = url.rfind('/'); - if (pos == TStringBuf::npos) { - break; - } else { - url = url.substr(0, pos + 1); - } - } - } - return {}; - } - - void SendCacheRequest(const TCacheKey& cacheKey, TCacheRecord& cacheRecord, const NActors::TActorContext& ctx) { - cacheRecord.Request = cacheRecord.Request->Duplicate(); - IncomingRequests[cacheRecord.Request.Get()] = cacheKey; - TActorId handler = GetRequestHandler(cacheRecord.Request); - if (handler) { - Send(handler, new NHttp::TEvHttpProxy::TEvHttpIncomingRequest(cacheRecord.Request)); - } else { - LOG_ERROR_S(ctx, HttpLog, "Can't find cache handler for " << cacheRecord.GetName()); - } - } - - void DropCacheRecord(THashMap<TCacheKey, TCacheRecord>::iterator it) { - if (it->second.Request) { - IncomingRequests.erase(it->second.Request.Get()); - } - for (auto& waiter : it->second.Waiters) { - NHttp::THttpOutgoingResponsePtr response; - response = waiter->Get()->Request->CreateResponseGatewayTimeout("Timeout", "text/plain"); - Send(waiter->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); - } - Cache.erase(it); - } - - void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) { - ctx.Send(event->Forward(HttpProxyId)); - } - - void Handle(NHttp::TEvHttpProxy::TEvHttpOutgoingRequest::TPtr event, const NActors::TActorContext& ctx) { - ctx.Send(event->Forward(HttpProxyId)); - } - - void Handle(NHttp::TEvHttpProxy::TEvAddListeningPort::TPtr event, const NActors::TActorContext& ctx) { - ctx.Send(event->Forward(HttpProxyId)); - } - - void Handle(NHttp::TEvHttpProxy::TEvRegisterHandler::TPtr event, const NActors::TActorContext& ctx) { - Handlers[event->Get()->Path] = event->Get()->Handler; - ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler(event->Get()->Path, ctx.SelfID)); - } - - void Handle(NHttp::TEvHttpProxy::TEvHttpOutgoingResponse::TPtr event, const NActors::TActorContext& ctx) { - NHttp::THttpIncomingRequestPtr request(event->Get()->Response->GetRequest()); - NHttp::THttpOutgoingResponsePtr response(event->Get()->Response); - auto itRequests = IncomingRequests.find(request.Get()); - if (itRequests == IncomingRequests.end()) { - LOG_ERROR_S(ctx, HttpLog, "Cache received response to unknown request " << request->Host << request->URL); - return; - } - - TCacheKey key = itRequests->second; - auto it = Cache.find(key); - if (it == Cache.end()) { - LOG_ERROR_S(ctx, HttpLog, "Cache received response to unknown cache key " << request->Host << request->URL); - return; - } - - IncomingRequests.erase(itRequests); - TCacheRecord& cacheRecord = it->second; - TStringBuf status; - TString error; - - if (event->Get()->Response != nullptr) { - status = event->Get()->Response->Status; - if (!status.StartsWith("2")) { - error = event->Get()->Response->Message; - } - } - if (cacheRecord.CachePolicy.RetriesCount > 0) { - auto itStatusToRetry = std::find(cacheRecord.CachePolicy.StatusesToRetry.begin(), cacheRecord.CachePolicy.StatusesToRetry.end(), status); - if (itStatusToRetry != cacheRecord.CachePolicy.StatusesToRetry.end()) { - if (cacheRecord.Retries < cacheRecord.CachePolicy.RetriesCount) { - ++cacheRecord.Retries; - LOG_WARN_S(ctx, HttpLog, "IncomingRetry " << cacheRecord.GetName() << ": " << status << " " << error); - SendCacheRequest(key, cacheRecord, ctx); - return; - } - } - } - for (auto& waiter : cacheRecord.Waiters) { - NHttp::THttpOutgoingResponsePtr response2; - response2 = response->Duplicate(waiter->Get()->Request); - ctx.Send(waiter->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response2)); - } - cacheRecord.Waiters.clear(); - if (!error.empty()) { - LOG_WARN_S(ctx, HttpLog, "Error from " << cacheRecord.GetName() << ": " << error); - if (!cacheRecord.Response) { - LOG_DEBUG_S(ctx, HttpLog, "IncomingDiscard " << cacheRecord.GetName()); - DropCacheRecord(it); - return; - } - } - if (cacheRecord.CachePolicy.TimeToRefresh) { - LOG_DEBUG_S(ctx, HttpLog, "IncomingUpdate " << cacheRecord.GetName()); - cacheRecord.UpdateResponse(response, error, ctx.Now()); - if (!cacheRecord.Enqueued) { - RefreshQueue.push({it->first, it->second.RefreshTime}); - cacheRecord.Enqueued = true; - } - LOG_DEBUG_S(ctx, HttpLog, "IncomingSchedule " << cacheRecord.GetName() << " at " << cacheRecord.RefreshTime << " until " << cacheRecord.DeathTime); - } else { - LOG_DEBUG_S(ctx, HttpLog, "IncomingDrop " << cacheRecord.GetName()); - DropCacheRecord(it); - } - } - - void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) { - const NHttp::THttpIncomingRequest* request = event->Get()->Request.Get(); - TCachePolicy policy = GetCachePolicy(request); - if (policy.TimeToExpire == TDuration() && policy.RetriesCount == 0) { - TActorId handler = GetRequestHandler(event->Get()->Request); - if (handler) { - ctx.Send(event->Forward(handler)); - } - return; - } - auto key = GetCacheKey(request, policy); - auto it = Cache.find(key); - if (it != Cache.end() && !policy.DiscardCache) { - it->second.UpdateExpireTime(); - if (it->second.IsValid()) { - LOG_DEBUG_S(ctx, HttpLog, "IncomingRespond " - << it->second.GetName() - << " (" - << ((it->second.Response != nullptr) ? ToString(it->second.Response->Size()) : TString("error")) - << ")"); - NHttp::THttpOutgoingResponsePtr response = it->second.Response; - if (response != nullptr) { - response = response->Duplicate(event->Get()->Request); - } - ctx.Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); - return; - } - } else { - it = Cache.emplace(key, policy).first; - it->second.CacheId = key.GetId(); // for debugging - it->second.InitRequest(event->Get()->Request); - if (policy.DiscardCache) { - LOG_DEBUG_S(ctx, HttpLog, "IncomingDiscardCache " << it->second.GetName()); - } - LOG_DEBUG_S(ctx, HttpLog, "IncomingInitiate " << it->second.GetName()); - SendCacheRequest(key, it->second, ctx); - } - it->second.Waiters.emplace_back(std::move(event)); - } - - void HandleRefresh(const NActors::TActorContext& ctx) { - while (!RefreshQueue.empty() && RefreshQueue.top().RefreshTime <= ctx.Now()) { - TRefreshRecord rrec = RefreshQueue.top(); - RefreshQueue.pop(); - auto it = Cache.find(rrec.Key); - if (it != Cache.end()) { - it->second.Enqueued = false; - if (it->second.DeathTime > ctx.Now()) { - LOG_DEBUG_S(ctx, HttpLog, "IncomingRefresh " << it->second.GetName()); - SendCacheRequest(it->first, it->second, ctx); - } else { - LOG_DEBUG_S(ctx, HttpLog, "IncomingForget " << it->second.GetName()); - DropCacheRecord(it); - } - } - } - ctx.Schedule(RefreshTimeout, new NActors::TEvents::TEvWakeup()); - } - - STFUNC(StateWork) { - switch (ev->GetTypeRewrite()) { - HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, Handle); - HFunc(NHttp::TEvHttpProxy::TEvHttpOutgoingRequest, Handle); - HFunc(NHttp::TEvHttpProxy::TEvAddListeningPort, Handle); - HFunc(NHttp::TEvHttpProxy::TEvRegisterHandler, Handle); - HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); - HFunc(NHttp::TEvHttpProxy::TEvHttpOutgoingResponse, Handle); - CFunc(NActors::TEvents::TSystem::Wakeup, HandleRefresh); - } - } -}; - -TCachePolicy GetDefaultCachePolicy(const THttpRequest* request, const TCachePolicy& defaultPolicy) { - TCachePolicy policy = defaultPolicy; - THeaders headers(request->Headers); - TStringBuf cacheControl(headers["Cache-Control"]); - while (TStringBuf cacheItem = cacheControl.NextTok(',')) { - Trim(cacheItem, ' '); - if (cacheItem == "no-store" || cacheItem == "no-cache") { - policy.DiscardCache = true; - } - TStringBuf itemName = cacheItem.NextTok('='); - TrimEnd(itemName, ' '); - TrimBegin(cacheItem, ' '); - if (itemName == "max-age") { - policy.TimeToRefresh = policy.TimeToExpire = TDuration::Seconds(FromString(cacheItem)); - } - if (itemName == "min-fresh") { - policy.TimeToRefresh = policy.TimeToExpire = TDuration::Seconds(FromString(cacheItem)); - } - if (itemName == "stale-if-error") { - policy.KeepOnError = true; - } - } - return policy; -} - + TGetCachePolicy GetCachePolicy; + static constexpr TDuration RefreshTimeout = TDuration::Seconds(1); + + struct TCacheKey { + TString Host; + TString URL; + TString Headers; + + operator size_t() const { + return MultiHash(Host, URL, Headers); + } + + TString GetId() const { + return MD5::Calc(Host + ':' + URL + ':' + Headers); + } + }; + + struct TCacheRecord { + TInstant RefreshTime; + TInstant DeathTime; + TCachePolicy CachePolicy; + NHttp::THttpOutgoingRequestPtr Request; + NHttp::THttpOutgoingRequestPtr OutgoingRequest; + TDuration Timeout; + NHttp::THttpIncomingResponsePtr Response; + TString Error; + TVector<NHttp::TEvHttpProxy::TEvHttpOutgoingRequest::TPtr> Waiters; + + TCacheRecord(const TCachePolicy cachePolicy) + : CachePolicy(cachePolicy) + {} + + bool IsValid() const { + return Response != nullptr || !Error.empty(); + } + + void UpdateResponse(NHttp::THttpIncomingResponsePtr response, const TString& error, TInstant now) { + if (error.empty() || Response == nullptr || !CachePolicy.KeepOnError) { + Response = response; + Error = error; + } + RefreshTime = now + CachePolicy.TimeToRefresh; + if (CachePolicy.PaceToRefresh) { + RefreshTime += TDuration::MilliSeconds(RandomNumber<ui64>() % CachePolicy.PaceToRefresh.MilliSeconds()); + } + } + + TString GetName() const { + return TStringBuilder() << (Request->Secure ? "https://" : "http://") << Request->Host << Request->URL; + } + }; + + struct TRefreshRecord { + TCacheKey Key; + TInstant RefreshTime; + + bool operator <(const TRefreshRecord& b) const { + return RefreshTime > b.RefreshTime; + } + }; + + THashMap<TCacheKey, TCacheRecord> Cache; + TPriorityQueue<TRefreshRecord> RefreshQueue; + THashMap<THttpOutgoingRequest*, TCacheKey> OutgoingRequests; + + THttpOutgoingCacheActor(const NActors::TActorId& httpProxyId, TGetCachePolicy getCachePolicy) + : HttpProxyId(httpProxyId) + , GetCachePolicy(std::move(getCachePolicy)) + {} + + void Bootstrap(const NActors::TActorContext&) { + // + Become(&THttpOutgoingCacheActor::StateWork, RefreshTimeout, new NActors::TEvents::TEvWakeup()); + } + + static TString GetCacheHeadersKey(const NHttp::THttpOutgoingRequest* request, const TCachePolicy& policy) { + TStringBuilder key; + if (!policy.HeadersToCacheKey.empty()) { + NHttp::THeaders headers(request->Headers); + for (const TString& header : policy.HeadersToCacheKey) { + key << headers[header]; + } + } + return key; + } + + static TCacheKey GetCacheKey(const NHttp::THttpOutgoingRequest* request, const TCachePolicy& policy) { + return { ToString(request->Host), ToString(request->URL), GetCacheHeadersKey(request, policy) }; + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpOutgoingResponse::TPtr event, const NActors::TActorContext& ctx) { + ctx.Send(event->Forward(HttpProxyId)); + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) { + ctx.Send(event->Forward(HttpProxyId)); + } + + void Handle(NHttp::TEvHttpProxy::TEvAddListeningPort::TPtr event, const NActors::TActorContext& ctx) { + ctx.Send(event->Forward(HttpProxyId)); + } + + void Handle(NHttp::TEvHttpProxy::TEvRegisterHandler::TPtr event, const NActors::TActorContext& ctx) { + ctx.Send(event->Forward(HttpProxyId)); + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) { + NHttp::THttpOutgoingRequestPtr request(event->Get()->Request); + NHttp::THttpIncomingResponsePtr response(event->Get()->Response); + auto itRequests = OutgoingRequests.find(request.Get()); + if (itRequests == OutgoingRequests.end()) { + LOG_ERROR_S(ctx, HttpLog, "Cache received response to unknown request " << request->Host << request->URL); + return; + } + auto key = itRequests->second; + OutgoingRequests.erase(itRequests); + auto it = Cache.find(key); + if (it == Cache.end()) { + LOG_ERROR_S(ctx, HttpLog, "Cache received response to unknown cache key " << request->Host << request->URL); + return; + } + TCacheRecord& cacheRecord = it->second; + cacheRecord.OutgoingRequest.Reset(); + for (auto& waiter : cacheRecord.Waiters) { + NHttp::THttpIncomingResponsePtr response2; + TString error2; + if (response != nullptr) { + response2 = response->Duplicate(waiter->Get()->Request); + } + if (!event->Get()->Error.empty()) { + error2 = event->Get()->Error; + } + ctx.Send(waiter->Sender, new NHttp::TEvHttpProxy::TEvHttpIncomingResponse(waiter->Get()->Request, response2, error2)); + } + cacheRecord.Waiters.clear(); + TString error; + if (event->Get()->Error.empty()) { + if (event->Get()->Response != nullptr && event->Get()->Response->Status != "200") { + error = event->Get()->Response->Message; + } + } else { + error = event->Get()->Error; + } + if (!error.empty()) { + LOG_WARN_S(ctx, HttpLog, "Error from " << cacheRecord.GetName() << ": " << error); + } + LOG_DEBUG_S(ctx, HttpLog, "OutgoingUpdate " << cacheRecord.GetName()); + cacheRecord.UpdateResponse(response, event->Get()->Error, ctx.Now()); + RefreshQueue.push({it->first, it->second.RefreshTime}); + LOG_DEBUG_S(ctx, HttpLog, "OutgoingSchedule " << cacheRecord.GetName() << " at " << cacheRecord.RefreshTime << " until " << cacheRecord.DeathTime); + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpOutgoingRequest::TPtr event, const NActors::TActorContext& ctx) { + const NHttp::THttpOutgoingRequest* request = event->Get()->Request.Get(); + auto policy = GetCachePolicy(request); + if (policy.TimeToExpire == TDuration()) { + ctx.Send(event->Forward(HttpProxyId)); + return; + } + auto key = GetCacheKey(request, policy); + auto it = Cache.find(key); + if (it != Cache.end()) { + if (it->second.IsValid()) { + LOG_DEBUG_S(ctx, HttpLog, "OutgoingRespond " + << it->second.GetName() + << " (" + << ((it->second.Response != nullptr) ? ToString(it->second.Response->Size()) : TString("error")) + << ")"); + NHttp::THttpIncomingResponsePtr response = it->second.Response; + if (response != nullptr) { + response = response->Duplicate(event->Get()->Request); + } + ctx.Send(event->Sender, + new NHttp::TEvHttpProxy::TEvHttpIncomingResponse(event->Get()->Request, + response, + it->second.Error)); + it->second.DeathTime = ctx.Now() + it->second.CachePolicy.TimeToExpire; // prolong active cache items + return; + } + } else { + it = Cache.emplace(key, policy).first; + it->second.Request = event->Get()->Request; + it->second.Timeout = event->Get()->Timeout; + it->second.OutgoingRequest = it->second.Request->Duplicate(); + OutgoingRequests[it->second.OutgoingRequest.Get()] = key; + LOG_DEBUG_S(ctx, HttpLog, "OutgoingInitiate " << it->second.GetName()); + ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(it->second.OutgoingRequest, it->second.Timeout)); + } + it->second.DeathTime = ctx.Now() + it->second.CachePolicy.TimeToExpire; + it->second.Waiters.emplace_back(std::move(event)); + } + + void HandleRefresh(const NActors::TActorContext& ctx) { + while (!RefreshQueue.empty() && RefreshQueue.top().RefreshTime <= ctx.Now()) { + TRefreshRecord rrec = RefreshQueue.top(); + RefreshQueue.pop(); + auto it = Cache.find(rrec.Key); + if (it != Cache.end()) { + if (it->second.DeathTime > ctx.Now()) { + LOG_DEBUG_S(ctx, HttpLog, "OutgoingRefresh " << it->second.GetName()); + it->second.OutgoingRequest = it->second.Request->Duplicate(); + OutgoingRequests[it->second.OutgoingRequest.Get()] = it->first; + ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(it->second.OutgoingRequest, it->second.Timeout)); + } else { + LOG_DEBUG_S(ctx, HttpLog, "OutgoingForget " << it->second.GetName()); + if (it->second.OutgoingRequest) { + OutgoingRequests.erase(it->second.OutgoingRequest.Get()); + } + Cache.erase(it); + } + } + } + ctx.Schedule(RefreshTimeout, new NActors::TEvents::TEvWakeup()); + } + + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, Handle); + HFunc(NHttp::TEvHttpProxy::TEvHttpOutgoingRequest, Handle); + HFunc(NHttp::TEvHttpProxy::TEvAddListeningPort, Handle); + HFunc(NHttp::TEvHttpProxy::TEvRegisterHandler, Handle); + HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + HFunc(NHttp::TEvHttpProxy::TEvHttpOutgoingResponse, Handle); + CFunc(NActors::TEvents::TSystem::Wakeup, HandleRefresh); + } + } +}; + +const TDuration THttpOutgoingCacheActor::RefreshTimeout; + +class THttpIncomingCacheActor : public NActors::TActorBootstrapped<THttpIncomingCacheActor>, THttpConfig { +public: + using TBase = NActors::TActorBootstrapped<THttpIncomingCacheActor>; + NActors::TActorId HttpProxyId; + TGetCachePolicy GetCachePolicy; + static constexpr TDuration RefreshTimeout = TDuration::Seconds(1); + THashMap<TString, TActorId> Handlers; + + struct TCacheKey { + TString Host; + TString URL; + TString Headers; + + operator size_t() const { + return MultiHash(Host, URL, Headers); + } + + TString GetId() const { + return MD5::Calc(Host + ':' + URL + ':' + Headers); + } + }; + + struct TCacheRecord { + TInstant RefreshTime; + TInstant DeathTime; + TCachePolicy CachePolicy; + TString CacheId; + NHttp::THttpIncomingRequestPtr Request; + TDuration Timeout; + NHttp::THttpOutgoingResponsePtr Response; + TVector<NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr> Waiters; + ui32 Retries = 0; + bool Enqueued = false; + + TCacheRecord(const TCachePolicy cachePolicy) + : CachePolicy(cachePolicy) + {} + + bool IsValid() const { + return Response != nullptr; + } + + void InitRequest(NHttp::THttpIncomingRequestPtr request) { + Request = request; + if (CachePolicy.TimeToExpire) { + DeathTime = NActors::TlsActivationContext->Now() + CachePolicy.TimeToExpire; + } + } + + void UpdateResponse(NHttp::THttpOutgoingResponsePtr response, const TString& error, TInstant now) { + if (error.empty() || !CachePolicy.KeepOnError) { + Response = response; + } + Retries = 0; + if (CachePolicy.TimeToRefresh) { + RefreshTime = now + CachePolicy.TimeToRefresh; + if (CachePolicy.PaceToRefresh) { + RefreshTime += TDuration::MilliSeconds(RandomNumber<ui64>() % CachePolicy.PaceToRefresh.MilliSeconds()); + } + } + } + + void UpdateExpireTime() { + if (CachePolicy.TimeToExpire) { + DeathTime = NActors::TlsActivationContext->Now() + CachePolicy.TimeToExpire; + } + } + + TString GetName() const { + return TStringBuilder() << (Request->Secure ? "https://" : "http://") << Request->Host << Request->URL + << " (" << CacheId << ")"; + } + }; + + struct TRefreshRecord { + TCacheKey Key; + TInstant RefreshTime; + + bool operator <(const TRefreshRecord& b) const { + return RefreshTime > b.RefreshTime; + } + }; + + THashMap<TCacheKey, TCacheRecord> Cache; + TPriorityQueue<TRefreshRecord> RefreshQueue; + THashMap<THttpIncomingRequest*, TCacheKey> IncomingRequests; + + THttpIncomingCacheActor(const NActors::TActorId& httpProxyId, TGetCachePolicy getCachePolicy) + : HttpProxyId(httpProxyId) + , GetCachePolicy(std::move(getCachePolicy)) + {} + + void Bootstrap(const NActors::TActorContext&) { + // + Become(&THttpIncomingCacheActor::StateWork, RefreshTimeout, new NActors::TEvents::TEvWakeup()); + } + + static TString GetCacheHeadersKey(const NHttp::THttpIncomingRequest* request, const TCachePolicy& policy) { + TStringBuilder key; + if (!policy.HeadersToCacheKey.empty()) { + NHttp::THeaders headers(request->Headers); + for (const TString& header : policy.HeadersToCacheKey) { + key << headers[header]; + } + } + return key; + } + + static TCacheKey GetCacheKey(const NHttp::THttpIncomingRequest* request, const TCachePolicy& policy) { + return { ToString(request->Host), ToString(request->URL), GetCacheHeadersKey(request, policy) }; + } + + TActorId GetRequestHandler(NHttp::THttpIncomingRequestPtr request) { + TStringBuf url = request->URL.Before('?'); + THashMap<TString, TActorId>::iterator it; + while (!url.empty()) { + it = Handlers.find(url); + if (it != Handlers.end()) { + return it->second; + } else { + if (url.EndsWith('/')) { + url.Trunc(url.size() - 1); + } + size_t pos = url.rfind('/'); + if (pos == TStringBuf::npos) { + break; + } else { + url = url.substr(0, pos + 1); + } + } + } + return {}; + } + + void SendCacheRequest(const TCacheKey& cacheKey, TCacheRecord& cacheRecord, const NActors::TActorContext& ctx) { + cacheRecord.Request = cacheRecord.Request->Duplicate(); + IncomingRequests[cacheRecord.Request.Get()] = cacheKey; + TActorId handler = GetRequestHandler(cacheRecord.Request); + if (handler) { + Send(handler, new NHttp::TEvHttpProxy::TEvHttpIncomingRequest(cacheRecord.Request)); + } else { + LOG_ERROR_S(ctx, HttpLog, "Can't find cache handler for " << cacheRecord.GetName()); + } + } + + void DropCacheRecord(THashMap<TCacheKey, TCacheRecord>::iterator it) { + if (it->second.Request) { + IncomingRequests.erase(it->second.Request.Get()); + } + for (auto& waiter : it->second.Waiters) { + NHttp::THttpOutgoingResponsePtr response; + response = waiter->Get()->Request->CreateResponseGatewayTimeout("Timeout", "text/plain"); + Send(waiter->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); + } + Cache.erase(it); + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) { + ctx.Send(event->Forward(HttpProxyId)); + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpOutgoingRequest::TPtr event, const NActors::TActorContext& ctx) { + ctx.Send(event->Forward(HttpProxyId)); + } + + void Handle(NHttp::TEvHttpProxy::TEvAddListeningPort::TPtr event, const NActors::TActorContext& ctx) { + ctx.Send(event->Forward(HttpProxyId)); + } + + void Handle(NHttp::TEvHttpProxy::TEvRegisterHandler::TPtr event, const NActors::TActorContext& ctx) { + Handlers[event->Get()->Path] = event->Get()->Handler; + ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler(event->Get()->Path, ctx.SelfID)); + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpOutgoingResponse::TPtr event, const NActors::TActorContext& ctx) { + NHttp::THttpIncomingRequestPtr request(event->Get()->Response->GetRequest()); + NHttp::THttpOutgoingResponsePtr response(event->Get()->Response); + auto itRequests = IncomingRequests.find(request.Get()); + if (itRequests == IncomingRequests.end()) { + LOG_ERROR_S(ctx, HttpLog, "Cache received response to unknown request " << request->Host << request->URL); + return; + } + + TCacheKey key = itRequests->second; + auto it = Cache.find(key); + if (it == Cache.end()) { + LOG_ERROR_S(ctx, HttpLog, "Cache received response to unknown cache key " << request->Host << request->URL); + return; + } + + IncomingRequests.erase(itRequests); + TCacheRecord& cacheRecord = it->second; + TStringBuf status; + TString error; + + if (event->Get()->Response != nullptr) { + status = event->Get()->Response->Status; + if (!status.StartsWith("2")) { + error = event->Get()->Response->Message; + } + } + if (cacheRecord.CachePolicy.RetriesCount > 0) { + auto itStatusToRetry = std::find(cacheRecord.CachePolicy.StatusesToRetry.begin(), cacheRecord.CachePolicy.StatusesToRetry.end(), status); + if (itStatusToRetry != cacheRecord.CachePolicy.StatusesToRetry.end()) { + if (cacheRecord.Retries < cacheRecord.CachePolicy.RetriesCount) { + ++cacheRecord.Retries; + LOG_WARN_S(ctx, HttpLog, "IncomingRetry " << cacheRecord.GetName() << ": " << status << " " << error); + SendCacheRequest(key, cacheRecord, ctx); + return; + } + } + } + for (auto& waiter : cacheRecord.Waiters) { + NHttp::THttpOutgoingResponsePtr response2; + response2 = response->Duplicate(waiter->Get()->Request); + ctx.Send(waiter->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response2)); + } + cacheRecord.Waiters.clear(); + if (!error.empty()) { + LOG_WARN_S(ctx, HttpLog, "Error from " << cacheRecord.GetName() << ": " << error); + if (!cacheRecord.Response) { + LOG_DEBUG_S(ctx, HttpLog, "IncomingDiscard " << cacheRecord.GetName()); + DropCacheRecord(it); + return; + } + } + if (cacheRecord.CachePolicy.TimeToRefresh) { + LOG_DEBUG_S(ctx, HttpLog, "IncomingUpdate " << cacheRecord.GetName()); + cacheRecord.UpdateResponse(response, error, ctx.Now()); + if (!cacheRecord.Enqueued) { + RefreshQueue.push({it->first, it->second.RefreshTime}); + cacheRecord.Enqueued = true; + } + LOG_DEBUG_S(ctx, HttpLog, "IncomingSchedule " << cacheRecord.GetName() << " at " << cacheRecord.RefreshTime << " until " << cacheRecord.DeathTime); + } else { + LOG_DEBUG_S(ctx, HttpLog, "IncomingDrop " << cacheRecord.GetName()); + DropCacheRecord(it); + } + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) { + const NHttp::THttpIncomingRequest* request = event->Get()->Request.Get(); + TCachePolicy policy = GetCachePolicy(request); + if (policy.TimeToExpire == TDuration() && policy.RetriesCount == 0) { + TActorId handler = GetRequestHandler(event->Get()->Request); + if (handler) { + ctx.Send(event->Forward(handler)); + } + return; + } + auto key = GetCacheKey(request, policy); + auto it = Cache.find(key); + if (it != Cache.end() && !policy.DiscardCache) { + it->second.UpdateExpireTime(); + if (it->second.IsValid()) { + LOG_DEBUG_S(ctx, HttpLog, "IncomingRespond " + << it->second.GetName() + << " (" + << ((it->second.Response != nullptr) ? ToString(it->second.Response->Size()) : TString("error")) + << ")"); + NHttp::THttpOutgoingResponsePtr response = it->second.Response; + if (response != nullptr) { + response = response->Duplicate(event->Get()->Request); + } + ctx.Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); + return; + } + } else { + it = Cache.emplace(key, policy).first; + it->second.CacheId = key.GetId(); // for debugging + it->second.InitRequest(event->Get()->Request); + if (policy.DiscardCache) { + LOG_DEBUG_S(ctx, HttpLog, "IncomingDiscardCache " << it->second.GetName()); + } + LOG_DEBUG_S(ctx, HttpLog, "IncomingInitiate " << it->second.GetName()); + SendCacheRequest(key, it->second, ctx); + } + it->second.Waiters.emplace_back(std::move(event)); + } + + void HandleRefresh(const NActors::TActorContext& ctx) { + while (!RefreshQueue.empty() && RefreshQueue.top().RefreshTime <= ctx.Now()) { + TRefreshRecord rrec = RefreshQueue.top(); + RefreshQueue.pop(); + auto it = Cache.find(rrec.Key); + if (it != Cache.end()) { + it->second.Enqueued = false; + if (it->second.DeathTime > ctx.Now()) { + LOG_DEBUG_S(ctx, HttpLog, "IncomingRefresh " << it->second.GetName()); + SendCacheRequest(it->first, it->second, ctx); + } else { + LOG_DEBUG_S(ctx, HttpLog, "IncomingForget " << it->second.GetName()); + DropCacheRecord(it); + } + } + } + ctx.Schedule(RefreshTimeout, new NActors::TEvents::TEvWakeup()); + } + + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, Handle); + HFunc(NHttp::TEvHttpProxy::TEvHttpOutgoingRequest, Handle); + HFunc(NHttp::TEvHttpProxy::TEvAddListeningPort, Handle); + HFunc(NHttp::TEvHttpProxy::TEvRegisterHandler, Handle); + HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + HFunc(NHttp::TEvHttpProxy::TEvHttpOutgoingResponse, Handle); + CFunc(NActors::TEvents::TSystem::Wakeup, HandleRefresh); + } + } +}; + +TCachePolicy GetDefaultCachePolicy(const THttpRequest* request, const TCachePolicy& defaultPolicy) { + TCachePolicy policy = defaultPolicy; + THeaders headers(request->Headers); + TStringBuf cacheControl(headers["Cache-Control"]); + while (TStringBuf cacheItem = cacheControl.NextTok(',')) { + Trim(cacheItem, ' '); + if (cacheItem == "no-store" || cacheItem == "no-cache") { + policy.DiscardCache = true; + } + TStringBuf itemName = cacheItem.NextTok('='); + TrimEnd(itemName, ' '); + TrimBegin(cacheItem, ' '); + if (itemName == "max-age") { + policy.TimeToRefresh = policy.TimeToExpire = TDuration::Seconds(FromString(cacheItem)); + } + if (itemName == "min-fresh") { + policy.TimeToRefresh = policy.TimeToExpire = TDuration::Seconds(FromString(cacheItem)); + } + if (itemName == "stale-if-error") { + policy.KeepOnError = true; + } + } + return policy; +} + NActors::IActor* CreateHttpCache(const NActors::TActorId& httpProxyId, TGetCachePolicy cachePolicy) { - return new THttpOutgoingCacheActor(httpProxyId, std::move(cachePolicy)); -} - -NActors::IActor* CreateOutgoingHttpCache(const NActors::TActorId& httpProxyId, TGetCachePolicy cachePolicy) { - return new THttpOutgoingCacheActor(httpProxyId, std::move(cachePolicy)); -} - -NActors::IActor* CreateIncomingHttpCache(const NActors::TActorId& httpProxyId, TGetCachePolicy cachePolicy) { - return new THttpIncomingCacheActor(httpProxyId, std::move(cachePolicy)); -} - -} + return new THttpOutgoingCacheActor(httpProxyId, std::move(cachePolicy)); +} + +NActors::IActor* CreateOutgoingHttpCache(const NActors::TActorId& httpProxyId, TGetCachePolicy cachePolicy) { + return new THttpOutgoingCacheActor(httpProxyId, std::move(cachePolicy)); +} + +NActors::IActor* CreateIncomingHttpCache(const NActors::TActorId& httpProxyId, TGetCachePolicy cachePolicy) { + return new THttpIncomingCacheActor(httpProxyId, std::move(cachePolicy)); +} + +} diff --git a/library/cpp/actors/http/http_cache.h b/library/cpp/actors/http/http_cache.h index ac38bdcac8..567a8105f0 100644 --- a/library/cpp/actors/http/http_cache.h +++ b/library/cpp/actors/http/http_cache.h @@ -1,27 +1,27 @@ -#pragma once +#pragma once #include <library/cpp/actors/core/actor.h> -#include "http.h" - -namespace NHttp { - -struct TCachePolicy { - TDuration TimeToExpire; - TDuration TimeToRefresh; - TDuration PaceToRefresh; - bool KeepOnError = false; - bool DiscardCache = false; - TArrayRef<TString> HeadersToCacheKey; - TArrayRef<TString> StatusesToRetry; - ui32 RetriesCount = 0; - - TCachePolicy() = default; -}; - -using TGetCachePolicy = std::function<TCachePolicy(const THttpRequest*)>; - +#include "http.h" + +namespace NHttp { + +struct TCachePolicy { + TDuration TimeToExpire; + TDuration TimeToRefresh; + TDuration PaceToRefresh; + bool KeepOnError = false; + bool DiscardCache = false; + TArrayRef<TString> HeadersToCacheKey; + TArrayRef<TString> StatusesToRetry; + ui32 RetriesCount = 0; + + TCachePolicy() = default; +}; + +using TGetCachePolicy = std::function<TCachePolicy(const THttpRequest*)>; + NActors::IActor* CreateHttpCache(const NActors::TActorId& httpProxyId, TGetCachePolicy cachePolicy); -NActors::IActor* CreateOutgoingHttpCache(const NActors::TActorId& httpProxyId, TGetCachePolicy cachePolicy); -NActors::IActor* CreateIncomingHttpCache(const NActors::TActorId& httpProxyId, TGetCachePolicy cachePolicy); -TCachePolicy GetDefaultCachePolicy(const THttpRequest* request, const TCachePolicy& policy = TCachePolicy()); - -} +NActors::IActor* CreateOutgoingHttpCache(const NActors::TActorId& httpProxyId, TGetCachePolicy cachePolicy); +NActors::IActor* CreateIncomingHttpCache(const NActors::TActorId& httpProxyId, TGetCachePolicy cachePolicy); +TCachePolicy GetDefaultCachePolicy(const THttpRequest* request, const TCachePolicy& policy = TCachePolicy()); + +} diff --git a/library/cpp/actors/http/http_config.h b/library/cpp/actors/http/http_config.h index faeff79449..eeafd2a019 100644 --- a/library/cpp/actors/http/http_config.h +++ b/library/cpp/actors/http/http_config.h @@ -1,19 +1,19 @@ -#pragma once -#include <util/network/sock.h> +#pragma once +#include <util/network/sock.h> #include <library/cpp/actors/core/log.h> #include <library/cpp/actors/protos/services_common.pb.h> - -namespace NHttp { - -struct THttpConfig { - static constexpr NActors::NLog::EComponent HttpLog = NActorsServices::EServiceCommon::HTTP; - static constexpr size_t BUFFER_SIZE = 64 * 1024; - static constexpr size_t BUFFER_MIN_STEP = 10 * 1024; - static constexpr int LISTEN_QUEUE = 10; - static constexpr TDuration SOCKET_TIMEOUT = TDuration::MilliSeconds(60000); - static constexpr TDuration CONNECTION_TIMEOUT = TDuration::MilliSeconds(60000); - using SocketType = TInet6StreamSocket; - using SocketAddressType = TSockAddrInet6; -}; - -} + +namespace NHttp { + +struct THttpConfig { + static constexpr NActors::NLog::EComponent HttpLog = NActorsServices::EServiceCommon::HTTP; + static constexpr size_t BUFFER_SIZE = 64 * 1024; + static constexpr size_t BUFFER_MIN_STEP = 10 * 1024; + static constexpr int LISTEN_QUEUE = 10; + static constexpr TDuration SOCKET_TIMEOUT = TDuration::MilliSeconds(60000); + static constexpr TDuration CONNECTION_TIMEOUT = TDuration::MilliSeconds(60000); + using SocketType = TInet6StreamSocket; + using SocketAddressType = TSockAddrInet6; +}; + +} diff --git a/library/cpp/actors/http/http_proxy.cpp b/library/cpp/actors/http/http_proxy.cpp index 36c6855d93..2217838624 100644 --- a/library/cpp/actors/http/http_proxy.cpp +++ b/library/cpp/actors/http/http_proxy.cpp @@ -1,314 +1,314 @@ #include <library/cpp/actors/core/events.h> #include <library/cpp/monlib/metrics/metric_registry.h> -#include "http_proxy.h" - -namespace NHttp { - -class THttpProxy : public NActors::TActorBootstrapped<THttpProxy>, public THttpConfig { -public: - IActor* AddListeningPort(TEvHttpProxy::TEvAddListeningPort::TPtr event, const NActors::TActorContext& ctx) { - IActor* listeningSocket = CreateHttpAcceptorActor(ctx.SelfID, Poller); +#include "http_proxy.h" + +namespace NHttp { + +class THttpProxy : public NActors::TActorBootstrapped<THttpProxy>, public THttpConfig { +public: + IActor* AddListeningPort(TEvHttpProxy::TEvAddListeningPort::TPtr event, const NActors::TActorContext& ctx) { + IActor* listeningSocket = CreateHttpAcceptorActor(ctx.SelfID, Poller); TActorId acceptorId = ctx.Register(listeningSocket); - ctx.Send(event->Forward(acceptorId)); - Acceptors.emplace_back(acceptorId); - return listeningSocket; - } - - IActor* AddOutgoingConnection(const TString& address, bool secure, const NActors::TActorContext& ctx) { - IActor* connectionSocket = CreateOutgoingConnectionActor(ctx.SelfID, address, secure, Poller); + ctx.Send(event->Forward(acceptorId)); + Acceptors.emplace_back(acceptorId); + return listeningSocket; + } + + IActor* AddOutgoingConnection(const TString& address, bool secure, const NActors::TActorContext& ctx) { + IActor* connectionSocket = CreateOutgoingConnectionActor(ctx.SelfID, address, secure, Poller); TActorId connectionId = ctx.Register(connectionSocket); - Connections.emplace(connectionId); - return connectionSocket; - } - - void Bootstrap(const NActors::TActorContext& ctx) { - Poller = ctx.Register(NActors::CreatePollerActor()); - Become(&THttpProxy::StateWork); - } - + Connections.emplace(connectionId); + return connectionSocket; + } + + void Bootstrap(const NActors::TActorContext& ctx) { + Poller = ctx.Register(NActors::CreatePollerActor()); + Become(&THttpProxy::StateWork); + } + THttpProxy(NMonitoring::TMetricRegistry& sensors) - : Sensors(sensors) - {} - -protected: - STFUNC(StateWork) { - switch (ev->GetTypeRewrite()) { - HFunc(TEvHttpProxy::TEvAddListeningPort, Handle); - HFunc(TEvHttpProxy::TEvRegisterHandler, Handle); - HFunc(TEvHttpProxy::TEvHttpIncomingRequest, Handle); - HFunc(TEvHttpProxy::TEvHttpOutgoingRequest, Handle); - HFunc(TEvHttpProxy::TEvHttpIncomingResponse, Handle); - HFunc(TEvHttpProxy::TEvHttpOutgoingResponse, Handle); - HFunc(TEvHttpProxy::TEvHttpAcceptorClosed, Handle); - HFunc(TEvHttpProxy::TEvHttpConnectionClosed, Handle); - HFunc(TEvHttpProxy::TEvResolveHostRequest, Handle); - HFunc(TEvHttpProxy::TEvReportSensors, Handle); + : Sensors(sensors) + {} + +protected: + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + HFunc(TEvHttpProxy::TEvAddListeningPort, Handle); + HFunc(TEvHttpProxy::TEvRegisterHandler, Handle); + HFunc(TEvHttpProxy::TEvHttpIncomingRequest, Handle); + HFunc(TEvHttpProxy::TEvHttpOutgoingRequest, Handle); + HFunc(TEvHttpProxy::TEvHttpIncomingResponse, Handle); + HFunc(TEvHttpProxy::TEvHttpOutgoingResponse, Handle); + HFunc(TEvHttpProxy::TEvHttpAcceptorClosed, Handle); + HFunc(TEvHttpProxy::TEvHttpConnectionClosed, Handle); + HFunc(TEvHttpProxy::TEvResolveHostRequest, Handle); + HFunc(TEvHttpProxy::TEvReportSensors, Handle); HFunc(NActors::TEvents::TEvPoison, Handle); - } - } - + } + } + void PassAway() override { Send(Poller, new NActors::TEvents::TEvPoisonPill()); for (const NActors::TActorId& connection : Connections) { Send(connection, new NActors::TEvents::TEvPoisonPill()); - } + } for (const NActors::TActorId& acceptor : Acceptors) { Send(acceptor, new NActors::TEvents::TEvPoisonPill()); - } + } NActors::TActorBootstrapped<THttpProxy>::PassAway(); - } - - void Handle(TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) { - TStringBuf url = event->Get()->Request->URL.Before('?'); + } + + void Handle(TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) { + TStringBuf url = event->Get()->Request->URL.Before('?'); THashMap<TString, TActorId>::iterator it; - while (!url.empty()) { - it = Handlers.find(url); - if (it != Handlers.end()) { - ctx.Send(event->Forward(it->second)); - return; - } else { - if (url.EndsWith('/')) { - url.Trunc(url.size() - 1); - } - size_t pos = url.rfind('/'); - if (pos == TStringBuf::npos) { - break; - } else { - url = url.substr(0, pos + 1); - } - } - } - ctx.Send(event->Sender, new TEvHttpProxy::TEvHttpOutgoingResponse(event->Get()->Request->CreateResponseNotFound())); - } - - void Handle(TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) { - Y_UNUSED(event); - Y_UNUSED(ctx); - Y_FAIL("This event shouldn't be there, it should go to the http connection owner directly"); - } - - void Handle(TEvHttpProxy::TEvHttpOutgoingResponse::TPtr event, const NActors::TActorContext& ctx) { - Y_UNUSED(event); - Y_UNUSED(ctx); - Y_FAIL("This event shouldn't be there, it should go to the http connection directly"); - } - - void Handle(TEvHttpProxy::TEvHttpOutgoingRequest::TPtr event, const NActors::TActorContext& ctx) { - TStringBuf host(event->Get()->Request->Host); - bool secure(event->Get()->Request->Secure); - NActors::IActor* actor = AddOutgoingConnection(TString(host), secure, ctx); - ctx.Send(event->Forward(actor->SelfId())); - } - - void Handle(TEvHttpProxy::TEvAddListeningPort::TPtr event, const NActors::TActorContext& ctx) { - AddListeningPort(event, ctx); - } - - void Handle(TEvHttpProxy::TEvHttpAcceptorClosed::TPtr event, const NActors::TActorContext&) { - for (auto it = Acceptors.begin(); it != Acceptors.end(); ++it) { - if (*it == event->Get()->ConnectionID) { - Acceptors.erase(it); - break; - } - } - } - - void Handle(TEvHttpProxy::TEvHttpConnectionClosed::TPtr event, const NActors::TActorContext&) { - Connections.erase(event->Get()->ConnectionID); - } - - void Handle(TEvHttpProxy::TEvRegisterHandler::TPtr event, const NActors::TActorContext&) { - Handlers[event->Get()->Path] = event->Get()->Handler; - } - - void Handle(TEvHttpProxy::TEvResolveHostRequest::TPtr event, const NActors::TActorContext& ctx) { - const TString& host(event->Get()->Host); - auto it = Hosts.find(host); - if (it == Hosts.end() || it->second.DeadlineTime > ctx.Now()) { - TString addressPart; - TIpPort portPart = 0; - CrackAddress(host, addressPart, portPart); - if (IsIPv6(addressPart)) { - TSockAddrInet6 address(addressPart.c_str(), portPart); - if (it == Hosts.end()) { - it = Hosts.emplace(host, THostEntry()).first; - } - it->second.Address = address; - it->second.DeadlineTime = ctx.Now() + HostsTimeToLive; - } else { - // TODO(xenoxeno): move to another, possible blocking actor - try { - const NDns::TResolvedHost* result = NDns::CachedResolve(NDns::TResolveInfo(addressPart, portPart)); - if (result != nullptr) { - auto pAddr = result->Addr.Begin(); - while (pAddr != result->Addr.End() && pAddr->ai_family != AF_INET6) { - ++pAddr; - } - if (pAddr == result->Addr.End()) { - ctx.Send(event->Sender, new TEvHttpProxy::TEvResolveHostResponse("Invalid address family resolved")); - return; - } - TSockAddrInet6 address = {}; - static_cast<sockaddr_in6&>(address) = *reinterpret_cast<sockaddr_in6*>(pAddr->ai_addr); - LOG_DEBUG_S(ctx, HttpLog, "Host " << host << " resolved to " << address.ToString()); - if (it == Hosts.end()) { - it = Hosts.emplace(host, THostEntry()).first; - } - it->second.Address = address; - it->second.DeadlineTime = ctx.Now() + HostsTimeToLive; - } else { - ctx.Send(event->Sender, new TEvHttpProxy::TEvResolveHostResponse("Error resolving host")); - return; - } - } - catch (const yexception& e) { - ctx.Send(event->Sender, new TEvHttpProxy::TEvResolveHostResponse(e.what())); - return; - } - } - } - ctx.Send(event->Sender, new TEvHttpProxy::TEvResolveHostResponse(it->first, it->second.Address)); - } - - void Handle(TEvHttpProxy::TEvReportSensors::TPtr event, const NActors::TActorContext&) { - const TEvHttpProxy::TEvReportSensors& sensors(*event->Get()); + while (!url.empty()) { + it = Handlers.find(url); + if (it != Handlers.end()) { + ctx.Send(event->Forward(it->second)); + return; + } else { + if (url.EndsWith('/')) { + url.Trunc(url.size() - 1); + } + size_t pos = url.rfind('/'); + if (pos == TStringBuf::npos) { + break; + } else { + url = url.substr(0, pos + 1); + } + } + } + ctx.Send(event->Sender, new TEvHttpProxy::TEvHttpOutgoingResponse(event->Get()->Request->CreateResponseNotFound())); + } + + void Handle(TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) { + Y_UNUSED(event); + Y_UNUSED(ctx); + Y_FAIL("This event shouldn't be there, it should go to the http connection owner directly"); + } + + void Handle(TEvHttpProxy::TEvHttpOutgoingResponse::TPtr event, const NActors::TActorContext& ctx) { + Y_UNUSED(event); + Y_UNUSED(ctx); + Y_FAIL("This event shouldn't be there, it should go to the http connection directly"); + } + + void Handle(TEvHttpProxy::TEvHttpOutgoingRequest::TPtr event, const NActors::TActorContext& ctx) { + TStringBuf host(event->Get()->Request->Host); + bool secure(event->Get()->Request->Secure); + NActors::IActor* actor = AddOutgoingConnection(TString(host), secure, ctx); + ctx.Send(event->Forward(actor->SelfId())); + } + + void Handle(TEvHttpProxy::TEvAddListeningPort::TPtr event, const NActors::TActorContext& ctx) { + AddListeningPort(event, ctx); + } + + void Handle(TEvHttpProxy::TEvHttpAcceptorClosed::TPtr event, const NActors::TActorContext&) { + for (auto it = Acceptors.begin(); it != Acceptors.end(); ++it) { + if (*it == event->Get()->ConnectionID) { + Acceptors.erase(it); + break; + } + } + } + + void Handle(TEvHttpProxy::TEvHttpConnectionClosed::TPtr event, const NActors::TActorContext&) { + Connections.erase(event->Get()->ConnectionID); + } + + void Handle(TEvHttpProxy::TEvRegisterHandler::TPtr event, const NActors::TActorContext&) { + Handlers[event->Get()->Path] = event->Get()->Handler; + } + + void Handle(TEvHttpProxy::TEvResolveHostRequest::TPtr event, const NActors::TActorContext& ctx) { + const TString& host(event->Get()->Host); + auto it = Hosts.find(host); + if (it == Hosts.end() || it->second.DeadlineTime > ctx.Now()) { + TString addressPart; + TIpPort portPart = 0; + CrackAddress(host, addressPart, portPart); + if (IsIPv6(addressPart)) { + TSockAddrInet6 address(addressPart.c_str(), portPart); + if (it == Hosts.end()) { + it = Hosts.emplace(host, THostEntry()).first; + } + it->second.Address = address; + it->second.DeadlineTime = ctx.Now() + HostsTimeToLive; + } else { + // TODO(xenoxeno): move to another, possible blocking actor + try { + const NDns::TResolvedHost* result = NDns::CachedResolve(NDns::TResolveInfo(addressPart, portPart)); + if (result != nullptr) { + auto pAddr = result->Addr.Begin(); + while (pAddr != result->Addr.End() && pAddr->ai_family != AF_INET6) { + ++pAddr; + } + if (pAddr == result->Addr.End()) { + ctx.Send(event->Sender, new TEvHttpProxy::TEvResolveHostResponse("Invalid address family resolved")); + return; + } + TSockAddrInet6 address = {}; + static_cast<sockaddr_in6&>(address) = *reinterpret_cast<sockaddr_in6*>(pAddr->ai_addr); + LOG_DEBUG_S(ctx, HttpLog, "Host " << host << " resolved to " << address.ToString()); + if (it == Hosts.end()) { + it = Hosts.emplace(host, THostEntry()).first; + } + it->second.Address = address; + it->second.DeadlineTime = ctx.Now() + HostsTimeToLive; + } else { + ctx.Send(event->Sender, new TEvHttpProxy::TEvResolveHostResponse("Error resolving host")); + return; + } + } + catch (const yexception& e) { + ctx.Send(event->Sender, new TEvHttpProxy::TEvResolveHostResponse(e.what())); + return; + } + } + } + ctx.Send(event->Sender, new TEvHttpProxy::TEvResolveHostResponse(it->first, it->second.Address)); + } + + void Handle(TEvHttpProxy::TEvReportSensors::TPtr event, const NActors::TActorContext&) { + const TEvHttpProxy::TEvReportSensors& sensors(*event->Get()); const static TString urlNotFound = "not-found"; const TString& url = (sensors.Status == "404" ? urlNotFound : sensors.Url); - Sensors.Rate({ - {"sensor", "count"}, - {"direction", sensors.Direction}, - {"peer", sensors.Host}, + Sensors.Rate({ + {"sensor", "count"}, + {"direction", sensors.Direction}, + {"peer", sensors.Host}, {"url", url}, - {"status", sensors.Status} - })->Inc(); - Sensors.HistogramRate({ - {"sensor", "time_us"}, - {"direction", sensors.Direction}, - {"peer", sensors.Host}, + {"status", sensors.Status} + })->Inc(); + Sensors.HistogramRate({ + {"sensor", "time_us"}, + {"direction", sensors.Direction}, + {"peer", sensors.Host}, {"url", url}, - {"status", sensors.Status} - }, - NMonitoring::ExplicitHistogram({1, 5, 10, 50, 100, 500, 1000, 5000, 10000, 30000, 60000}))->Record(sensors.Time.MicroSeconds()); - Sensors.HistogramRate({ - {"sensor", "time_ms"}, - {"direction", sensors.Direction}, - {"peer", sensors.Host}, + {"status", sensors.Status} + }, + NMonitoring::ExplicitHistogram({1, 5, 10, 50, 100, 500, 1000, 5000, 10000, 30000, 60000}))->Record(sensors.Time.MicroSeconds()); + Sensors.HistogramRate({ + {"sensor", "time_ms"}, + {"direction", sensors.Direction}, + {"peer", sensors.Host}, {"url", url}, - {"status", sensors.Status} - }, - NMonitoring::ExplicitHistogram({1, 5, 10, 50, 100, 500, 1000, 5000, 10000, 30000, 60000}))->Record(sensors.Time.MilliSeconds()); - } - + {"status", sensors.Status} + }, + NMonitoring::ExplicitHistogram({1, 5, 10, 50, 100, 500, 1000, 5000, 10000, 30000, 60000}))->Record(sensors.Time.MilliSeconds()); + } + void Handle(NActors::TEvents::TEvPoison::TPtr, const NActors::TActorContext&) { PassAway(); } NActors::TActorId Poller; TVector<TActorId> Acceptors; - - struct THostEntry { - TSockAddrInet6 Address; - TInstant DeadlineTime; - }; - - static constexpr TDuration HostsTimeToLive = TDuration::Seconds(60); - - THashMap<TString, THostEntry> Hosts; + + struct THostEntry { + TSockAddrInet6 Address; + TInstant DeadlineTime; + }; + + static constexpr TDuration HostsTimeToLive = TDuration::Seconds(60); + + THashMap<TString, THostEntry> Hosts; THashMap<TString, TActorId> Handlers; THashSet<TActorId> Connections; // outgoing NMonitoring::TMetricRegistry& Sensors; -}; - -TEvHttpProxy::TEvReportSensors* BuildOutgoingRequestSensors(const THttpOutgoingRequestPtr& request, const THttpIncomingResponsePtr& response) { - return new TEvHttpProxy::TEvReportSensors( - "out", - request->Host, - request->URL.Before('?'), - response ? response->Status : "504", +}; + +TEvHttpProxy::TEvReportSensors* BuildOutgoingRequestSensors(const THttpOutgoingRequestPtr& request, const THttpIncomingResponsePtr& response) { + return new TEvHttpProxy::TEvReportSensors( + "out", + request->Host, + request->URL.Before('?'), + response ? response->Status : "504", TDuration::Seconds(std::abs(request->Timer.Passed())) - ); -} - -TEvHttpProxy::TEvReportSensors* BuildIncomingRequestSensors(const THttpIncomingRequestPtr& request, const THttpOutgoingResponsePtr& response) { - return new TEvHttpProxy::TEvReportSensors( - "in", - request->Host, - request->URL.Before('?'), - response->Status, + ); +} + +TEvHttpProxy::TEvReportSensors* BuildIncomingRequestSensors(const THttpIncomingRequestPtr& request, const THttpOutgoingResponsePtr& response) { + return new TEvHttpProxy::TEvReportSensors( + "in", + request->Host, + request->URL.Before('?'), + response->Status, TDuration::Seconds(std::abs(request->Timer.Passed())) - ); -} - + ); +} + NActors::IActor* CreateHttpProxy(NMonitoring::TMetricRegistry& sensors) { - return new THttpProxy(sensors); -} - -bool IsIPv6(const TString& host) { - return host.find_first_not_of(":0123456789abcdef") == TString::npos; -} - -bool CrackURL(TStringBuf url, TStringBuf& scheme, TStringBuf& host, TStringBuf& uri) { - url.TrySplit("://", scheme, url); - auto pos = url.find('/'); - if (pos == TStringBuf::npos) { - host = url; - } else { - host = url.substr(0, pos); - uri = url.substr(pos); - } - return true; -} - -void CrackAddress(const TString& address, TString& hostname, TIpPort& port) { - size_t first_colon_pos = address.find(':'); - if (first_colon_pos != TString::npos) { - size_t last_colon_pos = address.rfind(':'); - if (last_colon_pos == first_colon_pos) { - // only one colon, simple case - port = FromStringWithDefault<TIpPort>(address.substr(first_colon_pos + 1), 0); - hostname = address.substr(0, first_colon_pos); - } else { - // ipv6? - size_t closing_bracket_pos = address.rfind(']'); - if (closing_bracket_pos == TString::npos || closing_bracket_pos > last_colon_pos) { - // whole address is ipv6 host - hostname = address; - } else { - port = FromStringWithDefault<TIpPort>(address.substr(last_colon_pos + 1), 0); - hostname = address.substr(0, last_colon_pos); - } - if (hostname.StartsWith('[') && hostname.EndsWith(']')) { - hostname = hostname.substr(1, hostname.size() - 2); - } - } - } else { - hostname = address; - } -} - - -void TrimBegin(TStringBuf& target, char delim) { - while (!target.empty() && *target.begin() == delim) { - target.Skip(1); - } -} - -void TrimEnd(TStringBuf& target, char delim) { - while (!target.empty() && target.back() == delim) { - target.Trunc(target.size() - 1); - } -} - -void Trim(TStringBuf& target, char delim) { - TrimBegin(target, delim); - TrimEnd(target, delim); -} - -void TrimEnd(TString& target, char delim) { - while (!target.empty() && target.back() == delim) { - target.resize(target.size() - 1); - } -} - -} + return new THttpProxy(sensors); +} + +bool IsIPv6(const TString& host) { + return host.find_first_not_of(":0123456789abcdef") == TString::npos; +} + +bool CrackURL(TStringBuf url, TStringBuf& scheme, TStringBuf& host, TStringBuf& uri) { + url.TrySplit("://", scheme, url); + auto pos = url.find('/'); + if (pos == TStringBuf::npos) { + host = url; + } else { + host = url.substr(0, pos); + uri = url.substr(pos); + } + return true; +} + +void CrackAddress(const TString& address, TString& hostname, TIpPort& port) { + size_t first_colon_pos = address.find(':'); + if (first_colon_pos != TString::npos) { + size_t last_colon_pos = address.rfind(':'); + if (last_colon_pos == first_colon_pos) { + // only one colon, simple case + port = FromStringWithDefault<TIpPort>(address.substr(first_colon_pos + 1), 0); + hostname = address.substr(0, first_colon_pos); + } else { + // ipv6? + size_t closing_bracket_pos = address.rfind(']'); + if (closing_bracket_pos == TString::npos || closing_bracket_pos > last_colon_pos) { + // whole address is ipv6 host + hostname = address; + } else { + port = FromStringWithDefault<TIpPort>(address.substr(last_colon_pos + 1), 0); + hostname = address.substr(0, last_colon_pos); + } + if (hostname.StartsWith('[') && hostname.EndsWith(']')) { + hostname = hostname.substr(1, hostname.size() - 2); + } + } + } else { + hostname = address; + } +} + + +void TrimBegin(TStringBuf& target, char delim) { + while (!target.empty() && *target.begin() == delim) { + target.Skip(1); + } +} + +void TrimEnd(TStringBuf& target, char delim) { + while (!target.empty() && target.back() == delim) { + target.Trunc(target.size() - 1); + } +} + +void Trim(TStringBuf& target, char delim) { + TrimBegin(target, delim); + TrimEnd(target, delim); +} + +void TrimEnd(TString& target, char delim) { + while (!target.empty() && target.back() == delim) { + target.resize(target.size() - 1); + } +} + +} diff --git a/library/cpp/actors/http/http_proxy.h b/library/cpp/actors/http/http_proxy.h index afd0170997..ce77bf8d5f 100644 --- a/library/cpp/actors/http/http_proxy.h +++ b/library/cpp/actors/http/http_proxy.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include <library/cpp/actors/core/actorsystem.h> #include <library/cpp/actors/core/actor.h> #include <library/cpp/actors/core/hfunc.h> @@ -9,231 +9,231 @@ #include <library/cpp/actors/interconnect/poller_actor.h> #include <library/cpp/dns/cache.h> #include <library/cpp/monlib/metrics/metric_registry.h> -#include <util/generic/variant.h> -#include "http.h" -#include "http_proxy_ssl.h" - -namespace NHttp { - -struct TSocketDescriptor : NActors::TSharedDescriptor, THttpConfig { - SocketType Socket; - - int GetDescriptor() override { - return static_cast<SOCKET>(Socket); - } -}; - -struct TEvHttpProxy { - enum EEv { +#include <util/generic/variant.h> +#include "http.h" +#include "http_proxy_ssl.h" + +namespace NHttp { + +struct TSocketDescriptor : NActors::TSharedDescriptor, THttpConfig { + SocketType Socket; + + int GetDescriptor() override { + return static_cast<SOCKET>(Socket); + } +}; + +struct TEvHttpProxy { + enum EEv { EvAddListeningPort = EventSpaceBegin(NActors::TEvents::ES_HTTP), - EvConfirmListen, - EvRegisterHandler, - EvHttpIncomingRequest, - EvHttpOutgoingRequest, - EvHttpIncomingResponse, - EvHttpOutgoingResponse, - EvHttpConnectionOpened, - EvHttpConnectionClosed, - EvHttpAcceptorClosed, - EvResolveHostRequest, - EvResolveHostResponse, - EvReportSensors, - EvEnd - }; - + EvConfirmListen, + EvRegisterHandler, + EvHttpIncomingRequest, + EvHttpOutgoingRequest, + EvHttpIncomingResponse, + EvHttpOutgoingResponse, + EvHttpConnectionOpened, + EvHttpConnectionClosed, + EvHttpAcceptorClosed, + EvResolveHostRequest, + EvResolveHostResponse, + EvReportSensors, + EvEnd + }; + static_assert(EvEnd < EventSpaceEnd(NActors::TEvents::ES_HTTP), "ES_HTTP event space is too small."); - - struct TEvAddListeningPort : NActors::TEventLocal<TEvAddListeningPort, EvAddListeningPort> { - TIpPort Port; - TString WorkerName; - bool Secure = false; - TString CertificateFile; - TString PrivateKeyFile; + + struct TEvAddListeningPort : NActors::TEventLocal<TEvAddListeningPort, EvAddListeningPort> { + TIpPort Port; + TString WorkerName; + bool Secure = false; + TString CertificateFile; + TString PrivateKeyFile; TString SslCertificatePem; - - TEvAddListeningPort(TIpPort port) - : Port(port) - {} - - TEvAddListeningPort(TIpPort port, const TString& workerName) - : Port(port) - , WorkerName(workerName) - {} - }; - - struct TEvConfirmListen : NActors::TEventLocal<TEvConfirmListen, EvConfirmListen> { - THttpConfig::SocketAddressType Address; - - TEvConfirmListen(const THttpConfig::SocketAddressType& address) - : Address(address) - {} - }; - - struct TEvRegisterHandler : NActors::TEventLocal<TEvRegisterHandler, EvRegisterHandler> { - TString Path; + + TEvAddListeningPort(TIpPort port) + : Port(port) + {} + + TEvAddListeningPort(TIpPort port, const TString& workerName) + : Port(port) + , WorkerName(workerName) + {} + }; + + struct TEvConfirmListen : NActors::TEventLocal<TEvConfirmListen, EvConfirmListen> { + THttpConfig::SocketAddressType Address; + + TEvConfirmListen(const THttpConfig::SocketAddressType& address) + : Address(address) + {} + }; + + struct TEvRegisterHandler : NActors::TEventLocal<TEvRegisterHandler, EvRegisterHandler> { + TString Path; TActorId Handler; - + TEvRegisterHandler(const TString& path, const TActorId& handler) - : Path(path) - , Handler(handler) - {} - }; - - struct TEvHttpIncomingRequest : NActors::TEventLocal<TEvHttpIncomingRequest, EvHttpIncomingRequest> { - THttpIncomingRequestPtr Request; - - TEvHttpIncomingRequest(THttpIncomingRequestPtr request) - : Request(std::move(request)) - {} - }; - - struct TEvHttpOutgoingRequest : NActors::TEventLocal<TEvHttpOutgoingRequest, EvHttpOutgoingRequest> { - THttpOutgoingRequestPtr Request; - TDuration Timeout; - - TEvHttpOutgoingRequest(THttpOutgoingRequestPtr request) - : Request(std::move(request)) - {} - - TEvHttpOutgoingRequest(THttpOutgoingRequestPtr request, TDuration timeout) - : Request(std::move(request)) - , Timeout(timeout) - {} - }; - - struct TEvHttpIncomingResponse : NActors::TEventLocal<TEvHttpIncomingResponse, EvHttpIncomingResponse> { - THttpOutgoingRequestPtr Request; - THttpIncomingResponsePtr Response; - TString Error; - - TEvHttpIncomingResponse(THttpOutgoingRequestPtr request, THttpIncomingResponsePtr response, const TString& error) - : Request(std::move(request)) - , Response(std::move(response)) - , Error(error) - {} - - TEvHttpIncomingResponse(THttpOutgoingRequestPtr request, THttpIncomingResponsePtr response) - : Request(std::move(request)) - , Response(std::move(response)) - {} - - TString GetError() const { - TStringBuilder error; - if (Response != nullptr && !Response->Status.StartsWith('2')) { - error << Response->Status << ' ' << Response->Message; - } - if (!Error.empty()) { - if (!error.empty()) { - error << ';'; - } - error << Error; - } - return error; - } - }; - - struct TEvHttpOutgoingResponse : NActors::TEventLocal<TEvHttpOutgoingResponse, EvHttpOutgoingResponse> { - THttpOutgoingResponsePtr Response; - - TEvHttpOutgoingResponse(THttpOutgoingResponsePtr response) - : Response(std::move(response)) - {} - }; - - struct TEvHttpConnectionOpened : NActors::TEventLocal<TEvHttpConnectionOpened, EvHttpConnectionOpened> { - TString PeerAddress; + : Path(path) + , Handler(handler) + {} + }; + + struct TEvHttpIncomingRequest : NActors::TEventLocal<TEvHttpIncomingRequest, EvHttpIncomingRequest> { + THttpIncomingRequestPtr Request; + + TEvHttpIncomingRequest(THttpIncomingRequestPtr request) + : Request(std::move(request)) + {} + }; + + struct TEvHttpOutgoingRequest : NActors::TEventLocal<TEvHttpOutgoingRequest, EvHttpOutgoingRequest> { + THttpOutgoingRequestPtr Request; + TDuration Timeout; + + TEvHttpOutgoingRequest(THttpOutgoingRequestPtr request) + : Request(std::move(request)) + {} + + TEvHttpOutgoingRequest(THttpOutgoingRequestPtr request, TDuration timeout) + : Request(std::move(request)) + , Timeout(timeout) + {} + }; + + struct TEvHttpIncomingResponse : NActors::TEventLocal<TEvHttpIncomingResponse, EvHttpIncomingResponse> { + THttpOutgoingRequestPtr Request; + THttpIncomingResponsePtr Response; + TString Error; + + TEvHttpIncomingResponse(THttpOutgoingRequestPtr request, THttpIncomingResponsePtr response, const TString& error) + : Request(std::move(request)) + , Response(std::move(response)) + , Error(error) + {} + + TEvHttpIncomingResponse(THttpOutgoingRequestPtr request, THttpIncomingResponsePtr response) + : Request(std::move(request)) + , Response(std::move(response)) + {} + + TString GetError() const { + TStringBuilder error; + if (Response != nullptr && !Response->Status.StartsWith('2')) { + error << Response->Status << ' ' << Response->Message; + } + if (!Error.empty()) { + if (!error.empty()) { + error << ';'; + } + error << Error; + } + return error; + } + }; + + struct TEvHttpOutgoingResponse : NActors::TEventLocal<TEvHttpOutgoingResponse, EvHttpOutgoingResponse> { + THttpOutgoingResponsePtr Response; + + TEvHttpOutgoingResponse(THttpOutgoingResponsePtr response) + : Response(std::move(response)) + {} + }; + + struct TEvHttpConnectionOpened : NActors::TEventLocal<TEvHttpConnectionOpened, EvHttpConnectionOpened> { + TString PeerAddress; TActorId ConnectionID; - + TEvHttpConnectionOpened(const TString& peerAddress, const TActorId& connectionID) - : PeerAddress(peerAddress) - , ConnectionID(connectionID) - {} - }; - - struct TEvHttpConnectionClosed : NActors::TEventLocal<TEvHttpConnectionClosed, EvHttpConnectionClosed> { + : PeerAddress(peerAddress) + , ConnectionID(connectionID) + {} + }; + + struct TEvHttpConnectionClosed : NActors::TEventLocal<TEvHttpConnectionClosed, EvHttpConnectionClosed> { TActorId ConnectionID; - TDeque<THttpIncomingRequestPtr> RecycledRequests; - + TDeque<THttpIncomingRequestPtr> RecycledRequests; + TEvHttpConnectionClosed(const TActorId& connectionID) - : ConnectionID(connectionID) - {} - + : ConnectionID(connectionID) + {} + TEvHttpConnectionClosed(const TActorId& connectionID, TDeque<THttpIncomingRequestPtr> recycledRequests) - : ConnectionID(connectionID) - , RecycledRequests(std::move(recycledRequests)) - {} - }; - - struct TEvHttpAcceptorClosed : NActors::TEventLocal<TEvHttpAcceptorClosed, EvHttpAcceptorClosed> { + : ConnectionID(connectionID) + , RecycledRequests(std::move(recycledRequests)) + {} + }; + + struct TEvHttpAcceptorClosed : NActors::TEventLocal<TEvHttpAcceptorClosed, EvHttpAcceptorClosed> { TActorId ConnectionID; - + TEvHttpAcceptorClosed(const TActorId& connectionID) - : ConnectionID(connectionID) - {} - }; - - struct TEvResolveHostRequest : NActors::TEventLocal<TEvResolveHostRequest, EvResolveHostRequest> { - TString Host; - - TEvResolveHostRequest(const TString& host) - : Host(host) - {} - }; - - struct TEvResolveHostResponse : NActors::TEventLocal<TEvResolveHostResponse, EvResolveHostResponse> { - TString Host; - TSockAddrInet6 Address; - TString Error; - - TEvResolveHostResponse(const TString& host, const TSockAddrInet6& address) - : Host(host) - , Address(address) - {} - - TEvResolveHostResponse(const TString& error) - : Error(error) - {} - }; - - struct TEvReportSensors : NActors::TEventLocal<TEvReportSensors, EvReportSensors> { - TString Direction; - TString Host; - TString Url; - TString Status; - TDuration Time; - - TEvReportSensors( - TStringBuf direction, - TStringBuf host, - TStringBuf url, - TStringBuf status, - TDuration time) - : Direction(direction) - , Host(host) - , Url(url) - , Status(status) - , Time(time) - {} - }; -}; - -struct TEndpointInfo { + : ConnectionID(connectionID) + {} + }; + + struct TEvResolveHostRequest : NActors::TEventLocal<TEvResolveHostRequest, EvResolveHostRequest> { + TString Host; + + TEvResolveHostRequest(const TString& host) + : Host(host) + {} + }; + + struct TEvResolveHostResponse : NActors::TEventLocal<TEvResolveHostResponse, EvResolveHostResponse> { + TString Host; + TSockAddrInet6 Address; + TString Error; + + TEvResolveHostResponse(const TString& host, const TSockAddrInet6& address) + : Host(host) + , Address(address) + {} + + TEvResolveHostResponse(const TString& error) + : Error(error) + {} + }; + + struct TEvReportSensors : NActors::TEventLocal<TEvReportSensors, EvReportSensors> { + TString Direction; + TString Host; + TString Url; + TString Status; + TDuration Time; + + TEvReportSensors( + TStringBuf direction, + TStringBuf host, + TStringBuf url, + TStringBuf status, + TDuration time) + : Direction(direction) + , Host(host) + , Url(url) + , Status(status) + , Time(time) + {} + }; +}; + +struct TEndpointInfo { TActorId Proxy; TActorId Owner; - TString WorkerName; - bool Secure; - TSslHelpers::TSslHolder<SSL_CTX> SecureContext; -}; - + TString WorkerName; + bool Secure; + TSslHelpers::TSslHolder<SSL_CTX> SecureContext; +}; + NActors::IActor* CreateHttpProxy(NMonitoring::TMetricRegistry& sensors); NActors::IActor* CreateHttpAcceptorActor(const TActorId& owner, const TActorId& poller); NActors::IActor* CreateOutgoingConnectionActor(const TActorId& owner, const TString& host, bool secure, const TActorId& poller); -NActors::IActor* CreateIncomingConnectionActor( - const TEndpointInfo& endpoint, - TIntrusivePtr<TSocketDescriptor> socket, - THttpConfig::SocketAddressType address, - THttpIncomingRequestPtr recycledRequest = nullptr); -TEvHttpProxy::TEvReportSensors* BuildOutgoingRequestSensors(const THttpOutgoingRequestPtr& request, const THttpIncomingResponsePtr& response); -TEvHttpProxy::TEvReportSensors* BuildIncomingRequestSensors(const THttpIncomingRequestPtr& request, const THttpOutgoingResponsePtr& response); - -} +NActors::IActor* CreateIncomingConnectionActor( + const TEndpointInfo& endpoint, + TIntrusivePtr<TSocketDescriptor> socket, + THttpConfig::SocketAddressType address, + THttpIncomingRequestPtr recycledRequest = nullptr); +TEvHttpProxy::TEvReportSensors* BuildOutgoingRequestSensors(const THttpOutgoingRequestPtr& request, const THttpIncomingResponsePtr& response); +TEvHttpProxy::TEvReportSensors* BuildIncomingRequestSensors(const THttpIncomingRequestPtr& request, const THttpOutgoingResponsePtr& response); + +} diff --git a/library/cpp/actors/http/http_proxy_acceptor.cpp b/library/cpp/actors/http/http_proxy_acceptor.cpp index 9780541b71..3d2557c6fe 100644 --- a/library/cpp/actors/http/http_proxy_acceptor.cpp +++ b/library/cpp/actors/http/http_proxy_acceptor.cpp @@ -1,135 +1,135 @@ -#include <util/network/sock.h> -#include "http_proxy.h" -#include "http_proxy_ssl.h" - -namespace NHttp { - -class TAcceptorActor : public NActors::TActor<TAcceptorActor>, public THttpConfig { -public: - using TBase = NActors::TActor<TAcceptorActor>; +#include <util/network/sock.h> +#include "http_proxy.h" +#include "http_proxy_ssl.h" + +namespace NHttp { + +class TAcceptorActor : public NActors::TActor<TAcceptorActor>, public THttpConfig { +public: + using TBase = NActors::TActor<TAcceptorActor>; const TActorId Owner; const TActorId Poller; - TIntrusivePtr<TSocketDescriptor> Socket; + TIntrusivePtr<TSocketDescriptor> Socket; NActors::TPollerToken::TPtr PollerToken; THashSet<TActorId> Connections; - TDeque<THttpIncomingRequestPtr> RecycledRequests; - TEndpointInfo Endpoint; - + TDeque<THttpIncomingRequestPtr> RecycledRequests; + TEndpointInfo Endpoint; + TAcceptorActor(const TActorId& owner, const TActorId& poller) - : NActors::TActor<TAcceptorActor>(&TAcceptorActor::StateInit) - , Owner(owner) - , Poller(poller) - , Socket(new TSocketDescriptor()) - { - // for unit tests :( - CheckedSetSockOpt(Socket->Socket, SOL_SOCKET, SO_REUSEADDR, (int)true, "reuse address"); -#ifdef SO_REUSEPORT - CheckedSetSockOpt(Socket->Socket, SOL_SOCKET, SO_REUSEPORT, (int)true, "reuse port"); -#endif - } - -protected: - STFUNC(StateListening) { - switch (ev->GetTypeRewrite()) { + : NActors::TActor<TAcceptorActor>(&TAcceptorActor::StateInit) + , Owner(owner) + , Poller(poller) + , Socket(new TSocketDescriptor()) + { + // for unit tests :( + CheckedSetSockOpt(Socket->Socket, SOL_SOCKET, SO_REUSEADDR, (int)true, "reuse address"); +#ifdef SO_REUSEPORT + CheckedSetSockOpt(Socket->Socket, SOL_SOCKET, SO_REUSEPORT, (int)true, "reuse port"); +#endif + } + +protected: + STFUNC(StateListening) { + switch (ev->GetTypeRewrite()) { HFunc(NActors::TEvPollerRegisterResult, Handle); HFunc(NActors::TEvPollerReady, Handle); - HFunc(TEvHttpProxy::TEvHttpConnectionClosed, Handle); - HFunc(TEvHttpProxy::TEvReportSensors, Handle); - } - } - - STFUNC(StateInit) { - switch (ev->GetTypeRewrite()) { - HFunc(TEvHttpProxy::TEvAddListeningPort, HandleInit); - } - } - - void HandleInit(TEvHttpProxy::TEvAddListeningPort::TPtr event, const NActors::TActorContext& ctx) { - SocketAddressType bindAddress("::", event->Get()->Port); - Endpoint.Owner = ctx.SelfID; - Endpoint.Proxy = Owner; - Endpoint.WorkerName = event->Get()->WorkerName; - Endpoint.Secure = event->Get()->Secure; - int err = 0; - if (Endpoint.Secure) { + HFunc(TEvHttpProxy::TEvHttpConnectionClosed, Handle); + HFunc(TEvHttpProxy::TEvReportSensors, Handle); + } + } + + STFUNC(StateInit) { + switch (ev->GetTypeRewrite()) { + HFunc(TEvHttpProxy::TEvAddListeningPort, HandleInit); + } + } + + void HandleInit(TEvHttpProxy::TEvAddListeningPort::TPtr event, const NActors::TActorContext& ctx) { + SocketAddressType bindAddress("::", event->Get()->Port); + Endpoint.Owner = ctx.SelfID; + Endpoint.Proxy = Owner; + Endpoint.WorkerName = event->Get()->WorkerName; + Endpoint.Secure = event->Get()->Secure; + int err = 0; + if (Endpoint.Secure) { if (!event->Get()->SslCertificatePem.empty()) { Endpoint.SecureContext = TSslHelpers::CreateServerContext(event->Get()->SslCertificatePem); } else { Endpoint.SecureContext = TSslHelpers::CreateServerContext(event->Get()->CertificateFile, event->Get()->PrivateKeyFile); } - if (Endpoint.SecureContext == nullptr) { - err = -1; - LOG_WARN_S(ctx, HttpLog, "Failed to construct server security context"); - } - } - if (err == 0) { - err = Socket->Socket.Bind(&bindAddress); - } - if (err == 0) { - err = Socket->Socket.Listen(LISTEN_QUEUE); - if (err == 0) { - LOG_INFO_S(ctx, HttpLog, "Listening on " << bindAddress.ToString()); - SetNonBlock(Socket->Socket); + if (Endpoint.SecureContext == nullptr) { + err = -1; + LOG_WARN_S(ctx, HttpLog, "Failed to construct server security context"); + } + } + if (err == 0) { + err = Socket->Socket.Bind(&bindAddress); + } + if (err == 0) { + err = Socket->Socket.Listen(LISTEN_QUEUE); + if (err == 0) { + LOG_INFO_S(ctx, HttpLog, "Listening on " << bindAddress.ToString()); + SetNonBlock(Socket->Socket); ctx.Send(Poller, new NActors::TEvPollerRegister(Socket, SelfId(), SelfId())); - TBase::Become(&TAcceptorActor::StateListening); - ctx.Send(event->Sender, new TEvHttpProxy::TEvConfirmListen(bindAddress), 0, event->Cookie); - return; - } - } - LOG_WARN_S(ctx, HttpLog, "Failed to listen on " << bindAddress.ToString() << " - retrying..."); + TBase::Become(&TAcceptorActor::StateListening); + ctx.Send(event->Sender, new TEvHttpProxy::TEvConfirmListen(bindAddress), 0, event->Cookie); + return; + } + } + LOG_WARN_S(ctx, HttpLog, "Failed to listen on " << bindAddress.ToString() << " - retrying..."); ctx.ExecutorThread.Schedule(TDuration::Seconds(1), event.Release()); - } - - void Die(const NActors::TActorContext& ctx) override { - ctx.Send(Owner, new TEvHttpProxy::TEvHttpAcceptorClosed(ctx.SelfID)); + } + + void Die(const NActors::TActorContext& ctx) override { + ctx.Send(Owner, new TEvHttpProxy::TEvHttpAcceptorClosed(ctx.SelfID)); for (const NActors::TActorId& connection : Connections) { - ctx.Send(connection, new NActors::TEvents::TEvPoisonPill()); - } - } - + ctx.Send(connection, new NActors::TEvents::TEvPoisonPill()); + } + } + void Handle(NActors::TEvPollerRegisterResult::TPtr ev, const NActors::TActorContext& /*ctx*/) { PollerToken = std::move(ev->Get()->PollerToken); PollerToken->Request(true, false); // request read polling } void Handle(NActors::TEvPollerReady::TPtr, const NActors::TActorContext& ctx) { - TIntrusivePtr<TSocketDescriptor> socket = new TSocketDescriptor(); - SocketAddressType addr; + TIntrusivePtr<TSocketDescriptor> socket = new TSocketDescriptor(); + SocketAddressType addr; int err; while ((err = Socket->Socket.Accept(&socket->Socket, &addr)) == 0) { - NActors::IActor* connectionSocket = nullptr; - if (RecycledRequests.empty()) { - connectionSocket = CreateIncomingConnectionActor(Endpoint, socket, addr); - } else { - connectionSocket = CreateIncomingConnectionActor(Endpoint, socket, addr, std::move(RecycledRequests.front())); - RecycledRequests.pop_front(); - } + NActors::IActor* connectionSocket = nullptr; + if (RecycledRequests.empty()) { + connectionSocket = CreateIncomingConnectionActor(Endpoint, socket, addr); + } else { + connectionSocket = CreateIncomingConnectionActor(Endpoint, socket, addr, std::move(RecycledRequests.front())); + RecycledRequests.pop_front(); + } NActors::TActorId connectionId = ctx.Register(connectionSocket); ctx.Send(Poller, new NActors::TEvPollerRegister(socket, connectionId, connectionId)); - Connections.emplace(connectionId); - socket = new TSocketDescriptor(); - } + Connections.emplace(connectionId); + socket = new TSocketDescriptor(); + } if (err == -EAGAIN || err == -EWOULDBLOCK) { // request poller for further connection polling Y_VERIFY(PollerToken); PollerToken->Request(true, false); } - } - - void Handle(TEvHttpProxy::TEvHttpConnectionClosed::TPtr event, const NActors::TActorContext&) { - Connections.erase(event->Get()->ConnectionID); - for (auto& req : event->Get()->RecycledRequests) { - req->Clear(); - RecycledRequests.push_back(std::move(req)); - } - } - - void Handle(TEvHttpProxy::TEvReportSensors::TPtr event, const NActors::TActorContext& ctx) { - ctx.Send(event->Forward(Owner)); - } -}; - + } + + void Handle(TEvHttpProxy::TEvHttpConnectionClosed::TPtr event, const NActors::TActorContext&) { + Connections.erase(event->Get()->ConnectionID); + for (auto& req : event->Get()->RecycledRequests) { + req->Clear(); + RecycledRequests.push_back(std::move(req)); + } + } + + void Handle(TEvHttpProxy::TEvReportSensors::TPtr event, const NActors::TActorContext& ctx) { + ctx.Send(event->Forward(Owner)); + } +}; + NActors::IActor* CreateHttpAcceptorActor(const TActorId& owner, const TActorId& poller) { - return new TAcceptorActor(owner, poller); -} - -} + return new TAcceptorActor(owner, poller); +} + +} diff --git a/library/cpp/actors/http/http_proxy_incoming.cpp b/library/cpp/actors/http/http_proxy_incoming.cpp index 80fe2af53d..0608e0e25b 100644 --- a/library/cpp/actors/http/http_proxy_incoming.cpp +++ b/library/cpp/actors/http/http_proxy_incoming.cpp @@ -1,80 +1,80 @@ -#include "http_proxy.h" -#include "http_proxy_sock_impl.h" - -namespace NHttp { - +#include "http_proxy.h" +#include "http_proxy_sock_impl.h" + +namespace NHttp { + using namespace NActors; -template <typename TSocketImpl> -class TIncomingConnectionActor : public TActor<TIncomingConnectionActor<TSocketImpl>>, public TSocketImpl, virtual public THttpConfig { -public: - using TBase = TActor<TIncomingConnectionActor<TSocketImpl>>; - static constexpr bool RecycleRequests = true; - - const TEndpointInfo& Endpoint; - SocketAddressType Address; - TList<THttpIncomingRequestPtr> Requests; - THashMap<THttpIncomingRequestPtr, THttpOutgoingResponsePtr> Responses; - THttpIncomingRequestPtr CurrentRequest; - THttpOutgoingResponsePtr CurrentResponse; - TDeque<THttpIncomingRequestPtr> RecycledRequests; - +template <typename TSocketImpl> +class TIncomingConnectionActor : public TActor<TIncomingConnectionActor<TSocketImpl>>, public TSocketImpl, virtual public THttpConfig { +public: + using TBase = TActor<TIncomingConnectionActor<TSocketImpl>>; + static constexpr bool RecycleRequests = true; + + const TEndpointInfo& Endpoint; + SocketAddressType Address; + TList<THttpIncomingRequestPtr> Requests; + THashMap<THttpIncomingRequestPtr, THttpOutgoingResponsePtr> Responses; + THttpIncomingRequestPtr CurrentRequest; + THttpOutgoingResponsePtr CurrentResponse; + TDeque<THttpIncomingRequestPtr> RecycledRequests; + THPTimer InactivityTimer; static constexpr TDuration InactivityTimeout = TDuration::Minutes(2); TEvPollerReady* InactivityEvent = nullptr; TPollerToken::TPtr PollerToken; - TIncomingConnectionActor( - const TEndpointInfo& endpoint, - TIntrusivePtr<TSocketDescriptor> socket, - SocketAddressType address, - THttpIncomingRequestPtr recycledRequest = nullptr) - : TBase(&TIncomingConnectionActor::StateAccepting) - , TSocketImpl(std::move(socket)) - , Endpoint(endpoint) - , Address(address) - { - if (recycledRequest != nullptr) { - RecycledRequests.emplace_back(std::move(recycledRequest)); - } - TSocketImpl::SetNonBlock(); - } - - void CleanupRequest(THttpIncomingRequestPtr& request) { - if (RecycleRequests) { - request->Clear(); - RecycledRequests.push_back(std::move(request)); - } else { - request = nullptr; - } - } - - void CleanupResponse(THttpOutgoingResponsePtr& response) { - CleanupRequest(response->Request); - // TODO: maybe recycle too? - response = nullptr; - } - + TIncomingConnectionActor( + const TEndpointInfo& endpoint, + TIntrusivePtr<TSocketDescriptor> socket, + SocketAddressType address, + THttpIncomingRequestPtr recycledRequest = nullptr) + : TBase(&TIncomingConnectionActor::StateAccepting) + , TSocketImpl(std::move(socket)) + , Endpoint(endpoint) + , Address(address) + { + if (recycledRequest != nullptr) { + RecycledRequests.emplace_back(std::move(recycledRequest)); + } + TSocketImpl::SetNonBlock(); + } + + void CleanupRequest(THttpIncomingRequestPtr& request) { + if (RecycleRequests) { + request->Clear(); + RecycledRequests.push_back(std::move(request)); + } else { + request = nullptr; + } + } + + void CleanupResponse(THttpOutgoingResponsePtr& response) { + CleanupRequest(response->Request); + // TODO: maybe recycle too? + response = nullptr; + } + TAutoPtr<IEventHandle> AfterRegister(const TActorId& self, const TActorId& parent) override { return new IEventHandle(self, parent, new TEvents::TEvBootstrap()); } void Die(const TActorContext& ctx) override { - ctx.Send(Endpoint.Owner, new TEvHttpProxy::TEvHttpConnectionClosed(ctx.SelfID, std::move(RecycledRequests))); - TSocketImpl::Shutdown(); - TBase::Die(ctx); - } - -protected: + ctx.Send(Endpoint.Owner, new TEvHttpProxy::TEvHttpConnectionClosed(ctx.SelfID, std::move(RecycledRequests))); + TSocketImpl::Shutdown(); + TBase::Die(ctx); + } + +protected: void Bootstrap(const TActorContext& ctx) { InactivityTimer.Reset(); ctx.Schedule(InactivityTimeout, InactivityEvent = new TEvPollerReady(nullptr, false, false)); - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") incoming connection opened"); - OnAccept(ctx); + LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") incoming connection opened"); + OnAccept(ctx); } - void OnAccept(const NActors::TActorContext& ctx) { + void OnAccept(const NActors::TActorContext& ctx) { int res; bool read = false, write = false; if ((res = TSocketImpl::OnAccept(Endpoint, read, write)) != 1) { @@ -86,21 +86,21 @@ protected: } else { LOG_ERROR_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed - error in Accept: " << strerror(-res)); return Die(ctx); - } - } - TBase::Become(&TIncomingConnectionActor::StateConnected); + } + } + TBase::Become(&TIncomingConnectionActor::StateConnected); ctx.Send(ctx.SelfID, new TEvPollerReady(nullptr, true, true)); - } - + } + void HandleAccepting(TEvPollerRegisterResult::TPtr ev, const NActors::TActorContext& ctx) { PollerToken = std::move(ev->Get()->PollerToken); - OnAccept(ctx); - } - + OnAccept(ctx); + } + void HandleAccepting(NActors::TEvPollerReady::TPtr, const NActors::TActorContext& ctx) { - OnAccept(ctx); - } - + OnAccept(ctx); + } + void HandleConnected(TEvPollerReady::TPtr event, const TActorContext& ctx) { if (event->Get()->Read) { for (;;) { @@ -114,7 +114,7 @@ protected: CurrentRequest->Address = Address; CurrentRequest->WorkerName = Endpoint.WorkerName; CurrentRequest->Secure = Endpoint.Secure; - } + } if (!CurrentRequest->EnsureEnoughSpaceAvailable()) { LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed - not enough space available"); return Die(ctx); @@ -134,13 +134,13 @@ protected: CurrentRequest = nullptr; } else if (CurrentRequest->IsError()) { LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") -! (" << CurrentRequest->Method << " " << CurrentRequest->URL << ")"); - bool success = Respond(CurrentRequest->CreateResponseBadRequest(), ctx); - if (!success) { - return; - } + bool success = Respond(CurrentRequest->CreateResponseBadRequest(), ctx); + if (!success) { + return; + } CurrentRequest = nullptr; } - } + } } else if (-res == EAGAIN || -res == EWOULDBLOCK) { if (PollerToken) { if (!read && !write) { @@ -148,18 +148,18 @@ protected: } PollerToken->Request(read, write); } - break; + break; } else if (-res == EINTR) { continue; } else if (!res) { // connection closed - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed"); - return Die(ctx); + LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed"); + return Die(ctx); } else { - LOG_ERROR_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed - error in Receive: " << strerror(-res)); - return Die(ctx); - } - } + LOG_ERROR_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed - error in Receive: " << strerror(-res)); + return Die(ctx); + } + } if (event->Get() == InactivityEvent) { const TDuration passed = TDuration::Seconds(std::abs(InactivityTimer.Passed())); if (passed >= InactivityTimeout) { @@ -173,83 +173,83 @@ protected: if (event->Get()->Write) { FlushOutput(ctx); } - } - + } + void HandleConnected(TEvPollerRegisterResult::TPtr ev, const TActorContext& /*ctx*/) { PollerToken = std::move(ev->Get()->PollerToken); PollerToken->Request(true, true); - } - - void HandleConnected(TEvHttpProxy::TEvHttpOutgoingResponse::TPtr event, const TActorContext& ctx) { - Respond(event->Get()->Response, ctx); - } - - bool Respond(THttpOutgoingResponsePtr response, const TActorContext& ctx) { - THttpIncomingRequestPtr request = response->GetRequest(); - response->Finish(); - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") <- (" << response->Status << " " << response->Message << ")"); - if (response->Status != "200" && response->Status != "404") { - static constexpr size_t MAX_LOGGED_SIZE = 1024; - LOG_DEBUG_S(ctx, HttpLog, - "(#" - << TSocketImpl::GetRawSocket() - << "," - << Address - << ") Request: " - << request->GetObfuscatedData().substr(0, MAX_LOGGED_SIZE)); - LOG_DEBUG_S(ctx, HttpLog, - "(#" - << TSocketImpl::GetRawSocket() - << "," - << Address - << ") Response: " - << TString(response->GetRawData()).substr(0, MAX_LOGGED_SIZE)); - } + } + + void HandleConnected(TEvHttpProxy::TEvHttpOutgoingResponse::TPtr event, const TActorContext& ctx) { + Respond(event->Get()->Response, ctx); + } + + bool Respond(THttpOutgoingResponsePtr response, const TActorContext& ctx) { + THttpIncomingRequestPtr request = response->GetRequest(); + response->Finish(); + LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") <- (" << response->Status << " " << response->Message << ")"); + if (response->Status != "200" && response->Status != "404") { + static constexpr size_t MAX_LOGGED_SIZE = 1024; + LOG_DEBUG_S(ctx, HttpLog, + "(#" + << TSocketImpl::GetRawSocket() + << "," + << Address + << ") Request: " + << request->GetObfuscatedData().substr(0, MAX_LOGGED_SIZE)); + LOG_DEBUG_S(ctx, HttpLog, + "(#" + << TSocketImpl::GetRawSocket() + << "," + << Address + << ") Response: " + << TString(response->GetRawData()).substr(0, MAX_LOGGED_SIZE)); + } THolder<TEvHttpProxy::TEvReportSensors> sensors(BuildIncomingRequestSensors(request, response)); - ctx.Send(Endpoint.Owner, sensors.Release()); - if (request == Requests.front() && CurrentResponse == nullptr) { - CurrentResponse = response; - return FlushOutput(ctx); - } else { - // we are ahead of our pipeline - Responses.emplace(request, response); - return true; - } - } - - bool FlushOutput(const TActorContext& ctx) { - while (CurrentResponse != nullptr) { - size_t size = CurrentResponse->Size(); - if (size == 0) { - Y_VERIFY(Requests.front() == CurrentResponse->GetRequest()); - bool close = CurrentResponse->IsConnectionClose(); - Requests.pop_front(); - CleanupResponse(CurrentResponse); - if (!Requests.empty()) { - auto it = Responses.find(Requests.front()); - if (it != Responses.end()) { - CurrentResponse = it->second; - Responses.erase(it); - continue; - } else { - LOG_ERROR_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed - FlushOutput request not found"); - Die(ctx); - return false; - } - } else { - if (close) { - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed"); - Die(ctx); - return false; - } else { - continue; - } - } - } + ctx.Send(Endpoint.Owner, sensors.Release()); + if (request == Requests.front() && CurrentResponse == nullptr) { + CurrentResponse = response; + return FlushOutput(ctx); + } else { + // we are ahead of our pipeline + Responses.emplace(request, response); + return true; + } + } + + bool FlushOutput(const TActorContext& ctx) { + while (CurrentResponse != nullptr) { + size_t size = CurrentResponse->Size(); + if (size == 0) { + Y_VERIFY(Requests.front() == CurrentResponse->GetRequest()); + bool close = CurrentResponse->IsConnectionClose(); + Requests.pop_front(); + CleanupResponse(CurrentResponse); + if (!Requests.empty()) { + auto it = Responses.find(Requests.front()); + if (it != Responses.end()) { + CurrentResponse = it->second; + Responses.erase(it); + continue; + } else { + LOG_ERROR_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed - FlushOutput request not found"); + Die(ctx); + return false; + } + } else { + if (close) { + LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed"); + Die(ctx); + return false; + } else { + continue; + } + } + } bool read = false, write = false; ssize_t res = TSocketImpl::Send(CurrentResponse->Data(), size, read, write); - if (res > 0) { - CurrentResponse->ChopHead(res); + if (res > 0) { + CurrentResponse->ChopHead(res); } else if (-res == EINTR) { continue; } else if (-res == EAGAIN || -res == EWOULDBLOCK) { @@ -258,45 +258,45 @@ protected: write = true; } PollerToken->Request(read, write); - } - break; + } + break; } else { CleanupResponse(CurrentResponse); LOG_ERROR_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed - error in FlushOutput: " << strerror(-res)); - Die(ctx); - return false; - } - } - return true; - } - - STFUNC(StateAccepting) { - switch (ev->GetTypeRewrite()) { + Die(ctx); + return false; + } + } + return true; + } + + STFUNC(StateAccepting) { + switch (ev->GetTypeRewrite()) { CFunc(TEvents::TEvBootstrap::EventType, Bootstrap); HFunc(TEvPollerReady, HandleAccepting); HFunc(TEvPollerRegisterResult, HandleAccepting); - } - } - - STFUNC(StateConnected) { - switch (ev->GetTypeRewrite()) { + } + } + + STFUNC(StateConnected) { + switch (ev->GetTypeRewrite()) { HFunc(TEvPollerReady, HandleConnected); - HFunc(TEvHttpProxy::TEvHttpOutgoingResponse, HandleConnected); + HFunc(TEvHttpProxy::TEvHttpOutgoingResponse, HandleConnected); HFunc(TEvPollerRegisterResult, HandleConnected); - } - } -}; - -IActor* CreateIncomingConnectionActor( - const TEndpointInfo& endpoint, - TIntrusivePtr<TSocketDescriptor> socket, - THttpConfig::SocketAddressType address, - THttpIncomingRequestPtr recycledRequest) { - if (endpoint.Secure) { - return new TIncomingConnectionActor<TSecureSocketImpl>(endpoint, std::move(socket), address, std::move(recycledRequest)); - } else { - return new TIncomingConnectionActor<TPlainSocketImpl>(endpoint, std::move(socket), address, std::move(recycledRequest)); - } -} - -} + } + } +}; + +IActor* CreateIncomingConnectionActor( + const TEndpointInfo& endpoint, + TIntrusivePtr<TSocketDescriptor> socket, + THttpConfig::SocketAddressType address, + THttpIncomingRequestPtr recycledRequest) { + if (endpoint.Secure) { + return new TIncomingConnectionActor<TSecureSocketImpl>(endpoint, std::move(socket), address, std::move(recycledRequest)); + } else { + return new TIncomingConnectionActor<TPlainSocketImpl>(endpoint, std::move(socket), address, std::move(recycledRequest)); + } +} + +} diff --git a/library/cpp/actors/http/http_proxy_outgoing.cpp b/library/cpp/actors/http/http_proxy_outgoing.cpp index d9189dba8a..5b3d07c614 100644 --- a/library/cpp/actors/http/http_proxy_outgoing.cpp +++ b/library/cpp/actors/http/http_proxy_outgoing.cpp @@ -1,92 +1,92 @@ -#include "http_proxy.h" -#include "http_proxy_sock_impl.h" - -namespace NHttp { - -template <typename TSocketImpl> -class TOutgoingConnectionActor : public NActors::TActor<TOutgoingConnectionActor<TSocketImpl>>, public TSocketImpl, virtual public THttpConfig { -public: - using TBase = NActors::TActor<TOutgoingConnectionActor<TSocketImpl>>; - using TSelf = TOutgoingConnectionActor<TSocketImpl>; +#include "http_proxy.h" +#include "http_proxy_sock_impl.h" + +namespace NHttp { + +template <typename TSocketImpl> +class TOutgoingConnectionActor : public NActors::TActor<TOutgoingConnectionActor<TSocketImpl>>, public TSocketImpl, virtual public THttpConfig { +public: + using TBase = NActors::TActor<TOutgoingConnectionActor<TSocketImpl>>; + using TSelf = TOutgoingConnectionActor<TSocketImpl>; const TActorId Owner; const TActorId Poller; - SocketAddressType Address; - TString Host; + SocketAddressType Address; + TString Host; TActorId RequestOwner; - THttpOutgoingRequestPtr Request; - THttpIncomingResponsePtr Response; - TInstant LastActivity; - TDuration ConnectionTimeout = CONNECTION_TIMEOUT; + THttpOutgoingRequestPtr Request; + THttpIncomingResponsePtr Response; + TInstant LastActivity; + TDuration ConnectionTimeout = CONNECTION_TIMEOUT; NActors::TPollerToken::TPtr PollerToken; - + TOutgoingConnectionActor(const TActorId& owner, const TString& host, const TActorId& poller) - : TBase(&TSelf::StateWaiting) - , Owner(owner) - , Poller(poller) - , Host(host) - { - TSocketImpl::SetNonBlock(); - TSocketImpl::SetTimeout(SOCKET_TIMEOUT); - } - + : TBase(&TSelf::StateWaiting) + , Owner(owner) + , Poller(poller) + , Host(host) + { + TSocketImpl::SetNonBlock(); + TSocketImpl::SetTimeout(SOCKET_TIMEOUT); + } + void Die(const NActors::TActorContext& ctx) override { - ctx.Send(Owner, new TEvHttpProxy::TEvHttpConnectionClosed(ctx.SelfID)); - TSocketImpl::Shutdown(); // to avoid errors when connection already closed - TBase::Die(ctx); - } - - void ReplyAndDie(const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") -> (" << Response->Status << " " << Response->Message << ")"); - ctx.Send(RequestOwner, new TEvHttpProxy::TEvHttpIncomingResponse(Request, Response)); + ctx.Send(Owner, new TEvHttpProxy::TEvHttpConnectionClosed(ctx.SelfID)); + TSocketImpl::Shutdown(); // to avoid errors when connection already closed + TBase::Die(ctx); + } + + void ReplyAndDie(const NActors::TActorContext& ctx) { + LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") -> (" << Response->Status << " " << Response->Message << ")"); + ctx.Send(RequestOwner, new TEvHttpProxy::TEvHttpIncomingResponse(Request, Response)); RequestOwner = TActorId(); THolder<TEvHttpProxy::TEvReportSensors> sensors(BuildOutgoingRequestSensors(Request, Response)); - ctx.Send(Owner, sensors.Release()); - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed"); - Die(ctx); - } - - void ReplyErrorAndDie(const NActors::TActorContext& ctx, const TString& error) { - LOG_ERROR_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed with error: " << error); - if (RequestOwner) { - ctx.Send(RequestOwner, new TEvHttpProxy::TEvHttpIncomingResponse(Request, Response, error)); + ctx.Send(Owner, sensors.Release()); + LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed"); + Die(ctx); + } + + void ReplyErrorAndDie(const NActors::TActorContext& ctx, const TString& error) { + LOG_ERROR_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connection closed with error: " << error); + if (RequestOwner) { + ctx.Send(RequestOwner, new TEvHttpProxy::TEvHttpIncomingResponse(Request, Response, error)); RequestOwner = TActorId(); THolder<TEvHttpProxy::TEvReportSensors> sensors(BuildOutgoingRequestSensors(Request, Response)); - ctx.Send(Owner, sensors.Release()); - Die(ctx); - } - } - -protected: - void FailConnection(const NActors::TActorContext& ctx, const TString& error) { - if (Request) { - return ReplyErrorAndDie(ctx, error); - } - return TBase::Become(&TOutgoingConnectionActor::StateFailed); - } - - void Connect(const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connecting"); - int res = TSocketImpl::Connect(Address); - RegisterPoller(ctx); - switch (-res) { - case 0: - return OnConnect(ctx); - case EINPROGRESS: - case EAGAIN: - return TBase::Become(&TOutgoingConnectionActor::StateConnecting); - default: - return ReplyErrorAndDie(ctx, strerror(-res)); - } - } - - void FlushOutput(const NActors::TActorContext& ctx) { - if (Request != nullptr) { - Request->Finish(); + ctx.Send(Owner, sensors.Release()); + Die(ctx); + } + } + +protected: + void FailConnection(const NActors::TActorContext& ctx, const TString& error) { + if (Request) { + return ReplyErrorAndDie(ctx, error); + } + return TBase::Become(&TOutgoingConnectionActor::StateFailed); + } + + void Connect(const NActors::TActorContext& ctx) { + LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connecting"); + int res = TSocketImpl::Connect(Address); + RegisterPoller(ctx); + switch (-res) { + case 0: + return OnConnect(ctx); + case EINPROGRESS: + case EAGAIN: + return TBase::Become(&TOutgoingConnectionActor::StateConnecting); + default: + return ReplyErrorAndDie(ctx, strerror(-res)); + } + } + + void FlushOutput(const NActors::TActorContext& ctx) { + if (Request != nullptr) { + Request->Finish(); while (auto size = Request->Size()) { bool read = false, write = false; ssize_t res = TSocketImpl::Send(Request->Data(), size, read, write); - if (res > 0) { - Request->ChopHead(res); + if (res > 0) { + Request->ChopHead(res); } else if (-res == EINTR) { continue; } else if (-res == EAGAIN || -res == EWOULDBLOCK) { @@ -97,30 +97,30 @@ protected: PollerToken->Request(read, write); } break; - } else { + } else { if (!res) { - ReplyAndDie(ctx); + ReplyAndDie(ctx); } else { - ReplyErrorAndDie(ctx, strerror(-res)); - } + ReplyErrorAndDie(ctx, strerror(-res)); + } break; - } - } - } - } - - void PullInput(const NActors::TActorContext& ctx) { + } + } + } + } + + void PullInput(const NActors::TActorContext& ctx) { for (;;) { - if (Response == nullptr) { - Response = new THttpIncomingResponse(Request); - } - if (!Response->EnsureEnoughSpaceAvailable()) { - return ReplyErrorAndDie(ctx, "Not enough space in socket buffer"); - } + if (Response == nullptr) { + Response = new THttpIncomingResponse(Request); + } + if (!Response->EnsureEnoughSpaceAvailable()) { + return ReplyErrorAndDie(ctx, "Not enough space in socket buffer"); + } bool read = false, write = false; ssize_t res = TSocketImpl::Recv(Response->Pos(), Response->Avail(), read, write); - if (res > 0) { - Response->Advance(res); + if (res > 0) { + Response->Advance(res); if (Response->IsDone() && Response->IsReady()) { return ReplyAndDie(ctx); } @@ -130,169 +130,169 @@ protected: if (PollerToken) { if (!read && !write) { read = true; - } + } PollerToken->Request(read, write); - } + } return; - } else { + } else { if (!res) { - Response->ConnectionClosed(); - } + Response->ConnectionClosed(); + } if (Response->IsDone() && Response->IsReady()) { return ReplyAndDie(ctx); } return ReplyErrorAndDie(ctx, strerror(-res)); - } + } } - } - - void RegisterPoller(const NActors::TActorContext& ctx) { + } + + void RegisterPoller(const NActors::TActorContext& ctx) { ctx.Send(Poller, new NActors::TEvPollerRegister(TSocketImpl::Socket, ctx.SelfID, ctx.SelfID)); - } - - void OnConnect(const NActors::TActorContext& ctx) { + } + + void OnConnect(const NActors::TActorContext& ctx) { bool read = false, write = false; if (int res = TSocketImpl::OnConnect(read, write); res != 1) { if (-res == EAGAIN) { if (PollerToken) { PollerToken->Request(read, write); } - return; + return; } else { return ReplyErrorAndDie(ctx, strerror(-res)); - } - } - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") outgoing connection opened"); - TBase::Become(&TOutgoingConnectionActor::StateConnected); - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") <- (" << Request->Method << " " << Request->URL << ")"); + } + } + LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") outgoing connection opened"); + TBase::Become(&TOutgoingConnectionActor::StateConnected); + LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") <- (" << Request->Method << " " << Request->URL << ")"); ctx.Send(ctx.SelfID, new NActors::TEvPollerReady(nullptr, true, true)); - } - - void HandleResolving(TEvHttpProxy::TEvResolveHostResponse::TPtr event, const NActors::TActorContext& ctx) { - LastActivity = ctx.Now(); - if (!event->Get()->Error.empty()) { - return FailConnection(ctx, event->Get()->Error); - } - Address = event->Get()->Address; - if (Address.GetPort() == 0) { - Address.SetPort(Request->Secure ? 443 : 80); - } - Connect(ctx); - } - + } + + void HandleResolving(TEvHttpProxy::TEvResolveHostResponse::TPtr event, const NActors::TActorContext& ctx) { + LastActivity = ctx.Now(); + if (!event->Get()->Error.empty()) { + return FailConnection(ctx, event->Get()->Error); + } + Address = event->Get()->Address; + if (Address.GetPort() == 0) { + Address.SetPort(Request->Secure ? 443 : 80); + } + Connect(ctx); + } + void HandleConnecting(NActors::TEvPollerReady::TPtr, const NActors::TActorContext& ctx) { - LastActivity = ctx.Now(); - int res = TSocketImpl::GetError(); - if (res == 0) { - OnConnect(ctx); - } else { - FailConnection(ctx, TStringBuilder() << strerror(res)); - } - } - + LastActivity = ctx.Now(); + int res = TSocketImpl::GetError(); + if (res == 0) { + OnConnect(ctx); + } else { + FailConnection(ctx, TStringBuilder() << strerror(res)); + } + } + void HandleConnecting(NActors::TEvPollerRegisterResult::TPtr ev, const NActors::TActorContext& ctx) { PollerToken = std::move(ev->Get()->PollerToken); - LastActivity = ctx.Now(); - int res = TSocketImpl::GetError(); - if (res == 0) { - OnConnect(ctx); - } else { - FailConnection(ctx, TStringBuilder() << strerror(res)); - } - } - - void HandleWaiting(TEvHttpProxy::TEvHttpOutgoingRequest::TPtr event, const NActors::TActorContext& ctx) { - LastActivity = ctx.Now(); - Request = std::move(event->Get()->Request); - Host = Request->Host; - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << ") resolving " << Host); - Request->Timer.Reset(); - RequestOwner = event->Sender; - ctx.Send(Owner, new TEvHttpProxy::TEvResolveHostRequest(Host)); - if (event->Get()->Timeout) { - ConnectionTimeout = event->Get()->Timeout; - TSocketImpl::SetTimeout(ConnectionTimeout); - } - ctx.Schedule(ConnectionTimeout, new NActors::TEvents::TEvWakeup()); - LastActivity = ctx.Now(); - TBase::Become(&TOutgoingConnectionActor::StateResolving); - } - + LastActivity = ctx.Now(); + int res = TSocketImpl::GetError(); + if (res == 0) { + OnConnect(ctx); + } else { + FailConnection(ctx, TStringBuilder() << strerror(res)); + } + } + + void HandleWaiting(TEvHttpProxy::TEvHttpOutgoingRequest::TPtr event, const NActors::TActorContext& ctx) { + LastActivity = ctx.Now(); + Request = std::move(event->Get()->Request); + Host = Request->Host; + LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << ") resolving " << Host); + Request->Timer.Reset(); + RequestOwner = event->Sender; + ctx.Send(Owner, new TEvHttpProxy::TEvResolveHostRequest(Host)); + if (event->Get()->Timeout) { + ConnectionTimeout = event->Get()->Timeout; + TSocketImpl::SetTimeout(ConnectionTimeout); + } + ctx.Schedule(ConnectionTimeout, new NActors::TEvents::TEvWakeup()); + LastActivity = ctx.Now(); + TBase::Become(&TOutgoingConnectionActor::StateResolving); + } + void HandleConnected(NActors::TEvPollerReady::TPtr event, const NActors::TActorContext& ctx) { - LastActivity = ctx.Now(); + LastActivity = ctx.Now(); if (event->Get()->Read) { PullInput(ctx); - } + } if (event->Get()->Write) { FlushOutput(ctx); } - } - + } + void HandleConnected(NActors::TEvPollerRegisterResult::TPtr ev, const NActors::TActorContext& ctx) { PollerToken = std::move(ev->Get()->PollerToken); - LastActivity = ctx.Now(); + LastActivity = ctx.Now(); PullInput(ctx); - FlushOutput(ctx); - } - - void HandleFailed(TEvHttpProxy::TEvHttpOutgoingRequest::TPtr event, const NActors::TActorContext& ctx) { - Request = std::move(event->Get()->Request); - RequestOwner = event->Sender; - ReplyErrorAndDie(ctx, "Failed"); - } - - void HandleTimeout(const NActors::TActorContext& ctx) { - TDuration inactivityTime = ctx.Now() - LastActivity; - if (inactivityTime >= ConnectionTimeout) { - FailConnection(ctx, "Connection timed out"); - } else { - ctx.Schedule(Min(ConnectionTimeout - inactivityTime, TDuration::MilliSeconds(100)), new NActors::TEvents::TEvWakeup()); - } - } - - STFUNC(StateWaiting) { - switch (ev->GetTypeRewrite()) { - HFunc(TEvHttpProxy::TEvHttpOutgoingRequest, HandleWaiting); - CFunc(NActors::TEvents::TEvWakeup::EventType, HandleTimeout); - } - } - - STFUNC(StateResolving) { - switch (ev->GetTypeRewrite()) { - HFunc(TEvHttpProxy::TEvResolveHostResponse, HandleResolving); - CFunc(NActors::TEvents::TEvWakeup::EventType, HandleTimeout); - } - } - - STFUNC(StateConnecting) { - switch (ev->GetTypeRewrite()) { + FlushOutput(ctx); + } + + void HandleFailed(TEvHttpProxy::TEvHttpOutgoingRequest::TPtr event, const NActors::TActorContext& ctx) { + Request = std::move(event->Get()->Request); + RequestOwner = event->Sender; + ReplyErrorAndDie(ctx, "Failed"); + } + + void HandleTimeout(const NActors::TActorContext& ctx) { + TDuration inactivityTime = ctx.Now() - LastActivity; + if (inactivityTime >= ConnectionTimeout) { + FailConnection(ctx, "Connection timed out"); + } else { + ctx.Schedule(Min(ConnectionTimeout - inactivityTime, TDuration::MilliSeconds(100)), new NActors::TEvents::TEvWakeup()); + } + } + + STFUNC(StateWaiting) { + switch (ev->GetTypeRewrite()) { + HFunc(TEvHttpProxy::TEvHttpOutgoingRequest, HandleWaiting); + CFunc(NActors::TEvents::TEvWakeup::EventType, HandleTimeout); + } + } + + STFUNC(StateResolving) { + switch (ev->GetTypeRewrite()) { + HFunc(TEvHttpProxy::TEvResolveHostResponse, HandleResolving); + CFunc(NActors::TEvents::TEvWakeup::EventType, HandleTimeout); + } + } + + STFUNC(StateConnecting) { + switch (ev->GetTypeRewrite()) { HFunc(NActors::TEvPollerReady, HandleConnecting); - CFunc(NActors::TEvents::TEvWakeup::EventType, HandleTimeout); + CFunc(NActors::TEvents::TEvWakeup::EventType, HandleTimeout); HFunc(NActors::TEvPollerRegisterResult, HandleConnecting); - } - } - - STFUNC(StateConnected) { - switch (ev->GetTypeRewrite()) { + } + } + + STFUNC(StateConnected) { + switch (ev->GetTypeRewrite()) { HFunc(NActors::TEvPollerReady, HandleConnected); - CFunc(NActors::TEvents::TEvWakeup::EventType, HandleTimeout); + CFunc(NActors::TEvents::TEvWakeup::EventType, HandleTimeout); HFunc(NActors::TEvPollerRegisterResult, HandleConnected); - } - } - - STFUNC(StateFailed) { - switch (ev->GetTypeRewrite()) { - HFunc(TEvHttpProxy::TEvHttpOutgoingRequest, HandleFailed); - } - } -}; - + } + } + + STFUNC(StateFailed) { + switch (ev->GetTypeRewrite()) { + HFunc(TEvHttpProxy::TEvHttpOutgoingRequest, HandleFailed); + } + } +}; + NActors::IActor* CreateOutgoingConnectionActor(const TActorId& owner, const TString& host, bool secure, const TActorId& poller) { - if (secure) { - return new TOutgoingConnectionActor<TSecureSocketImpl>(owner, host, poller); - } else { - return new TOutgoingConnectionActor<TPlainSocketImpl>(owner, host, poller); - } -} - -} + if (secure) { + return new TOutgoingConnectionActor<TSecureSocketImpl>(owner, host, poller); + } else { + return new TOutgoingConnectionActor<TPlainSocketImpl>(owner, host, poller); + } +} + +} diff --git a/library/cpp/actors/http/http_proxy_sock_impl.h b/library/cpp/actors/http/http_proxy_sock_impl.h index bf8c71d05a..e7812cc5e1 100644 --- a/library/cpp/actors/http/http_proxy_sock_impl.h +++ b/library/cpp/actors/http/http_proxy_sock_impl.h @@ -1,262 +1,262 @@ -#pragma once - -#include "http.h" -#include "http_proxy.h" - -namespace NHttp { - -struct TPlainSocketImpl : virtual public THttpConfig { - TIntrusivePtr<TSocketDescriptor> Socket; - - TPlainSocketImpl() - : Socket(new TSocketDescriptor()) - {} - - TPlainSocketImpl(TIntrusivePtr<TSocketDescriptor> socket) - : Socket(std::move(socket)) - {} - - SOCKET GetRawSocket() const { - return static_cast<SOCKET>(Socket->Socket); - } - - void SetNonBlock(bool nonBlock = true) noexcept { - try { - ::SetNonBlock(Socket->Socket, nonBlock); - } - catch (const yexception&) { - } - } - - void SetTimeout(TDuration timeout) noexcept { - try { - ::SetSocketTimeout(Socket->Socket, timeout.Seconds(), timeout.MilliSecondsOfSecond()); - } - catch (const yexception&) { - } - } - - void Shutdown() { - //Socket->Socket.ShutDown(SHUT_RDWR); // KIKIMR-3895 - ::shutdown(Socket->Socket, SHUT_RDWR); - } - - int Connect(const SocketAddressType& address) { - return Socket->Socket.Connect(&address); - } - +#pragma once + +#include "http.h" +#include "http_proxy.h" + +namespace NHttp { + +struct TPlainSocketImpl : virtual public THttpConfig { + TIntrusivePtr<TSocketDescriptor> Socket; + + TPlainSocketImpl() + : Socket(new TSocketDescriptor()) + {} + + TPlainSocketImpl(TIntrusivePtr<TSocketDescriptor> socket) + : Socket(std::move(socket)) + {} + + SOCKET GetRawSocket() const { + return static_cast<SOCKET>(Socket->Socket); + } + + void SetNonBlock(bool nonBlock = true) noexcept { + try { + ::SetNonBlock(Socket->Socket, nonBlock); + } + catch (const yexception&) { + } + } + + void SetTimeout(TDuration timeout) noexcept { + try { + ::SetSocketTimeout(Socket->Socket, timeout.Seconds(), timeout.MilliSecondsOfSecond()); + } + catch (const yexception&) { + } + } + + void Shutdown() { + //Socket->Socket.ShutDown(SHUT_RDWR); // KIKIMR-3895 + ::shutdown(Socket->Socket, SHUT_RDWR); + } + + int Connect(const SocketAddressType& address) { + return Socket->Socket.Connect(&address); + } + static constexpr int OnConnect(bool&, bool&) { return 1; - } - + } + static constexpr int OnAccept(const TEndpointInfo&, bool&, bool&) { return 1; - } - - bool IsGood() { - int res; - GetSockOpt(Socket->Socket, SOL_SOCKET, SO_ERROR, res); - return res == 0; - } - - int GetError() { - int res; - GetSockOpt(Socket->Socket, SOL_SOCKET, SO_ERROR, res); - return res; - } - + } + + bool IsGood() { + int res; + GetSockOpt(Socket->Socket, SOL_SOCKET, SO_ERROR, res); + return res == 0; + } + + int GetError() { + int res; + GetSockOpt(Socket->Socket, SOL_SOCKET, SO_ERROR, res); + return res; + } + ssize_t Send(const void* data, size_t size, bool&, bool&) { - return Socket->Socket.Send(data, size); - } - + return Socket->Socket.Send(data, size); + } + ssize_t Recv(void* data, size_t size, bool&, bool&) { - return Socket->Socket.Recv(data, size); - } -}; - -struct TSecureSocketImpl : TPlainSocketImpl, TSslHelpers { - static TSecureSocketImpl* IO(BIO* bio) noexcept { - return static_cast<TSecureSocketImpl*>(BIO_get_data(bio)); - } - - static int IoWrite(BIO* bio, const char* data, int dlen) noexcept { - BIO_clear_retry_flags(bio); - int res = IO(bio)->Socket->Socket.Send(data, dlen); - if (-res == EAGAIN) { - BIO_set_retry_write(bio); - } - return res; - } - - static int IoRead(BIO* bio, char* data, int dlen) noexcept { - BIO_clear_retry_flags(bio); - int res = IO(bio)->Socket->Socket.Recv(data, dlen); - if (-res == EAGAIN) { - BIO_set_retry_read(bio); - } - return res; - } - - static int IoPuts(BIO* bio, const char* buf) noexcept { - Y_UNUSED(bio); - Y_UNUSED(buf); - return -2; - } - - static int IoGets(BIO* bio, char* buf, int size) noexcept { - Y_UNUSED(bio); - Y_UNUSED(buf); - Y_UNUSED(size); - return -2; - } - - static long IoCtrl(BIO* bio, int cmd, long larg, void* parg) noexcept { - Y_UNUSED(larg); - Y_UNUSED(parg); - - if (cmd == BIO_CTRL_FLUSH) { - IO(bio)->Flush(); - return 1; - } - - return -2; - } - - static int IoCreate(BIO* bio) noexcept { - BIO_set_data(bio, nullptr); - BIO_set_init(bio, 1); - return 1; - } - - static int IoDestroy(BIO* bio) noexcept { - BIO_set_data(bio, nullptr); - BIO_set_init(bio, 0); - return 1; - } - - static BIO_METHOD* CreateIoMethod() { - BIO_METHOD* method = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "SecureSocketImpl"); - BIO_meth_set_write(method, IoWrite); - BIO_meth_set_read(method, IoRead); - BIO_meth_set_puts(method, IoPuts); - BIO_meth_set_gets(method, IoGets); - BIO_meth_set_ctrl(method, IoCtrl); - BIO_meth_set_create(method, IoCreate); - BIO_meth_set_destroy(method, IoDestroy); - return method; - } - - static BIO_METHOD* IoMethod() { - static BIO_METHOD* method = CreateIoMethod(); - return method; - } - - TSslHolder<BIO> Bio; - TSslHolder<SSL_CTX> Ctx; - TSslHolder<SSL> Ssl; - - TSecureSocketImpl() = default; - - TSecureSocketImpl(TIntrusivePtr<TSocketDescriptor> socket) - : TPlainSocketImpl(std::move(socket)) - {} - - void InitClientSsl() { + return Socket->Socket.Recv(data, size); + } +}; + +struct TSecureSocketImpl : TPlainSocketImpl, TSslHelpers { + static TSecureSocketImpl* IO(BIO* bio) noexcept { + return static_cast<TSecureSocketImpl*>(BIO_get_data(bio)); + } + + static int IoWrite(BIO* bio, const char* data, int dlen) noexcept { + BIO_clear_retry_flags(bio); + int res = IO(bio)->Socket->Socket.Send(data, dlen); + if (-res == EAGAIN) { + BIO_set_retry_write(bio); + } + return res; + } + + static int IoRead(BIO* bio, char* data, int dlen) noexcept { + BIO_clear_retry_flags(bio); + int res = IO(bio)->Socket->Socket.Recv(data, dlen); + if (-res == EAGAIN) { + BIO_set_retry_read(bio); + } + return res; + } + + static int IoPuts(BIO* bio, const char* buf) noexcept { + Y_UNUSED(bio); + Y_UNUSED(buf); + return -2; + } + + static int IoGets(BIO* bio, char* buf, int size) noexcept { + Y_UNUSED(bio); + Y_UNUSED(buf); + Y_UNUSED(size); + return -2; + } + + static long IoCtrl(BIO* bio, int cmd, long larg, void* parg) noexcept { + Y_UNUSED(larg); + Y_UNUSED(parg); + + if (cmd == BIO_CTRL_FLUSH) { + IO(bio)->Flush(); + return 1; + } + + return -2; + } + + static int IoCreate(BIO* bio) noexcept { + BIO_set_data(bio, nullptr); + BIO_set_init(bio, 1); + return 1; + } + + static int IoDestroy(BIO* bio) noexcept { + BIO_set_data(bio, nullptr); + BIO_set_init(bio, 0); + return 1; + } + + static BIO_METHOD* CreateIoMethod() { + BIO_METHOD* method = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "SecureSocketImpl"); + BIO_meth_set_write(method, IoWrite); + BIO_meth_set_read(method, IoRead); + BIO_meth_set_puts(method, IoPuts); + BIO_meth_set_gets(method, IoGets); + BIO_meth_set_ctrl(method, IoCtrl); + BIO_meth_set_create(method, IoCreate); + BIO_meth_set_destroy(method, IoDestroy); + return method; + } + + static BIO_METHOD* IoMethod() { + static BIO_METHOD* method = CreateIoMethod(); + return method; + } + + TSslHolder<BIO> Bio; + TSslHolder<SSL_CTX> Ctx; + TSslHolder<SSL> Ssl; + + TSecureSocketImpl() = default; + + TSecureSocketImpl(TIntrusivePtr<TSocketDescriptor> socket) + : TPlainSocketImpl(std::move(socket)) + {} + + void InitClientSsl() { Bio.Reset(BIO_new(IoMethod())); - BIO_set_data(Bio.Get(), this); - BIO_set_nbio(Bio.Get(), 1); - Ctx = CreateClientContext(); - Ssl = ConstructSsl(Ctx.Get(), Bio.Get()); - SSL_set_connect_state(Ssl.Get()); - } - - void InitServerSsl(SSL_CTX* ctx) { + BIO_set_data(Bio.Get(), this); + BIO_set_nbio(Bio.Get(), 1); + Ctx = CreateClientContext(); + Ssl = ConstructSsl(Ctx.Get(), Bio.Get()); + SSL_set_connect_state(Ssl.Get()); + } + + void InitServerSsl(SSL_CTX* ctx) { Bio.Reset(BIO_new(IoMethod())); - BIO_set_data(Bio.Get(), this); - BIO_set_nbio(Bio.Get(), 1); - Ssl = ConstructSsl(ctx, Bio.Get()); - SSL_set_accept_state(Ssl.Get()); - } - - void Flush() {} - + BIO_set_data(Bio.Get(), this); + BIO_set_nbio(Bio.Get(), 1); + Ssl = ConstructSsl(ctx, Bio.Get()); + SSL_set_accept_state(Ssl.Get()); + } + + void Flush() {} + ssize_t Send(const void* data, size_t size, bool& read, bool& write) { - ssize_t res = SSL_write(Ssl.Get(), data, size); - if (res < 0) { - res = SSL_get_error(Ssl.Get(), res); - switch(res) { - case SSL_ERROR_WANT_READ: + ssize_t res = SSL_write(Ssl.Get(), data, size); + if (res < 0) { + res = SSL_get_error(Ssl.Get(), res); + switch(res) { + case SSL_ERROR_WANT_READ: read = true; return -EAGAIN; - case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_WRITE: write = true; - return -EAGAIN; - default: - return -EIO; - } - } - return res; - } - + return -EAGAIN; + default: + return -EIO; + } + } + return res; + } + ssize_t Recv(void* data, size_t size, bool& read, bool& write) { - ssize_t res = SSL_read(Ssl.Get(), data, size); - if (res < 0) { - res = SSL_get_error(Ssl.Get(), res); - switch(res) { - case SSL_ERROR_WANT_READ: + ssize_t res = SSL_read(Ssl.Get(), data, size); + if (res < 0) { + res = SSL_get_error(Ssl.Get(), res); + switch(res) { + case SSL_ERROR_WANT_READ: read = true; return -EAGAIN; - case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_WRITE: write = true; - return -EAGAIN; - default: - return -EIO; - } - } - return res; - } - + return -EAGAIN; + default: + return -EIO; + } + } + return res; + } + int OnConnect(bool& read, bool& write) { - if (!Ssl) { - InitClientSsl(); - } - int res = SSL_connect(Ssl.Get()); + if (!Ssl) { + InitClientSsl(); + } + int res = SSL_connect(Ssl.Get()); if (res <= 0) { - res = SSL_get_error(Ssl.Get(), res); - switch(res) { - case SSL_ERROR_WANT_READ: + res = SSL_get_error(Ssl.Get(), res); + switch(res) { + case SSL_ERROR_WANT_READ: read = true; return -EAGAIN; - case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_WRITE: write = true; - return -EAGAIN; - default: - return -EIO; - } - } - return res; - } - + return -EAGAIN; + default: + return -EIO; + } + } + return res; + } + int OnAccept(const TEndpointInfo& endpoint, bool& read, bool& write) { - if (!Ssl) { - InitServerSsl(endpoint.SecureContext.Get()); - } - int res = SSL_accept(Ssl.Get()); + if (!Ssl) { + InitServerSsl(endpoint.SecureContext.Get()); + } + int res = SSL_accept(Ssl.Get()); if (res <= 0) { - res = SSL_get_error(Ssl.Get(), res); - switch(res) { - case SSL_ERROR_WANT_READ: + res = SSL_get_error(Ssl.Get(), res); + switch(res) { + case SSL_ERROR_WANT_READ: read = true; return -EAGAIN; - case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_WRITE: write = true; - return -EAGAIN; - default: - return -EIO; - } - } - return res; - } -}; - -} + return -EAGAIN; + default: + return -EIO; + } + } + return res; + } +}; + +} diff --git a/library/cpp/actors/http/http_proxy_ssl.h b/library/cpp/actors/http/http_proxy_ssl.h index ffce12997f..12fb372b3c 100644 --- a/library/cpp/actors/http/http_proxy_ssl.h +++ b/library/cpp/actors/http/http_proxy_ssl.h @@ -1,22 +1,22 @@ -#pragma once - -#include <openssl/bio.h> -#include <openssl/ssl.h> -#include <openssl/err.h> -#include <openssl/tls1.h> - -namespace NHttp { - -struct TSslHelpers { - struct TSslDestroy { - static void Destroy(SSL_CTX* ctx) noexcept { - SSL_CTX_free(ctx); - } - - static void Destroy(SSL* ssl) noexcept { - SSL_free(ssl); - } - +#pragma once + +#include <openssl/bio.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/tls1.h> + +namespace NHttp { + +struct TSslHelpers { + struct TSslDestroy { + static void Destroy(SSL_CTX* ctx) noexcept { + SSL_CTX_free(ctx); + } + + static void Destroy(SSL* ssl) noexcept { + SSL_free(ssl); + } + static void Destroy(X509* cert) noexcept { X509_free(cert); } @@ -25,48 +25,48 @@ struct TSslHelpers { EVP_PKEY_free(pkey); } - static void Destroy(BIO* bio) noexcept { - BIO_free(bio); - } - }; - - template <typename T> - using TSslHolder = THolder<T, TSslDestroy>; - - static TSslHolder<SSL_CTX> CreateSslCtx(const SSL_METHOD* method) { - TSslHolder<SSL_CTX> ctx(SSL_CTX_new(method)); - - if (ctx) { - SSL_CTX_set_options(ctx.Get(), SSL_OP_NO_SSLv2); - SSL_CTX_set_options(ctx.Get(), SSL_OP_NO_SSLv3); - SSL_CTX_set_options(ctx.Get(), SSL_OP_MICROSOFT_SESS_ID_BUG); - SSL_CTX_set_options(ctx.Get(), SSL_OP_NETSCAPE_CHALLENGE_BUG); - } - - return ctx; - } - - static TSslHolder<SSL_CTX> CreateClientContext() { - return CreateSslCtx(SSLv23_client_method()); - } - - static TSslHolder<SSL_CTX> CreateServerContext(const TString& certificate, const TString& key) { - TSslHolder<SSL_CTX> ctx = CreateSslCtx(SSLv23_server_method()); - SSL_CTX_set_ecdh_auto(ctx.Get(), 1); - int res; - res = SSL_CTX_use_certificate_chain_file(ctx.Get(), certificate.c_str()); - if (res < 0) { - // TODO(xenoxeno): more diagnostics? - return nullptr; - } - res = SSL_CTX_use_PrivateKey_file(ctx.Get(), key.c_str(), SSL_FILETYPE_PEM); - if (res < 0) { - // TODO(xenoxeno): more diagnostics? - return nullptr; - } - return ctx; - } - + static void Destroy(BIO* bio) noexcept { + BIO_free(bio); + } + }; + + template <typename T> + using TSslHolder = THolder<T, TSslDestroy>; + + static TSslHolder<SSL_CTX> CreateSslCtx(const SSL_METHOD* method) { + TSslHolder<SSL_CTX> ctx(SSL_CTX_new(method)); + + if (ctx) { + SSL_CTX_set_options(ctx.Get(), SSL_OP_NO_SSLv2); + SSL_CTX_set_options(ctx.Get(), SSL_OP_NO_SSLv3); + SSL_CTX_set_options(ctx.Get(), SSL_OP_MICROSOFT_SESS_ID_BUG); + SSL_CTX_set_options(ctx.Get(), SSL_OP_NETSCAPE_CHALLENGE_BUG); + } + + return ctx; + } + + static TSslHolder<SSL_CTX> CreateClientContext() { + return CreateSslCtx(SSLv23_client_method()); + } + + static TSslHolder<SSL_CTX> CreateServerContext(const TString& certificate, const TString& key) { + TSslHolder<SSL_CTX> ctx = CreateSslCtx(SSLv23_server_method()); + SSL_CTX_set_ecdh_auto(ctx.Get(), 1); + int res; + res = SSL_CTX_use_certificate_chain_file(ctx.Get(), certificate.c_str()); + if (res < 0) { + // TODO(xenoxeno): more diagnostics? + return nullptr; + } + res = SSL_CTX_use_PrivateKey_file(ctx.Get(), key.c_str(), SSL_FILETYPE_PEM); + if (res < 0) { + // TODO(xenoxeno): more diagnostics? + return nullptr; + } + return ctx; + } + static bool LoadX509Chain(TSslHolder<SSL_CTX>& ctx, const TString& pem) { TSslHolder<BIO> bio(BIO_new_mem_buf(pem.c_str(), pem.size())); if (bio == nullptr) { @@ -116,16 +116,16 @@ struct TSslHelpers { return ctx; } - static TSslHolder<SSL> ConstructSsl(SSL_CTX* ctx, BIO* bio) { - TSslHolder<SSL> ssl(SSL_new(ctx)); - - if (ssl) { - BIO_up_ref(bio); // SSL_set_bio consumes only one reference if rbio and wbio are the same - SSL_set_bio(ssl.Get(), bio, bio); - } - - return ssl; - } -}; - -} + static TSslHolder<SSL> ConstructSsl(SSL_CTX* ctx, BIO* bio) { + TSslHolder<SSL> ssl(SSL_new(ctx)); + + if (ssl) { + BIO_up_ref(bio); // SSL_set_bio consumes only one reference if rbio and wbio are the same + SSL_set_bio(ssl.Get(), bio, bio); + } + + return ssl; + } +}; + +} diff --git a/library/cpp/actors/http/http_static.cpp b/library/cpp/actors/http/http_static.cpp index c075c5f693..452b0a8498 100644 --- a/library/cpp/actors/http/http_static.cpp +++ b/library/cpp/actors/http/http_static.cpp @@ -5,91 +5,91 @@ #include <library/cpp/actors/core/scheduler_basic.h> #include <library/cpp/actors/http/http.h> #include <library/cpp/resource/resource.h> -#include <util/folder/path.h> -#include <util/stream/file.h> - -namespace NHttp { - -class THttpStaticContentHandler : public NActors::TActor<THttpStaticContentHandler> { -public: - using TBase = NActors::TActor<THttpStaticContentHandler>; - const TFsPath URL; - const TFsPath FilePath; - const TFsPath ResourcePath; - const TFsPath Index; - - THttpStaticContentHandler(const TString& url, const TString& filePath, const TString& resourcePath, const TString& index) - : TBase(&THttpStaticContentHandler::StateWork) - , URL(url) - , FilePath(filePath) - , ResourcePath(resourcePath) - , Index(index) - {} - - static TInstant GetCompileTime() { - tm compileTime; - strptime(__DATE__ " " __TIME__, "%B %d %Y %H:%M:%S", &compileTime); - return TInstant::Seconds(mktime(&compileTime)); - } - - void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) { - THttpOutgoingResponsePtr response; - if (event->Get()->Request->Method != "GET") { - response = event->Get()->Request->CreateResponseBadRequest("Wrong request"); - ctx.Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); - return; - } - TFsPath url(event->Get()->Request->URL.Before('?')); - if (!url.IsAbsolute()) { - response = event->Get()->Request->CreateResponseBadRequest("Completely wrong URL"); - ctx.Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); - return; - } - if (url.GetPath().EndsWith('/') && Index.IsDefined()) { - url /= Index; - } - url = url.RelativeTo(URL); - try { - // TODO: caching? - TString contentType = mimetypeByExt(url.GetExtension().c_str()); - TString data; - TFileStat filestat; - TFsPath resourcename(ResourcePath / url); - if (NResource::FindExact(resourcename.GetPath(), &data)) { - static TInstant compileTime(GetCompileTime()); - filestat.MTime = compileTime.Seconds(); - } else { - TFsPath filename(FilePath / url); - if (!filename.IsSubpathOf(FilePath) && filename != FilePath) { - response = event->Get()->Request->CreateResponseBadRequest("Wrong URL"); - ctx.Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); - return; - } - if (filename.Stat(filestat) && filestat.IsFile()) { - data = TUnbufferedFileInput(filename).ReadAll(); - } - } - if (!filestat.IsNull()) { - response = event->Get()->Request->CreateResponseOK(data, contentType, TInstant::Seconds(filestat.MTime)); - } else { - response = event->Get()->Request->CreateResponseNotFound("File not found"); - } - } - catch (const yexception&) { - response = event->Get()->Request->CreateResponseServiceUnavailable("Not available"); - } - ctx.Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); - } - - STFUNC(StateWork) { - switch (ev->GetTypeRewrite()) { - HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); - } - } -}; - -NActors::IActor* CreateHttpStaticContentHandler(const TString& url, const TString& filePath, const TString& resourcePath, const TString& index) { - return new THttpStaticContentHandler(url, filePath, resourcePath, index); -} - -} +#include <util/folder/path.h> +#include <util/stream/file.h> + +namespace NHttp { + +class THttpStaticContentHandler : public NActors::TActor<THttpStaticContentHandler> { +public: + using TBase = NActors::TActor<THttpStaticContentHandler>; + const TFsPath URL; + const TFsPath FilePath; + const TFsPath ResourcePath; + const TFsPath Index; + + THttpStaticContentHandler(const TString& url, const TString& filePath, const TString& resourcePath, const TString& index) + : TBase(&THttpStaticContentHandler::StateWork) + , URL(url) + , FilePath(filePath) + , ResourcePath(resourcePath) + , Index(index) + {} + + static TInstant GetCompileTime() { + tm compileTime; + strptime(__DATE__ " " __TIME__, "%B %d %Y %H:%M:%S", &compileTime); + return TInstant::Seconds(mktime(&compileTime)); + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) { + THttpOutgoingResponsePtr response; + if (event->Get()->Request->Method != "GET") { + response = event->Get()->Request->CreateResponseBadRequest("Wrong request"); + ctx.Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); + return; + } + TFsPath url(event->Get()->Request->URL.Before('?')); + if (!url.IsAbsolute()) { + response = event->Get()->Request->CreateResponseBadRequest("Completely wrong URL"); + ctx.Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); + return; + } + if (url.GetPath().EndsWith('/') && Index.IsDefined()) { + url /= Index; + } + url = url.RelativeTo(URL); + try { + // TODO: caching? + TString contentType = mimetypeByExt(url.GetExtension().c_str()); + TString data; + TFileStat filestat; + TFsPath resourcename(ResourcePath / url); + if (NResource::FindExact(resourcename.GetPath(), &data)) { + static TInstant compileTime(GetCompileTime()); + filestat.MTime = compileTime.Seconds(); + } else { + TFsPath filename(FilePath / url); + if (!filename.IsSubpathOf(FilePath) && filename != FilePath) { + response = event->Get()->Request->CreateResponseBadRequest("Wrong URL"); + ctx.Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); + return; + } + if (filename.Stat(filestat) && filestat.IsFile()) { + data = TUnbufferedFileInput(filename).ReadAll(); + } + } + if (!filestat.IsNull()) { + response = event->Get()->Request->CreateResponseOK(data, contentType, TInstant::Seconds(filestat.MTime)); + } else { + response = event->Get()->Request->CreateResponseNotFound("File not found"); + } + } + catch (const yexception&) { + response = event->Get()->Request->CreateResponseServiceUnavailable("Not available"); + } + ctx.Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); + } + + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + } + } +}; + +NActors::IActor* CreateHttpStaticContentHandler(const TString& url, const TString& filePath, const TString& resourcePath, const TString& index) { + return new THttpStaticContentHandler(url, filePath, resourcePath, index); +} + +} diff --git a/library/cpp/actors/http/http_static.h b/library/cpp/actors/http/http_static.h index f91e15dfb1..f2ee13d003 100644 --- a/library/cpp/actors/http/http_static.h +++ b/library/cpp/actors/http/http_static.h @@ -1,9 +1,9 @@ -#pragma once +#pragma once #include <library/cpp/actors/core/actor.h> -#include "http.h" - -namespace NHttp { - -NActors::IActor* CreateHttpStaticContentHandler(const TString& url, const TString& filePath, const TString& resourcePath, const TString& index = TString()); - -} +#include "http.h" + +namespace NHttp { + +NActors::IActor* CreateHttpStaticContentHandler(const TString& url, const TString& filePath, const TString& resourcePath, const TString& index = TString()); + +} diff --git a/library/cpp/actors/http/http_ut.cpp b/library/cpp/actors/http/http_ut.cpp index 4c922f8d0f..209f61f4de 100644 --- a/library/cpp/actors/http/http_ut.cpp +++ b/library/cpp/actors/http/http_ut.cpp @@ -3,86 +3,86 @@ #include <library/cpp/actors/core/executor_pool_basic.h> #include <library/cpp/actors/core/scheduler_basic.h> #include <library/cpp/actors/testlib/test_runtime.h> -#include <util/system/tempfile.h> -#include "http.h" -#include "http_proxy.h" - - - -enum EService : NActors::NLog::EComponent { - MIN, - Logger, - MVP, - MAX -}; - -namespace { - -template <typename HttpType> -void EatWholeString(TIntrusivePtr<HttpType>& request, const TString& data) { - request->EnsureEnoughSpaceAvailable(data.size()); - auto size = std::min(request->Avail(), data.size()); - memcpy(request->Pos(), data.data(), size); - request->Advance(size); -} - -template <typename HttpType> -void EatPartialString(TIntrusivePtr<HttpType>& request, const TString& data) { - for (char c : data) { - request->EnsureEnoughSpaceAvailable(1); - memcpy(request->Pos(), &c, 1); - request->Advance(1); - } -} - -} - +#include <util/system/tempfile.h> +#include "http.h" +#include "http_proxy.h" + + + +enum EService : NActors::NLog::EComponent { + MIN, + Logger, + MVP, + MAX +}; + +namespace { + +template <typename HttpType> +void EatWholeString(TIntrusivePtr<HttpType>& request, const TString& data) { + request->EnsureEnoughSpaceAvailable(data.size()); + auto size = std::min(request->Avail(), data.size()); + memcpy(request->Pos(), data.data(), size); + request->Advance(size); +} + +template <typename HttpType> +void EatPartialString(TIntrusivePtr<HttpType>& request, const TString& data) { + for (char c : data) { + request->EnsureEnoughSpaceAvailable(1); + memcpy(request->Pos(), &c, 1); + request->Advance(1); + } +} + +} + Y_UNIT_TEST_SUITE(HttpProxy) { Y_UNIT_TEST(BasicParsing) { - NHttp::THttpIncomingRequestPtr request = new NHttp::THttpIncomingRequest(); - EatWholeString(request, "GET /test HTTP/1.1\r\nHost: test\r\nSome-Header: 32344\r\n\r\n"); - UNIT_ASSERT_EQUAL(request->Stage, NHttp::THttpIncomingRequest::EParseStage::Done); - UNIT_ASSERT_EQUAL(request->Method, "GET"); - UNIT_ASSERT_EQUAL(request->URL, "/test"); - UNIT_ASSERT_EQUAL(request->Protocol, "HTTP"); - UNIT_ASSERT_EQUAL(request->Version, "1.1"); - UNIT_ASSERT_EQUAL(request->Host, "test"); - UNIT_ASSERT_EQUAL(request->Headers, "Host: test\r\nSome-Header: 32344\r\n\r\n"); - } - + NHttp::THttpIncomingRequestPtr request = new NHttp::THttpIncomingRequest(); + EatWholeString(request, "GET /test HTTP/1.1\r\nHost: test\r\nSome-Header: 32344\r\n\r\n"); + UNIT_ASSERT_EQUAL(request->Stage, NHttp::THttpIncomingRequest::EParseStage::Done); + UNIT_ASSERT_EQUAL(request->Method, "GET"); + UNIT_ASSERT_EQUAL(request->URL, "/test"); + UNIT_ASSERT_EQUAL(request->Protocol, "HTTP"); + UNIT_ASSERT_EQUAL(request->Version, "1.1"); + UNIT_ASSERT_EQUAL(request->Host, "test"); + UNIT_ASSERT_EQUAL(request->Headers, "Host: test\r\nSome-Header: 32344\r\n\r\n"); + } + Y_UNIT_TEST(BasicParsingChunkedBody) { - 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"); - UNIT_ASSERT_EQUAL(response->Stage, NHttp::THttpIncomingResponse::EParseStage::Done); - UNIT_ASSERT_EQUAL(response->Status, "200"); - UNIT_ASSERT_EQUAL(response->Connection, "close"); - UNIT_ASSERT_EQUAL(response->Protocol, "HTTP"); - UNIT_ASSERT_EQUAL(response->Version, "1.1"); - UNIT_ASSERT_EQUAL(response->TransferEncoding, "chunked"); - UNIT_ASSERT_EQUAL(response->Body, "this is test."); - } - - Y_UNIT_TEST(InvalidParsingChunkedBody) { - 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\n5\r\nthis\r\n4\r\n is \r\n5\r\ntest.\r\n0\r\n\r\n"); - UNIT_ASSERT(response->IsError()); - } - - Y_UNIT_TEST(AdvancedParsingChunkedBody) { - 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\n6\r\nthis\r\n\r\n4\r\n is \r\n5\r\ntest.\r\n0\r\n\r\n"); - UNIT_ASSERT_EQUAL(response->Stage, NHttp::THttpIncomingResponse::EParseStage::Done); - UNIT_ASSERT_EQUAL(response->Status, "200"); - UNIT_ASSERT_EQUAL(response->Connection, "close"); - UNIT_ASSERT_EQUAL(response->Protocol, "HTTP"); - UNIT_ASSERT_EQUAL(response->Version, "1.1"); - UNIT_ASSERT_EQUAL(response->TransferEncoding, "chunked"); - UNIT_ASSERT_EQUAL(response->Body, "this\r\n is test."); - } - + 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"); + UNIT_ASSERT_EQUAL(response->Stage, NHttp::THttpIncomingResponse::EParseStage::Done); + UNIT_ASSERT_EQUAL(response->Status, "200"); + UNIT_ASSERT_EQUAL(response->Connection, "close"); + UNIT_ASSERT_EQUAL(response->Protocol, "HTTP"); + UNIT_ASSERT_EQUAL(response->Version, "1.1"); + UNIT_ASSERT_EQUAL(response->TransferEncoding, "chunked"); + UNIT_ASSERT_EQUAL(response->Body, "this is test."); + } + + Y_UNIT_TEST(InvalidParsingChunkedBody) { + 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\n5\r\nthis\r\n4\r\n is \r\n5\r\ntest.\r\n0\r\n\r\n"); + UNIT_ASSERT(response->IsError()); + } + + Y_UNIT_TEST(AdvancedParsingChunkedBody) { + 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\n6\r\nthis\r\n\r\n4\r\n is \r\n5\r\ntest.\r\n0\r\n\r\n"); + UNIT_ASSERT_EQUAL(response->Stage, NHttp::THttpIncomingResponse::EParseStage::Done); + UNIT_ASSERT_EQUAL(response->Status, "200"); + UNIT_ASSERT_EQUAL(response->Connection, "close"); + UNIT_ASSERT_EQUAL(response->Protocol, "HTTP"); + UNIT_ASSERT_EQUAL(response->Version, "1.1"); + UNIT_ASSERT_EQUAL(response->TransferEncoding, "chunked"); + UNIT_ASSERT_EQUAL(response->Body, "this\r\n is test."); + } + Y_UNIT_TEST(CreateRepsonseWithCompressedBody) { NHttp::THttpIncomingRequestPtr request = nullptr; NHttp::THttpOutgoingResponsePtr response = new NHttp::THttpOutgoingResponse(request, "HTTP", "1.1", "200", "OK"); @@ -95,264 +95,264 @@ Y_UNIT_TEST_SUITE(HttpProxy) { } Y_UNIT_TEST(BasicPartialParsing) { - NHttp::THttpIncomingRequestPtr request = new NHttp::THttpIncomingRequest(); - EatPartialString(request, "GET /test HTTP/1.1\r\nHost: test\r\nSome-Header: 32344\r\n\r\n"); - UNIT_ASSERT_EQUAL(request->Stage, NHttp::THttpIncomingRequest::EParseStage::Done); - UNIT_ASSERT_EQUAL(request->Method, "GET"); - UNIT_ASSERT_EQUAL(request->URL, "/test"); - UNIT_ASSERT_EQUAL(request->Protocol, "HTTP"); - UNIT_ASSERT_EQUAL(request->Version, "1.1"); - UNIT_ASSERT_EQUAL(request->Host, "test"); - UNIT_ASSERT_EQUAL(request->Headers, "Host: test\r\nSome-Header: 32344\r\n\r\n"); - } - + NHttp::THttpIncomingRequestPtr request = new NHttp::THttpIncomingRequest(); + EatPartialString(request, "GET /test HTTP/1.1\r\nHost: test\r\nSome-Header: 32344\r\n\r\n"); + UNIT_ASSERT_EQUAL(request->Stage, NHttp::THttpIncomingRequest::EParseStage::Done); + UNIT_ASSERT_EQUAL(request->Method, "GET"); + UNIT_ASSERT_EQUAL(request->URL, "/test"); + UNIT_ASSERT_EQUAL(request->Protocol, "HTTP"); + UNIT_ASSERT_EQUAL(request->Version, "1.1"); + UNIT_ASSERT_EQUAL(request->Host, "test"); + UNIT_ASSERT_EQUAL(request->Headers, "Host: test\r\nSome-Header: 32344\r\n\r\n"); + } + Y_UNIT_TEST(BasicPartialParsingChunkedBody) { - NHttp::THttpOutgoingRequestPtr request = nullptr; //new NHttp::THttpOutgoingRequest(); - NHttp::THttpIncomingResponsePtr response = new NHttp::THttpIncomingResponse(request); - EatPartialString(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"); - UNIT_ASSERT_EQUAL(response->Stage, NHttp::THttpIncomingResponse::EParseStage::Done); - UNIT_ASSERT_EQUAL(response->Status, "200"); - UNIT_ASSERT_EQUAL(response->Connection, "close"); - UNIT_ASSERT_EQUAL(response->Protocol, "HTTP"); - UNIT_ASSERT_EQUAL(response->Version, "1.1"); - UNIT_ASSERT_EQUAL(response->TransferEncoding, "chunked"); - UNIT_ASSERT_EQUAL(response->Body, "this is test."); - } - + NHttp::THttpOutgoingRequestPtr request = nullptr; //new NHttp::THttpOutgoingRequest(); + NHttp::THttpIncomingResponsePtr response = new NHttp::THttpIncomingResponse(request); + EatPartialString(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"); + UNIT_ASSERT_EQUAL(response->Stage, NHttp::THttpIncomingResponse::EParseStage::Done); + UNIT_ASSERT_EQUAL(response->Status, "200"); + UNIT_ASSERT_EQUAL(response->Connection, "close"); + UNIT_ASSERT_EQUAL(response->Protocol, "HTTP"); + UNIT_ASSERT_EQUAL(response->Version, "1.1"); + UNIT_ASSERT_EQUAL(response->TransferEncoding, "chunked"); + UNIT_ASSERT_EQUAL(response->Body, "this is test."); + } + Y_UNIT_TEST(AdvancedParsing) { - NHttp::THttpIncomingRequestPtr request = new NHttp::THttpIncomingRequest(); - EatWholeString(request, "GE"); - EatWholeString(request, "T"); - EatWholeString(request, " "); - EatWholeString(request, "/test"); - EatWholeString(request, " HTTP/1.1\r"); - EatWholeString(request, "\nHo"); - EatWholeString(request, "st: test"); - EatWholeString(request, "\r\n"); - EatWholeString(request, "Some-Header: 32344\r\n\r"); - EatWholeString(request, "\n"); - UNIT_ASSERT_EQUAL(request->Stage, NHttp::THttpIncomingRequest::EParseStage::Done); - UNIT_ASSERT_EQUAL(request->Method, "GET"); - UNIT_ASSERT_EQUAL(request->URL, "/test"); - UNIT_ASSERT_EQUAL(request->Protocol, "HTTP"); - UNIT_ASSERT_EQUAL(request->Version, "1.1"); - UNIT_ASSERT_EQUAL(request->Host, "test"); - UNIT_ASSERT_EQUAL(request->Headers, "Host: test\r\nSome-Header: 32344\r\n\r\n"); - } - + NHttp::THttpIncomingRequestPtr request = new NHttp::THttpIncomingRequest(); + EatWholeString(request, "GE"); + EatWholeString(request, "T"); + EatWholeString(request, " "); + EatWholeString(request, "/test"); + EatWholeString(request, " HTTP/1.1\r"); + EatWholeString(request, "\nHo"); + EatWholeString(request, "st: test"); + EatWholeString(request, "\r\n"); + EatWholeString(request, "Some-Header: 32344\r\n\r"); + EatWholeString(request, "\n"); + UNIT_ASSERT_EQUAL(request->Stage, NHttp::THttpIncomingRequest::EParseStage::Done); + UNIT_ASSERT_EQUAL(request->Method, "GET"); + UNIT_ASSERT_EQUAL(request->URL, "/test"); + UNIT_ASSERT_EQUAL(request->Protocol, "HTTP"); + UNIT_ASSERT_EQUAL(request->Version, "1.1"); + UNIT_ASSERT_EQUAL(request->Host, "test"); + UNIT_ASSERT_EQUAL(request->Headers, "Host: test\r\nSome-Header: 32344\r\n\r\n"); + } + Y_UNIT_TEST(AdvancedPartialParsing) { - NHttp::THttpIncomingRequestPtr request = new NHttp::THttpIncomingRequest(); - EatPartialString(request, "GE"); - EatPartialString(request, "T"); - EatPartialString(request, " "); - EatPartialString(request, "/test"); - EatPartialString(request, " HTTP/1.1\r"); - EatPartialString(request, "\nHo"); - EatPartialString(request, "st: test"); - EatPartialString(request, "\r\n"); - EatPartialString(request, "Some-Header: 32344\r\n\r"); - EatPartialString(request, "\n"); - UNIT_ASSERT_EQUAL(request->Stage, NHttp::THttpIncomingRequest::EParseStage::Done); - UNIT_ASSERT_EQUAL(request->Method, "GET"); - UNIT_ASSERT_EQUAL(request->URL, "/test"); - UNIT_ASSERT_EQUAL(request->Protocol, "HTTP"); - UNIT_ASSERT_EQUAL(request->Version, "1.1"); - UNIT_ASSERT_EQUAL(request->Host, "test"); - UNIT_ASSERT_EQUAL(request->Headers, "Host: test\r\nSome-Header: 32344\r\n\r\n"); - } - - Y_UNIT_TEST(BasicRenderBodyWithHeadersAndCookies) { - NHttp::THttpOutgoingRequestPtr request = NHttp::THttpOutgoingRequest::CreateRequestGet("http://www.yandex.ru/data/url"); - NHttp::THeadersBuilder headers; - NHttp::TCookiesBuilder cookies; - cookies.Set("cookie1", "123456"); - cookies.Set("cookie2", "45678"); - headers.Set("Cookie", cookies.Render()); - request->Set(headers); - TString requestData; - request->AsString(requestData); - UNIT_ASSERT_VALUES_EQUAL(requestData, "GET /data/url HTTP/1.1\r\nHost: www.yandex.ru\r\nAccept: */*\r\nCookie: cookie1=123456; cookie2=45678;\r\n"); - } - + NHttp::THttpIncomingRequestPtr request = new NHttp::THttpIncomingRequest(); + EatPartialString(request, "GE"); + EatPartialString(request, "T"); + EatPartialString(request, " "); + EatPartialString(request, "/test"); + EatPartialString(request, " HTTP/1.1\r"); + EatPartialString(request, "\nHo"); + EatPartialString(request, "st: test"); + EatPartialString(request, "\r\n"); + EatPartialString(request, "Some-Header: 32344\r\n\r"); + EatPartialString(request, "\n"); + UNIT_ASSERT_EQUAL(request->Stage, NHttp::THttpIncomingRequest::EParseStage::Done); + UNIT_ASSERT_EQUAL(request->Method, "GET"); + UNIT_ASSERT_EQUAL(request->URL, "/test"); + UNIT_ASSERT_EQUAL(request->Protocol, "HTTP"); + UNIT_ASSERT_EQUAL(request->Version, "1.1"); + UNIT_ASSERT_EQUAL(request->Host, "test"); + UNIT_ASSERT_EQUAL(request->Headers, "Host: test\r\nSome-Header: 32344\r\n\r\n"); + } + + Y_UNIT_TEST(BasicRenderBodyWithHeadersAndCookies) { + NHttp::THttpOutgoingRequestPtr request = NHttp::THttpOutgoingRequest::CreateRequestGet("http://www.yandex.ru/data/url"); + NHttp::THeadersBuilder headers; + NHttp::TCookiesBuilder cookies; + cookies.Set("cookie1", "123456"); + cookies.Set("cookie2", "45678"); + headers.Set("Cookie", cookies.Render()); + request->Set(headers); + TString requestData; + request->AsString(requestData); + UNIT_ASSERT_VALUES_EQUAL(requestData, "GET /data/url HTTP/1.1\r\nHost: www.yandex.ru\r\nAccept: */*\r\nCookie: cookie1=123456; cookie2=45678;\r\n"); + } + Y_UNIT_TEST(BasicRunning) { NActors::TTestActorRuntimeBase actorSystem; - TPortManager portManager; - TIpPort port = portManager.GetTcpPort(); - TAutoPtr<NActors::IEventHandle> handle; + TPortManager portManager; + TIpPort port = portManager.GetTcpPort(); + TAutoPtr<NActors::IEventHandle> handle; actorSystem.Initialize(); NMonitoring::TMetricRegistry sensors; - - NActors::IActor* proxy = NHttp::CreateHttpProxy(sensors); + + NActors::IActor* proxy = NHttp::CreateHttpProxy(sensors); NActors::TActorId proxyId = actorSystem.Register(proxy); actorSystem.Send(new NActors::IEventHandle(proxyId, TActorId(), new NHttp::TEvHttpProxy::TEvAddListeningPort(port)), 0, true); - actorSystem.DispatchEvents(); - + actorSystem.DispatchEvents(); + NActors::TActorId serverId = actorSystem.AllocateEdgeActor(); - actorSystem.Send(new NActors::IEventHandle(proxyId, serverId, new NHttp::TEvHttpProxy::TEvRegisterHandler("/test", serverId)), 0, true); - + actorSystem.Send(new NActors::IEventHandle(proxyId, serverId, new NHttp::TEvHttpProxy::TEvRegisterHandler("/test", serverId)), 0, true); + NActors::TActorId clientId = actorSystem.AllocateEdgeActor(); - NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestGet("http://[::1]:" + ToString(port) + "/test"); - actorSystem.Send(new NActors::IEventHandle(proxyId, clientId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)), 0, true); - - NHttp::TEvHttpProxy::TEvHttpIncomingRequest* request = actorSystem.GrabEdgeEvent<NHttp::TEvHttpProxy::TEvHttpIncomingRequest>(handle); - - UNIT_ASSERT_EQUAL(request->Request->URL, "/test"); - - NHttp::THttpOutgoingResponsePtr httpResponse = request->Request->CreateResponseString("HTTP/1.1 200 Found\r\nConnection: Close\r\nTransfer-Encoding: chunked\r\n\r\n6\r\npassed\r\n0\r\n\r\n"); - actorSystem.Send(new NActors::IEventHandle(handle->Sender, serverId, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)), 0, true); - - NHttp::TEvHttpProxy::TEvHttpIncomingResponse* response = actorSystem.GrabEdgeEvent<NHttp::TEvHttpProxy::TEvHttpIncomingResponse>(handle); - - UNIT_ASSERT_EQUAL(response->Response->Status, "200"); - UNIT_ASSERT_EQUAL(response->Response->Body, "passed"); - } - - Y_UNIT_TEST(TlsRunning) { - NActors::TTestActorRuntimeBase actorSystem; - TPortManager portManager; - TIpPort port = portManager.GetTcpPort(); - TAutoPtr<NActors::IEventHandle> handle; - actorSystem.Initialize(); + NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestGet("http://[::1]:" + ToString(port) + "/test"); + actorSystem.Send(new NActors::IEventHandle(proxyId, clientId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)), 0, true); + + NHttp::TEvHttpProxy::TEvHttpIncomingRequest* request = actorSystem.GrabEdgeEvent<NHttp::TEvHttpProxy::TEvHttpIncomingRequest>(handle); + + UNIT_ASSERT_EQUAL(request->Request->URL, "/test"); + + NHttp::THttpOutgoingResponsePtr httpResponse = request->Request->CreateResponseString("HTTP/1.1 200 Found\r\nConnection: Close\r\nTransfer-Encoding: chunked\r\n\r\n6\r\npassed\r\n0\r\n\r\n"); + actorSystem.Send(new NActors::IEventHandle(handle->Sender, serverId, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)), 0, true); + + NHttp::TEvHttpProxy::TEvHttpIncomingResponse* response = actorSystem.GrabEdgeEvent<NHttp::TEvHttpProxy::TEvHttpIncomingResponse>(handle); + + UNIT_ASSERT_EQUAL(response->Response->Status, "200"); + UNIT_ASSERT_EQUAL(response->Response->Body, "passed"); + } + + Y_UNIT_TEST(TlsRunning) { + NActors::TTestActorRuntimeBase actorSystem; + TPortManager portManager; + TIpPort port = portManager.GetTcpPort(); + TAutoPtr<NActors::IEventHandle> handle; + actorSystem.Initialize(); NMonitoring::TMetricRegistry sensors; - - TString certificateContent = R"___(-----BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCzRZjodO7Aqe1w -RyOj6kG6g2nn8ZGAxfao4mLT0jDTbVksrhV/h2s3uldLkFo5WrNQ8WZe+iIbXeFL -s8tO6hslzreo9sih2IHoRcH5KnS/6YTqVhRTJb1jE2dM8NwYbwTi+T2Pe0FrBPjI -kgVO50gAtYl9C+fc715uZiSKW+rRlP5OoFTwxrOjiU27RPZjFYyWK9wTI1Es9uRr -lbZbLl5cY6dK2J1AViRraaYKCWO26VbOPWLsY4OD3e+ZXIc3OMCz6Yb0wmRPeJ60 -bbbkGfI8O27kDdv69MAWHIm0yYMzKEnom1dce7rNQNDEqJfocsYIsg+EvayT1yQ9 -KTBegw7LAgMBAAECggEBAKaOCrotqYQmXArsjRhFFDwMy+BKdzyEr93INrlFl0dX -WHpCYobRcbOc1G3H94tB0UdqgAnNqtJyLlb+++ydZAuEOu4oGc8EL+10ofq0jzOd -6Xct8kQt0/6wkFDTlii9PHUDy0X65ZRgUiNGRtg/2I2QG+SpowmI+trm2xwQueFs -VaWrjc3cVvXx0b8Lu7hqZUv08kgC38stzuRk/n2T5VWSAr7Z4ZWQbO918Dv35HUw -Wy/0jNUFP9CBCvFJ4l0OoH9nYhWFG+HXWzNdw6/Hca4jciRKo6esCiOZ9uWYv/ec -/NvX9rgFg8G8/SrTisX10+Bbeq+R1RKwq/IG409TH4ECgYEA14L+3QsgNIUMeYAx -jSCyk22R/tOHI1BM+GtKPUhnwHlAssrcPcxXMJovl6WL93VauYjym0wpCz9urSpA -I2CqTsG8GYciA6Dr3mHgD6cK0jj9UPAU6EnZ5S0mjhPqKZqutu9QegzD2uESvuN8 -36xezwQthzAf0nI/P3sJGjVXjikCgYEA1POm5xcV6SmM6HnIdadEebhzZIJ9TXQz -ry3Jj3a7CKyD5C7fAdkHUTCjgT/2ElxPi9ABkZnC+d/cW9GtJFa0II5qO/agm3KQ -ZXYiutu9A7xACHYFXRiJEjVUdGG9dKMVOHUEa8IHEgrrcUVM/suy/GgutywIfaXs -y58IFP24K9MCgYEAk6zjz7wL+XEiNy+sxLQfKf7vB9sSwxQHakK6wHuY/L8Zomp3 -uLEJHfjJm/SIkK0N2g0JkXkCtv5kbKyC/rsCeK0wo52BpVLjzaLr0k34kE0U6B1b -dkEE2pGx1bG3x4KDLj+Wuct9ecK5Aa0IqIyI+vo16GkFpUM8K9e3SQo8UOECgYEA -sCZYAkILYtJ293p9giz5rIISGasDAUXE1vxWBXEeJ3+kneTTnZCrx9Im/ewtnWR0 -fF90XL9HFDDD88POqAd8eo2zfKR2l/89SGBfPBg2EtfuU9FkgGyiPciVcqvC7q9U -B15saMKX3KnhtdGwbfeLt9RqCCTJZT4SUSDcq5hwdvcCgYAxY4Be8mNipj8Cgg22 -mVWSolA0TEzbtUcNk6iGodpi+Z0LKpsPC0YRqPRyh1K+rIltG1BVdmUBHcMlOYxl -lWWvbJH6PkJWy4n2MF7PO45kjN3pPZg4hgH63JjZeAineBwEArUGb9zHnvzcdRvF -wuQ2pZHL/HJ0laUSieHDJ5917w== ------END PRIVATE KEY----- - - ------BEGIN CERTIFICATE----- -MIIDjTCCAnWgAwIBAgIURt5IBx0J3xgEaQvmyrFH2A+NkpMwDQYJKoZIhvcNAQEL -BQAwVjELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9z -Y293MQ8wDQYDVQQKDAZZYW5kZXgxFDASBgNVBAMMC3Rlc3Qtc2VydmVyMB4XDTE5 -MDkyMDE3MTQ0MVoXDTQ3MDIwNDE3MTQ0MVowVjELMAkGA1UEBhMCUlUxDzANBgNV -BAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MQ8wDQYDVQQKDAZZYW5kZXgxFDAS -BgNVBAMMC3Rlc3Qtc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAs0WY6HTuwKntcEcjo+pBuoNp5/GRgMX2qOJi09Iw021ZLK4Vf4drN7pXS5Ba -OVqzUPFmXvoiG13hS7PLTuobJc63qPbIodiB6EXB+Sp0v+mE6lYUUyW9YxNnTPDc -GG8E4vk9j3tBawT4yJIFTudIALWJfQvn3O9ebmYkilvq0ZT+TqBU8Mazo4lNu0T2 -YxWMlivcEyNRLPbka5W2Wy5eXGOnStidQFYka2mmCgljtulWzj1i7GODg93vmVyH -NzjAs+mG9MJkT3ietG225BnyPDtu5A3b+vTAFhyJtMmDMyhJ6JtXXHu6zUDQxKiX -6HLGCLIPhL2sk9ckPSkwXoMOywIDAQABo1MwUTAdBgNVHQ4EFgQUDv/xuJ4CvCgG -fPrZP3hRAt2+/LwwHwYDVR0jBBgwFoAUDv/xuJ4CvCgGfPrZP3hRAt2+/LwwDwYD -VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAinKpMYaA2tjLpAnPVbjy -/ZxSBhhB26RiQp3Re8XOKyhTWqgYE6kldYT0aXgK9x9mPC5obQannDDYxDc7lX+/ -qP/u1X81ZcDRo/f+qQ3iHfT6Ftt/4O3qLnt45MFM6Q7WabRm82x3KjZTqpF3QUdy -tumWiuAP5DMd1IRDtnKjFHO721OsEsf6NLcqdX89bGeqXDvrkwg3/PNwTyW5E7cj -feY8L2eWtg6AJUnIBu11wvfzkLiH3QKzHvO/SIZTGf5ihDsJ3aKEE9UNauTL3bVc -CRA/5XcX13GJwHHj6LCoc3sL7mt8qV9HKY2AOZ88mpObzISZxgPpdKCfjsrdm63V -6g== ------END CERTIFICATE-----)___"; - - TTempFileHandle certificateFile; - - certificateFile.Write(certificateContent.data(), certificateContent.size()); - - NActors::IActor* proxy = NHttp::CreateHttpProxy(sensors); + + TString certificateContent = R"___(-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCzRZjodO7Aqe1w +RyOj6kG6g2nn8ZGAxfao4mLT0jDTbVksrhV/h2s3uldLkFo5WrNQ8WZe+iIbXeFL +s8tO6hslzreo9sih2IHoRcH5KnS/6YTqVhRTJb1jE2dM8NwYbwTi+T2Pe0FrBPjI +kgVO50gAtYl9C+fc715uZiSKW+rRlP5OoFTwxrOjiU27RPZjFYyWK9wTI1Es9uRr +lbZbLl5cY6dK2J1AViRraaYKCWO26VbOPWLsY4OD3e+ZXIc3OMCz6Yb0wmRPeJ60 +bbbkGfI8O27kDdv69MAWHIm0yYMzKEnom1dce7rNQNDEqJfocsYIsg+EvayT1yQ9 +KTBegw7LAgMBAAECggEBAKaOCrotqYQmXArsjRhFFDwMy+BKdzyEr93INrlFl0dX +WHpCYobRcbOc1G3H94tB0UdqgAnNqtJyLlb+++ydZAuEOu4oGc8EL+10ofq0jzOd +6Xct8kQt0/6wkFDTlii9PHUDy0X65ZRgUiNGRtg/2I2QG+SpowmI+trm2xwQueFs +VaWrjc3cVvXx0b8Lu7hqZUv08kgC38stzuRk/n2T5VWSAr7Z4ZWQbO918Dv35HUw +Wy/0jNUFP9CBCvFJ4l0OoH9nYhWFG+HXWzNdw6/Hca4jciRKo6esCiOZ9uWYv/ec +/NvX9rgFg8G8/SrTisX10+Bbeq+R1RKwq/IG409TH4ECgYEA14L+3QsgNIUMeYAx +jSCyk22R/tOHI1BM+GtKPUhnwHlAssrcPcxXMJovl6WL93VauYjym0wpCz9urSpA +I2CqTsG8GYciA6Dr3mHgD6cK0jj9UPAU6EnZ5S0mjhPqKZqutu9QegzD2uESvuN8 +36xezwQthzAf0nI/P3sJGjVXjikCgYEA1POm5xcV6SmM6HnIdadEebhzZIJ9TXQz +ry3Jj3a7CKyD5C7fAdkHUTCjgT/2ElxPi9ABkZnC+d/cW9GtJFa0II5qO/agm3KQ +ZXYiutu9A7xACHYFXRiJEjVUdGG9dKMVOHUEa8IHEgrrcUVM/suy/GgutywIfaXs +y58IFP24K9MCgYEAk6zjz7wL+XEiNy+sxLQfKf7vB9sSwxQHakK6wHuY/L8Zomp3 +uLEJHfjJm/SIkK0N2g0JkXkCtv5kbKyC/rsCeK0wo52BpVLjzaLr0k34kE0U6B1b +dkEE2pGx1bG3x4KDLj+Wuct9ecK5Aa0IqIyI+vo16GkFpUM8K9e3SQo8UOECgYEA +sCZYAkILYtJ293p9giz5rIISGasDAUXE1vxWBXEeJ3+kneTTnZCrx9Im/ewtnWR0 +fF90XL9HFDDD88POqAd8eo2zfKR2l/89SGBfPBg2EtfuU9FkgGyiPciVcqvC7q9U +B15saMKX3KnhtdGwbfeLt9RqCCTJZT4SUSDcq5hwdvcCgYAxY4Be8mNipj8Cgg22 +mVWSolA0TEzbtUcNk6iGodpi+Z0LKpsPC0YRqPRyh1K+rIltG1BVdmUBHcMlOYxl +lWWvbJH6PkJWy4n2MF7PO45kjN3pPZg4hgH63JjZeAineBwEArUGb9zHnvzcdRvF +wuQ2pZHL/HJ0laUSieHDJ5917w== +-----END PRIVATE KEY----- + + +-----BEGIN CERTIFICATE----- +MIIDjTCCAnWgAwIBAgIURt5IBx0J3xgEaQvmyrFH2A+NkpMwDQYJKoZIhvcNAQEL +BQAwVjELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9z +Y293MQ8wDQYDVQQKDAZZYW5kZXgxFDASBgNVBAMMC3Rlc3Qtc2VydmVyMB4XDTE5 +MDkyMDE3MTQ0MVoXDTQ3MDIwNDE3MTQ0MVowVjELMAkGA1UEBhMCUlUxDzANBgNV +BAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MQ8wDQYDVQQKDAZZYW5kZXgxFDAS +BgNVBAMMC3Rlc3Qtc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAs0WY6HTuwKntcEcjo+pBuoNp5/GRgMX2qOJi09Iw021ZLK4Vf4drN7pXS5Ba +OVqzUPFmXvoiG13hS7PLTuobJc63qPbIodiB6EXB+Sp0v+mE6lYUUyW9YxNnTPDc +GG8E4vk9j3tBawT4yJIFTudIALWJfQvn3O9ebmYkilvq0ZT+TqBU8Mazo4lNu0T2 +YxWMlivcEyNRLPbka5W2Wy5eXGOnStidQFYka2mmCgljtulWzj1i7GODg93vmVyH +NzjAs+mG9MJkT3ietG225BnyPDtu5A3b+vTAFhyJtMmDMyhJ6JtXXHu6zUDQxKiX +6HLGCLIPhL2sk9ckPSkwXoMOywIDAQABo1MwUTAdBgNVHQ4EFgQUDv/xuJ4CvCgG +fPrZP3hRAt2+/LwwHwYDVR0jBBgwFoAUDv/xuJ4CvCgGfPrZP3hRAt2+/LwwDwYD +VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAinKpMYaA2tjLpAnPVbjy +/ZxSBhhB26RiQp3Re8XOKyhTWqgYE6kldYT0aXgK9x9mPC5obQannDDYxDc7lX+/ +qP/u1X81ZcDRo/f+qQ3iHfT6Ftt/4O3qLnt45MFM6Q7WabRm82x3KjZTqpF3QUdy +tumWiuAP5DMd1IRDtnKjFHO721OsEsf6NLcqdX89bGeqXDvrkwg3/PNwTyW5E7cj +feY8L2eWtg6AJUnIBu11wvfzkLiH3QKzHvO/SIZTGf5ihDsJ3aKEE9UNauTL3bVc +CRA/5XcX13GJwHHj6LCoc3sL7mt8qV9HKY2AOZ88mpObzISZxgPpdKCfjsrdm63V +6g== +-----END CERTIFICATE-----)___"; + + TTempFileHandle certificateFile; + + certificateFile.Write(certificateContent.data(), certificateContent.size()); + + NActors::IActor* proxy = NHttp::CreateHttpProxy(sensors); NActors::TActorId proxyId = actorSystem.Register(proxy); - + THolder<NHttp::TEvHttpProxy::TEvAddListeningPort> add = MakeHolder<NHttp::TEvHttpProxy::TEvAddListeningPort>(port); - ///////// https configuration - add->Secure = true; - add->CertificateFile = certificateFile.Name(); - add->PrivateKeyFile = certificateFile.Name(); - ///////// + ///////// https configuration + add->Secure = true; + add->CertificateFile = certificateFile.Name(); + add->PrivateKeyFile = certificateFile.Name(); + ///////// actorSystem.Send(new NActors::IEventHandle(proxyId, TActorId(), add.Release()), 0, true); - actorSystem.DispatchEvents(); - + actorSystem.DispatchEvents(); + NActors::TActorId serverId = actorSystem.AllocateEdgeActor(); - actorSystem.Send(new NActors::IEventHandle(proxyId, serverId, new NHttp::TEvHttpProxy::TEvRegisterHandler("/test", serverId)), 0, true); - + actorSystem.Send(new NActors::IEventHandle(proxyId, serverId, new NHttp::TEvHttpProxy::TEvRegisterHandler("/test", serverId)), 0, true); + NActors::TActorId clientId = actorSystem.AllocateEdgeActor(); - NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestGet("https://[::1]:" + ToString(port) + "/test"); - actorSystem.Send(new NActors::IEventHandle(proxyId, clientId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)), 0, true); - - NHttp::TEvHttpProxy::TEvHttpIncomingRequest* request = actorSystem.GrabEdgeEvent<NHttp::TEvHttpProxy::TEvHttpIncomingRequest>(handle); - - UNIT_ASSERT_EQUAL(request->Request->URL, "/test"); - - NHttp::THttpOutgoingResponsePtr httpResponse = request->Request->CreateResponseString("HTTP/1.1 200 Found\r\nConnection: Close\r\nTransfer-Encoding: chunked\r\n\r\n6\r\npassed\r\n0\r\n\r\n"); - actorSystem.Send(new NActors::IEventHandle(handle->Sender, serverId, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)), 0, true); - - NHttp::TEvHttpProxy::TEvHttpIncomingResponse* response = actorSystem.GrabEdgeEvent<NHttp::TEvHttpProxy::TEvHttpIncomingResponse>(handle); - - UNIT_ASSERT_EQUAL(response->Response->Status, "200"); - UNIT_ASSERT_EQUAL(response->Response->Body, "passed"); - } - + NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestGet("https://[::1]:" + ToString(port) + "/test"); + actorSystem.Send(new NActors::IEventHandle(proxyId, clientId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)), 0, true); + + NHttp::TEvHttpProxy::TEvHttpIncomingRequest* request = actorSystem.GrabEdgeEvent<NHttp::TEvHttpProxy::TEvHttpIncomingRequest>(handle); + + UNIT_ASSERT_EQUAL(request->Request->URL, "/test"); + + NHttp::THttpOutgoingResponsePtr httpResponse = request->Request->CreateResponseString("HTTP/1.1 200 Found\r\nConnection: Close\r\nTransfer-Encoding: chunked\r\n\r\n6\r\npassed\r\n0\r\n\r\n"); + actorSystem.Send(new NActors::IEventHandle(handle->Sender, serverId, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)), 0, true); + + NHttp::TEvHttpProxy::TEvHttpIncomingResponse* response = actorSystem.GrabEdgeEvent<NHttp::TEvHttpProxy::TEvHttpIncomingResponse>(handle); + + UNIT_ASSERT_EQUAL(response->Response->Status, "200"); + UNIT_ASSERT_EQUAL(response->Response->Body, "passed"); + } + /*Y_UNIT_TEST(AdvancedRunning) { THolder<NActors::TActorSystemSetup> setup = MakeHolder<NActors::TActorSystemSetup>(); - setup->NodeId = 1; - setup->ExecutorsCount = 1; - setup->Executors = new TAutoPtr<NActors::IExecutorPool>[1]; - setup->Executors[0] = new NActors::TBasicExecutorPool(0, 2, 10); - setup->Scheduler = new NActors::TBasicSchedulerThread(NActors::TSchedulerConfig(512, 100)); - NActors::TActorSystem actorSystem(setup); - actorSystem.Start(); - NHttp::THttpProxy* incomingProxy = new NHttp::THttpProxy(); + setup->NodeId = 1; + setup->ExecutorsCount = 1; + setup->Executors = new TAutoPtr<NActors::IExecutorPool>[1]; + setup->Executors[0] = new NActors::TBasicExecutorPool(0, 2, 10); + setup->Scheduler = new NActors::TBasicSchedulerThread(NActors::TSchedulerConfig(512, 100)); + NActors::TActorSystem actorSystem(setup); + actorSystem.Start(); + NHttp::THttpProxy* incomingProxy = new NHttp::THttpProxy(); NActors::TActorId incomingProxyId = actorSystem.Register(incomingProxy); - actorSystem.Send(incomingProxyId, new NHttp::TEvHttpProxy::TEvAddListeningPort(13337)); - - NHttp::THttpProxy* outgoingProxy = new NHttp::THttpProxy(); + actorSystem.Send(incomingProxyId, new NHttp::TEvHttpProxy::TEvAddListeningPort(13337)); + + NHttp::THttpProxy* outgoingProxy = new NHttp::THttpProxy(); NActors::TActorId outgoingProxyId = actorSystem.Register(outgoingProxy); - + THolder<NHttp::THttpStaticStringRequest> httpRequest = MakeHolder<NHttp::THttpStaticStringRequest>("GET /test HTTP/1.1\r\n\r\n"); - actorSystem.Send(outgoingProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest("[::]:13337", std::move(httpRequest))); - - Sleep(TDuration::Minutes(60)); - }*/ - - Y_UNIT_TEST(TooLongHeader) { - NActors::TTestActorRuntimeBase actorSystem; - TPortManager portManager; - TIpPort port = portManager.GetTcpPort(); - TAutoPtr<NActors::IEventHandle> handle; - actorSystem.Initialize(); - NMonitoring::TMetricRegistry sensors; - - NActors::IActor* proxy = NHttp::CreateHttpProxy(sensors); - NActors::TActorId proxyId = actorSystem.Register(proxy); - actorSystem.Send(new NActors::IEventHandle(proxyId, TActorId(), new NHttp::TEvHttpProxy::TEvAddListeningPort(port)), 0, true); - actorSystem.DispatchEvents(); - - NActors::TActorId serverId = actorSystem.AllocateEdgeActor(); - actorSystem.Send(new NActors::IEventHandle(proxyId, serverId, new NHttp::TEvHttpProxy::TEvRegisterHandler("/test", serverId)), 0, true); - - NActors::TActorId clientId = actorSystem.AllocateEdgeActor(); - NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestGet("http://[::1]:" + ToString(port) + "/test"); - httpRequest->Set("Connection", "close"); - TString longHeader; - longHeader.append(9000, 'X'); - httpRequest->Set(longHeader, "data"); - actorSystem.Send(new NActors::IEventHandle(proxyId, clientId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)), 0, true); - - NHttp::TEvHttpProxy::TEvHttpIncomingResponse* response = actorSystem.GrabEdgeEvent<NHttp::TEvHttpProxy::TEvHttpIncomingResponse>(handle); - - UNIT_ASSERT_EQUAL(response->Response->Status, "400"); - UNIT_ASSERT_EQUAL(response->Response->Body, "Invalid http header"); - } -} + actorSystem.Send(outgoingProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest("[::]:13337", std::move(httpRequest))); + + Sleep(TDuration::Minutes(60)); + }*/ + + Y_UNIT_TEST(TooLongHeader) { + NActors::TTestActorRuntimeBase actorSystem; + TPortManager portManager; + TIpPort port = portManager.GetTcpPort(); + TAutoPtr<NActors::IEventHandle> handle; + actorSystem.Initialize(); + NMonitoring::TMetricRegistry sensors; + + NActors::IActor* proxy = NHttp::CreateHttpProxy(sensors); + NActors::TActorId proxyId = actorSystem.Register(proxy); + actorSystem.Send(new NActors::IEventHandle(proxyId, TActorId(), new NHttp::TEvHttpProxy::TEvAddListeningPort(port)), 0, true); + actorSystem.DispatchEvents(); + + NActors::TActorId serverId = actorSystem.AllocateEdgeActor(); + actorSystem.Send(new NActors::IEventHandle(proxyId, serverId, new NHttp::TEvHttpProxy::TEvRegisterHandler("/test", serverId)), 0, true); + + NActors::TActorId clientId = actorSystem.AllocateEdgeActor(); + NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestGet("http://[::1]:" + ToString(port) + "/test"); + httpRequest->Set("Connection", "close"); + TString longHeader; + longHeader.append(9000, 'X'); + httpRequest->Set(longHeader, "data"); + actorSystem.Send(new NActors::IEventHandle(proxyId, clientId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)), 0, true); + + NHttp::TEvHttpProxy::TEvHttpIncomingResponse* response = actorSystem.GrabEdgeEvent<NHttp::TEvHttpProxy::TEvHttpIncomingResponse>(handle); + + UNIT_ASSERT_EQUAL(response->Response->Status, "400"); + UNIT_ASSERT_EQUAL(response->Response->Body, "Invalid http header"); + } +} diff --git a/library/cpp/actors/http/ut/ya.make b/library/cpp/actors/http/ut/ya.make index 8b4c04c4d3..12d360dabf 100644 --- a/library/cpp/actors/http/ut/ya.make +++ b/library/cpp/actors/http/ut/ya.make @@ -1,18 +1,18 @@ UNITTEST_FOR(library/cpp/actors/http) - -OWNER(xenoxeno) - -SIZE(SMALL) - -PEERDIR( + +OWNER(xenoxeno) + +SIZE(SMALL) + +PEERDIR( library/cpp/actors/testlib -) - +) + IF (NOT OS_WINDOWS) -SRCS( - http_ut.cpp -) +SRCS( + http_ut.cpp +) ELSE() ENDIF() - -END() + +END() diff --git a/library/cpp/actors/http/ya.make b/library/cpp/actors/http/ya.make index 7ce68b7a75..60c9c93a09 100644 --- a/library/cpp/actors/http/ya.make +++ b/library/cpp/actors/http/ya.make @@ -1,33 +1,33 @@ -RECURSE_FOR_TESTS(ut) - -LIBRARY() - -OWNER(xenoxeno g:kikimr) - -SRCS( - http_cache.cpp - http_cache.h - http_config.h - http_proxy_acceptor.cpp - http_proxy_incoming.cpp - http_proxy_outgoing.cpp - http_proxy_sock_impl.h - http_proxy_ssl.h - http_proxy.cpp - http_proxy.h - http_static.cpp - http_static.h - http.cpp - http.h -) - -PEERDIR( - contrib/libs/openssl +RECURSE_FOR_TESTS(ut) + +LIBRARY() + +OWNER(xenoxeno g:kikimr) + +SRCS( + http_cache.cpp + http_cache.h + http_config.h + http_proxy_acceptor.cpp + http_proxy_incoming.cpp + http_proxy_outgoing.cpp + http_proxy_sock_impl.h + http_proxy_ssl.h + http_proxy.cpp + http_proxy.h + http_static.cpp + http_static.h + http.cpp + http.h +) + +PEERDIR( + contrib/libs/openssl library/cpp/actors/core library/cpp/actors/interconnect library/cpp/dns library/cpp/monlib/metrics library/cpp/string_utils/quote -) - -END() +) + +END() diff --git a/library/cpp/actors/interconnect/interconnect_common.h b/library/cpp/actors/interconnect/interconnect_common.h index 285709a00c..30e36c4242 100644 --- a/library/cpp/actors/interconnect/interconnect_common.h +++ b/library/cpp/actors/interconnect/interconnect_common.h @@ -65,8 +65,8 @@ namespace NActors { using TRegisterMonPageCallback = std::function<void(const TString& path, const TString& title, TActorSystem* actorSystem, const TActorId& actorId)>; - using TInitWhiteboardCallback = std::function<void(ui16 icPort, TActorSystem* actorSystem)>; - + using TInitWhiteboardCallback = std::function<void(ui16 icPort, TActorSystem* actorSystem)>; + using TUpdateWhiteboardCallback = std::function<void(const TString& peer, bool connected, bool green, bool yellow, bool orange, bool red, TActorSystem* actorSystem)>; @@ -84,7 +84,7 @@ namespace NActors { TVector<TString> AcceptUUID; ui64 StartTime = GetCycleCountFast(); TString TechnicalSelfHostName; - TInitWhiteboardCallback InitWhiteboard; + TInitWhiteboardCallback InitWhiteboard; TUpdateWhiteboardCallback UpdateWhiteboard; ui32 HandshakeBallastSize = 0; TAtomic StartedSessionKiller = 0; diff --git a/library/cpp/actors/interconnect/interconnect_tcp_server.cpp b/library/cpp/actors/interconnect/interconnect_tcp_server.cpp index b95c994598..557092a13c 100644 --- a/library/cpp/actors/interconnect/interconnect_tcp_server.cpp +++ b/library/cpp/actors/interconnect/interconnect_tcp_server.cpp @@ -79,9 +79,9 @@ namespace NActors { return; } } - if (const auto& callback = ProxyCommonCtx->InitWhiteboard) { - callback(Address.GetPort(), TlsActivationContext->ExecutorThread.ActorSystem); - } + if (const auto& callback = ProxyCommonCtx->InitWhiteboard) { + callback(Address.GetPort(), TlsActivationContext->ExecutorThread.ActorSystem); + } const bool success = ctx.Send(MakePollerActorId(), new TEvPollerRegister(Listener, SelfId(), {})); Y_VERIFY(success); Become(&TThis::Listen); diff --git a/library/cpp/actors/protos/interconnect.proto b/library/cpp/actors/protos/interconnect.proto index 2e3b0d0d15..76885ef5bd 100644 --- a/library/cpp/actors/protos/interconnect.proto +++ b/library/cpp/actors/protos/interconnect.proto @@ -17,8 +17,8 @@ message TEvNodeInfo { extend google.protobuf.FieldOptions { optional string PrintName = 50376; -} - +} + message TNodeLocation { // compatibility section -- will be removed in future versions optional uint32 DataCenterNum = 1 [deprecated=true]; diff --git a/library/cpp/actors/testlib/test_runtime.cpp b/library/cpp/actors/testlib/test_runtime.cpp index 6fa25b9965..0010a1a135 100644 --- a/library/cpp/actors/testlib/test_runtime.cpp +++ b/library/cpp/actors/testlib/test_runtime.cpp @@ -36,24 +36,24 @@ namespace NActors { ui64 TScheduledEventQueueItem::NextUniqueId = 0; void PrintEvent(TAutoPtr<IEventHandle>& ev, const TTestActorRuntimeBase* runtime) { - Cerr << "mailbox: " << ev->GetRecipientRewrite().Hint() << ", type: " << Sprintf("%08x", ev->GetTypeRewrite()) - << ", from " << ev->Sender.LocalId(); + Cerr << "mailbox: " << ev->GetRecipientRewrite().Hint() << ", type: " << Sprintf("%08x", ev->GetTypeRewrite()) + << ", from " << ev->Sender.LocalId(); TString name = runtime->GetActorName(ev->Sender); - if (!name.empty()) - Cerr << " \"" << name << "\""; - Cerr << ", to " << ev->GetRecipientRewrite().LocalId(); - name = runtime->GetActorName(ev->GetRecipientRewrite()); - if (!name.empty()) - Cerr << " \"" << name << "\""; - Cerr << ", "; + if (!name.empty()) + Cerr << " \"" << name << "\""; + Cerr << ", to " << ev->GetRecipientRewrite().LocalId(); + name = runtime->GetActorName(ev->GetRecipientRewrite()); + if (!name.empty()) + Cerr << " \"" << name << "\""; + Cerr << ", "; if (ev->HasEvent()) - Cerr << " : " << (PRINT_EVENT_BODY ? ev->GetBase()->ToString() : ev->GetBase()->ToStringHeader()); + Cerr << " : " << (PRINT_EVENT_BODY ? ev->GetBase()->ToString() : ev->GetBase()->ToStringHeader()); else if (ev->HasBuffer()) - Cerr << " : BUFFER"; + Cerr << " : BUFFER"; else - Cerr << " : EMPTY"; + Cerr << " : EMPTY"; - Cerr << "\n"; + Cerr << "\n"; } TTestActorRuntimeBase::TNodeDataBase::TNodeDataBase() { @@ -103,8 +103,8 @@ namespace NActors { } if (verbose) { - Cerr << "Got event at " << TInstant::MicroSeconds(Runtime->CurrentTimestamp) << ", "; - PrintEvent(ev, Runtime); + Cerr << "Got event at " << TInstant::MicroSeconds(Runtime->CurrentTimestamp) << ", "; + PrintEvent(ev, Runtime); } if (!Runtime->EventFilterFunc(*Runtime, ev)) { @@ -114,11 +114,11 @@ namespace NActors { Runtime->GetMailbox(nodeId, mailboxHint).Send(ev); Runtime->MailboxesHasEvents.Signal(); if (verbose) - Cerr << "Event was added to sent queue\n"; + Cerr << "Event was added to sent queue\n"; } else { if (verbose) - Cerr << "Event was dropped\n"; + Cerr << "Event was dropped\n"; } } @@ -316,8 +316,8 @@ namespace NActors { } if (verbose) { - Cerr << "Got scheduled event at " << TInstant::MicroSeconds(Runtime->CurrentTimestamp) << ", "; - PrintEvent(ev, Runtime); + Cerr << "Got scheduled event at " << TInstant::MicroSeconds(Runtime->CurrentTimestamp) << ", "; + PrintEvent(ev, Runtime); } auto now = Runtime->GetTimeProvider()->Now(); @@ -331,13 +331,13 @@ namespace NActors { Runtime->GetMailbox(Runtime->FirstNodeId + NodeIndex, mailboxHint).Schedule(TScheduledEventQueueItem(deadline, ev, cookie)); Runtime->MailboxesHasEvents.Signal(); if (verbose) - Cerr << "Event was added to scheduled queue\n"; + Cerr << "Event was added to scheduled queue\n"; } else { if (cookie) { cookie->Detach(); } if (verbose) { - Cerr << "Scheduled event for " << ev->GetRecipientRewrite().ToString() << " was dropped\n"; + Cerr << "Scheduled event for " << ev->GetRecipientRewrite().ToString() << " was dropped\n"; } } } @@ -351,8 +351,8 @@ namespace NActors { } if (verbose) { - Cerr << "Got event at " << TInstant::MicroSeconds(Runtime->CurrentTimestamp) << ", "; - PrintEvent(ev, Runtime); + Cerr << "Got event at " << TInstant::MicroSeconds(Runtime->CurrentTimestamp) << ", "; + PrintEvent(ev, Runtime); } if (!Runtime->EventFilterFunc(*Runtime, ev)) { @@ -385,10 +385,10 @@ namespace NActors { Runtime->MailboxesHasEvents.Signal(); } if (verbose) - Cerr << "Event was added to sent queue\n"; + Cerr << "Event was added to sent queue\n"; } else { if (verbose) - Cerr << "Event was dropped\n"; + Cerr << "Event was dropped\n"; } return true; } @@ -462,7 +462,7 @@ namespace NActors { , ClusterUUID(MakeClusterId()) , FirstNodeId(NextNodeId) , NodeCount(nodeCount) - , DataCenterCount(dataCenterCount) + , DataCenterCount(dataCenterCount) , UseRealThreads(useRealThreads) , LocalId(0) , DispatchCyclesCount(0) @@ -529,16 +529,16 @@ namespace NActors { TTestActorRuntimeBase::TTestActorRuntimeBase(ui32 nodeCount, ui32 dataCenterCount) : TTestActorRuntimeBase(nodeCount, dataCenterCount, false) { - } - + } + TTestActorRuntimeBase::TTestActorRuntimeBase(ui32 nodeCount, bool useRealThreads) : TTestActorRuntimeBase(nodeCount, nodeCount, useRealThreads) { - } - + } + TTestActorRuntimeBase::~TTestActorRuntimeBase() { CleanupNodes(); Cerr.Flush(); - Cerr.Flush(); + Cerr.Flush(); Clog.Flush(); DisableActorCallstack(); @@ -582,58 +582,58 @@ namespace NActors { void TTestActorRuntimeBase::DefaultRegistrationObserver(TTestActorRuntimeBase& runtime, const TActorId& parentId, const TActorId& actorId) { if (runtime.ScheduleWhiteList.find(parentId) != runtime.ScheduleWhiteList.end()) { runtime.ScheduleWhiteList.insert(actorId); - runtime.ScheduleWhiteListParent[actorId] = parentId; + runtime.ScheduleWhiteListParent[actorId] = parentId; } } - class TScheduledTreeItem { - public: + class TScheduledTreeItem { + public: TString Name; - ui64 Count; + ui64 Count; TVector<TScheduledTreeItem> Children; - + TScheduledTreeItem(const TString& name) - : Name(name) - , Count(0) - {} - + : Name(name) + , Count(0) + {} + TScheduledTreeItem* GetItem(const TString& name) { - TScheduledTreeItem* item = nullptr; - for (TScheduledTreeItem& i : Children) { - if (i.Name == name) { - item = &i; - break; - } - } - if (item != nullptr) - return item; - Children.emplace_back(name); - return &Children.back(); - } - - void RecursiveSort() { - Sort(Children, [](const TScheduledTreeItem& a, const TScheduledTreeItem& b) -> bool { return a.Count > b.Count; }); - for (TScheduledTreeItem& item : Children) { - item.RecursiveSort(); - } - } - + TScheduledTreeItem* item = nullptr; + for (TScheduledTreeItem& i : Children) { + if (i.Name == name) { + item = &i; + break; + } + } + if (item != nullptr) + return item; + Children.emplace_back(name); + return &Children.back(); + } + + void RecursiveSort() { + Sort(Children, [](const TScheduledTreeItem& a, const TScheduledTreeItem& b) -> bool { return a.Count > b.Count; }); + for (TScheduledTreeItem& item : Children) { + item.RecursiveSort(); + } + } + void Print(IOutputStream& stream, const TString& prefix) { - for (auto it = Children.begin(); it != Children.end(); ++it) { - bool lastChild = (std::next(it) == Children.end()); + for (auto it = Children.begin(); it != Children.end(); ++it) { + bool lastChild = (std::next(it) == Children.end()); TString connectionPrefix = lastChild ? "└─ " : "├─ "; TString subChildPrefix = lastChild ? " " : "│ "; - stream << prefix << connectionPrefix << it->Name << " (" << it->Count << ")\n"; - it->Print(stream, prefix + subChildPrefix); - } - } - + stream << prefix << connectionPrefix << it->Name << " (" << it->Count << ")\n"; + it->Print(stream, prefix + subChildPrefix); + } + } + void Print(IOutputStream& stream) { - stream << Name << " (" << Count << ")\n"; + stream << Name << " (" << Count << ")\n"; Print(stream, TString()); - } - }; - + } + }; + void TTestActorRuntimeBase::CollapsedTimeScheduledEventsSelector(TTestActorRuntimeBase& runtime, TScheduledEventsList& scheduledEvents, TEventsList& queue) { if (scheduledEvents.empty()) return; @@ -641,30 +641,30 @@ namespace NActors { TInstant time = scheduledEvents.begin()->Deadline; while (!scheduledEvents.empty() && scheduledEvents.begin()->Deadline == time) { static THashMap<std::pair<TActorId, TString>, ui64> eventTypes; - auto& item = *scheduledEvents.begin(); + auto& item = *scheduledEvents.begin(); TString name = item.Event->GetBase() ? TypeName(*item.Event->GetBase()) : Sprintf("%08" PRIx32, item.Event->Type); eventTypes[std::make_pair(item.Event->Recipient, name)]++; runtime.ScheduledCount++; if (runtime.ScheduledCount > runtime.ScheduledLimit) { -// TScheduledTreeItem root("Root"); +// TScheduledTreeItem root("Root"); // TVector<TString> path; -// for (const auto& pr : eventTypes) { -// path.clear(); -// path.push_back(runtime.GetActorName(pr.first.first)); -// for (auto it = runtime.ScheduleWhiteListParent.find(pr.first.first); it != runtime.ScheduleWhiteListParent.end(); it = runtime.ScheduleWhiteListParent.find(it->second)) { -// path.insert(path.begin(), runtime.GetActorName(it->second)); -// } -// path.push_back("<" + pr.first.second + ">"); // event name; -// ui64 count = pr.second; -// TScheduledTreeItem* item = &root; -// item->Count += count; +// for (const auto& pr : eventTypes) { +// path.clear(); +// path.push_back(runtime.GetActorName(pr.first.first)); +// for (auto it = runtime.ScheduleWhiteListParent.find(pr.first.first); it != runtime.ScheduleWhiteListParent.end(); it = runtime.ScheduleWhiteListParent.find(it->second)) { +// path.insert(path.begin(), runtime.GetActorName(it->second)); +// } +// path.push_back("<" + pr.first.second + ">"); // event name; +// ui64 count = pr.second; +// TScheduledTreeItem* item = &root; +// item->Count += count; // for (TString name : path) { -// item = item->GetItem(name); -// item->Count += count; -// } -// } -// root.RecursiveSort(); -// root.Print(Cerr); +// item = item->GetItem(name); +// item->Count += count; +// } +// } +// root.RecursiveSort(); +// root.Print(Cerr); ythrow TSchedulingLimitReachedException(runtime.ScheduledLimit); } @@ -755,10 +755,10 @@ namespace NActors { IsInitialized = true; } - void SetupCrossDC() { - - } - + void SetupCrossDC() { + + } + TDuration TTestActorRuntimeBase::SetDispatchTimeout(TDuration timeout) { TGuard<TMutex> guard(Mutex); TDuration oldTimeout = DispatchTimeout; @@ -798,11 +798,11 @@ namespace NActors { } void TTestActorRuntimeBase::UpdateCurrentTime(TInstant newTime) { - static int counter = 0; - ++counter; - if (VERBOSE) { - Cerr << "UpdateCurrentTime(" << counter << "," << newTime << ")\n"; - } + static int counter = 0; + ++counter; + if (VERBOSE) { + Cerr << "UpdateCurrentTime(" << counter << "," << newTime << ")\n"; + } TGuard<TMutex> guard(Mutex); Y_VERIFY(!UseRealThreads); if (newTime.MicroSeconds() > CurrentTimestamp) { @@ -836,7 +836,7 @@ namespace NActors { TGuard<TMutex> guard(Mutex); ui64 nextId = ++LocalId; if (VERBOSE) { - Cerr << "Allocated id: " << nextId << "\n"; + Cerr << "Allocated id: " << nextId << "\n"; } return nextId; @@ -1068,7 +1068,7 @@ namespace NActors { const TDuration scheduledEventsInspectInterval = TDuration::MilliSeconds(10); TInstant inspectScheduledEventsAt = dispatchTime + scheduledEventsInspectInterval; if (verbose) { - Cerr << "Start dispatch at " << TInstant::MicroSeconds(CurrentTimestamp) << ", deadline is " << deadline << "\n"; + Cerr << "Start dispatch at " << TInstant::MicroSeconds(CurrentTimestamp) << ", deadline is " << deadline << "\n"; } struct TTempEdgeEventsCaptor { @@ -1181,14 +1181,14 @@ namespace NActors { ythrow TWithBackTrace<yexception>() << "Dispatched " << DispatchedEventsLimit << " events, limit reached."; } - + auto ev = mbox.second->Pop(); - if (BlockedOutput.find(ev->Sender) == BlockedOutput.end()) { - //UpdateCurrentTime(TInstant::MicroSeconds(CurrentTimestamp + 10)); - if (verbose) { - Cerr << "Process event at " << TInstant::MicroSeconds(CurrentTimestamp) << ", "; - PrintEvent(ev, this); - } + if (BlockedOutput.find(ev->Sender) == BlockedOutput.end()) { + //UpdateCurrentTime(TInstant::MicroSeconds(CurrentTimestamp + 10)); + if (verbose) { + Cerr << "Process event at " << TInstant::MicroSeconds(CurrentTimestamp) << ", "; + PrintEvent(ev, this); + } } hasProgress = true; @@ -1263,7 +1263,7 @@ namespace NActors { if (!mbox.second->IsEmpty()) { if (verbose) { - Cerr << "Dispatch complete with non-empty queue at " << TInstant::MicroSeconds(CurrentTimestamp) << "\n"; + Cerr << "Dispatch complete with non-empty queue at " << TInstant::MicroSeconds(CurrentTimestamp) << "\n"; } return true; @@ -1277,7 +1277,7 @@ namespace NActors { if (dispatchTime >= deadline) { if (verbose) { - Cerr << "Reach deadline at " << TInstant::MicroSeconds(CurrentTimestamp) << "\n"; + Cerr << "Reach deadline at " << TInstant::MicroSeconds(CurrentTimestamp) << "\n"; } ythrow TWithBackTrace<TEmptyEventQueueException>(); @@ -1319,8 +1319,8 @@ namespace NActors { nextScheduleMbox->PushScheduled(capturedScheduledEvents); for (auto& event : selectedEvents) { if (verbose && (BlockedOutput.find(event->Sender) == BlockedOutput.end())) { - Cerr << "Selected scheduled event at " << TInstant::MicroSeconds(CurrentTimestamp) << ", "; - PrintEvent(event, this); + Cerr << "Selected scheduled event at " << TInstant::MicroSeconds(CurrentTimestamp) << ", "; + PrintEvent(event, this); } nextScheduleMbox->Send(event); @@ -1330,7 +1330,7 @@ namespace NActors { if (!isEmpty) { if (verbose) { - Cerr << "Process selected events at " << TInstant::MicroSeconds(CurrentTimestamp) << "\n"; + Cerr << "Process selected events at " << TInstant::MicroSeconds(CurrentTimestamp) << "\n"; } deadline = dispatchTime + DispatchTimeout; @@ -1339,7 +1339,7 @@ namespace NActors { if (nearestMailboxDeadline.Defined()) { if (verbose) { - Cerr << "Forward time to " << *nearestMailboxDeadline.Get() << "\n"; + Cerr << "Forward time to " << *nearestMailboxDeadline.Get() << "\n"; } UpdateCurrentTime(*nearestMailboxDeadline.Get()); @@ -1369,8 +1369,8 @@ namespace NActors { void TTestActorRuntimeBase::UpdateFinalEventsStatsForEachContext(IEventHandle& ev) { TDispatchContext* context = CurrentDispatchContext; while (context) { - for (const auto& finalEvent : context->Options->FinalEvents) { - if (finalEvent.EventCheck(ev)) { + for (const auto& finalEvent : context->Options->FinalEvents) { + if (finalEvent.EventCheck(ev)) { auto& freq = context->FinalEventFrequency[&finalEvent]; if (++freq >= finalEvent.RequiredCount) { context->FinalEventFound = true; @@ -1397,7 +1397,7 @@ namespace NActors { TInstant deadline = TInstant::MicroSeconds(CurrentTimestamp) + duration; GetMailbox(nodeId, mailboxHint).Schedule(TScheduledEventQueueItem(deadline, ev, nullptr)); if (VERBOSE) - Cerr << "Event was added to scheduled queue\n"; + Cerr << "Event was added to scheduled queue\n"; } void TTestActorRuntimeBase::ClearCounters() { @@ -1497,14 +1497,14 @@ namespace NActors { void TTestActorRuntimeBase::EnableScheduleForActor(const TActorId& actorId, bool allow) { TGuard<TMutex> guard(Mutex); if (allow) { - if (VERBOSE) { - Cerr << "Actor " << actorId << " added to schedule whitelist"; - } + if (VERBOSE) { + Cerr << "Actor " << actorId << " added to schedule whitelist"; + } ScheduleWhiteList.insert(actorId); } else { - if (VERBOSE) { - Cerr << "Actor " << actorId << " removed from schedule whitelist"; - } + if (VERBOSE) { + Cerr << "Actor " << actorId << " removed from schedule whitelist"; + } ScheduleWhiteList.erase(actorId); } } @@ -1560,8 +1560,8 @@ namespace NActors { ui64 recipientLocalId = ev->GetRecipientRewrite().LocalId(); if ((BlockedOutput.find(ev->Sender) == BlockedOutput.end()) && VERBOSE) { - Cerr << "Send event, "; - PrintEvent(evHolder, this); + Cerr << "Send event, "; + PrintEvent(evHolder, this); } EvCounters[ev->GetTypeRewrite()]++; @@ -1589,7 +1589,7 @@ namespace NActors { TlsActivationContext = prevTlsActivationContext; } else { if (VERBOSE) { - Cerr << "Failed to find actor with local id: " << recipientLocalId << "\n"; + Cerr << "Failed to find actor with local id: " << recipientLocalId << "\n"; } auto forwardedEv = ev->ForwardOnNondelivery(TEvents::TEvUndelivered::ReasonActorUnknown); @@ -1727,18 +1727,18 @@ namespace NActors { } void TTestActorRuntimeBase::ClearMailbox(ui32 nodeId, ui32 hint) { - TGuard<TMutex> guard(Mutex); - auto mboxId = TEventMailboxId(nodeId, hint); - Mailboxes.erase(mboxId); - } - + TGuard<TMutex> guard(Mutex); + auto mboxId = TEventMailboxId(nodeId, hint); + Mailboxes.erase(mboxId); + } + TString TTestActorRuntimeBase::GetActorName(const TActorId& actorId) const { - auto it = ActorNames.find(actorId); - if (it != ActorNames.end()) - return it->second; - return actorId.ToString(); - } - + auto it = ActorNames.find(actorId); + if (it != ActorNames.end()) + return it->second; + return actorId.ToString(); + } + struct TStrandingActorDecoratorContext : public TThrRefBase { TStrandingActorDecoratorContext() : Queue(new TQueueType) diff --git a/library/cpp/actors/testlib/test_runtime.h b/library/cpp/actors/testlib/test_runtime.h index 26e3b45c98..589309afd7 100644 --- a/library/cpp/actors/testlib/test_runtime.h +++ b/library/cpp/actors/testlib/test_runtime.h @@ -74,20 +74,20 @@ namespace NActors { struct TDispatchOptions { struct TFinalEventCondition { - std::function<bool(IEventHandle& ev)> EventCheck; + std::function<bool(IEventHandle& ev)> EventCheck; ui32 RequiredCount; TFinalEventCondition(ui32 eventType, ui32 requiredCount = 1) - : EventCheck([eventType](IEventHandle& ev) -> bool { return ev.GetTypeRewrite() == eventType; }) - , RequiredCount(requiredCount) - { - } - - TFinalEventCondition(std::function<bool(IEventHandle& ev)> eventCheck, ui32 requiredCount = 1) - : EventCheck(eventCheck) + : EventCheck([eventType](IEventHandle& ev) -> bool { return ev.GetTypeRewrite() == eventType; }) , RequiredCount(requiredCount) { } + + TFinalEventCondition(std::function<bool(IEventHandle& ev)> eventCheck, ui32 requiredCount = 1) + : EventCheck(eventCheck) + , RequiredCount(requiredCount) + { + } }; TVector<TFinalEventCondition> FinalEvents; @@ -359,14 +359,14 @@ namespace NActors { return GrabEdgeEventIf(handle, truth, simTimeout); } - template <typename TEvent> - THolder<TEvent> GrabEdgeEvent(TDuration simTimeout = TDuration::Max()) { - TAutoPtr<IEventHandle> handle; - std::function<bool(const TEvent&)> truth = [](const TEvent&) { return true; }; - GrabEdgeEventIf(handle, truth, simTimeout); + template <typename TEvent> + THolder<TEvent> GrabEdgeEvent(TDuration simTimeout = TDuration::Max()) { + TAutoPtr<IEventHandle> handle; + std::function<bool(const TEvent&)> truth = [](const TEvent&) { return true; }; + GrabEdgeEventIf(handle, truth, simTimeout); return THolder(handle ? handle->Release<TEvent>().Release() : nullptr); - } - + } + template<class TEvent> typename TEvent::TPtr GrabEdgeEvent(const TSet<TActorId>& edgeFilter, TDuration simTimeout = TDuration::Max()) { return GrabEdgeEventIf<TEvent>(edgeFilter, [](const typename TEvent::TPtr&) { return true; }, simTimeout); @@ -378,33 +378,33 @@ namespace NActors { return GrabEdgeEvent<TEvent>(edgeFilter, simTimeout); } - // replace with std::variant<> - template <typename... TEvents> - std::tuple<TEvents*...> GrabEdgeEvents(TAutoPtr<IEventHandle>& handle, TDuration simTimeout = TDuration::Max()) { - handle.Destroy(); - auto eventTypes = { TEvents::EventType... }; + // replace with std::variant<> + template <typename... TEvents> + std::tuple<TEvents*...> GrabEdgeEvents(TAutoPtr<IEventHandle>& handle, TDuration simTimeout = TDuration::Max()) { + handle.Destroy(); + auto eventTypes = { TEvents::EventType... }; WaitForEdgeEvents([&](TTestActorRuntimeBase&, TAutoPtr<IEventHandle>& event) { - if (std::find(std::begin(eventTypes), std::end(eventTypes), event->GetTypeRewrite()) == std::end(eventTypes)) - return false; - handle = event; - return true; + if (std::find(std::begin(eventTypes), std::end(eventTypes), event->GetTypeRewrite()) == std::end(eventTypes)) + return false; + handle = event; + return true; }, {}, simTimeout); - if (simTimeout == TDuration::Max()) - Y_VERIFY(handle); - if (handle) { - return std::make_tuple(handle->Type == TEvents::EventType - ? reinterpret_cast<TAutoPtr<TEventHandle<TEvents>>&>(handle)->Get() - : static_cast<TEvents*>(nullptr)...); - } - return {}; - } - + if (simTimeout == TDuration::Max()) + Y_VERIFY(handle); + if (handle) { + return std::make_tuple(handle->Type == TEvents::EventType + ? reinterpret_cast<TAutoPtr<TEventHandle<TEvents>>&>(handle)->Get() + : static_cast<TEvents*>(nullptr)...); + } + return {}; + } + template <typename TEvent> TEvent* GrabEdgeEventRethrow(TAutoPtr<IEventHandle>& handle, TDuration simTimeout = TDuration::Max()) { try { return GrabEdgeEvent<TEvent>(handle, simTimeout); } catch (...) { - ythrow TWithBackTrace<yexception>() << "Exception occured while waiting for " << TypeName<TEvent>() << ": " << CurrentExceptionMessage(); + ythrow TWithBackTrace<yexception>() << "Exception occured while waiting for " << TypeName<TEvent>() << ": " << CurrentExceptionMessage(); } } @@ -426,33 +426,33 @@ namespace NActors { } } - template <typename... TEvents> + template <typename... TEvents> static TString TypeNames() { static TString names[] = { TypeName<TEvents>()... }; TString result; for (const TString& s : names) { - if (result.empty()) { - result += '<'; - } else { - result += ','; - } - result += s; - } - if (!result.empty()) { - result += '>'; - } - return result; - } - - template <typename... TEvents> - std::tuple<TEvents*...> GrabEdgeEventsRethrow(TAutoPtr<IEventHandle>& handle, TDuration simTimeout = TDuration::Max()) { - try { - return GrabEdgeEvents<TEvents...>(handle, simTimeout); - } catch (...) { - ythrow TWithBackTrace<yexception>() << "Exception occured while waiting for " << TypeNames<TEvents...>() << ": " << CurrentExceptionMessage(); - } - } - + if (result.empty()) { + result += '<'; + } else { + result += ','; + } + result += s; + } + if (!result.empty()) { + result += '>'; + } + return result; + } + + template <typename... TEvents> + std::tuple<TEvents*...> GrabEdgeEventsRethrow(TAutoPtr<IEventHandle>& handle, TDuration simTimeout = TDuration::Max()) { + try { + return GrabEdgeEvents<TEvents...>(handle, simTimeout); + } catch (...) { + ythrow TWithBackTrace<yexception>() << "Exception occured while waiting for " << TypeNames<TEvents...>() << ": " << CurrentExceptionMessage(); + } + } + void ResetScheduledCount() { ScheduledCount = 0; } @@ -496,7 +496,7 @@ namespace NActors { IActor* FindActor(const TActorId& actorId, TNodeDataBase* node) const; void SendInternal(IEventHandle* ev, ui32 nodeIndex, bool viaActorSystem); TEventMailBox& GetMailbox(ui32 nodeId, ui32 hint); - void ClearMailbox(ui32 nodeId, ui32 hint); + void ClearMailbox(ui32 nodeId, ui32 hint); void HandleNonEmptyMailboxesForEachContext(TEventMailboxId mboxId); void UpdateFinalEventsStatsForEachContext(IEventHandle& ev); bool DispatchEventsInternal(const TDispatchOptions& options, TInstant simDeadline); @@ -515,7 +515,7 @@ namespace NActors { const TString ClusterUUID; const ui32 FirstNodeId; const ui32 NodeCount; - const ui32 DataCenterCount; + const ui32 DataCenterCount; const bool UseRealThreads; ui64 LocalId; diff --git a/library/cpp/grpc/client/grpc_client_low.h b/library/cpp/grpc/client/grpc_client_low.h index ab0a0627be..d5ffe74736 100644 --- a/library/cpp/grpc/client/grpc_client_low.h +++ b/library/cpp/grpc/client/grpc_client_low.h @@ -126,7 +126,7 @@ public: // Represents grpc status and error message string struct TGrpcStatus { TString Msg; - TString Details; + TString Details; int GRpcStatusCode; bool InternalError; @@ -141,20 +141,20 @@ struct TGrpcStatus { , InternalError(internalError) { } - TGrpcStatus(grpc::StatusCode status, TString msg, TString details = {}) + TGrpcStatus(grpc::StatusCode status, TString msg, TString details = {}) : Msg(std::move(msg)) - , Details(std::move(details)) + , Details(std::move(details)) , GRpcStatusCode(status) , InternalError(false) { } TGrpcStatus(const grpc::Status& status) - : TGrpcStatus(status.error_code(), TString(status.error_message()), TString(status.error_details())) + : TGrpcStatus(status.error_code(), TString(status.error_message()), TString(status.error_details())) { } TGrpcStatus& operator=(const grpc::Status& status) { Msg = TString(status.error_message()); - Details = TString(status.error_details()); + Details = TString(status.error_details()); GRpcStatusCode = status.error_code(); InternalError = false; return *this; @@ -178,9 +178,9 @@ bool inline IsGRpcStatusGood(const TGrpcStatus& status) { template<typename TResponse> using TResponseCallback = std::function<void (TGrpcStatus&&, TResponse&&)>; -template<typename TResponse> -using TAdvancedResponseCallback = std::function<void (const grpc::ClientContext&, TGrpcStatus&&, TResponse&&)>; - +template<typename TResponse> +using TAdvancedResponseCallback = std::function<void (const grpc::ClientContext&, TGrpcStatus&&, TResponse&&)>; + // Call associated metadata struct TCallMeta { std::shared_ptr<grpc::CallCredentials> CallCredentials; @@ -305,86 +305,86 @@ private: bool Replied_ = false; }; -template<typename TStub, typename TRequest, typename TResponse> -class TAdvancedRequestProcessor - : public TThrRefBase - , public IQueueClientEvent - , public TGRpcRequestProcessorCommon { - using TAsyncReaderPtr = std::unique_ptr<grpc::ClientAsyncResponseReader<TResponse>>; - template<typename> friend class TServiceConnection; -public: - using TPtr = TIntrusivePtr<TAdvancedRequestProcessor>; - using TAsyncRequest = TAsyncReaderPtr (TStub::*)(grpc::ClientContext*, const TRequest&, grpc::CompletionQueue*); - +template<typename TStub, typename TRequest, typename TResponse> +class TAdvancedRequestProcessor + : public TThrRefBase + , public IQueueClientEvent + , public TGRpcRequestProcessorCommon { + using TAsyncReaderPtr = std::unique_ptr<grpc::ClientAsyncResponseReader<TResponse>>; + template<typename> friend class TServiceConnection; +public: + using TPtr = TIntrusivePtr<TAdvancedRequestProcessor>; + using TAsyncRequest = TAsyncReaderPtr (TStub::*)(grpc::ClientContext*, const TRequest&, grpc::CompletionQueue*); + explicit TAdvancedRequestProcessor(TAdvancedResponseCallback<TResponse>&& callback) : Callback_(std::move(callback)) - { } - - ~TAdvancedRequestProcessor() { - if (!Replied_ && Callback_) { - Callback_(Context, TGrpcStatus::Internal("request left unhandled"), std::move(Reply_)); - Callback_ = nullptr; // free resources as early as possible - } - } - - bool Execute(bool ok) override { + { } + + ~TAdvancedRequestProcessor() { + if (!Replied_ && Callback_) { + Callback_(Context, TGrpcStatus::Internal("request left unhandled"), std::move(Reply_)); + Callback_ = nullptr; // free resources as early as possible + } + } + + bool Execute(bool ok) override { { std::unique_lock<std::mutex> guard(Mutex_); - LocalContext.reset(); - } - TGrpcStatus status; - if (ok) { - status = Status; - } else { - status = TGrpcStatus::Internal("Unexpected error"); - } - Replied_ = true; - Callback_(Context, std::move(status), std::move(Reply_)); - Callback_ = nullptr; // free resources as early as possible - return false; - } - - void Destroy() override { - UnRef(); - } - -private: - IQueueClientEvent* FinishedEvent() { - Ref(); - return this; - } - - void Start(TStub& stub, TAsyncRequest asyncRequest, const TRequest& request, IQueueClientContextProvider* provider) { - auto context = provider->CreateContext(); - if (!context) { - Replied_ = true; - Callback_(Context, TGrpcStatus(grpc::StatusCode::CANCELLED, "Client is shutting down"), std::move(Reply_)); - Callback_ = nullptr; - return; - } + LocalContext.reset(); + } + TGrpcStatus status; + if (ok) { + status = Status; + } else { + status = TGrpcStatus::Internal("Unexpected error"); + } + Replied_ = true; + Callback_(Context, std::move(status), std::move(Reply_)); + Callback_ = nullptr; // free resources as early as possible + return false; + } + + void Destroy() override { + UnRef(); + } + +private: + IQueueClientEvent* FinishedEvent() { + Ref(); + return this; + } + + void Start(TStub& stub, TAsyncRequest asyncRequest, const TRequest& request, IQueueClientContextProvider* provider) { + auto context = provider->CreateContext(); + if (!context) { + Replied_ = true; + Callback_(Context, TGrpcStatus(grpc::StatusCode::CANCELLED, "Client is shutting down"), std::move(Reply_)); + Callback_ = nullptr; + return; + } { std::unique_lock<std::mutex> guard(Mutex_); - LocalContext = context; - Reader_ = (stub.*asyncRequest)(&Context, request, context->CompletionQueue()); - Reader_->Finish(&Reply_, &Status, FinishedEvent()); - } - context->SubscribeStop([self = TPtr(this)] { - self->Stop(); - }); - } - - void Stop() { - Context.TryCancel(); - } - - TAdvancedResponseCallback<TResponse> Callback_; - TResponse Reply_; + LocalContext = context; + Reader_ = (stub.*asyncRequest)(&Context, request, context->CompletionQueue()); + Reader_->Finish(&Reply_, &Status, FinishedEvent()); + } + context->SubscribeStop([self = TPtr(this)] { + self->Stop(); + }); + } + + void Stop() { + Context.TryCancel(); + } + + TAdvancedResponseCallback<TResponse> Callback_; + TResponse Reply_; std::mutex Mutex_; - TAsyncReaderPtr Reader_; - - bool Replied_ = false; -}; - + TAsyncReaderPtr Reader_; + + bool Replied_ = false; +}; + template<class TResponse> class IStreamRequestReadProcessor : public TThrRefBase { public: @@ -1255,21 +1255,21 @@ public: } /* - * Start simple request - */ - template<typename TRequest, typename TResponse> - void DoAdvancedRequest(const TRequest& request, + * Start simple request + */ + template<typename TRequest, typename TResponse> + void DoAdvancedRequest(const TRequest& request, TAdvancedResponseCallback<TResponse> callback, - typename TAdvancedRequestProcessor<TStub, TRequest, TResponse>::TAsyncRequest asyncRequest, - const TCallMeta& metas = { }, - IQueueClientContextProvider* provider = nullptr) - { - auto processor = MakeIntrusive<TAdvancedRequestProcessor<TStub, TRequest, TResponse>>(std::move(callback)); - processor->ApplyMeta(metas); - processor->Start(*Stub_, asyncRequest, request, provider ? provider : Provider_); - } - - /* + typename TAdvancedRequestProcessor<TStub, TRequest, TResponse>::TAsyncRequest asyncRequest, + const TCallMeta& metas = { }, + IQueueClientContextProvider* provider = nullptr) + { + auto processor = MakeIntrusive<TAdvancedRequestProcessor<TStub, TRequest, TResponse>>(std::move(callback)); + processor->ApplyMeta(metas); + processor->Start(*Stub_, asyncRequest, request, provider ? provider : Provider_); + } + + /* * Start bidirectional streamming */ template<typename TRequest, typename TResponse> diff --git a/library/cpp/http/fetch/httpagent.h b/library/cpp/http/fetch/httpagent.h index 96475cc05d..10696b6a3a 100644 --- a/library/cpp/http/fetch/httpagent.h +++ b/library/cpp/http/fetch/httpagent.h @@ -1,316 +1,316 @@ -#pragma once - -#include <cstdio> -#include <cstring> -#include <cstdlib> - -#include <library/cpp/uri/http_url.h> -#include <util/datetime/base.h> -#include <util/network/hostip.h> -#include <util/network/ip.h> -#include <util/network/sock.h> -#include <util/generic/scope.h> -#include <util/generic/utility.h> -#include <util/string/cast.h> - -#include "exthttpcodes.h" -#include "sockhandler.h" - -class TIpResolver { -public: - TAddrList Resolve(const char* host, TIpPort port) const { - try { - TAddrList result; - TNetworkAddress na(host, port); - for (auto i = na.Begin(); i != na.End(); ++i) { - const struct addrinfo& ai = *i; - switch (ai.ai_family) { - case AF_INET: - result.push_back(new NAddr::TIPv4Addr(*(sockaddr_in*)ai.ai_addr)); - break; - case AF_INET6: - result.push_back(new NAddr::TIPv6Addr(*(sockaddr_in6*)ai.ai_addr)); - break; - } - } - return result; - } catch (const TNetworkResolutionError&) { - } - return TAddrList(); - } -}; - -namespace NResolverHelpers { - Y_HAS_MEMBER(Resolve); - - template <typename TResolver> - std::enable_if_t<TClassHasResolve<TResolver>::value, TAddrList> Resolve(const TResolver& r, const char* host, TIpPort port) { - return r.Resolve(host, port); - } - - template <typename TResolver> - std::enable_if_t<!TClassHasResolve<TResolver>::value, TAddrList> Resolve(const TResolver& r, const char* host, TIpPort port) { - ui32 ip = 0; - if (r.GetHostIP(host, &ip)) { - // error - return TAddrList(); - } - if (!ip) { - return TAddrList(); - } - - return TAddrList::MakeV4Addr(ip, port); - } -} - -template <typename TBase> -class TIpResolverWrapper { -private: - TBase Base; - -public: - TIpResolverWrapper() = default; - - template <typename T> - TIpResolverWrapper(T&& base) - : Base(std::forward(base)) - { - } - - TAddrList Resolve(const char* host, TIpPort port) const { - return NResolverHelpers::Resolve(Base, host, port); - } -}; - -template <class TSocketHandler = TSimpleSocketHandler, class TDnsClient = TIpResolver> -class THttpAgent { -public: - THttpAgent() - : Persistent(0) - , Timeout(TDuration::MicroSeconds(150)) - , Hostheader(nullptr) - , Footer(nullptr) - , AltFooter(nullptr) - , PostData(nullptr) - , PostDataLen(0) - , Method(nullptr) - , MethodLen(0) - , HostheaderLen(0) - { - SetIdentification("YandexSomething/1.0", "webadmin@yandex.ru"); - } - - ~THttpAgent() { - Disconnect(); - free(Hostheader); - free(Footer); - } - - void SetIdentification(const char* user_agent, const char* http_from) { - free(Footer); - size_t len = user_agent ? strlen(user_agent) + 15 : 0; - len += http_from ? strlen(http_from) + 9 : 0; - len += 3; - Footer = (char*)malloc(len); - if (user_agent) - strcat(strcat(strcpy(Footer, "User-Agent: "), user_agent), "\r\n"); - if (http_from) - strcat(strcat(strcat(Footer, "From: "), http_from), "\r\n"); - } - - void SetUserAgentFooter(const char* altFooter) { - AltFooter = altFooter; - } - - void SetPostData(const char* postData, size_t postDataLen) { - PostData = postData; - PostDataLen = postDataLen; - } - - void SetMethod(const char* method, size_t methodLen) { - Method = method; - MethodLen = methodLen; - } - - // deprecated - ui32 GetIp() const { - return Addrs.GetV4Addr().first; - } - - int GetScheme() const { - return THttpURL::SchemeHTTP; - } - void SetTimeout(TDuration tim) { - Timeout = tim; - } - - void SetConnectTimeout(TDuration timeout) { - ConnectTimeout = timeout; - } - - int Disconnected() { - return !Persistent || !Socket.Good(); - } - - int SetHost(const char* hostname, TIpPort port) { - Disconnect(); - TAddrList addrs = DnsClient.Resolve(hostname, port); - if (!addrs.size()) { - return 1; - } - - SetHost(hostname, port, addrs); - return 0; - } - - int SetHost(const char* hostname, TIpPort port, const TAddrList& addrs) { - Disconnect(); - Addrs = addrs; - size_t reqHostheaderLen = strlen(hostname) + 20; - if (HostheaderLen < reqHostheaderLen) { - free(Hostheader); - Hostheader = (char*)malloc((HostheaderLen = reqHostheaderLen)); - } - if (port == 80) - sprintf(Hostheader, "Host: %s\r\n", hostname); - else - sprintf(Hostheader, "Host: %s:%u\r\n", hostname, port); - pHostBeg = strchr(Hostheader, ' ') + 1; - pHostEnd = strchr(pHostBeg, '\r'); - // convert hostname to lower case since some web server don't like - // uppper case (Task ROBOT-562) - for (char* p = pHostBeg; p < pHostEnd; p++) - *p = tolower(*p); - return 0; - } - - // deprecated v4-only - int SetHost(const char* hostname, TIpPort port, ui32 ip) { - return SetHost(hostname, port, TAddrList::MakeV4Addr(ip, port)); - } - - void SetHostHeader(const char* host) { - size_t reqHostheaderLen = strlen(host) + 20; - if (HostheaderLen < reqHostheaderLen) { - delete[] Hostheader; - Hostheader = new char[(HostheaderLen = reqHostheaderLen)]; - } - sprintf(Hostheader, "Host: %s\r\n", host); - } - - void SetSocket(SOCKET fd) { - Socket.SetSocket(fd); - } - - SOCKET PickOutSocket() { - return Socket.PickOutSocket(); - } - - void Disconnect() { - Socket.Disconnect(); - } - - ssize_t read(void* buffer, size_t buflen) { - return Socket.read(buffer, buflen); - } - - int RequestGet(const char* url, const char* const* headers, int persistent = 1, bool head_request = false) { - if (!Addrs.size()) - return HTTP_DNS_FAILURE; - char message[MessageMax]; - ssize_t messlen = 0; - if (Method) { - strncpy(message, Method, MethodLen); - message[MethodLen] = ' '; - messlen = MethodLen + 1; - } else if (PostData) { - strcpy(message, "POST "); - messlen = 5; - } else if (head_request) { - strcpy(message, "HEAD "); - messlen = 5; - } else { - strcpy(message, "GET "); - messlen = 4; - } -#define _AppendMessage(mes) messlen += Min(MessageMax - messlen, \ - (ssize_t)strlcpy(message + messlen, (mes), MessageMax - messlen)) - _AppendMessage(url); - _AppendMessage(" HTTP/1.1\r\n"); - if (*url == '/') //if not then Host is a proxy - _AppendMessage(Hostheader); - _AppendMessage("Connection: "); - _AppendMessage(persistent ? "Keep-Alive\r\n" : "Close\r\n"); - while (headers && *headers) - _AppendMessage(*headers++); - if (AltFooter) - _AppendMessage(AltFooter); - else - _AppendMessage(Footer); - _AppendMessage("\r\n"); -#undef _AppendMessage - if (messlen >= MessageMax) - return HTTP_HEADER_TOO_LARGE; - - if (!Persistent) - Disconnect(); - Persistent = persistent; - int connected = Socket.Good(); - for (int attempt = !connected; attempt < 2; attempt++) { - const auto connectTimeout = ConnectTimeout ? ConnectTimeout : Timeout; - if (!Socket.Good() && Socket.Connect(Addrs, connectTimeout)) - return HTTP_CONNECT_FAILED; - - int sendOk = Socket.send(message, messlen); - if (sendOk && PostData && PostDataLen) - sendOk = Socket.send(PostData, PostDataLen); - if (!sendOk) { - int err = errno; - Disconnect(); - errno = err; - continue; - } - - if (!Socket.peek()) { - int err = errno; - Disconnect(); - if (err == EINTR) { - errno = err; - return HTTP_INTERRUPTED; - } - } else { - if (!persistent) - Socket.shutdown(); - return 0; - } - } - return connected ? HTTP_CONNECTION_LOST : HTTP_CONNECT_FAILED; - } - -protected: - TSocketHandler Socket; - TIpResolverWrapper<TDnsClient> DnsClient; - TAddrList Addrs; - int Persistent; - TDuration Timeout; - TDuration ConnectTimeout; - char *Hostheader, *Footer, *pHostBeg, *pHostEnd; - const char* AltFooter; // alternative footer can be set by the caller - const char* PostData; - size_t PostDataLen; - const char* Method; - size_t MethodLen; - unsigned short HostheaderLen; +#pragma once + +#include <cstdio> +#include <cstring> +#include <cstdlib> + +#include <library/cpp/uri/http_url.h> +#include <util/datetime/base.h> +#include <util/network/hostip.h> +#include <util/network/ip.h> +#include <util/network/sock.h> +#include <util/generic/scope.h> +#include <util/generic/utility.h> +#include <util/string/cast.h> + +#include "exthttpcodes.h" +#include "sockhandler.h" + +class TIpResolver { +public: + TAddrList Resolve(const char* host, TIpPort port) const { + try { + TAddrList result; + TNetworkAddress na(host, port); + for (auto i = na.Begin(); i != na.End(); ++i) { + const struct addrinfo& ai = *i; + switch (ai.ai_family) { + case AF_INET: + result.push_back(new NAddr::TIPv4Addr(*(sockaddr_in*)ai.ai_addr)); + break; + case AF_INET6: + result.push_back(new NAddr::TIPv6Addr(*(sockaddr_in6*)ai.ai_addr)); + break; + } + } + return result; + } catch (const TNetworkResolutionError&) { + } + return TAddrList(); + } +}; + +namespace NResolverHelpers { + Y_HAS_MEMBER(Resolve); + + template <typename TResolver> + std::enable_if_t<TClassHasResolve<TResolver>::value, TAddrList> Resolve(const TResolver& r, const char* host, TIpPort port) { + return r.Resolve(host, port); + } + + template <typename TResolver> + std::enable_if_t<!TClassHasResolve<TResolver>::value, TAddrList> Resolve(const TResolver& r, const char* host, TIpPort port) { + ui32 ip = 0; + if (r.GetHostIP(host, &ip)) { + // error + return TAddrList(); + } + if (!ip) { + return TAddrList(); + } + + return TAddrList::MakeV4Addr(ip, port); + } +} + +template <typename TBase> +class TIpResolverWrapper { +private: + TBase Base; + +public: + TIpResolverWrapper() = default; + + template <typename T> + TIpResolverWrapper(T&& base) + : Base(std::forward(base)) + { + } + + TAddrList Resolve(const char* host, TIpPort port) const { + return NResolverHelpers::Resolve(Base, host, port); + } +}; + +template <class TSocketHandler = TSimpleSocketHandler, class TDnsClient = TIpResolver> +class THttpAgent { +public: + THttpAgent() + : Persistent(0) + , Timeout(TDuration::MicroSeconds(150)) + , Hostheader(nullptr) + , Footer(nullptr) + , AltFooter(nullptr) + , PostData(nullptr) + , PostDataLen(0) + , Method(nullptr) + , MethodLen(0) + , HostheaderLen(0) + { + SetIdentification("YandexSomething/1.0", "webadmin@yandex.ru"); + } + + ~THttpAgent() { + Disconnect(); + free(Hostheader); + free(Footer); + } + + void SetIdentification(const char* user_agent, const char* http_from) { + free(Footer); + size_t len = user_agent ? strlen(user_agent) + 15 : 0; + len += http_from ? strlen(http_from) + 9 : 0; + len += 3; + Footer = (char*)malloc(len); + if (user_agent) + strcat(strcat(strcpy(Footer, "User-Agent: "), user_agent), "\r\n"); + if (http_from) + strcat(strcat(strcat(Footer, "From: "), http_from), "\r\n"); + } + + void SetUserAgentFooter(const char* altFooter) { + AltFooter = altFooter; + } + + void SetPostData(const char* postData, size_t postDataLen) { + PostData = postData; + PostDataLen = postDataLen; + } + + void SetMethod(const char* method, size_t methodLen) { + Method = method; + MethodLen = methodLen; + } + + // deprecated + ui32 GetIp() const { + return Addrs.GetV4Addr().first; + } + + int GetScheme() const { + return THttpURL::SchemeHTTP; + } + void SetTimeout(TDuration tim) { + Timeout = tim; + } + + void SetConnectTimeout(TDuration timeout) { + ConnectTimeout = timeout; + } + + int Disconnected() { + return !Persistent || !Socket.Good(); + } + + int SetHost(const char* hostname, TIpPort port) { + Disconnect(); + TAddrList addrs = DnsClient.Resolve(hostname, port); + if (!addrs.size()) { + return 1; + } + + SetHost(hostname, port, addrs); + return 0; + } + + int SetHost(const char* hostname, TIpPort port, const TAddrList& addrs) { + Disconnect(); + Addrs = addrs; + size_t reqHostheaderLen = strlen(hostname) + 20; + if (HostheaderLen < reqHostheaderLen) { + free(Hostheader); + Hostheader = (char*)malloc((HostheaderLen = reqHostheaderLen)); + } + if (port == 80) + sprintf(Hostheader, "Host: %s\r\n", hostname); + else + sprintf(Hostheader, "Host: %s:%u\r\n", hostname, port); + pHostBeg = strchr(Hostheader, ' ') + 1; + pHostEnd = strchr(pHostBeg, '\r'); + // convert hostname to lower case since some web server don't like + // uppper case (Task ROBOT-562) + for (char* p = pHostBeg; p < pHostEnd; p++) + *p = tolower(*p); + return 0; + } + + // deprecated v4-only + int SetHost(const char* hostname, TIpPort port, ui32 ip) { + return SetHost(hostname, port, TAddrList::MakeV4Addr(ip, port)); + } + + void SetHostHeader(const char* host) { + size_t reqHostheaderLen = strlen(host) + 20; + if (HostheaderLen < reqHostheaderLen) { + delete[] Hostheader; + Hostheader = new char[(HostheaderLen = reqHostheaderLen)]; + } + sprintf(Hostheader, "Host: %s\r\n", host); + } + + void SetSocket(SOCKET fd) { + Socket.SetSocket(fd); + } + + SOCKET PickOutSocket() { + return Socket.PickOutSocket(); + } + + void Disconnect() { + Socket.Disconnect(); + } + + ssize_t read(void* buffer, size_t buflen) { + return Socket.read(buffer, buflen); + } + + int RequestGet(const char* url, const char* const* headers, int persistent = 1, bool head_request = false) { + if (!Addrs.size()) + return HTTP_DNS_FAILURE; + char message[MessageMax]; + ssize_t messlen = 0; + if (Method) { + strncpy(message, Method, MethodLen); + message[MethodLen] = ' '; + messlen = MethodLen + 1; + } else if (PostData) { + strcpy(message, "POST "); + messlen = 5; + } else if (head_request) { + strcpy(message, "HEAD "); + messlen = 5; + } else { + strcpy(message, "GET "); + messlen = 4; + } +#define _AppendMessage(mes) messlen += Min(MessageMax - messlen, \ + (ssize_t)strlcpy(message + messlen, (mes), MessageMax - messlen)) + _AppendMessage(url); + _AppendMessage(" HTTP/1.1\r\n"); + if (*url == '/') //if not then Host is a proxy + _AppendMessage(Hostheader); + _AppendMessage("Connection: "); + _AppendMessage(persistent ? "Keep-Alive\r\n" : "Close\r\n"); + while (headers && *headers) + _AppendMessage(*headers++); + if (AltFooter) + _AppendMessage(AltFooter); + else + _AppendMessage(Footer); + _AppendMessage("\r\n"); +#undef _AppendMessage + if (messlen >= MessageMax) + return HTTP_HEADER_TOO_LARGE; + + if (!Persistent) + Disconnect(); + Persistent = persistent; + int connected = Socket.Good(); + for (int attempt = !connected; attempt < 2; attempt++) { + const auto connectTimeout = ConnectTimeout ? ConnectTimeout : Timeout; + if (!Socket.Good() && Socket.Connect(Addrs, connectTimeout)) + return HTTP_CONNECT_FAILED; + + int sendOk = Socket.send(message, messlen); + if (sendOk && PostData && PostDataLen) + sendOk = Socket.send(PostData, PostDataLen); + if (!sendOk) { + int err = errno; + Disconnect(); + errno = err; + continue; + } + + if (!Socket.peek()) { + int err = errno; + Disconnect(); + if (err == EINTR) { + errno = err; + return HTTP_INTERRUPTED; + } + } else { + if (!persistent) + Socket.shutdown(); + return 0; + } + } + return connected ? HTTP_CONNECTION_LOST : HTTP_CONNECT_FAILED; + } + +protected: + TSocketHandler Socket; + TIpResolverWrapper<TDnsClient> DnsClient; + TAddrList Addrs; + int Persistent; + TDuration Timeout; + TDuration ConnectTimeout; + char *Hostheader, *Footer, *pHostBeg, *pHostEnd; + const char* AltFooter; // alternative footer can be set by the caller + const char* PostData; + size_t PostDataLen; + const char* Method; + size_t MethodLen; + unsigned short HostheaderLen; static const ssize_t MessageMax = 32768; -}; - -struct TNoTimer { - inline void OnBeforeSend() { - } - inline void OnAfterSend() { - } - inline void OnBeforeRecv() { - } - inline void OnAfterRecv() { - } -}; +}; + +struct TNoTimer { + inline void OnBeforeSend() { + } + inline void OnAfterSend() { + } + inline void OnBeforeRecv() { + } + inline void OnAfterRecv() { + } +}; diff --git a/library/cpp/http/fetch/sockhandler.h b/library/cpp/http/fetch/sockhandler.h index e18149f657..776704ba8c 100644 --- a/library/cpp/http/fetch/sockhandler.h +++ b/library/cpp/http/fetch/sockhandler.h @@ -1,130 +1,130 @@ -#pragma once - -#include <library/cpp/logger/all.h> - -#include <util/generic/buffer.h> -#include <util/generic/map.h> -#include <util/generic/vector.h> -#include <util/network/address.h> -#include <util/network/ip.h> -#include <util/network/socket.h> -#include <util/system/mutex.h> -#include <util/system/yassert.h> - -#include <cerrno> -#include <util/generic/noncopyable.h> - -class TAddrList: public TVector<NAddr::IRemoteAddrRef> { -private: - using TBase = TVector<NAddr::IRemoteAddrRef>; - -public: - //msvc doesn't support base class constructor inheritance - TAddrList() = default; - - template <typename T> - TAddrList(T&& arg) - : TBase(std::forward<T>(arg)) - { - } - - template <typename T1, typename T2> - TAddrList(T1&& arg1, T2&& arg2) - : TBase(std::forward<T1>(arg1), std::forward<T2>(arg2)) - { - } - - TAddrList(std::initializer_list<NAddr::IRemoteAddrRef> list) - : TBase(list) - { - } - - static TAddrList MakeV4Addr(ui32 ip, TIpPort port) { - return TAddrList({new NAddr::TIPv4Addr(TIpAddress(htonl(ip), htons(port)))}); - } - - std::pair<ui32, TIpPort> GetV4Addr() const { - for (const auto& addrRef : *this) { - const sockaddr* sa = addrRef->Addr(); - if (sa->sa_family == AF_INET) { - const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(sa); - return std::make_pair(ntohl(sin->sin_addr.s_addr), ntohs(sin->sin_port)); - } - } - return std::make_pair(0, 0); - } -}; - -class TSimpleSocketHandler { -public: - TSimpleSocketHandler() = default; - - int Good() const { - return static_cast<bool>(Socket); - } - - int Connect(const TAddrList& addrs, TDuration timeout) { - try { - for (const auto& item : addrs) { - const sockaddr* sa = item->Addr(); - TSocketHolder s(socket(sa->sa_family, SOCK_STREAM, 0)); - if (s.Closed()) { - continue; - } - -#ifndef WIN32 - if (fcntl(s, F_SETFD, FD_CLOEXEC)) // no inherit on fork()/exec() - return errno ? errno : EBADF; -#endif - if (connect(s, sa, item->Len())) { - s.Close(); - continue; - } - - Socket.Reset(new TSocket(s.Release())); - Socket->SetSocketTimeout(timeout.Seconds(), timeout.MilliSecondsOfSecond()); - Socket->SetZeroLinger(); - Socket->SetKeepAlive(true); - return 0; - } - } catch (...) { - return EBADF; - } - return errno ? errno : EBADF; - } - - void Disconnect() { - if (!Socket) - return; - Socket->ShutDown(SHUT_RDWR); - Socket.Destroy(); - } - - void SetSocket(SOCKET fd) { - Socket.Reset(new TSocket(fd)); - } - - void shutdown() { - Socket->ShutDown(SHUT_WR); - } - - int send(const void* message, size_t messlen) { - return ((ssize_t)messlen == Socket->Send(message, messlen)); - } - - int peek() { - char buf[1]; - return (1 == recv(*Socket, buf, 1, MSG_PEEK)); - } - - ssize_t read(void* buffer, size_t buflen) { - return Socket->Recv(buffer, buflen); - } - - THolder<TSocket> PickOutSocket() { - return std::move(Socket); - } - -protected: - THolder<TSocket> Socket; -}; +#pragma once + +#include <library/cpp/logger/all.h> + +#include <util/generic/buffer.h> +#include <util/generic/map.h> +#include <util/generic/vector.h> +#include <util/network/address.h> +#include <util/network/ip.h> +#include <util/network/socket.h> +#include <util/system/mutex.h> +#include <util/system/yassert.h> + +#include <cerrno> +#include <util/generic/noncopyable.h> + +class TAddrList: public TVector<NAddr::IRemoteAddrRef> { +private: + using TBase = TVector<NAddr::IRemoteAddrRef>; + +public: + //msvc doesn't support base class constructor inheritance + TAddrList() = default; + + template <typename T> + TAddrList(T&& arg) + : TBase(std::forward<T>(arg)) + { + } + + template <typename T1, typename T2> + TAddrList(T1&& arg1, T2&& arg2) + : TBase(std::forward<T1>(arg1), std::forward<T2>(arg2)) + { + } + + TAddrList(std::initializer_list<NAddr::IRemoteAddrRef> list) + : TBase(list) + { + } + + static TAddrList MakeV4Addr(ui32 ip, TIpPort port) { + return TAddrList({new NAddr::TIPv4Addr(TIpAddress(htonl(ip), htons(port)))}); + } + + std::pair<ui32, TIpPort> GetV4Addr() const { + for (const auto& addrRef : *this) { + const sockaddr* sa = addrRef->Addr(); + if (sa->sa_family == AF_INET) { + const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(sa); + return std::make_pair(ntohl(sin->sin_addr.s_addr), ntohs(sin->sin_port)); + } + } + return std::make_pair(0, 0); + } +}; + +class TSimpleSocketHandler { +public: + TSimpleSocketHandler() = default; + + int Good() const { + return static_cast<bool>(Socket); + } + + int Connect(const TAddrList& addrs, TDuration timeout) { + try { + for (const auto& item : addrs) { + const sockaddr* sa = item->Addr(); + TSocketHolder s(socket(sa->sa_family, SOCK_STREAM, 0)); + if (s.Closed()) { + continue; + } + +#ifndef WIN32 + if (fcntl(s, F_SETFD, FD_CLOEXEC)) // no inherit on fork()/exec() + return errno ? errno : EBADF; +#endif + if (connect(s, sa, item->Len())) { + s.Close(); + continue; + } + + Socket.Reset(new TSocket(s.Release())); + Socket->SetSocketTimeout(timeout.Seconds(), timeout.MilliSecondsOfSecond()); + Socket->SetZeroLinger(); + Socket->SetKeepAlive(true); + return 0; + } + } catch (...) { + return EBADF; + } + return errno ? errno : EBADF; + } + + void Disconnect() { + if (!Socket) + return; + Socket->ShutDown(SHUT_RDWR); + Socket.Destroy(); + } + + void SetSocket(SOCKET fd) { + Socket.Reset(new TSocket(fd)); + } + + void shutdown() { + Socket->ShutDown(SHUT_WR); + } + + int send(const void* message, size_t messlen) { + return ((ssize_t)messlen == Socket->Send(message, messlen)); + } + + int peek() { + char buf[1]; + return (1 == recv(*Socket, buf, 1, MSG_PEEK)); + } + + ssize_t read(void* buffer, size_t buflen) { + return Socket->Recv(buffer, buflen); + } + + THolder<TSocket> PickOutSocket() { + return std::move(Socket); + } + +protected: + THolder<TSocket> Socket; +}; diff --git a/library/cpp/http/io/stream.cpp b/library/cpp/http/io/stream.cpp index 6689be684f..115bc9b066 100644 --- a/library/cpp/http/io/stream.cpp +++ b/library/cpp/http/io/stream.cpp @@ -640,15 +640,15 @@ private: return IsRequest(FirstLine_); } - inline bool HasResponseBody() const noexcept { - if (IsHttpResponse()) { + inline bool HasResponseBody() const noexcept { + if (IsHttpResponse()) { if (Request_ && Request_->FirstLine().StartsWith(TStringBuf("HEAD"))) - return false; + return false; if (FirstLine_.size() > 9 && strncmp(FirstLine_.data() + 9, "204", 3) == 0) - return false; - return true; - } - return false; + return false; + return true; + } + return false; } inline bool IsHttpResponse() const noexcept { @@ -772,7 +772,7 @@ private: } if (IsHttpResponse()) { - if (Request_ && IsCompressionEnabled() && HasResponseBody()) { + if (Request_ && IsCompressionEnabled() && HasResponseBody()) { TString scheme = Request_->BestCompressionScheme(ComprSchemas_); if (scheme != "identity") { AddOrReplaceHeader(THttpInputHeader("Content-Encoding", scheme)); @@ -826,7 +826,7 @@ private: } } - if (!haveContentLength && !chunked && (IsHttpRequest() || HasResponseBody()) && SupportChunkedTransfer() && (keepAlive || encoder || IsHttpRequest())) { + if (!haveContentLength && !chunked && (IsHttpRequest() || HasResponseBody()) && SupportChunkedTransfer() && (keepAlive || encoder || IsHttpRequest())) { AddHeader(THttpInputHeader("Transfer-Encoding", "chunked")); chunked = true; } diff --git a/library/cpp/http/ya.make b/library/cpp/http/ya.make index fa2d1edef6..6027c72bc4 100644 --- a/library/cpp/http/ya.make +++ b/library/cpp/http/ya.make @@ -5,7 +5,7 @@ RECURSE( coro examples fetch - fetch_gpl + fetch_gpl io io/fuzz io/list_codings diff --git a/library/cpp/messagebus/message.h b/library/cpp/messagebus/message.h index 005ca10c65..2d2c08fc09 100644 --- a/library/cpp/messagebus/message.h +++ b/library/cpp/messagebus/message.h @@ -56,12 +56,12 @@ namespace NBus { void BeginWork() { SetInWork(true); } - + // for internal use only void EndWork() { SetInWork(false); } - + TBusIdentity(); ~TBusIdentity(); diff --git a/library/cpp/monlib/service/mon_service_http_request.cpp b/library/cpp/monlib/service/mon_service_http_request.cpp index 5d805631d9..b22e53d370 100644 --- a/library/cpp/monlib/service/mon_service_http_request.cpp +++ b/library/cpp/monlib/service/mon_service_http_request.cpp @@ -42,44 +42,44 @@ const TCgiParameters& TMonService2HttpRequest::GetPostParams() const { } TStringBuf TMonService2HttpRequest::GetHeader(TStringBuf name) const { - const THttpHeaders& headers = HttpRequest->GetHeaders(); - const THttpInputHeader* header = headers.FindHeader(name); - if (header != nullptr) { - return header->Value(); - } - return TStringBuf(); -} - + const THttpHeaders& headers = HttpRequest->GetHeaders(); + const THttpInputHeader* header = headers.FindHeader(name); + if (header != nullptr) { + return header->Value(); + } + return TStringBuf(); +} + const THttpHeaders& TMonService2HttpRequest::GetHeaders() const { return HttpRequest->GetHeaders(); } - -TString TMonService2HttpRequest::GetRemoteAddr() const { - return HttpRequest->GetRemoteAddr(); -} - + +TString TMonService2HttpRequest::GetRemoteAddr() const { + return HttpRequest->GetRemoteAddr(); +} + TStringBuf TMonService2HttpRequest::GetCookie(TStringBuf name) const { - TStringBuf cookie = GetHeader("Cookie"); - size_t size = cookie.size(); - size_t start = 0; - while (start < size) { - size_t semicolon = cookie.find(';', start); - auto pair = cookie.substr(start, semicolon - start); - if (!pair.empty()) { - size_t equal = pair.find('='); - if (equal != TStringBuf::npos) { - auto cookieName = pair.substr(0, equal); - if (cookieName == name) { - size_t valueStart = equal + 1; - auto cookieValue = pair.substr(valueStart, semicolon - valueStart); - return cookieValue; - } - } - start = semicolon; - while (start < size && (cookie[start] == ' ' || cookie[start] == ';')) { - ++start; - } - } - } - return TStringBuf(); -} + TStringBuf cookie = GetHeader("Cookie"); + size_t size = cookie.size(); + size_t start = 0; + while (start < size) { + size_t semicolon = cookie.find(';', start); + auto pair = cookie.substr(start, semicolon - start); + if (!pair.empty()) { + size_t equal = pair.find('='); + if (equal != TStringBuf::npos) { + auto cookieName = pair.substr(0, equal); + if (cookieName == name) { + size_t valueStart = equal + 1; + auto cookieValue = pair.substr(valueStart, semicolon - valueStart); + return cookieValue; + } + } + start = semicolon; + while (start < size && (cookie[start] == ' ' || cookie[start] == ';')) { + ++start; + } + } + } + return TStringBuf(); +} diff --git a/library/cpp/monlib/service/mon_service_http_request.h b/library/cpp/monlib/service/mon_service_http_request.h index b4f2f8f0c5..d46e2bcd31 100644 --- a/library/cpp/monlib/service/mon_service_http_request.h +++ b/library/cpp/monlib/service/mon_service_http_request.h @@ -24,7 +24,7 @@ namespace NMonitoring { virtual const THttpHeaders& GetHeaders() const = 0; virtual TStringBuf GetHeader(TStringBuf name) const = 0; virtual TStringBuf GetCookie(TStringBuf name) const = 0; - virtual TString GetRemoteAddr() const = 0; + virtual TString GetRemoteAddr() const = 0; virtual TString GetServiceTitle() const = 0; @@ -71,8 +71,8 @@ namespace NMonitoring { TStringBuf GetHeader(TStringBuf name) const override; TStringBuf GetCookie(TStringBuf name) const override; const THttpHeaders& GetHeaders() const override; - TString GetRemoteAddr() const override; - + TString GetRemoteAddr() const override; + IMonPage* GetPage() const override { return MonPage; } diff --git a/library/cpp/monlib/service/monservice.cpp b/library/cpp/monlib/service/monservice.cpp index d1b9cda1d2..b45ba2c19b 100644 --- a/library/cpp/monlib/service/monservice.cpp +++ b/library/cpp/monlib/service/monservice.cpp @@ -41,14 +41,14 @@ TMonService2::TMonService2(const THttpServerOptions& options, TSimpleSharedPtr<I TMonService2::TMonService2(ui16 port, ui32 threads, const TString& title, THolder<IAuthProvider> auth) : TMonService2(port, TString(), threads, title, std::move(auth)) -{ -} - +{ +} + TMonService2::TMonService2(ui16 port, const TString& title, THolder<IAuthProvider> auth) : TMonService2(port, TString(), 0, title, std::move(auth)) { } - + void TMonService2::OutputIndex(IOutputStream& out) { IndexMonPage->OutputIndex(out, true); } @@ -123,7 +123,7 @@ IMonPage* TMonService2::FindPage(const TString& relativePath) { TIndexMonPage* TMonService2::FindIndexPage(const TString& relativePath) { return IndexMonPage->FindIndexPage(relativePath); } - -void TMonService2::SortPages() { - IndexMonPage->SortPages(); -} + +void TMonService2::SortPages() { + IndexMonPage->SortPages(); +} diff --git a/library/cpp/monlib/service/monservice.h b/library/cpp/monlib/service/monservice.h index 8f5e52fcdb..7a6c0383eb 100644 --- a/library/cpp/monlib/service/monservice.h +++ b/library/cpp/monlib/service/monservice.h @@ -20,23 +20,23 @@ namespace NMonitoring { THolder<IAuthProvider> AuthProvider_; public: - static THttpServerOptions HttpServerOptions(ui16 port, const TString& host, ui32 threads) { + static THttpServerOptions HttpServerOptions(ui16 port, const TString& host, ui32 threads) { THttpServerOptions opts(port); - if (!host.empty()) { - opts.SetHost(host); - } + if (!host.empty()) { + opts.SetHost(host); + } opts.SetClientTimeout(TDuration::Minutes(1)); opts.EnableCompression(true); opts.SetThreads(threads); - opts.SetMaxConnections(std::max<ui32>(100, threads)); - opts.EnableRejectExcessConnections(true); + opts.SetMaxConnections(std::max<ui32>(100, threads)); + opts.EnableRejectExcessConnections(true); return opts; } - static THttpServerOptions HttpServerOptions(ui16 port, ui32 threads) { - return HttpServerOptions(port, TString(), threads); - } - + static THttpServerOptions HttpServerOptions(ui16 port, ui32 threads) { + return HttpServerOptions(port, TString(), threads); + } + public: explicit TMonService2(ui16 port, const TString& title = GetProgramName(), THolder<IAuthProvider> auth = nullptr); explicit TMonService2(ui16 port, ui32 threads, const TString& title = GetProgramName(), THolder<IAuthProvider> auth = nullptr); diff --git a/library/cpp/monlib/service/pages/index_mon_page.cpp b/library/cpp/monlib/service/pages/index_mon_page.cpp index 83ff8b529a..461cd30698 100644 --- a/library/cpp/monlib/service/pages/index_mon_page.cpp +++ b/library/cpp/monlib/service/pages/index_mon_page.cpp @@ -136,16 +136,16 @@ void TIndexMonPage::OutputBody(IMonHttpRequest& req) { out << "<div>\n" << "</body>\n"; } - + void TIndexMonPage::SortPages() { - TGuard<TMutex> g(Mtx); - std::sort(Pages.begin(), Pages.end(), [](const TMonPagePtr& a, const TMonPagePtr& b) { + TGuard<TMutex> g(Mtx); + std::sort(Pages.begin(), Pages.end(), [](const TMonPagePtr& a, const TMonPagePtr& b) { return AsciiCompareIgnoreCase(a->GetTitle(), b->GetTitle()) < 0; - }); -} - -void TIndexMonPage::ClearPages() { - TGuard<TMutex> g(Mtx); - Pages.clear(); - PagesByPath.clear(); -} + }); +} + +void TIndexMonPage::ClearPages() { + TGuard<TMutex> g(Mtx); + Pages.clear(); + PagesByPath.clear(); +} diff --git a/library/cpp/monlib/service/pages/index_mon_page.h b/library/cpp/monlib/service/pages/index_mon_page.h index bf514a3105..ea4df882ad 100644 --- a/library/cpp/monlib/service/pages/index_mon_page.h +++ b/library/cpp/monlib/service/pages/index_mon_page.h @@ -32,7 +32,7 @@ namespace NMonitoring { TIndexMonPage* FindIndexPage(const TString& relativePath); void SortPages(); - void ClearPages(); + void ClearPages(); }; } diff --git a/library/cpp/monlib/service/pages/templates.h b/library/cpp/monlib/service/pages/templates.h index b4656f059f..6a48a9229e 100644 --- a/library/cpp/monlib/service/pages/templates.h +++ b/library/cpp/monlib/service/pages/templates.h @@ -123,32 +123,32 @@ namespace NMonitoring { { Str << "<" << tag; - if (!cls.empty()) { + if (!cls.empty()) { Str << " class=\"" << cls << "\""; } - if (!for0.empty()) { + if (!for0.empty()) { Str << " for=\"" << for0 << "\""; } - if (!id.empty()) { + if (!id.empty()) { Str << "id=\"" << id << "\""; } Str << ">"; } TTag(IOutputStream& str, std::initializer_list<std::pair<TStringBuf, TStringBuf>> attributes) - : Str(str) - { - Str << "<" << tag; - for (const std::pair<TStringBuf, TStringBuf>& attr : attributes) { - if (!attr.second.empty()) { - Str << ' ' << attr.first << "=\"" << attr.second << "\""; - } - } - Str << ">"; - } - + : Str(str) + { + Str << "<" << tag; + for (const std::pair<TStringBuf, TStringBuf>& attr : attributes) { + if (!attr.second.empty()) { + Str << ' ' << attr.first << "=\"" << attr.second << "\""; + } + } + Str << ">"; + } + ~TTag() { try { Str << "</" << tag << ">"; diff --git a/library/cpp/monlib/service/service.cpp b/library/cpp/monlib/service/service.cpp index 929efbf816..0a87d8dff4 100644 --- a/library/cpp/monlib/service/service.cpp +++ b/library/cpp/monlib/service/service.cpp @@ -15,10 +15,10 @@ namespace NMonitoring { class THttpClient: public IHttpRequest { public: - void ServeRequest(THttpInput& in, IOutputStream& out, const NAddr::IRemoteAddr* remoteAddr, const THandler& Handler) { + void ServeRequest(THttpInput& in, IOutputStream& out, const NAddr::IRemoteAddr* remoteAddr, const THandler& Handler) { try { try { - RemoteAddr = remoteAddr; + RemoteAddr = remoteAddr; THttpHeaderParser parser; parser.Init(&Header); if (parser.Execute(in.FirstLine().data(), in.FirstLine().size()) < 0) { @@ -34,15 +34,15 @@ namespace NMonitoring { out << "HTTP/1.1 400 Bad request\r\nConnection: Close\r\n\r\n"; return; } - Headers = &in.Headers(); + Headers = &in.Headers(); CgiParams.Scan(Url.Get(THttpURL::FieldQuery)); } catch (...) { out << "HTTP/1.1 500 Internal server error\r\nConnection: Close\r\n\r\n"; YSYSLOG(TLOG_ERR, "THttpClient: internal error while serving monitoring request: %s", CurrentExceptionMessage().data()); } - if (Header.http_method == HTTP_METHOD_POST) - TransferData(&in, &PostContent); + if (Header.http_method == HTTP_METHOD_POST) + TransferData(&in, &PostContent); Handler(out, *this); out.Finish(); @@ -64,49 +64,49 @@ namespace NMonitoring { return CgiParams; } const TCgiParameters& GetPostParams() const override { - if (PostParams.empty() && !PostContent.Buffer().Empty()) - const_cast<THttpClient*>(this)->ScanPostParams(); + if (PostParams.empty() && !PostContent.Buffer().Empty()) + const_cast<THttpClient*>(this)->ScanPostParams(); return PostParams; } - TStringBuf GetPostContent() const override { - return TStringBuf(PostContent.Buffer().Data(), PostContent.Buffer().Size()); - } + TStringBuf GetPostContent() const override { + return TStringBuf(PostContent.Buffer().Data(), PostContent.Buffer().Size()); + } HTTP_METHOD GetMethod() const override { return (HTTP_METHOD)Header.http_method; } - void ScanPostParams() { + void ScanPostParams() { PostParams.Scan(TStringBuf(PostContent.Buffer().data(), PostContent.Buffer().size())); - } - - const THttpHeaders& GetHeaders() const override { - if (Headers != nullptr) { - return *Headers; - } - static THttpHeaders defaultHeaders; - return defaultHeaders; - } - - TString GetRemoteAddr() const override { - return RemoteAddr ? NAddr::PrintHostAndPort(*RemoteAddr) : TString(); - } - + } + + const THttpHeaders& GetHeaders() const override { + if (Headers != nullptr) { + return *Headers; + } + static THttpHeaders defaultHeaders; + return defaultHeaders; + } + + TString GetRemoteAddr() const override { + return RemoteAddr ? NAddr::PrintHostAndPort(*RemoteAddr) : TString(); + } + private: THttpRequestHeader Header; - const THttpHeaders* Headers = nullptr; + const THttpHeaders* Headers = nullptr; THttpURL Url; TCgiParameters CgiParams; TCgiParameters PostParams; - TBufferOutput PostContent; - const NAddr::IRemoteAddr* RemoteAddr = nullptr; + TBufferOutput PostContent; + const NAddr::IRemoteAddr* RemoteAddr = nullptr; }; /* TCoHttpServer */ class TCoHttpServer::TConnection: public THttpClient { public: - TConnection(const TCoHttpServer::TAcceptFull& acc, const TCoHttpServer& parent) - : Socket(acc.S->Release()) - , RemoteAddr(acc.Remote) + TConnection(const TCoHttpServer::TAcceptFull& acc, const TCoHttpServer& parent) + : Socket(acc.S->Release()) + , RemoteAddr(acc.Remote) , Parent(parent) { } @@ -119,7 +119,7 @@ namespace NMonitoring { THttpOutput out(&io, &in); // buffer reply so there will be ne context switching TStringStream s; - ServeRequest(in, s, RemoteAddr, Parent.Handler); + ServeRequest(in, s, RemoteAddr, Parent.Handler); out << s.Str(); out.Finish(); } catch (...) { @@ -129,7 +129,7 @@ namespace NMonitoring { private: TSocketHolder Socket; - const NAddr::IRemoteAddr* RemoteAddr; + const NAddr::IRemoteAddr* RemoteAddr; const TCoHttpServer& Parent; }; @@ -156,7 +156,7 @@ namespace NMonitoring { } void TCoHttpServer::OnAcceptFull(const TAcceptFull& acc) { - THolder<TConnection> conn(new TConnection(acc, *this)); + THolder<TConnection> conn(new TConnection(acc, *this)); Executor.Create(*conn, "client"); Y_UNUSED(conn.Release()); } @@ -195,7 +195,7 @@ namespace NMonitoring { } bool Reply(void*) override { - ServeRequest(Input(), Output(), NAddr::GetPeerAddr(Socket()).Get(), Parent.Handler); + ServeRequest(Input(), Output(), NAddr::GetPeerAddr(Socket()).Get(), Parent.Handler); return true; } diff --git a/library/cpp/monlib/service/service.h b/library/cpp/monlib/service/service.h index 2f66dddaf8..0a10cf2a3d 100644 --- a/library/cpp/monlib/service/service.h +++ b/library/cpp/monlib/service/service.h @@ -21,10 +21,10 @@ namespace NMonitoring { virtual const char* GetPath() const = 0; virtual const TCgiParameters& GetParams() const = 0; virtual const TCgiParameters& GetPostParams() const = 0; - virtual TStringBuf GetPostContent() const = 0; + virtual TStringBuf GetPostContent() const = 0; virtual HTTP_METHOD GetMethod() const = 0; - virtual const THttpHeaders& GetHeaders() const = 0; - virtual TString GetRemoteAddr() const = 0; + virtual const THttpHeaders& GetHeaders() const = 0; + virtual TString GetRemoteAddr() const = 0; }; // first param - output stream to write result to // second param - URL of request diff --git a/library/cpp/threading/future/core/future-inl.h b/library/cpp/threading/future/core/future-inl.h index 5fd4296a93..b67d79e301 100644 --- a/library/cpp/threading/future/core/future-inl.h +++ b/library/cpp/threading/future/core/future-inl.h @@ -73,22 +73,22 @@ namespace NThreading { default: Y_ASSERT(state == ValueSet); } - } - + } + public: TFutureState() : State(NotReady) , NullValue(0) { - } - + } + template <typename TT> TFutureState(TT&& value) : State(ValueSet) , Value(std::forward<TT>(value)) { } - + TFutureState(std::exception_ptr exception, TError) : State(ExceptionSet) , Exception(std::move(exception)) @@ -101,11 +101,11 @@ namespace NThreading { Value.~T(); } } - + bool HasValue() const { return AtomicGet(State) >= ValueMoved; // ValueMoved, ValueSet, ValueRead - } - + } + void TryRethrow() const { int state = AtomicGet(State); TryRethrowWithState(state); @@ -148,7 +148,7 @@ namespace NThreading { readyEvent = ReadyEvent.Get(); callbacks = std::move(Callbacks); - + AtomicSet(State, ValueSet); } @@ -635,7 +635,7 @@ namespace NThreading { }); return promise; } - + template <typename T> inline bool TFuture<T>::Initialized() const { return bool(State); @@ -790,7 +790,7 @@ namespace NThreading { EnsureInitialized(); State->SetValue(value); } - + template <typename T> inline void TPromise<T>::SetValue(T&& value) { EnsureInitialized(); diff --git a/library/cpp/threading/future/future_ut.cpp b/library/cpp/threading/future/future_ut.cpp index 05950a568d..8dc8788086 100644 --- a/library/cpp/threading/future/future_ut.cpp +++ b/library/cpp/threading/future/future_ut.cpp @@ -447,15 +447,15 @@ namespace { explicit TRec(int) { } }; - + auto promise = NewPromise<TRec>(); promise.SetValue(TRec(1)); - + auto future = MakeFuture(TRec(1)); auto rec = future.ExtractValue(); Y_UNUSED(rec); } - + Y_UNIT_TEST(ShouldNotExtractAfterGet) { TPromise<int> promise = NewPromise<int>(); promise.SetValue(123); @@ -463,7 +463,7 @@ namespace { UNIT_ASSERT_EQUAL(promise.GetValue(), 123); UNIT_CHECK_GENERATED_EXCEPTION(promise.ExtractValue(), TFutureException); } - + Y_UNIT_TEST(ShouldNotGetAfterExtract) { TPromise<int> promise = NewPromise<int>(); promise.SetValue(123); @@ -471,7 +471,7 @@ namespace { UNIT_ASSERT_EQUAL(promise.ExtractValue(), 123); UNIT_CHECK_GENERATED_EXCEPTION(promise.GetValue(), TFutureException); } - + Y_UNIT_TEST(ShouldNotExtractAfterExtract) { TPromise<int> promise = NewPromise<int>(); promise.SetValue(123); @@ -635,6 +635,6 @@ namespace { TestApplyLvalueCopyImpl<void>(); TestApplyLvalueCopyImpl<int>(); } - } - + } + } |