1 /**
2  * @file http.cpp
3  * @brief Generic host HTTP I/O interface
4  *
5  * (c) 2013-2014 by Mega Limited, Auckland, New Zealand
6  *
7  * This file is part of the MEGA SDK - Client Access Engine.
8  *
9  * Applications using the MEGA API must present a valid application key
10  * and comply with the the rules set forth in the Terms of Service.
11  *
12  * The MEGA SDK is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15  *
16  * @copyright Simplified (2-clause) BSD License.
17  *
18  * You should have received a copy of the license along with this
19  * program.
20  */
21 
22 #include "mega/http.h"
23 #include "mega/megaclient.h"
24 #include "mega/logging.h"
25 #include "mega/proxy.h"
26 #include "mega/base64.h"
27 #include "mega/testhooks.h"
28 
29 #if defined(WIN32) && !defined(WINDOWS_PHONE)
30 #include <winhttp.h>
31 #endif
32 
33 #if defined(__APPLE__) && !(TARGET_OS_IPHONE)
34 #include "mega/osx/osxutils.h"
35 #endif
36 
37 namespace mega {
38 
39 // interval to calculate the mean speed (ds)
40 const int SpeedController::SPEED_MEAN_INTERVAL_DS = 50;
41 
42 // max time to calculate the mean speed
43 const int SpeedController::SPEED_MAX_VALUES = 10000;
44 
45 // data receive timeout (ds)
46 const int HttpIO::NETWORKTIMEOUT = 6000;
47 
48 // request timeout (ds)
49 const int HttpIO::REQUESTTIMEOUT = 1200;
50 
51 // wait request timeout (ds)
52 const int HttpIO::SCREQUESTTIMEOUT = 400;
53 
54 // connect timeout (ds)
55 const int HttpIO::CONNECTTIMEOUT = 120;
56 
57 #ifdef _WIN32
mega_inet_ntop(int af,const void * src,char * dst,int cnt)58 const char* mega_inet_ntop(int af, const void* src, char* dst, int cnt)
59 {
60     wchar_t ip[INET6_ADDRSTRLEN];
61     int len = INET6_ADDRSTRLEN;
62     int ret = 1;
63 
64     if (af == AF_INET)
65     {
66         struct sockaddr_in in = {};
67         in.sin_family = AF_INET;
68         memcpy(&in.sin_addr, src, sizeof(struct in_addr));
69         ret = WSAAddressToString((struct sockaddr*) &in, sizeof(struct sockaddr_in), 0, ip, (LPDWORD)&len);
70     }
71     else if (af == AF_INET6)
72     {
73         struct sockaddr_in6 in = {};
74         in.sin6_family = AF_INET6;
75         memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
76         ret = WSAAddressToString((struct sockaddr*) &in, sizeof(struct sockaddr_in6), 0, ip, (LPDWORD)&len);
77     }
78 
79     if (ret != 0)
80     {
81         return NULL;
82     }
83 
84     if (!WideCharToMultiByte(CP_UTF8, 0, ip, len, dst, cnt, NULL, NULL))
85     {
86         return NULL;
87     }
88 
89     return dst;
90 }
91 #endif
92 
HttpIO()93 HttpIO::HttpIO()
94 {
95     success = false;
96     noinetds = 0;
97     inetback = false;
98     lastdata = NEVER;
99     downloadSpeed = 0;
100     uploadSpeed = 0;
101 }
102 
103 // signal Internet status - if the Internet was down for more than one minute,
104 // set the inetback flag to trigger a reconnect
inetstatus(bool up)105 void HttpIO::inetstatus(bool up)
106 {
107     if (up)
108     {
109         if (noinetds && Waiter::ds - noinetds > 600)
110         {
111             inetback = true;
112         }
113 
114         noinetds = 0;
115     }
116     else if (!noinetds)
117     {
118         noinetds = Waiter::ds;
119     }
120 }
121 
122 // returns true once if an outage just ended
inetisback()123 bool HttpIO::inetisback()
124 {
125     if(inetback)
126     {
127         inetback = false;
128         return true;
129     }
130 
131     return false;
132 }
133 
updatedownloadspeed(m_off_t size)134 void HttpIO::updatedownloadspeed(m_off_t size)
135 {
136     downloadSpeed = downloadSpeedController.calculateSpeed(size);
137 }
138 
updateuploadspeed(m_off_t size)139 void HttpIO::updateuploadspeed(m_off_t size)
140 {
141     uploadSpeed = uploadSpeedController.calculateSpeed(size);
142 }
143 
getautoproxy()144 Proxy *HttpIO::getautoproxy()
145 {
146     Proxy* proxy = new Proxy();
147     proxy->setProxyType(Proxy::NONE);
148 
149 #if defined(WIN32) && !defined(WINDOWS_PHONE)
150     WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyConfig = { 0 };
151 
152     if (WinHttpGetIEProxyConfigForCurrentUser(&ieProxyConfig) == TRUE)
153     {
154         if (ieProxyConfig.lpszProxy)
155         {
156             string proxyURL;
157             proxy->setProxyType(Proxy::CUSTOM);
158             int len = static_cast<int>(wcslen(ieProxyConfig.lpszProxy));
159             proxyURL.assign((const char*)ieProxyConfig.lpszProxy, len * sizeof(wchar_t) + 1);
160 
161             // only save one proxy
162             for (int i = 0; i < len; i++)
163             {
164                 wchar_t* character = (wchar_t*)(proxyURL.data() + i * sizeof(wchar_t));
165 
166                 if (*character == ' ' || *character == ';')
167                 {
168                     proxyURL.resize(i*sizeof(wchar_t));
169                     len = i;
170                     break;
171                 }
172             }
173 
174             // remove protocol prefix, if any
175             for (int i = len - 1; i >= 0; i--)
176             {
177                 wchar_t* character = (wchar_t*)(proxyURL.data() + i * sizeof(wchar_t));
178 
179                 if (*character == '/' || *character == '=')
180                 {
181                     proxyURL = proxyURL.substr((i + 1) * sizeof(wchar_t));
182                     break;
183                 }
184             }
185 
186             proxy->setProxyURL(&proxyURL);
187         }
188         else if (ieProxyConfig.lpszAutoConfigUrl || ieProxyConfig.fAutoDetect == TRUE)
189         {
190             WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions;
191 
192             if (ieProxyConfig.lpszAutoConfigUrl)
193             {
194                 autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
195                 autoProxyOptions.lpszAutoConfigUrl = ieProxyConfig.lpszAutoConfigUrl;
196                 autoProxyOptions.dwAutoDetectFlags = 0;
197             }
198             else
199             {
200                 autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
201                 autoProxyOptions.lpszAutoConfigUrl = NULL;
202                 autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
203             }
204 
205             autoProxyOptions.fAutoLogonIfChallenged = TRUE;
206             autoProxyOptions.lpvReserved = NULL;
207             autoProxyOptions.dwReserved = 0;
208 
209             WINHTTP_PROXY_INFO proxyInfo;
210 
211             HINTERNET hSession = WinHttpOpen(L"MEGAsync proxy detection",
212                                    WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
213                                    WINHTTP_NO_PROXY_NAME,
214                                    WINHTTP_NO_PROXY_BYPASS,
215                                    WINHTTP_FLAG_ASYNC);
216 
217             if (WinHttpGetProxyForUrl(hSession, L"https://g.api.mega.co.nz/", &autoProxyOptions, &proxyInfo))
218             {
219                 if (proxyInfo.lpszProxy)
220                 {
221                     string proxyURL;
222                     proxy->setProxyType(Proxy::CUSTOM);
223                     proxyURL.assign((const char*)proxyInfo.lpszProxy, wcslen(proxyInfo.lpszProxy) * sizeof(wchar_t));
224                     proxy->setProxyURL(&proxyURL);
225                 }
226             }
227             WinHttpCloseHandle(hSession);
228         }
229     }
230 
231     if (ieProxyConfig.lpszProxy)
232     {
233         GlobalFree(ieProxyConfig.lpszProxy);
234     }
235 
236     if (ieProxyConfig.lpszProxyBypass)
237     {
238         GlobalFree(ieProxyConfig.lpszProxyBypass);
239     }
240 
241     if (ieProxyConfig.lpszAutoConfigUrl)
242     {
243         GlobalFree(ieProxyConfig.lpszAutoConfigUrl);
244     }
245 #endif
246 
247 #if defined(__APPLE__) && !(TARGET_OS_IPHONE)
248     getOSXproxy(proxy);
249 #endif
250 
251     return proxy;
252 }
253 
getMEGADNSservers(string * dnsservers,bool getfromnetwork)254 void HttpIO::getMEGADNSservers(string *dnsservers, bool getfromnetwork)
255 {
256     if (!dnsservers)
257     {
258         return;
259     }
260 
261     dnsservers->clear();
262     if (getfromnetwork)
263     {
264         struct addrinfo *aiList = NULL;
265         struct addrinfo *hp;
266 
267         struct addrinfo hints = {};
268         hints.ai_family = AF_UNSPEC;
269 
270 #ifndef __MINGW32__
271         hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
272 #endif
273 
274         if (!getaddrinfo("ns.mega.co.nz", NULL, &hints, &aiList))
275         {
276             hp = aiList;
277             while (hp)
278             {
279                 char straddr[INET6_ADDRSTRLEN];
280                 straddr[0] = 0;
281 
282                 if (hp->ai_family == AF_INET)
283                 {
284                     sockaddr_in *addr = (sockaddr_in *)hp->ai_addr;
285                     mega_inet_ntop(hp->ai_family, &addr->sin_addr, straddr, sizeof(straddr));
286                 }
287                 else if(hp->ai_family == AF_INET6)
288                 {
289                     sockaddr_in6 *addr = (sockaddr_in6 *)hp->ai_addr;
290                     mega_inet_ntop(hp->ai_family, &addr->sin6_addr, straddr, sizeof(straddr));
291                 }
292 
293                 if (straddr[0])
294                 {
295                     if(dnsservers->size())
296                     {
297                         dnsservers->append(",");
298                     }
299                     dnsservers->append(straddr);
300                 }
301 
302                 hp = hp->ai_next;
303             }
304             freeaddrinfo(aiList);
305         }
306     }
307 
308     if (!getfromnetwork || !dnsservers->size())
309     {
310         *dnsservers = MEGA_DNS_SERVERS;
311         LOG_info << "Using hardcoded MEGA DNS servers: " << *dnsservers;
312     }
313     else
314     {
315         LOG_info << "Using current MEGA DNS servers: " << *dnsservers;
316     }
317 }
318 
setmaxdownloadspeed(m_off_t)319 bool HttpIO::setmaxdownloadspeed(m_off_t)
320 {
321     return false;
322 }
323 
setmaxuploadspeed(m_off_t)324 bool HttpIO::setmaxuploadspeed(m_off_t)
325 {
326     return false;
327 }
328 
getmaxdownloadspeed()329 m_off_t HttpIO::getmaxdownloadspeed()
330 {
331     return 0;
332 }
333 
getmaxuploadspeed()334 m_off_t HttpIO::getmaxuploadspeed()
335 {
336     return 0;
337 }
338 
post(MegaClient * client,const char * data,unsigned len)339 void HttpReq::post(MegaClient* client, const char* data, unsigned len)
340 {
341     if (httpio)
342     {
343         LOG_warn << "Ensuring that the request is finished before sending it again";
344         httpio->cancel(this);
345         init();
346     }
347 
348     httpio = client->httpio;
349     bufpos = 0;
350     outpos = 0;
351     notifiedbufpos = 0;
352     inpurge = 0;
353     method = METHOD_POST;
354     contentlength = -1;
355     lastdata = Waiter::ds;
356 
357     DEBUG_TEST_HOOK_HTTPREQ_POST(this)
358 
359     httpio->post(this, data, len);
360 }
361 
get(MegaClient * client)362 void HttpReq::get(MegaClient *client)
363 {
364     if (httpio)
365     {
366         LOG_warn << "Ensuring that the request is finished before sending it again";
367         httpio->cancel(this);
368         init();
369     }
370 
371     httpio = client->httpio;
372     bufpos = 0;
373     outpos = 0;
374     notifiedbufpos = 0;
375     inpurge = 0;
376     method = METHOD_GET;
377     contentlength = -1;
378     lastdata = Waiter::ds;
379 
380     httpio->post(this);
381 }
382 
dns(MegaClient * client)383 void HttpReq::dns(MegaClient *client)
384 {
385     if (httpio)
386     {
387         LOG_warn << "Ensuring that the request is finished before sending it again";
388         httpio->cancel(this);
389         init();
390     }
391 
392     httpio = client->httpio;
393     bufpos = 0;
394     outpos = 0;
395     notifiedbufpos = 0;
396     inpurge = 0;
397     method = METHOD_NONE;
398     contentlength = -1;
399     lastdata = Waiter::ds;
400 
401     httpio->post(this);
402 }
403 
disconnect()404 void HttpReq::disconnect()
405 {
406     if (httpio)
407     {
408         httpio->cancel(this);
409         httpio = NULL;
410         init();
411     }
412 }
413 
HttpReq(bool b)414 HttpReq::HttpReq(bool b)
415 {
416     binary = b;
417     status = REQ_READY;
418     buf = NULL;
419     httpio = NULL;
420     httpiohandle = NULL;
421     out = &outbuf;
422     method = METHOD_NONE;
423     timeoutms = 0;
424     type = REQ_JSON;
425     buflen = 0;
426     protect = false;
427     minspeed = false;
428 
429     init();
430 }
431 
~HttpReq()432 HttpReq::~HttpReq()
433 {
434     if (httpio)
435     {
436         httpio->cancel(this);
437     }
438 
439     delete[] buf;
440 }
441 
init()442 void HttpReq::init()
443 {
444     httpstatus = 0;
445     inpurge = 0;
446     sslcheckfailed = false;
447     bufpos = 0;
448     notifiedbufpos = 0;
449     contentlength = 0;
450     timeleft = -1;
451     lastdata = NEVER;
452     outpos = 0;
453     in.clear();
454     contenttype.clear();
455 }
456 
setreq(const char * u,contenttype_t t)457 void HttpReq::setreq(const char* u, contenttype_t t)
458 {
459     if (u)
460     {
461         posturl = u;
462     }
463 
464     type = t;
465 }
466 
467 // add data to fixed or variable buffer
put(void * data,unsigned len,bool purge)468 void HttpReq::put(void* data, unsigned len, bool purge)
469 {
470     if (buf)
471     {
472         if (bufpos + len > buflen)
473         {
474             len = static_cast<unsigned>(buflen - bufpos);
475         }
476 
477         memcpy(buf + bufpos, data, len);
478     }
479     else
480     {
481         if (inpurge && purge)
482         {
483             in.erase(0, inpurge);
484             inpurge = 0;
485         }
486 
487         in.append((char*)data, len);
488     }
489 
490     bufpos += len;
491 }
492 
493 
http_buf_t(byte * b,size_t s,size_t e)494 HttpReq::http_buf_t::http_buf_t(byte* b, size_t s, size_t e)
495     : start(s), end(e), buf(b)
496 {
497 }
498 
~http_buf_t()499 HttpReq::http_buf_t::~http_buf_t()
500 {
501     delete[] buf;
502 }
503 
swap(http_buf_t & other)504 void HttpReq::http_buf_t::swap(http_buf_t& other)
505 {
506     byte* tb = buf; buf = other.buf; other.buf = tb;
507     size_t ts = start; start = other.start; other.start = ts;
508     size_t te = end; end = other.end; other.end = te;
509 }
510 
isNull()511 bool HttpReq::http_buf_t::isNull()
512 {
513     return buf == NULL;
514 }
515 
datastart()516 byte* HttpReq::http_buf_t::datastart()
517 {
518     return buf + start;
519 }
520 
datalen()521 size_t HttpReq::http_buf_t::datalen()
522 {
523     return end - start;
524 }
525 
526 
527 // give up ownership of the buffer for client to use.
release_buf()528 struct HttpReq::http_buf_t* HttpReq::release_buf()
529 {
530     HttpReq::http_buf_t* result = new HttpReq::http_buf_t(buf, inpurge, (size_t)bufpos);
531     buf = NULL;
532     inpurge = 0;
533     buflen = 0;
534     bufpos = 0;
535     outpos = 0;
536     notifiedbufpos = 0;
537     contentlength = -1;
538     in.clear();
539     return result;
540 }
541 
542 
data()543 char* HttpReq::data()
544 {
545     return (char*)in.data() + inpurge;
546 }
547 
size()548 size_t HttpReq::size()
549 {
550     return in.size() - inpurge;
551 }
552 
553 // set amount of purgeable in data at 0
purge(size_t numbytes)554 void HttpReq::purge(size_t numbytes)
555 {
556     inpurge += numbytes;
557 }
558 
559 // set total response size
setcontentlength(m_off_t len)560 void HttpReq::setcontentlength(m_off_t len)
561 {
562     if (!buf && type != REQ_BINARY)
563     {
564         in.reserve(static_cast<size_t>(len));
565     }
566 
567     contentlength = len;
568 }
569 
570 // make space for receiving data; adjust len if out of space
reserveput(unsigned * len)571 byte* HttpReq::reserveput(unsigned* len)
572 {
573     if (buf)
574     {
575         if (bufpos + *len > buflen)
576         {
577             *len = static_cast<unsigned>(buflen - bufpos);
578         }
579 
580         return buf + bufpos;
581     }
582     else
583     {
584         if (inpurge)
585         {
586             // FIXME: optimize erase()/resize() -> single copy/resize()
587             in.erase(0, inpurge);
588             bufpos -= inpurge;
589             inpurge = 0;
590         }
591 
592         if (bufpos + *len > (int) in.size())
593         {
594             in.resize(static_cast<size_t>(bufpos + *len));
595         }
596 
597         *len = static_cast<unsigned>(in.size() - bufpos);
598 
599         return (byte*)in.data() + bufpos;
600     }
601 }
602 
603 // number of bytes transferred in this request
transferred(MegaClient *)604 m_off_t HttpReq::transferred(MegaClient*)
605 {
606     if (buf)
607     {
608         return bufpos;
609     }
610     else
611     {
612         return in.size();
613     }
614 }
615 
HttpReqDL()616 HttpReqDL::HttpReqDL()
617     : dlpos(0)
618     , buffer_released(false)
619 {
620 }
621 
622 // prepare file chunk download
prepare(const char * tempurl,SymmCipher *,uint64_t,m_off_t pos,m_off_t npos)623 void HttpReqDL::prepare(const char* tempurl, SymmCipher* /*key*/,
624                         uint64_t /*ctriv*/, m_off_t pos,
625                         m_off_t npos)
626 {
627     char urlbuf[512];
628 
629     snprintf(urlbuf, sizeof urlbuf, "%s/%" PRIu64 "-%" PRIu64, tempurl, pos, npos ? npos - 1 : 0);
630     setreq(urlbuf, REQ_BINARY);
631 
632     dlpos = pos;
633     size = (unsigned)(npos - pos);
634     buffer_released = false;
635 
636     if (!buf || buflen != size)
637     {
638         // (re)allocate buffer
639         if (buf)
640         {
641             delete[] buf;
642             buf = NULL;
643         }
644 
645         if (size)
646         {
647             buf = new byte[(size + SymmCipher::BLOCKSIZE - 1) & - SymmCipher::BLOCKSIZE];
648         }
649         buflen = size;
650     }
651 }
652 
653 
654 
EncryptByChunks(SymmCipher * k,chunkmac_map * m,uint64_t iv)655 EncryptByChunks::EncryptByChunks(SymmCipher* k, chunkmac_map* m, uint64_t iv) : key(k), macs(m), ctriv(iv)
656 {
657     memset(crc, 0, CRCSIZE);
658 }
659 
updateCRC(byte * data,unsigned size,unsigned offset)660 void EncryptByChunks::updateCRC(byte* data, unsigned size, unsigned offset)
661 {
662     uint32_t *intc = (uint32_t *)crc;
663 
664     unsigned ol = offset % CRCSIZE;
665     if (ol)
666     {
667         unsigned ll = CRCSIZE - ol;
668         if (ll > size) //last chunks could be smaller than CRCSIZE!
669         {
670             ll = size;
671         }
672         size -= ll;
673         while (ll--)
674         {
675             crc[ol++] ^= *data++;
676         }
677     }
678 
679     uint32_t *intdata = (uint32_t *)data;
680     int ll = size % CRCSIZE;
681     int l = size / CRCSIZE;
682     if (l)
683     {
684         l *= 3;
685         while (l)
686         {
687             l -= 3;
688             intc[0] ^= intdata[l];
689             intc[1] ^= intdata[l + 1];
690             intc[2] ^= intdata[l + 2];
691         }
692     }
693     if (ll)
694     {
695         data += (size - ll);
696         while (ll--)
697         {
698             crc[ll] ^= data[ll];
699         }
700     }
701 }
702 
encrypt(m_off_t pos,m_off_t npos,string & urlSuffix)703 bool EncryptByChunks::encrypt(m_off_t pos, m_off_t npos, string& urlSuffix)
704 {
705     byte* buf;
706     m_off_t startpos = pos;
707     m_off_t finalpos = npos;
708     m_off_t endpos = ChunkedHash::chunkceil(startpos, finalpos);
709     m_off_t chunksize = endpos - startpos;
710     while (chunksize)
711     {
712         byte mac[SymmCipher::BLOCKSIZE] = { 0 };
713         buf = nextbuffer(unsigned(chunksize));
714         if (!buf) return false;
715         key->ctr_crypt(buf, unsigned(chunksize), startpos, ctriv, mac, 1);
716         memcpy((*macs)[startpos].mac, mac, sizeof mac);
717         (*macs)[startpos].finished = false;  // finished is only set true after confirmation of the chunk uploading.
718         LOG_debug << "Encrypted chunk: " << startpos << " - " << endpos << "   Size: " << chunksize;
719 
720         updateCRC(buf, unsigned(chunksize), unsigned(startpos - pos));
721 
722         startpos = endpos;
723         endpos = ChunkedHash::chunkceil(startpos, finalpos);
724         chunksize = endpos - startpos;
725     }
726     assert(endpos == finalpos);
727     buf = nextbuffer(0);   // last call in case caller does buffer post-processing (such as write to file as we go)
728 
729     ostringstream s;
730     s << "/" << pos << "?c=" << Base64Str<EncryptByChunks::CRCSIZE>(crc);
731     urlSuffix = s.str();
732 
733     return !!buf;
734 }
735 
736 
EncryptBufferByChunks(byte * b,SymmCipher * k,chunkmac_map * m,uint64_t iv)737 EncryptBufferByChunks::EncryptBufferByChunks(byte* b, SymmCipher* k, chunkmac_map* m, uint64_t iv)
738     : EncryptByChunks(k, m, iv)
739     , chunkstart(b)
740 {
741 }
742 
nextbuffer(unsigned bufsize)743 byte* EncryptBufferByChunks::nextbuffer(unsigned bufsize)
744 {
745     byte* pos = chunkstart;
746     chunkstart += bufsize;
747     return pos;
748 }
749 
750 // prepare chunk for uploading: mac and encrypt
prepare(const char * tempurl,SymmCipher * key,uint64_t ctriv,m_off_t pos,m_off_t npos)751 void HttpReqUL::prepare(const char* tempurl, SymmCipher* key,
752                         uint64_t ctriv, m_off_t pos,
753                         m_off_t npos)
754 {
755     EncryptBufferByChunks eb((byte*)out->data(), key, &mChunkmacs, ctriv);
756 
757     string urlSuffix;
758     eb.encrypt(pos, npos, urlSuffix);
759 
760     // unpad for POSTing
761     size = (unsigned)(npos - pos);
762     out->resize(size);
763 
764     setreq((tempurl + urlSuffix).c_str(), REQ_BINARY);
765 }
766 
767 // number of bytes sent in this request
transferred(MegaClient * client)768 m_off_t HttpReqUL::transferred(MegaClient* client)
769 {
770     if (httpiohandle)
771     {
772         return client->httpio->postpos(httpiohandle);
773     }
774 
775     return 0;
776 }
777 
SpeedController()778 SpeedController::SpeedController()
779 {
780     partialBytes = 0;
781     meanSpeed = 0;
782     lastUpdate = 0;
783     speedCounter = 0;
784 }
785 
calculateSpeed(long long numBytes)786 m_off_t SpeedController::calculateSpeed(long long numBytes)
787 {
788     dstime currentTime = Waiter::ds;
789     if (numBytes <= 0 && lastUpdate == currentTime)
790     {
791         return (partialBytes * 10) / SPEED_MEAN_INTERVAL_DS;
792     }
793 
794     while (transferBytes.size())
795     {
796         map<dstime, m_off_t>::iterator it = transferBytes.begin();
797         dstime deltaTime = currentTime - it->first;
798         if (deltaTime < SPEED_MEAN_INTERVAL_DS)
799         {
800             break;
801         }
802 
803         partialBytes -= it->second;
804         transferBytes.erase(it);
805     }
806 
807     if (numBytes > 0)
808     {
809         transferBytes[currentTime] += numBytes;
810         partialBytes += numBytes;
811     }
812 
813     m_off_t speed = (partialBytes * 10) / SPEED_MEAN_INTERVAL_DS;
814     if (numBytes)
815     {
816         meanSpeed = meanSpeed * speedCounter + speed;
817         speedCounter++;
818         meanSpeed /= speedCounter;
819         if (speedCounter > SPEED_MAX_VALUES)
820         {
821             speedCounter = SPEED_MAX_VALUES;
822         }
823     }
824     lastUpdate = currentTime;
825     return speed;
826 }
827 
getMeanSpeed()828 m_off_t SpeedController::getMeanSpeed()
829 {
830     return meanSpeed;
831 }
832 
GenericHttpReq(PrnGen & rng,bool binary)833 GenericHttpReq::GenericHttpReq(PrnGen &rng, bool binary)
834     : HttpReq(binary), bt(rng), maxbt(rng)
835 {
836     tag = 0;
837     maxretries = 0;
838     numretry = 0;
839     isbtactive = false;
840 }
841 
842 } // namespace
843