aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/http/fetch_gpl/httpagent.h
diff options
context:
space:
mode:
authorqrort <qrort@yandex-team.com>2022-11-30 23:47:12 +0300
committerqrort <qrort@yandex-team.com>2022-11-30 23:47:12 +0300
commit22f8ae0e3f5d68b92aecccdf96c1d841a0334311 (patch)
treebffa27765faf54126ad44bcafa89fadecb7a73d7 /library/cpp/http/fetch_gpl/httpagent.h
parent332b99e2173f0425444abb759eebcb2fafaa9209 (diff)
downloadydb-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.h292
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;
+};