httpagent.h 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #pragma once
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <cstdlib>
  5. #include <library/cpp/uri/http_url.h>
  6. #include <util/datetime/base.h>
  7. #include <util/network/hostip.h>
  8. #include <util/network/ip.h>
  9. #include <util/network/sock.h>
  10. #include <util/generic/scope.h>
  11. #include <util/generic/utility.h>
  12. #include <util/string/cast.h>
  13. #include "exthttpcodes.h"
  14. #include "sockhandler.h"
  15. class TIpResolver {
  16. public:
  17. TAddrList Resolve(const char* host, TIpPort port) const {
  18. try {
  19. TAddrList result;
  20. TNetworkAddress na(host, port);
  21. for (auto i = na.Begin(); i != na.End(); ++i) {
  22. const struct addrinfo& ai = *i;
  23. switch (ai.ai_family) {
  24. case AF_INET:
  25. result.push_back(new NAddr::TIPv4Addr(*(sockaddr_in*)ai.ai_addr));
  26. break;
  27. case AF_INET6:
  28. result.push_back(new NAddr::TIPv6Addr(*(sockaddr_in6*)ai.ai_addr));
  29. break;
  30. }
  31. }
  32. return result;
  33. } catch (const TNetworkResolutionError&) {
  34. }
  35. return TAddrList();
  36. }
  37. };
  38. namespace NResolverHelpers {
  39. Y_HAS_MEMBER(Resolve);
  40. template <typename TResolver>
  41. std::enable_if_t<TClassHasResolve<TResolver>::value, TAddrList> Resolve(const TResolver& r, const char* host, TIpPort port) {
  42. return r.Resolve(host, port);
  43. }
  44. template <typename TResolver>
  45. std::enable_if_t<!TClassHasResolve<TResolver>::value, TAddrList> Resolve(const TResolver& r, const char* host, TIpPort port) {
  46. ui32 ip = 0;
  47. if (r.GetHostIP(host, &ip)) {
  48. // error
  49. return TAddrList();
  50. }
  51. if (!ip) {
  52. return TAddrList();
  53. }
  54. return TAddrList::MakeV4Addr(ip, port);
  55. }
  56. }
  57. template <typename TBase>
  58. class TIpResolverWrapper {
  59. private:
  60. TBase Base;
  61. public:
  62. TIpResolverWrapper() = default;
  63. template <typename T>
  64. TIpResolverWrapper(T&& base)
  65. : Base(std::forward(base))
  66. {
  67. }
  68. TAddrList Resolve(const char* host, TIpPort port) const {
  69. return NResolverHelpers::Resolve(Base, host, port);
  70. }
  71. };
  72. template <class TSocketHandler = TSimpleSocketHandler, class TDnsClient = TIpResolver>
  73. class THttpAgent {
  74. public:
  75. THttpAgent()
  76. : Persistent(0)
  77. , Timeout(TDuration::MicroSeconds(150))
  78. , Hostheader(nullptr)
  79. , Footer(nullptr)
  80. , AltFooter(nullptr)
  81. , PostData(nullptr)
  82. , PostDataLen(0)
  83. , Method(nullptr)
  84. , MethodLen(0)
  85. , HostheaderLen(0)
  86. {
  87. SetIdentification("YandexSomething/1.0", "webadmin@yandex.ru");
  88. }
  89. ~THttpAgent() {
  90. Disconnect();
  91. free(Hostheader);
  92. free(Footer);
  93. }
  94. void SetIdentification(const char* user_agent, const char* http_from) {
  95. free(Footer);
  96. size_t len = user_agent ? strlen(user_agent) + 15 : 0;
  97. len += http_from ? strlen(http_from) + 9 : 0;
  98. len += 3;
  99. Footer = (char*)malloc(len);
  100. if (user_agent)
  101. strcat(strcat(strcpy(Footer, "User-Agent: "), user_agent), "\r\n");
  102. if (http_from)
  103. strcat(strcat(strcat(Footer, "From: "), http_from), "\r\n");
  104. }
  105. void SetUserAgentFooter(const char* altFooter) {
  106. AltFooter = altFooter;
  107. }
  108. void SetPostData(const char* postData, size_t postDataLen) {
  109. PostData = postData;
  110. PostDataLen = postDataLen;
  111. }
  112. void SetMethod(const char* method, size_t methodLen) {
  113. Method = method;
  114. MethodLen = methodLen;
  115. }
  116. // deprecated
  117. ui32 GetIp() const {
  118. return Addrs.GetV4Addr().first;
  119. }
  120. int GetScheme() const {
  121. return THttpURL::SchemeHTTP;
  122. }
  123. void SetTimeout(TDuration tim) {
  124. Timeout = tim;
  125. }
  126. void SetConnectTimeout(TDuration timeout) {
  127. ConnectTimeout = timeout;
  128. }
  129. int Disconnected() {
  130. return !Persistent || !Socket.Good();
  131. }
  132. int SetHost(const char* hostname, TIpPort port) {
  133. Disconnect();
  134. TAddrList addrs = DnsClient.Resolve(hostname, port);
  135. if (!addrs.size()) {
  136. return 1;
  137. }
  138. SetHost(hostname, port, addrs);
  139. return 0;
  140. }
  141. int SetHost(const char* hostname, TIpPort port, const TAddrList& addrs) {
  142. Disconnect();
  143. Addrs = addrs;
  144. size_t reqHostheaderLen = strlen(hostname) + 20;
  145. if (HostheaderLen < reqHostheaderLen) {
  146. free(Hostheader);
  147. Hostheader = (char*)malloc((HostheaderLen = reqHostheaderLen));
  148. }
  149. if (port == 80)
  150. snprintf(Hostheader, HostheaderLen, "Host: %s\r\n", hostname);
  151. else
  152. snprintf(Hostheader, HostheaderLen, "Host: %s:%u\r\n", hostname, port);
  153. pHostBeg = strchr(Hostheader, ' ') + 1;
  154. pHostEnd = strchr(pHostBeg, '\r');
  155. // convert hostname to lower case since some web server don't like
  156. // uppper case (Task ROBOT-562)
  157. for (char* p = pHostBeg; p < pHostEnd; p++)
  158. *p = tolower(*p);
  159. return 0;
  160. }
  161. // deprecated v4-only
  162. int SetHost(const char* hostname, TIpPort port, ui32 ip) {
  163. return SetHost(hostname, port, TAddrList::MakeV4Addr(ip, port));
  164. }
  165. void SetHostHeader(const char* host) {
  166. size_t reqHostheaderLen = strlen(host) + 20;
  167. if (HostheaderLen < reqHostheaderLen) {
  168. delete[] Hostheader;
  169. Hostheader = new char[(HostheaderLen = reqHostheaderLen)];
  170. }
  171. snprintf(Hostheader, HostheaderLen, "Host: %s\r\n", host);
  172. }
  173. void SetSocket(SOCKET fd) {
  174. Socket.SetSocket(fd);
  175. }
  176. SOCKET PickOutSocket() {
  177. return Socket.PickOutSocket();
  178. }
  179. void Disconnect() {
  180. Socket.Disconnect();
  181. }
  182. ssize_t read(void* buffer, size_t buflen) {
  183. return Socket.read(buffer, buflen);
  184. }
  185. int RequestGet(const char* url, const char* const* headers, int persistent = 1, bool head_request = false) {
  186. if (!Addrs.size())
  187. return HTTP_DNS_FAILURE;
  188. char message[MessageMax];
  189. ssize_t messlen = 0;
  190. if (Method) {
  191. strncpy(message, Method, MethodLen);
  192. message[MethodLen] = ' ';
  193. messlen = MethodLen + 1;
  194. } else if (PostData) {
  195. strcpy(message, "POST ");
  196. messlen = 5;
  197. } else if (head_request) {
  198. strcpy(message, "HEAD ");
  199. messlen = 5;
  200. } else {
  201. strcpy(message, "GET ");
  202. messlen = 4;
  203. }
  204. #define _AppendMessage(mes) messlen += Min(MessageMax - messlen, \
  205. (ssize_t)strlcpy(message + messlen, (mes), MessageMax - messlen))
  206. _AppendMessage(url);
  207. _AppendMessage(" HTTP/1.1\r\n");
  208. if (*url == '/') //if not then Host is a proxy
  209. _AppendMessage(Hostheader);
  210. _AppendMessage("Connection: ");
  211. _AppendMessage(persistent ? "Keep-Alive\r\n" : "Close\r\n");
  212. while (headers && *headers)
  213. _AppendMessage(*headers++);
  214. if (AltFooter)
  215. _AppendMessage(AltFooter);
  216. else
  217. _AppendMessage(Footer);
  218. _AppendMessage("\r\n");
  219. #undef _AppendMessage
  220. if (messlen >= MessageMax)
  221. return HTTP_HEADER_TOO_LARGE;
  222. if (!Persistent)
  223. Disconnect();
  224. Persistent = persistent;
  225. int connected = Socket.Good();
  226. for (int attempt = !connected; attempt < 2; attempt++) {
  227. const auto connectTimeout = ConnectTimeout ? ConnectTimeout : Timeout;
  228. if (!Socket.Good() && Socket.Connect(Addrs, connectTimeout))
  229. return HTTP_CONNECT_FAILED;
  230. int sendOk = Socket.send(message, messlen);
  231. if (sendOk && PostData && PostDataLen)
  232. sendOk = Socket.send(PostData, PostDataLen);
  233. if (!sendOk) {
  234. int err = errno;
  235. Disconnect();
  236. errno = err;
  237. continue;
  238. }
  239. if (!Socket.peek()) {
  240. int err = errno;
  241. Disconnect();
  242. if (err == EINTR) {
  243. errno = err;
  244. return HTTP_INTERRUPTED;
  245. }
  246. } else {
  247. if (!persistent)
  248. Socket.shutdown();
  249. return 0;
  250. }
  251. }
  252. return connected ? HTTP_CONNECTION_LOST : HTTP_CONNECT_FAILED;
  253. }
  254. protected:
  255. TSocketHandler Socket;
  256. TIpResolverWrapper<TDnsClient> DnsClient;
  257. TAddrList Addrs;
  258. int Persistent;
  259. TDuration Timeout;
  260. TDuration ConnectTimeout;
  261. char *Hostheader, *Footer, *pHostBeg, *pHostEnd;
  262. const char* AltFooter; // alternative footer can be set by the caller
  263. const char* PostData;
  264. size_t PostDataLen;
  265. const char* Method;
  266. size_t MethodLen;
  267. unsigned short HostheaderLen;
  268. static const ssize_t MessageMax = 65536;
  269. };
  270. struct TNoTimer {
  271. inline void OnBeforeSend() {
  272. }
  273. inline void OnAfterSend() {
  274. }
  275. inline void OnBeforeRecv() {
  276. }
  277. inline void OnAfterRecv() {
  278. }
  279. };