diff options
author | qrort <qrort@yandex-team.com> | 2022-11-30 23:47:12 +0300 |
---|---|---|
committer | qrort <qrort@yandex-team.com> | 2022-11-30 23:47:12 +0300 |
commit | 22f8ae0e3f5d68b92aecccdf96c1d841a0334311 (patch) | |
tree | bffa27765faf54126ad44bcafa89fadecb7a73d7 /library/cpp/http/fetch_gpl/httpagent.h | |
parent | 332b99e2173f0425444abb759eebcb2fafaa9209 (diff) | |
download | ydb-22f8ae0e3f5d68b92aecccdf96c1d841a0334311.tar.gz |
validate canons without yatest_common
Diffstat (limited to 'library/cpp/http/fetch_gpl/httpagent.h')
-rw-r--r-- | library/cpp/http/fetch_gpl/httpagent.h | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/library/cpp/http/fetch_gpl/httpagent.h b/library/cpp/http/fetch_gpl/httpagent.h new file mode 100644 index 0000000000..b77c246c4f --- /dev/null +++ b/library/cpp/http/fetch_gpl/httpagent.h @@ -0,0 +1,292 @@ +#pragma once + +#include <library/cpp/http/fetch/httpagent.h> + +template <class TSockHndl = TSimpleSocketHandler, + class TDnsClient = TIpResolver, + class TErrorLogger = TSslSocketBase::TFakeLogger, + class TTimer = TNoTimer, + template <class, class> class TSslSocketImpl = TSslSocketHandler> +class THttpsAgent: public TTimer { +public: + typedef TSslSocketImpl<TSockHndl, TErrorLogger> TSocket; + THttpsAgent() + : Socket(new TSocket) + , Scheme(0) + , Persistent(0) + , Timeout(TDuration::MicroSeconds(150)) + , Hostheader(nullptr) + , Footer(nullptr) + , pHostBeg(nullptr) + , pHostEnd(nullptr) + , AltFooter(nullptr) + , PostData(nullptr) + , PostDataLen(0) + , Method(nullptr) + , MethodLen(0) + , HostheaderLen(0) + { + SetIdentification("YandexSomething/1.0", "webadmin@yandex.ru"); + } + + ~THttpsAgent() { + Disconnect(); + delete[] Hostheader; + delete[] Footer; + } + + void SetIdentification(const char* userAgent, const char* httpFrom) { + Y_VERIFY(Socket.Get(), "HttpsAgent: socket is picked out. Can't use until a valid socket is set"); + delete[] Footer; + size_t len = userAgent ? strlen(userAgent) + 15 : 0; + len += httpFrom ? strlen(httpFrom) + 9 : 0; + len += 3; + Footer = new char[len]; + if (userAgent) + strcat(strcat(strcpy(Footer, "User-Agent: "), userAgent), "\r\n"); + if (httpFrom) + strcat(strcat(strcat(Footer, "From: "), httpFrom), "\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 Scheme; + } + + void SetTimeout(TDuration tim) { + Timeout = tim; + } + + void SetConnectTimeout(TDuration timeout) { + ConnectTimeout = timeout; + } + + int Disconnected() { + return !Persistent || !Socket.Get() || !Socket->Good(); + } + + int SetHost(const char* hostname, TIpPort port, int scheme = THttpURL::SchemeHTTP) { + TStringBuf host{hostname}; + if (host.StartsWith('[') && host.EndsWith(']')) { + TString tmp = ToString(host.Skip(1).Chop(1)); + TSockAddrInet6 sa(tmp.data(), port); + NAddr::IRemoteAddrRef addr = new NAddr::TIPv6Addr(sa); + SetHost(hostname, port, {addr}, scheme); + return 0; + } + TAddrList addrs = DnsClient.Resolve(hostname, port); + if (!addrs.size()) { + return 1; + } + SetHost(hostname, port, addrs, scheme); + return 0; + } + + int SetHost(const char* hostname, TIpPort port, const TAddrList& addrs, int scheme = THttpURL::SchemeHTTP) { + Disconnect(); + Addrs = addrs; + Scheme = scheme; + size_t reqHostheaderLen = strlen(hostname) + 20; + if (HostheaderLen < reqHostheaderLen) { + delete[] Hostheader; + Hostheader = new char[(HostheaderLen = reqHostheaderLen)]; + } + if (Scheme == THttpURL::SchemeHTTPS && port == 443 || 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); + SocketCtx.Host = pHostBeg; + SocketCtx.HostLen = pHostEnd - pHostBeg; + return 0; + } + + // deprecated v4-only version + int SetHost(const char* hostname, TIpPort port, ui32 ip, int scheme = THttpURL::SchemeHTTP, TIpPort connPort = 0) { + connPort = connPort ? connPort : port; + return SetHost(hostname, port, TAddrList::MakeV4Addr(ip, connPort), scheme); + } + + 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); + pHostBeg = strchr(Hostheader, ' ') + 1; + pHostEnd = strchr(pHostBeg, '\r'); + SocketCtx.Host = pHostBeg; + SocketCtx.HostLen = pHostEnd - pHostBeg; + } + + void SetSocket(TSocket* s) { + Y_VERIFY(s, "HttpsAgent: socket handler is null"); + SocketCtx.FreeBuffers(); + if (s->HasSsl()) + SocketCtx.AllocBuffers(); + Socket.Reset(s); + } + + void SetPersistent(const bool value) { + Persistent = value; + } + + TSocket* PickOutSocket() { + SocketCtx.FreeBuffers(); + SocketCtx.CachedSession.Destroy(); + return Socket.Release(); + } + + void Disconnect() { + if (Socket.Get()) + Socket->Disconnect(); + SocketCtx.FreeBuffers(); + SocketCtx.CachedSession.Destroy(); + } + + ssize_t read(void* buffer, size_t buflen) { + Y_VERIFY(Socket.Get(), "HttpsAgent: socket is picked out. Can't use until a valid socket is set"); + ssize_t ret = Socket->read(&SocketCtx, buffer, buflen); + TTimer::OnAfterRecv(); + return ret; + } + + int RequestGet(const char* url, const char* const* headers, int persistent = 1, bool head_request = false) { + Y_VERIFY(Socket.Get(), "HttpsAgent: socket is picked out. Can't use until a valid socket is set"); + 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"); + _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) + Socket->Disconnect(&SocketCtx); + Persistent = persistent; + int connected = Socket->Good(); + SocketCtx.FreeBuffers(); + if (Scheme == THttpURL::SchemeHTTPS) { + SocketCtx.AllocBuffers(); + } + + bool success = false; + Y_SCOPE_EXIT(&success, this) { if (!success) { this->SocketCtx.FreeBuffers(); }; }; + + TTimer::OnBeforeSend(); + for (int attempt = !connected; attempt < 2; attempt++) { + const auto connectTimeout = ConnectTimeout ? ConnectTimeout : Timeout; + if (!Socket->Good() && Socket->Connect(&SocketCtx, Addrs, connectTimeout , Scheme == THttpURL::SchemeHTTPS, true)) { + return SocketCtx.SslError ? HTTP_SSL_ERROR : HTTP_CONNECT_FAILED; + } else { // We successfully connected + connected = true; + } + + int sendOk = Socket->send(&SocketCtx, message, messlen); + if (sendOk && PostData && PostDataLen) + sendOk = Socket->send(&SocketCtx, PostData, PostDataLen); + if (!sendOk) { + int err = errno; + Socket->Disconnect(&SocketCtx); + errno = err; + continue; + } + TTimer::OnAfterSend(); + + if (!Socket->peek(&SocketCtx)) { + int err = errno; + Socket->Disconnect(&SocketCtx); + if (err == EINTR) { + errno = err; + return HTTP_INTERRUPTED; + } + if (err == ETIMEDOUT) { + errno = err; + return HTTP_TIMEDOUT_WHILE_BYTES_RECEIVING; + } + } else { + TTimer::OnBeforeRecv(); + if (!persistent) { + Socket->shutdown(); + } + success = true; + return 0; + } + } + return SocketCtx.SslError ? HTTP_SSL_ERROR : (connected ? HTTP_CONNECTION_LOST : HTTP_CONNECT_FAILED); + } + + ui16 CertCheckErrors() const { + return SocketCtx.CertErrors; + } + +protected: + THolder<TSocket> Socket; + typename TSocket::TSocketCtx SocketCtx; + TIpResolverWrapper<TDnsClient> DnsClient; + TAddrList Addrs; + int Scheme; + 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 = 65536; +}; |