123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- #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)
- snprintf(Hostheader, HostheaderLen, "Host: %s\r\n", hostname);
- else
- snprintf(Hostheader, HostheaderLen, "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)];
- }
- snprintf(Hostheader, HostheaderLen, "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 = 65536;
- };
- struct TNoTimer {
- inline void OnBeforeSend() {
- }
- inline void OnAfterSend() {
- }
- inline void OnBeforeRecv() {
- }
- inline void OnAfterRecv() {
- }
- };
|