http.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. // $Id: http.cc,v 1.59 2004/05/08 19:42:35 mdz Exp $
  4. /* ######################################################################
  5. HTTP Acquire Method - This is the HTTP acquire method for APT.
  6. It uses HTTP/1.1 and many of the fancy options there-in, such as
  7. pipelining, range, if-range and so on.
  8. It is based on a doubly buffered select loop. A groupe of requests are
  9. fed into a single output buffer that is constantly fed out the
  10. socket. This provides ideal pipelining as in many cases all of the
  11. requests will fit into a single packet. The input socket is buffered
  12. the same way and fed into the fd for the file (may be a pipe in future).
  13. This double buffering provides fairly substantial transfer rates,
  14. compared to wget the http method is about 4% faster. Most importantly,
  15. when HTTP is compared with FTP as a protocol the speed difference is
  16. huge. In tests over the internet from two sites to llug (via ATM) this
  17. program got 230k/s sustained http transfer rates. FTP on the other
  18. hand topped out at 170k/s. That combined with the time to setup the
  19. FTP connection makes HTTP a vastly superior protocol.
  20. ##################################################################### */
  21. /*}}}*/
  22. // Include Files /*{{{*/
  23. // #include <config.h>
  24. #include <apt-pkg/fileutl.h>
  25. #include <apt-pkg/configuration.h>
  26. #include <apt-pkg/error.h>
  27. #include <apt-pkg/hashes.h>
  28. #include <apt-pkg/netrc.h>
  29. #include <apt-pkg/strutl.h>
  30. #include <apt-pkg/proxy.h>
  31. #include <stddef.h>
  32. #include <stdlib.h>
  33. #include <sys/select.h>
  34. #include <cstring>
  35. #include <sys/sysctl.h>
  36. #include <sys/stat.h>
  37. #include <sys/time.h>
  38. #include <unistd.h>
  39. #include <stdio.h>
  40. #include <errno.h>
  41. #include <arpa/inet.h>
  42. #include <iostream>
  43. #include <sstream>
  44. #include "config.h"
  45. #include "connect.h"
  46. #include "http.h"
  47. // #include <apti18n.h>
  48. #include <netdb.h>
  49. #include <dlfcn.h>
  50. #include <lockdown.h>
  51. #include <CoreFoundation/CoreFoundation.h>
  52. #include <CFNetwork/CFNetwork.h>
  53. extern "C" CFDictionaryRef SCDynamicStoreCopyProxies(void *);
  54. /*}}}*/
  55. using namespace std;
  56. #define _(str) str
  57. CFStringRef Firmware_;
  58. const char *Machine_;
  59. CFStringRef UniqueID_;
  60. void CfrsError(const char *name, CFReadStreamRef rs) {
  61. CFStreamError se = CFReadStreamGetError(rs);
  62. if (se.domain == kCFStreamErrorDomainCustom) {
  63. } else if (se.domain == kCFStreamErrorDomainPOSIX) {
  64. _error->Error("POSIX: %s", strerror(se.error));
  65. } else if (se.domain == kCFStreamErrorDomainMacOSStatus) {
  66. _error->Error("MacOSStatus: %d", (int)se.error);
  67. } else if (se.domain == kCFStreamErrorDomainNetDB) {
  68. _error->Error("NetDB: %s %s", name, gai_strerror(se.error));
  69. } else if (se.domain == kCFStreamErrorDomainMach) {
  70. _error->Error("Mach: %d", (int)se.error);
  71. } else if (se.domain == kCFStreamErrorDomainHTTP) {
  72. switch (se.error) {
  73. case kCFStreamErrorHTTPParseFailure:
  74. _error->Error("Parse failure");
  75. break;
  76. case kCFStreamErrorHTTPRedirectionLoop:
  77. _error->Error("Redirection loop");
  78. break;
  79. case kCFStreamErrorHTTPBadURL:
  80. _error->Error("Bad URL");
  81. break;
  82. default:
  83. _error->Error("Unknown HTTP error: %d", (int)se.error);
  84. break;
  85. }
  86. } else if (se.domain == kCFStreamErrorDomainSOCKS) {
  87. _error->Error("SOCKS: %d", (int)se.error);
  88. } else if (se.domain == kCFStreamErrorDomainSystemConfiguration) {
  89. _error->Error("SystemConfiguration: %d", (int)se.error);
  90. } else if (se.domain == kCFStreamErrorDomainSSL) {
  91. _error->Error("SSL: %d", (int)se.error);
  92. } else {
  93. _error->Error("Domain #%d: %d", (int)se.domain, (int)se.error);
  94. }
  95. }
  96. unsigned long TimeOut = 120;
  97. static const CFOptionFlags kNetworkEvents =
  98. kCFStreamEventOpenCompleted |
  99. kCFStreamEventHasBytesAvailable |
  100. kCFStreamEventEndEncountered |
  101. kCFStreamEventErrorOccurred |
  102. 0;
  103. static void CFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType event, void *arg) {
  104. switch (event) {
  105. case kCFStreamEventOpenCompleted:
  106. break;
  107. case kCFStreamEventHasBytesAvailable:
  108. case kCFStreamEventEndEncountered:
  109. *reinterpret_cast<int *>(arg) = 1;
  110. CFRunLoopStop(CFRunLoopGetCurrent());
  111. break;
  112. case kCFStreamEventErrorOccurred:
  113. *reinterpret_cast<int *>(arg) = -1;
  114. CFRunLoopStop(CFRunLoopGetCurrent());
  115. break;
  116. }
  117. }
  118. /* http://lists.apple.com/archives/Macnetworkprog/2006/Apr/msg00014.html */
  119. int CFReadStreamOpen(CFReadStreamRef stream, double timeout) {
  120. CFStreamClientContext context;
  121. int value(0);
  122. memset(&context, 0, sizeof(context));
  123. context.info = &value;
  124. if (CFReadStreamSetClient(stream, kNetworkEvents, CFReadStreamCallback, &context)) {
  125. CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
  126. if (CFReadStreamOpen(stream))
  127. CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, false);
  128. else
  129. value = -1;
  130. CFReadStreamSetClient(stream, kCFStreamEventNone, NULL, NULL);
  131. }
  132. return value;
  133. }
  134. // HttpMethod::SendReq - Send the HTTP request /*{{{*/
  135. // ---------------------------------------------------------------------
  136. /* This places the http request in the outbound buffer */
  137. void HttpMethod::SendReq(FetchItem *Itm)
  138. {
  139. }
  140. /*}}}*/
  141. std::unique_ptr<ServerState> HttpMethod::CreateServerState(URI const &uri)/*{{{*/
  142. {
  143. return NULL;
  144. }
  145. /*}}}*/
  146. void HttpMethod::RotateDNS() /*{{{*/
  147. {
  148. }
  149. /*}}}*/
  150. ServerMethod::DealWithHeadersResult HttpMethod::DealWithHeaders(FetchResult &Res)/*{{{*/
  151. {
  152. auto ret = ServerMethod::DealWithHeaders(Res);
  153. if (ret != ServerMethod::FILE_IS_OPEN)
  154. return ret;
  155. // Open the file
  156. delete File;
  157. File = new FileFd(Queue->DestFile,FileFd::WriteAny);
  158. if (_error->PendingError() == true)
  159. return ERROR_NOT_FROM_SERVER;
  160. FailFile = Queue->DestFile;
  161. FailFile.c_str(); // Make sure we don't do a malloc in the signal handler
  162. FailFd = File->Fd();
  163. FailTime = Server->Date;
  164. if (Server->InitHashes(Queue->ExpectedHashes) == false || Server->AddPartialFileToHashes(*File) == false)
  165. {
  166. _error->Errno("read",_("Problem hashing file"));
  167. return ERROR_NOT_FROM_SERVER;
  168. }
  169. if (Server->StartPos > 0)
  170. Res.ResumePoint = Server->StartPos;
  171. SetNonBlock(File->Fd(),true);
  172. return FILE_IS_OPEN;
  173. }
  174. // HttpMethod::Loop - Main loop /*{{{*/
  175. int HttpMethod::Loop()
  176. {
  177. signal(SIGTERM,SigTerm);
  178. signal(SIGINT,SigTerm);
  179. Server = 0;
  180. std::set<std::string> cached;
  181. int FailCounter = 0;
  182. while (1)
  183. {
  184. // We have no commands, wait for some to arrive
  185. if (Queue == 0)
  186. {
  187. if (WaitFd(STDIN_FILENO) == false)
  188. return 0;
  189. }
  190. /* Run messages, we can accept 0 (no message) if we didn't
  191. do a WaitFd above.. Otherwise the FD is closed. */
  192. int Result = Run(true);
  193. if (Result != -1 && (Result != 0 || Queue == 0))
  194. {
  195. if(FailReason.empty() == false ||
  196. ConfigFindB("DependOnSTDIN", true) == true)
  197. return 100;
  198. else
  199. return 0;
  200. }
  201. if (Queue == 0)
  202. continue;
  203. CFStringEncoding se = kCFStringEncodingUTF8;
  204. URI uri2 = Queue->Uri;
  205. string uriString = static_cast<string>(uri2);
  206. char *url = strdup(uriString.c_str());
  207. url:
  208. URI uri = std::string(url);
  209. std::string hs = uri.Host;
  210. if (cached.find(hs) != cached.end()) {
  211. _error->Error("Cached Failure");
  212. Fail(true);
  213. free(url);
  214. FailCounter = 0;
  215. continue;
  216. }
  217. std::string urs = uri;
  218. for (;;) {
  219. size_t bad = urs.find_first_of("+");
  220. if (bad == std::string::npos)
  221. break;
  222. // XXX: generalize
  223. urs = urs.substr(0, bad) + "%2b" + urs.substr(bad + 1);
  224. }
  225. CFStringRef sr = CFStringCreateWithCString(kCFAllocatorDefault, urs.c_str(), se);
  226. CFURLRef ur = CFURLCreateWithString(kCFAllocatorDefault, sr, NULL);
  227. CFRelease(sr);
  228. CFHTTPMessageRef hm = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), ur, kCFHTTPVersion1_1);
  229. CFRelease(ur);
  230. struct stat SBuf;
  231. if (stat(Queue->DestFile.c_str(), &SBuf) >= 0 && SBuf.st_size > 0) {
  232. sr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("bytes=%li-"), (long) SBuf.st_size - 1);
  233. CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Range"), sr);
  234. CFRelease(sr);
  235. sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(SBuf.st_mtime, false).c_str(), se);
  236. CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("If-Range"), sr);
  237. CFRelease(sr);
  238. CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("no-cache"));
  239. } else if (Queue->LastModified != 0) {
  240. sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(Queue->LastModified, true).c_str(), se);
  241. CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("If-Modified-Since"), sr);
  242. CFRelease(sr);
  243. CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("no-cache"));
  244. } else
  245. CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("max-age=0"));
  246. if (Firmware_ != NULL)
  247. CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Firmware"), Firmware_);
  248. sr = CFStringCreateWithCString(kCFAllocatorDefault, Machine_, se);
  249. CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Machine"), sr);
  250. CFRelease(sr);
  251. if (UniqueID_ != NULL)
  252. CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Unique-ID"), UniqueID_);
  253. CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("User-Agent"), CFSTR("Telesphoreo APT-HTTP/1.0.592"));
  254. CFReadStreamRef rs = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, hm);
  255. CFRelease(hm);
  256. #define _kCFStreamPropertyReadTimeout CFSTR("_kCFStreamPropertyReadTimeout")
  257. #define _kCFStreamPropertyWriteTimeout CFSTR("_kCFStreamPropertyWriteTimeout")
  258. #define _kCFStreamPropertySocketImmediateBufferTimeOut CFSTR("_kCFStreamPropertySocketImmediateBufferTimeOut")
  259. /*SInt32 to(TimeOut);
  260. CFNumberRef nm(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &to));os_log(OS_LOG_DEFAULT, "[%{public}s:%{public}d]",__BASE_FILE__,__LINE__);*/
  261. double to = TimeOut;
  262. CFNumberRef nm(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &to));
  263. CFReadStreamSetProperty(rs, _kCFStreamPropertyReadTimeout, nm);
  264. CFReadStreamSetProperty(rs, _kCFStreamPropertyWriteTimeout, nm);
  265. CFReadStreamSetProperty(rs, _kCFStreamPropertySocketImmediateBufferTimeOut, nm);
  266. CFRelease(nm);
  267. CFDictionaryRef dr = SCDynamicStoreCopyProxies(NULL);
  268. CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPProxy, dr);
  269. CFRelease(dr);
  270. //CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue);
  271. CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);
  272. FetchResult Res;
  273. CFIndex rd;
  274. UInt32 sc;
  275. uint8_t data[10240];
  276. size_t offset = 0;
  277. Status("Connecting to %s", hs.c_str());
  278. switch (CFReadStreamOpen(rs, to)) {
  279. case -1:
  280. CfrsError("Open", rs);
  281. goto fail;
  282. case 0:
  283. _error->Error("Host Unreachable");
  284. cached.insert(hs);
  285. goto fail;
  286. case 1:
  287. /* success */
  288. break;
  289. fail:
  290. Fail(true);
  291. goto done;
  292. }
  293. rd = CFReadStreamRead(rs, data, sizeof(data));
  294. if (rd == -1) {
  295. CfrsError(uri.Host.c_str(), rs);
  296. cached.insert(hs);
  297. Fail(true);
  298. goto done;
  299. }
  300. Res.Filename = Queue->DestFile;
  301. hm = (CFHTTPMessageRef) CFReadStreamCopyProperty(rs, kCFStreamPropertyHTTPResponseHeader);
  302. sc = CFHTTPMessageGetResponseStatusCode(hm);
  303. if (sc == 301 || sc == 302) {
  304. sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Location"));
  305. if (sr == NULL) {
  306. Fail();
  307. goto done_;
  308. } else {
  309. size_t ln = CFStringGetLength(sr) + 1;
  310. free(url);
  311. url = static_cast<char *>(malloc(ln));
  312. if (!CFStringGetCString(sr, url, ln, se)) {
  313. Fail();
  314. goto done_;
  315. }
  316. CFRelease(sr);
  317. goto url;
  318. }
  319. }
  320. sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Content-Range"));
  321. if (sr != NULL) {
  322. size_t ln = CFStringGetLength(sr) + 1;
  323. char cr[ln];
  324. if (!CFStringGetCString(sr, cr, ln, se)) {
  325. Fail();
  326. goto done_;
  327. }
  328. CFRelease(sr);
  329. if (sscanf(cr, "bytes %lu-%*u/%llu", &offset, &Res.Size) != 2) {
  330. _error->Error(_("The HTTP server sent an invalid Content-Range header"));
  331. Fail();
  332. goto done_;
  333. }
  334. if (offset > Res.Size) {
  335. _error->Error(_("This HTTP server has broken range support"));
  336. Fail();
  337. goto done_;
  338. }
  339. } else {
  340. sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Content-Length"));
  341. if (sr != NULL) {
  342. Res.Size = CFStringGetIntValue(sr);
  343. CFRelease(sr);
  344. }
  345. }
  346. time(&Res.LastModified);
  347. sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Last-Modified"));
  348. if (sr != NULL) {
  349. size_t ln = CFStringGetLength(sr) + 1;
  350. char cr[ln];
  351. if (!CFStringGetCString(sr, cr, ln, se)) {
  352. Fail();
  353. goto done_;
  354. }
  355. CFRelease(sr);
  356. if (!RFC1123StrToTime(cr, Res.LastModified)) {
  357. _error->Error(_("Unknown date format"));
  358. Fail();
  359. goto done_;
  360. }
  361. }
  362. if (sc < 200 || (sc >= 300 && sc != 304)) {
  363. sr = CFHTTPMessageCopyResponseStatusLine(hm);
  364. size_t ln = CFStringGetLength(sr) + 1;
  365. char cr[ln];
  366. if (!CFStringGetCString(sr, cr, ln, se)) {
  367. Fail();
  368. goto done;
  369. }
  370. CFRelease(sr);
  371. _error->Error("%s", cr);
  372. Fail();
  373. goto done_;
  374. }
  375. CFRelease(hm);
  376. if (sc == 304) {
  377. unlink(Queue->DestFile.c_str());
  378. Res.IMSHit = true;
  379. Res.LastModified = Queue->LastModified;
  380. URIDone(Res);
  381. } else {
  382. Hashes hash;
  383. File = new FileFd(Queue->DestFile, FileFd::WriteAny);
  384. if (_error->PendingError() == true) {
  385. delete File;
  386. File = NULL;
  387. Fail();
  388. goto done;
  389. }
  390. FailFile = Queue->DestFile;
  391. FailFile.c_str(); // Make sure we dont do a malloc in the signal handler
  392. FailFd = File->Fd();
  393. FailTime = Res.LastModified;
  394. Res.ResumePoint = offset;
  395. ftruncate(File->Fd(), offset);
  396. if (offset != 0) {
  397. lseek(File->Fd(), 0, SEEK_SET);
  398. if (!hash.AddFD(File->Fd(), offset)) {
  399. _error->Errno("read", _("Problem hashing file"));
  400. delete File;
  401. File = NULL;
  402. Fail();
  403. goto done;
  404. }
  405. }
  406. lseek(File->Fd(), 0, SEEK_END);
  407. URIStart(Res);
  408. read: if (rd == -1) {
  409. CfrsError("rd", rs);
  410. Fail(true);
  411. } else if (rd == 0) {
  412. if (Res.Size == 0)
  413. Res.Size = File->Size();
  414. // Timestamp
  415. struct timeval times[2];
  416. times[0].tv_sec = times[1].tv_sec = Res.LastModified;
  417. times[0].tv_usec = times[1].tv_usec = 0;
  418. utimes(Queue->DestFile.c_str(), times);
  419. Res.TakeHashes(hash);
  420. URIDone(Res);
  421. } else {
  422. hash.Add(data, rd);
  423. uint8_t *dt = data;
  424. while (rd != 0) {
  425. int sz = write(File->Fd(), dt, rd);
  426. if (sz == -1) {
  427. delete File;
  428. File = NULL;
  429. Fail();
  430. goto done;
  431. }
  432. dt += sz;
  433. rd -= sz;
  434. }
  435. rd = CFReadStreamRead(rs, data, sizeof(data));
  436. goto read;
  437. }
  438. }
  439. goto done;
  440. done_:
  441. CFRelease(hm);
  442. done:
  443. CFReadStreamClose(rs);
  444. CFRelease(rs);
  445. free(url);
  446. FailCounter = 0;
  447. }
  448. return 0;
  449. }
  450. HttpMethod::HttpMethod(std::string &&pProg) : ServerMethod(pProg.c_str(), "1.2", Pipeline | SendConfig)/*{{{*/
  451. {
  452. auto addName = std::inserter(methodNames, methodNames.begin());
  453. if (Binary != "http")
  454. addName = "http";
  455. auto const plus = Binary.find('+');
  456. if (plus != std::string::npos)
  457. addName = Binary.substr(0, plus);
  458. File = 0;
  459. Server = 0;
  460. }
  461. /*}}}*/
  462. int main(int, const char *argv[])
  463. {
  464. // ignore SIGPIPE, this can happen on write() if the socket
  465. // closes the connection (this is dealt with via ServerDie())
  466. signal(SIGPIPE, SIG_IGN);
  467. size_t size;
  468. sysctlbyname("hw.machine", NULL, &size, NULL, 0);
  469. char *machine = new char[size];
  470. sysctlbyname("hw.machine", machine, &size, NULL, 0);
  471. Machine_ = machine;
  472. const char *path = "/System/Library/CoreServices/SystemVersion.plist";
  473. CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (uint8_t *) path, strlen(path), false);
  474. CFPropertyListRef plist; {
  475. CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
  476. CFReadStreamOpen(stream);
  477. plist = CFPropertyListCreateFromStream(kCFAllocatorDefault, stream, 0, kCFPropertyListImmutable, NULL, NULL);
  478. CFReadStreamClose(stream);
  479. }
  480. CFRelease(url);
  481. if (plist != NULL) {
  482. Firmware_ = (CFStringRef) CFRetain(CFDictionaryGetValue((CFDictionaryRef) plist, CFSTR("ProductVersion")));
  483. CFRelease(plist);
  484. }
  485. if (UniqueID_ == NULL)
  486. if (void *libMobileGestalt = dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_GLOBAL | RTLD_LAZY))
  487. if (CFStringRef (*$MGCopyAnswer)(CFStringRef) = (CFStringRef (*)(CFStringRef)) dlsym(libMobileGestalt, "MGCopyAnswer"))
  488. UniqueID_ = $MGCopyAnswer(CFSTR("UniqueDeviceID"));
  489. if (UniqueID_ == NULL)
  490. if (void *lockdown = lockdown_connect()) {
  491. UniqueID_ = lockdown_copy_value(lockdown, NULL, kLockdownUniqueDeviceIDKey);
  492. lockdown_disconnect(lockdown);
  493. }
  494. std::string Binary = flNotDir(argv[0]);
  495. if (Binary.find('+') == std::string::npos && Binary != "http")
  496. Binary.append("+http");
  497. return HttpMethod(std::move(Binary)).Loop();
  498. }