diff options
author | Alexey Efimov <xeno@prnwatch.com> | 2022-05-18 19:23:33 +0300 |
---|---|---|
committer | Alexey Efimov <xeno@prnwatch.com> | 2022-05-18 19:23:33 +0300 |
commit | 90e99413865b0990ff8a5e35251074288bde15d9 (patch) | |
tree | b5c17609b32840b95a50f755cdfbe0ab25fc55cb /library | |
parent | e1b4a8a09912622730eea7b2d3ca1267ef9dc135 (diff) | |
download | ydb-90e99413865b0990ff8a5e35251074288bde15d9.tar.gz |
add support for mixed and ipv4-only environment KIKIMR-14869
ref:ec57c92c9ef01b8aad21861c915e620ec8823586
Diffstat (limited to 'library')
-rw-r--r-- | library/cpp/actors/http/http.h | 5 | ||||
-rw-r--r-- | library/cpp/actors/http/http_config.h | 6 | ||||
-rw-r--r-- | library/cpp/actors/http/http_proxy.cpp | 57 | ||||
-rw-r--r-- | library/cpp/actors/http/http_proxy.h | 16 | ||||
-rw-r--r-- | library/cpp/actors/http/http_proxy_acceptor.cpp | 27 | ||||
-rw-r--r-- | library/cpp/actors/http/http_proxy_outgoing.cpp | 65 | ||||
-rw-r--r-- | library/cpp/actors/http/http_proxy_sock64.h | 79 | ||||
-rw-r--r-- | library/cpp/actors/http/http_proxy_sock_impl.h | 12 | ||||
-rw-r--r-- | library/cpp/actors/http/http_ut.cpp | 36 |
9 files changed, 250 insertions, 53 deletions
diff --git a/library/cpp/actors/http/http.h b/library/cpp/actors/http/http.h index 38e616cafa..2f11bfe04d 100644 --- a/library/cpp/actors/http/http.h +++ b/library/cpp/actors/http/http.h @@ -15,13 +15,14 @@ struct THash<TIntrusivePtr<Type>> { }; template<> -inline void Out<TSockAddrInet6>(IOutputStream& o, const TSockAddrInet6& x) { - o << x.ToString(); +inline void Out<NHttp::THttpConfig::SocketAddressType>(IOutputStream& o, const NHttp::THttpConfig::SocketAddressType& x) { + o << x->ToString(); } namespace NHttp { bool IsIPv6(const TString& host); +bool IsIPv4(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); diff --git a/library/cpp/actors/http/http_config.h b/library/cpp/actors/http/http_config.h index faeff79449..1a2f8646a3 100644 --- a/library/cpp/actors/http/http_config.h +++ b/library/cpp/actors/http/http_config.h @@ -3,6 +3,8 @@ #include <library/cpp/actors/core/log.h> #include <library/cpp/actors/protos/services_common.pb.h> +class TInet64StreamSocket; + namespace NHttp { struct THttpConfig { @@ -12,8 +14,8 @@ struct THttpConfig { 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; + using SocketType = TInet64StreamSocket; + using SocketAddressType = std::shared_ptr<ISockAddr>; }; } diff --git a/library/cpp/actors/http/http_proxy.cpp b/library/cpp/actors/http/http_proxy.cpp index e347a3e1c2..394cb17bd3 100644 --- a/library/cpp/actors/http/http_proxy.cpp +++ b/library/cpp/actors/http/http_proxy.cpp @@ -119,7 +119,7 @@ protected: } void Handle(TEvHttpProxy::TEvRegisterHandler::TPtr event, const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, HttpLog, "Register handler " << event->Get()->Path << " to " << event->Get()->Handler); + LOG_TRACE_S(ctx, HttpLog, "Register handler " << event->Get()->Path << " to " << event->Get()->Handler); Handlers[event->Get()->Path] = event->Get()->Handler; } @@ -131,11 +131,16 @@ protected: 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.Address = std::make_shared<TSockAddrInet6>(addressPart.data(), portPart); + it->second.DeadlineTime = ctx.Now() + HostsTimeToLive; + } else if (IsIPv4(addressPart)) { + if (it == Hosts.end()) { + it = Hosts.emplace(host, THostEntry()).first; + } + it->second.Address = std::make_shared<TSockAddrInet>(addressPart.data(), portPart); it->second.DeadlineTime = ctx.Now() + HostsTimeToLive; } else { // TODO(xenoxeno): move to another, possible blocking actor @@ -143,21 +148,31 @@ protected: 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) { + while (pAddr != result->Addr.End() && pAddr->ai_family != AF_INET && 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; + THttpConfig::SocketAddressType address; + switch (pAddr->ai_family) { + case AF_INET: + address = std::make_shared<TSockAddrInet>(); + break; + case AF_INET6: + address = std::make_shared<TSockAddrInet6>(); + break; + } + if (address) { + memcpy(address->SockAddr(), pAddr->ai_addr, pAddr->ai_addrlen); + 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; } - it->second.Address = address; - it->second.DeadlineTime = ctx.Now() + HostsTimeToLive; } else { ctx.Send(event->Sender, new TEvHttpProxy::TEvResolveHostResponse("Error resolving host")); return; @@ -222,7 +237,7 @@ protected: TVector<TActorId> Acceptors; struct THostEntry { - TSockAddrInet6 Address; + THttpConfig::SocketAddressType Address; TInstant DeadlineTime; }; @@ -259,7 +274,23 @@ NActors::IActor* CreateHttpProxy(std::weak_ptr<NMonitoring::TMetricRegistry> reg } bool IsIPv6(const TString& host) { - return host.find_first_not_of(":0123456789abcdef") == TString::npos; + if (host.find_first_not_of(":0123456789abcdef") != TString::npos) { + return false; + } + if (std::count(host.begin(), host.end(), ':') < 2) { + return false; + } + return true; +} + +bool IsIPv4(const TString& host) { + if (host.find_first_not_of(".0123456789") != TString::npos) { + return false; + } + if (std::count(host.begin(), host.end(), '.') != 3) { + return false; + } + return true; } bool CrackURL(TStringBuf url, TStringBuf& scheme, TStringBuf& host, TStringBuf& uri) { diff --git a/library/cpp/actors/http/http_proxy.h b/library/cpp/actors/http/http_proxy.h index bd907c13a7..b95954e561 100644 --- a/library/cpp/actors/http/http_proxy.h +++ b/library/cpp/actors/http/http_proxy.h @@ -11,6 +11,7 @@ #include <library/cpp/monlib/metrics/metric_registry.h> #include <util/generic/variant.h> #include "http.h" +#include "http_proxy_sock64.h" #include "http_proxy_ssl.h" namespace NHttp { @@ -18,6 +19,17 @@ namespace NHttp { struct TSocketDescriptor : NActors::TSharedDescriptor, THttpConfig { SocketType Socket; + TSocketDescriptor() = default; + + TSocketDescriptor(int af) + : Socket(af) + { + } + + TSocketDescriptor(SocketType&& s) + : Socket(std::move(s)) + {} + int GetDescriptor() override { return static_cast<SOCKET>(Socket); } @@ -188,10 +200,10 @@ struct TEvHttpProxy { struct TEvResolveHostResponse : NActors::TEventLocal<TEvResolveHostResponse, EvResolveHostResponse> { TString Host; - TSockAddrInet6 Address; + THttpConfig::SocketAddressType Address; TString Error; - TEvResolveHostResponse(const TString& host, const TSockAddrInet6& address) + TEvResolveHostResponse(const TString& host, THttpConfig::SocketAddressType address) : Host(host) , Address(address) {} diff --git a/library/cpp/actors/http/http_proxy_acceptor.cpp b/library/cpp/actors/http/http_proxy_acceptor.cpp index d74ef0abac..f73c239780 100644 --- a/library/cpp/actors/http/http_proxy_acceptor.cpp +++ b/library/cpp/actors/http/http_proxy_acceptor.cpp @@ -22,9 +22,9 @@ public: , Socket(new TSocketDescriptor()) { // for unit tests :( - CheckedSetSockOpt(Socket->Socket, SOL_SOCKET, SO_REUSEADDR, (int)true, "reuse address"); + SetSockOpt(Socket->Socket, SOL_SOCKET, SO_REUSEADDR, (int)true); #ifdef SO_REUSEPORT - CheckedSetSockOpt(Socket->Socket, SOL_SOCKET, SO_REUSEPORT, (int)true, "reuse port"); + SetSockOpt(Socket->Socket, SOL_SOCKET, SO_REUSEPORT, (int)true); #endif } @@ -45,7 +45,7 @@ protected: } void HandleInit(TEvHttpProxy::TEvAddListeningPort::TPtr event, const NActors::TActorContext& ctx) { - SocketAddressType bindAddress(event->Get()->Address ? event->Get()->Address.data() : "::", event->Get()->Port); + SocketAddressType bindAddress(Socket->Socket.MakeAddress(event->Get()->Address,event->Get()->Port)); Endpoint = std::make_shared<TPrivateEndpointInfo>(event->Get()->CompressContentTypes); Endpoint->Owner = ctx.SelfID; Endpoint->Proxy = Owner; @@ -64,12 +64,12 @@ protected: } } if (err == 0) { - err = Socket->Socket.Bind(&bindAddress); + err = Socket->Socket.Bind(bindAddress.get()); } if (err == 0) { err = Socket->Socket.Listen(LISTEN_QUEUE); if (err == 0) { - LOG_INFO_S(ctx, HttpLog, "Listening on " << bindAddress.ToString()); + 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); @@ -77,7 +77,7 @@ protected: return; } } - LOG_WARN_S(ctx, HttpLog, "Failed to listen on " << bindAddress.ToString() << " - retrying..."); + LOG_WARN_S(ctx, HttpLog, "Failed to listen on " << bindAddress->ToString() << " - retrying..."); ctx.ExecutorThread.Schedule(TDuration::Seconds(1), event.Release()); } @@ -94,10 +94,13 @@ protected: } void Handle(NActors::TEvPollerReady::TPtr, const NActors::TActorContext& ctx) { - TIntrusivePtr<TSocketDescriptor> socket = new TSocketDescriptor(); - SocketAddressType addr; - int err; - while ((err = Socket->Socket.Accept(&socket->Socket, &addr)) == 0) { + for (;;) { + SocketAddressType addr; + std::optional<SocketType> s = Socket->Socket.Accept(addr); + if (!s) { + break; + } + TIntrusivePtr<TSocketDescriptor> socket = new TSocketDescriptor(std::move(s).value()); NActors::IActor* connectionSocket = nullptr; if (RecycledRequests.empty()) { connectionSocket = CreateIncomingConnectionActor(Endpoint, socket, addr); @@ -108,9 +111,9 @@ protected: NActors::TActorId connectionId = ctx.Register(connectionSocket); ctx.Send(Poller, new NActors::TEvPollerRegister(socket, connectionId, connectionId)); Connections.emplace(connectionId); - socket = new TSocketDescriptor(); } - if (err == -EAGAIN || err == -EWOULDBLOCK) { // request poller for further connection polling + int err = errno; + if (err == EAGAIN || err == EWOULDBLOCK) { // request poller for further connection polling Y_VERIFY(PollerToken); PollerToken->Request(true, false); } diff --git a/library/cpp/actors/http/http_proxy_outgoing.cpp b/library/cpp/actors/http/http_proxy_outgoing.cpp index d9189dba8a..8866773016 100644 --- a/library/cpp/actors/http/http_proxy_outgoing.cpp +++ b/library/cpp/actors/http/http_proxy_outgoing.cpp @@ -25,8 +25,6 @@ public: , Poller(poller) , Host(host) { - TSocketImpl::SetNonBlock(); - TSocketImpl::SetTimeout(SOCKET_TIMEOUT); } void Die(const NActors::TActorContext& ctx) override { @@ -35,18 +33,30 @@ public: TBase::Die(ctx); } + TString GetSocketName() { + TStringBuilder builder; + if (TSocketImpl::Socket) { + builder << "(#" << TSocketImpl::GetRawSocket(); + if (Address && Address->SockAddr()->sa_family) { + builder << "," << Address; + } + builder << ") "; + } + return builder; + } + void ReplyAndDie(const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") -> (" << Response->Status << " " << Response->Message << ")"); + LOG_DEBUG_S(ctx, HttpLog, GetSocketName() << "-> (" << 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"); + LOG_DEBUG_S(ctx, HttpLog, GetSocketName() << "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); + LOG_ERROR_S(ctx, HttpLog, GetSocketName() << "connection closed with error: " << error); if (RequestOwner) { ctx.Send(RequestOwner, new TEvHttpProxy::TEvHttpIncomingResponse(Request, Response, error)); RequestOwner = TActorId(); @@ -65,7 +75,10 @@ protected: } void Connect(const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") connecting"); + LOG_DEBUG_S(ctx, HttpLog, GetSocketName() << "connecting"); + TSocketImpl::Create(Address->SockAddr()->sa_family); + TSocketImpl::SetNonBlock(); + TSocketImpl::SetTimeout(ConnectionTimeout); int res = TSocketImpl::Connect(Address); RegisterPoller(ctx); switch (-res) { @@ -162,20 +175,41 @@ protected: return ReplyErrorAndDie(ctx, strerror(-res)); } } - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") outgoing connection opened"); + LOG_DEBUG_S(ctx, HttpLog, GetSocketName() << "outgoing connection opened"); TBase::Become(&TOutgoingConnectionActor::StateConnected); - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << "," << Address << ") <- (" << Request->Method << " " << Request->URL << ")"); + LOG_DEBUG_S(ctx, HttpLog, GetSocketName() << "<- (" << Request->Method << " " << Request->URL << ")"); ctx.Send(ctx.SelfID, new NActors::TEvPollerReady(nullptr, true, true)); } + static int GetPort(SocketAddressType address) { + switch (address->SockAddr()->sa_family) { + case AF_INET: + return ntohs(reinterpret_cast<sockaddr_in*>(address->SockAddr())->sin_port); + case AF_INET6: + return ntohs(reinterpret_cast<sockaddr_in6*>(address->SockAddr())->sin6_port); + } + return {}; + } + + static void SetPort(SocketAddressType address, int port) { + switch (address->SockAddr()->sa_family) { + case AF_INET: + reinterpret_cast<sockaddr_in*>(address->SockAddr())->sin_port = htons(port); + break; + case AF_INET6: + reinterpret_cast<sockaddr_in6*>(address->SockAddr())->sin6_port = htons(port); + break; + } + } + 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); + if (GetPort(Address) == 0) { + SetPort(Address, Request->Secure ? 443 : 80); } Connect(ctx); } @@ -205,13 +239,12 @@ protected: LastActivity = ctx.Now(); Request = std::move(event->Get()->Request); Host = Request->Host; - LOG_DEBUG_S(ctx, HttpLog, "(#" << TSocketImpl::GetRawSocket() << ") resolving " << Host); + LOG_DEBUG_S(ctx, HttpLog, GetSocketName() << "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(); @@ -220,12 +253,12 @@ protected: void HandleConnected(NActors::TEvPollerReady::TPtr event, const NActors::TActorContext& ctx) { LastActivity = ctx.Now(); - if (event->Get()->Read) { - PullInput(ctx); - } - if (event->Get()->Write) { + if (event->Get()->Write && RequestOwner) { FlushOutput(ctx); } + if (event->Get()->Read && RequestOwner) { + PullInput(ctx); + } } void HandleConnected(NActors::TEvPollerRegisterResult::TPtr ev, const NActors::TActorContext& ctx) { diff --git a/library/cpp/actors/http/http_proxy_sock64.h b/library/cpp/actors/http/http_proxy_sock64.h new file mode 100644 index 0000000000..7f64a0f353 --- /dev/null +++ b/library/cpp/actors/http/http_proxy_sock64.h @@ -0,0 +1,79 @@ +#pragma once +#include <util/network/sock.h> +#include "http.h" + +class TInet64StreamSocket: public TStreamSocket { +protected: + TInet64StreamSocket(const TInet64StreamSocket& parent, SOCKET fd) + : TStreamSocket(fd) + , AF(parent.AF) + { + } + +public: + TInet64StreamSocket(int af = {}) { + CreateSocket(af); + } + + std::shared_ptr<ISockAddr> MakeAddress(const TString& address, int port) { + if (!address) { + if (AF == AF_INET6) { + return std::make_shared<TSockAddrInet6>("::", port); + } else { + return std::make_shared<TSockAddrInet>(INADDR_ANY, port); + } + } + if (NHttp::IsIPv6(address)) { + return std::make_shared<TSockAddrInet6>(address.data(), port); + } else { + return std::make_shared<TSockAddrInet>(address.data(), port); + } + } + + static std::shared_ptr<ISockAddr> MakeAddress(const sockaddr_storage& storage) { + std::shared_ptr<ISockAddr> addr; + switch (storage.ss_family) { + case AF_INET: + addr = std::make_shared<TSockAddrInet>(); + break; + case AF_INET6: + addr = std::make_shared<TSockAddrInet6>(); + break; + } + if (addr) { + memcpy(addr->SockAddr(), &storage, addr->Size()); + } + return addr; + } + + std::optional<TInet64StreamSocket> Accept(std::shared_ptr<ISockAddr>& acceptedAddr) { + sockaddr_storage addrStorage = {}; + socklen_t addrLen = sizeof(addrStorage); + SOCKET s = accept((SOCKET)*this, reinterpret_cast<sockaddr*>(&addrStorage), &addrLen); + if (s == INVALID_SOCKET) { + return {}; + } + acceptedAddr = MakeAddress(addrStorage); + return TInet64StreamSocket(*this, s); + } + +protected: + int AF = 0; + + void CreateSocket(int af) { + SOCKET s; + if (af == 0) { + s = socket(AF = AF_INET6, SOCK_STREAM, 0); + if (s < 0) { + s = socket(AF = AF_INET, SOCK_STREAM, 0); + } + } else { + s = socket(AF = af, SOCK_STREAM, 0); + } + if (AF == AF_INET6) { + SetSockOpt(s, SOL_SOCKET, IPV6_V6ONLY, (int)false); + } + TSocketHolder sock(s); + sock.Swap(*this); + } +}; diff --git a/library/cpp/actors/http/http_proxy_sock_impl.h b/library/cpp/actors/http/http_proxy_sock_impl.h index bd0a079dae..b5fd429462 100644 --- a/library/cpp/actors/http/http_proxy_sock_impl.h +++ b/library/cpp/actors/http/http_proxy_sock_impl.h @@ -8,9 +8,11 @@ namespace NHttp { struct TPlainSocketImpl : virtual public THttpConfig { TIntrusivePtr<TSocketDescriptor> Socket; - TPlainSocketImpl() - : Socket(new TSocketDescriptor()) - {} + TPlainSocketImpl() = default; + + void Create(int af) { + Socket = new TSocketDescriptor(af); + } TPlainSocketImpl(TIntrusivePtr<TSocketDescriptor> socket) : Socket(std::move(socket)) @@ -41,8 +43,8 @@ struct TPlainSocketImpl : virtual public THttpConfig { ::shutdown(Socket->Socket, SHUT_RDWR); } - int Connect(const SocketAddressType& address) { - return Socket->Socket.Connect(&address); + int Connect(SocketAddressType address) { + return Socket->Socket.Connect(address.get()); } static constexpr int OnConnect(bool&, bool&) { diff --git a/library/cpp/actors/http/http_ut.cpp b/library/cpp/actors/http/http_ut.cpp index e87de6b6e1..ad3b49e869 100644 --- a/library/cpp/actors/http/http_ut.cpp +++ b/library/cpp/actors/http/http_ut.cpp @@ -186,12 +186,46 @@ Y_UNIT_TEST_SUITE(HttpProxy) { 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) { + Y_UNIT_TEST(BasicRunning4) { NActors::TTestActorRuntimeBase actorSystem; TPortManager portManager; TIpPort port = portManager.GetTcpPort(); TAutoPtr<NActors::IEventHandle> handle; actorSystem.Initialize(); + actorSystem.SetLogPriority(NActorsServices::HTTP, NActors::NLog::PRI_DEBUG); + + NActors::IActor* proxy = NHttp::CreateHttpProxy(); + 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://127.0.0.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(BasicRunning6) { + NActors::TTestActorRuntimeBase actorSystem; + TPortManager portManager; + TIpPort port = portManager.GetTcpPort(); + TAutoPtr<NActors::IEventHandle> handle; + actorSystem.Initialize(); + actorSystem.SetLogPriority(NActorsServices::HTTP, NActors::NLog::PRI_DEBUG); NActors::IActor* proxy = NHttp::CreateHttpProxy(); NActors::TActorId proxyId = actorSystem.Register(proxy); |