diff --git a/node/HttpClient.cpp b/node/HttpClient.cpp index 8accb43db..ec5c22382 100644 --- a/node/HttpClient.cpp +++ b/node/HttpClient.cpp @@ -27,17 +27,17 @@ #include "Constants.hpp" +#include +#include +#include + #ifdef __WINDOWS__ #include #include #include #include #include -#endif - -#include -#include -#include +#endif // __WINDOWS__ #ifdef __UNIX_LIKE__ #include @@ -48,7 +48,7 @@ #include #include #include -#endif +#endif // __UNIX_LIKE__ #include #include @@ -62,8 +62,6 @@ namespace ZeroTier { -const std::map HttpClient::NO_HEADERS; - #ifdef __UNIX_LIKE__ // The *nix implementation calls 'curl' externally rather than linking to it. @@ -76,27 +74,36 @@ const std::map HttpClient::NO_HEADERS; #endif // Paths where "curl" may be found on the system -#define NUM_CURL_PATHS 5 -static const char *CURL_PATHS[NUM_CURL_PATHS] = { "/usr/bin/curl","/bin/curl","/usr/local/bin/curl","/usr/sbin/curl","/sbin/curl" }; +#define NUM_CURL_PATHS 6 +static const char *CURL_PATHS[NUM_CURL_PATHS] = { "/usr/bin/curl","/bin/curl","/usr/local/bin/curl","/usr/sbin/curl","/sbin/curl","/usr/libexec/curl" }; // Maximum message length #define CURL_MAX_MESSAGE_LENGTH (1024 * 1024 * 64) // Internal private thread class that performs request, notifies handler, // and then commits suicide by deleting itself. -class P_Req : NonCopyable +class HttpClient_Private_Request : NonCopyable { public: - P_Req(const char *method,const std::string &url,const std::map &headers,unsigned int timeout,void (*handler)(void *,int,const std::string &,bool,const std::string &),void *arg) : + HttpClient_Private_Request(HttpClient *parent,const char *method,const std::string &url,const std::map &headers,unsigned int timeout,void (*handler)(void *,int,const std::string &,const std::string &),void *arg) : _url(url), _headers(headers), _timeout(timeout), _handler(handler), - _arg(arg) + _arg(arg), + _parent(parent), + _pid(0), + _cancelled(false) { _myThread = Thread::start(this); } + ~HttpClient_Private_Request() + { + Mutex::Lock _l(_parent->_requests_m); + _parent->_requests.erase((HttpClient::Request)this); + } + void threadMain() { char *curlArgs[1024]; @@ -111,14 +118,14 @@ public: break; } } + if (!curlPath.length()) { - _handler(_arg,-1,_url,false,"unable to locate 'curl' binary in /usr/bin, /bin, /usr/local/bin, /usr/sbin, or /sbin"); + _doH(_arg,-1,_url,"unable to locate 'curl' binary in /usr/bin, /bin, /usr/local/bin, /usr/sbin, or /sbin"); delete this; return; } - if (!_url.length()) { - _handler(_arg,-1,_url,false,"cannot fetch empty URL"); + _doH(_arg,-1,_url,"cannot fetch empty URL"); delete this; return; } @@ -147,17 +154,17 @@ public: ::pipe(curlStdout); ::pipe(curlStderr); - long pid = (long)vfork(); - if (pid < 0) { + _pid = (long)vfork(); + if (_pid < 0) { // fork() failed ::close(curlStdout[0]); ::close(curlStdout[1]); ::close(curlStderr[0]); ::close(curlStderr[1]); - _handler(_arg,-1,_url,false,"unable to fork()"); + _doH(_arg,-1,_url,"unable to fork()"); delete this; return; - } else if (pid > 0) { + } else if (_pid > 0) { // fork() succeeded, in parent process ::close(curlStdout[1]); ::close(curlStderr[1]); @@ -189,7 +196,7 @@ public: } else if (n < 0) break; if (_body.length() > CURL_MAX_MESSAGE_LENGTH) { - ::kill(pid,SIGKILL); + ::kill(_pid,SIGKILL); tooLong = true; break; } @@ -200,12 +207,12 @@ public: break; if (Utils::now() >= timesOutAt) { - ::kill(pid,SIGKILL); + ::kill(_pid,SIGKILL); timedOut = true; break; } - if (waitpid(pid,&exitCode,WNOHANG) > 0) { + if (waitpid(_pid,&exitCode,WNOHANG) > 0) { for(;;) { // Drain output... int n = (int)::read(curlStdout[0],buf,sizeof(buf)); @@ -219,23 +226,24 @@ public: } } } - pid = 0; + _pid = 0; break; } } - if (pid > 0) - waitpid(pid,&exitCode,0); + if (_pid > 0) + waitpid(_pid,&exitCode,0); + _pid = 0; ::close(curlStdout[0]); ::close(curlStderr[0]); if (timedOut) - _handler(_arg,-1,_url,false,"connection timed out"); + _doH(_arg,-1,_url,"connection timed out"); else if (tooLong) - _handler(_arg,-1,_url,false,"response too long"); + _doH(_arg,-1,_url,"response too long"); else if (exitCode) - _handler(_arg,-1,_url,false,"connection failed (curl returned non-zero exit code)"); + _doH(_arg,-1,_url,"connection failed (curl returned non-zero exit code)"); else { unsigned long idx = 0; @@ -254,7 +262,7 @@ public: headers.back().push_back(c); } if (headers.empty()||(!headers.front().length())) { - _handler(_arg,-1,_url,false,"HTTP response empty"); + _doH(_arg,-1,_url,"HTTP response empty"); delete this; return; } @@ -262,24 +270,24 @@ public: // Parse first line -- HTTP status code and response size_t scPos = headers.front().find(' '); if (scPos == std::string::npos) { - _handler(_arg,-1,_url,false,"invalid HTTP response (no status line)"); + _doH(_arg,-1,_url,"invalid HTTP response (no status line)"); delete this; return; } ++scPos; unsigned int rcode = Utils::strToUInt(headers.front().substr(scPos,3).c_str()); if ((!rcode)||(rcode > 999)) { - _handler(_arg,-1,_url,false,"invalid HTTP response (invalid response code)"); + _doH(_arg,-1,_url,"invalid HTTP response (invalid response code)"); delete this; return; } // Serve up the resulting data to the handler if (rcode == 200) - _handler(_arg,rcode,_url,false,_body.substr(idx)); + _doH(_arg,rcode,_url,_body.substr(idx)); else if ((scPos + 4) < headers.front().length()) - _handler(_arg,rcode,_url,false,headers.front().substr(scPos+4)); - else _handler(_arg,rcode,_url,false,"(no status message from server)"); + _doH(_arg,rcode,_url,headers.front().substr(scPos+4)); + else _doH(_arg,rcode,_url,"(no status message from server)"); } delete this; @@ -295,27 +303,41 @@ public: } } + inline void cancel() + { + { + Mutex::Lock _l(_cancelled_m); + _cancelled = true; + if (_pid > 0) + ::kill(_pid,SIGKILL); + } + Thread::join(_myThread); + } + +private: + inline void _doH(void *arg,int code,const std::string &url,const std::string &body) + { + Mutex::Lock _l(_cancelled_m); + try { + if ((!_cancelled)&&(_handler)) + _handler(arg,code,url,body); + } catch ( ... ) {} + } + const std::string _url; std::string _body; std::map _headers; unsigned int _timeout; - void (*_handler)(void *,int,const std::string &,bool,const std::string &); + void (*_handler)(void *,int,const std::string &,const std::string &); void *_arg; + HttpClient *_parent; + long _pid; + volatile bool _cancelled; + Mutex _cancelled_m; Thread _myThread; }; -HttpClient::Request HttpClient::_do( - const char *method, - const std::string &url, - const std::map &headers, - unsigned int timeout, - void (*handler)(void *,int,const std::string &,bool,const std::string &), - void *arg) -{ - return (HttpClient::Request)(new P_Req(method,url,headers,timeout,handler,arg)); -} - -#endif +#endif // __UNIX_LIKE__ #ifdef __WINDOWS__ @@ -323,10 +345,10 @@ HttpClient::Request HttpClient::_do( // Internal private thread class that performs request, notifies handler, // and then commits suicide by deleting itself. -class P_Req : NonCopyable +class HttpClient_Private_Request : NonCopyable { public: - P_Req(const char *method,const std::string &url,const std::map &headers,unsigned int timeout,void (*handler)(void *,int,const std::string &,bool,const std::string &),void *arg) : + HttpClient_Private_Request(const char *method,const std::string &url,const std::map &headers,unsigned int timeout,void (*handler)(void *,int,const std::string &,const std::string &),void *arg) : _url(url), _headers(headers), _timeout(timeout), @@ -449,22 +471,52 @@ closeAndReturnFromHttp: std::string _body; std::map _headers; unsigned int _timeout; - void (*_handler)(void *,int,const std::string &,bool,const std::string &); + void (*_handler)(void *,int,const std::string &,const std::string &); void *_arg; Thread _myThread; }; +#endif // __WINDOWS__ + +const std::map HttpClient::NO_HEADERS; + +HttpClient::HttpClient() +{ +} + +HttpClient::~HttpClient() +{ + std::set reqs; + { + Mutex::Lock _l(_requests_m); + reqs = _requests; + } + for(std::set::iterator r(reqs.begin());r!=reqs.end();++r) + this->cancel(*r); +} + +void HttpClient::cancel(HttpClient::Request req) +{ + { + Mutex::Lock _l(_requests_m); + if (_requests.count(req) == 0) + return; + } + ((HttpClient_Private_Request *)req)->cancel(); +} + HttpClient::Request HttpClient::_do( const char *method, const std::string &url, const std::map &headers, unsigned int timeout, - void (*handler)(void *,int,const std::string &,bool,const std::string &), + void (*handler)(void *,int,const std::string &,const std::string &), void *arg) { - return (HttpClient::Request)(new P_Req(method,url,headers,timeout,handler,arg)); + HttpClient::Request r = (HttpClient::Request)(new HttpClient_Private_Request(this,method,url,headers,timeout,handler,arg)); + Mutex::Lock _l(_requests_m); + _requests.insert(r); + return r; } -#endif - } // namespace ZeroTier diff --git a/node/HttpClient.hpp b/node/HttpClient.hpp index 59c1bcac2..f65b88e8d 100644 --- a/node/HttpClient.hpp +++ b/node/HttpClient.hpp @@ -30,11 +30,15 @@ #include #include +#include #include "Constants.hpp" +#include "Mutex.hpp" namespace ZeroTier { +class HttpClient_Private_Request; + /** * HTTP client that does queries in the background * @@ -55,8 +59,12 @@ namespace ZeroTier { class HttpClient { public: + friend class HttpClient_Private_Request; typedef void * Request; + HttpClient(); + ~HttpClient(); + /** * Empty map for convenience use */ @@ -65,24 +73,37 @@ public: /** * Request a URL using the GET method */ - static inline Request GET( + inline Request GET( const std::string &url, const std::map &headers, unsigned int timeout, - void (*handler)(void *,int,const std::string &,bool,const std::string &), + void (*handler)(void *,int,const std::string &,const std::string &), void *arg) { return _do("GET",url,headers,timeout,handler,arg); } + /** + * Cancel a request + * + * If the request is not active, this does nothing. This may take some time + * depending on HTTP implementation. It may also not kill instantly, but + * it will prevent the handler function from ever being called and cause the + * request to die silently when complete. + */ + void cancel(Request req); + private: - static Request _do( + Request _do( const char *method, const std::string &url, const std::map &headers, unsigned int timeout, - void (*handler)(void *,int,const std::string &,bool,const std::string &), + void (*handler)(void *,int,const std::string &,const std::string &), void *arg); + + std::set _requests; + Mutex _requests_m; }; } // namespace ZeroTier diff --git a/node/Node.cpp b/node/Node.cpp index 5d7de540e..755784468 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -250,6 +250,7 @@ struct _NodeImpl delete renv.sw; renv.sw = (Switch *)0; // order matters less from here down delete renv.mc; renv.mc = (Multicaster *)0; delete renv.antiRec; renv.antiRec = (AntiRecursion *)0; + delete renv.http; renv.http = (HttpClient *)0; delete renv.prng; renv.prng = (CMWC4096 *)0; delete renv.log; renv.log = (Logger *)0; // but stop logging last of all @@ -408,7 +409,7 @@ static void _CBztTraffic(const SharedPtr &fromSock,void *arg,const InetA _r->sw->onRemotePacket(fromSock,from,data); } -static void _cbHandleGetRootTopology(void *arg,int code,const std::string &url,bool onDisk,const std::string &body) +static void _cbHandleGetRootTopology(void *arg,int code,const std::string &url,const std::string &body) { RuntimeEnvironment *_r = (RuntimeEnvironment *)arg; if (_r->shutdownInProgress) @@ -524,6 +525,7 @@ Node::ReasonForTermination Node::run() } Utils::lockDownFile(configAuthTokenPath.c_str(),false); + _r->http = new HttpClient(); _r->antiRec = new AntiRecursion(); _r->mc = new Multicaster(); _r->sw = new Switch(_r); @@ -757,7 +759,7 @@ Node::ReasonForTermination Node::run() if ((now - lastRootTopologyFetch) >= ZT_UPDATE_ROOT_TOPOLOGY_CHECK_INTERVAL) { lastRootTopologyFetch = now; - HttpClient::GET(ZT_DEFAULTS.rootTopologyUpdateURL,HttpClient::NO_HEADERS,60,&_cbHandleGetRootTopology,_r); + _r->http->GET(ZT_DEFAULTS.rootTopologyUpdateURL,HttpClient::NO_HEADERS,60,&_cbHandleGetRootTopology,_r); } // Sleep for loop interval or until something interesting happens. diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index 78d950ce0..21fbc73a4 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -48,6 +48,7 @@ class SocketManager; class AntiRecursion; class EthernetTapFactory; class RoutingTable; +class HttpClient; /** * Holds global state for an instance of ZeroTier::Node @@ -73,6 +74,7 @@ public: routingTable((RoutingTable *)0), log((Logger *)0), prng((CMWC4096 *)0), + http((HttpClient *)0), antiRec((AntiRecursion *)0), mc((Multicaster *)0), sw((Switch *)0), @@ -119,6 +121,7 @@ public: Logger *log; // null if logging is disabled CMWC4096 *prng; + HttpClient *http; AntiRecursion *antiRec; Multicaster *mc; Switch *sw; diff --git a/node/SoftwareUpdater.cpp b/node/SoftwareUpdater.cpp index e34fd3ca4..d3a606fa5 100644 --- a/node/SoftwareUpdater.cpp +++ b/node/SoftwareUpdater.cpp @@ -33,6 +33,7 @@ #include "../version.h" +#include "Constants.hpp" #include "SoftwareUpdater.hpp" #include "Dictionary.hpp" #include "C25519.hpp" @@ -42,6 +43,7 @@ #include "Thread.hpp" #include "Node.hpp" #include "Utils.hpp" +#include "HttpClient.hpp" #ifdef __UNIX_LIKE__ #include @@ -85,6 +87,32 @@ void SoftwareUpdater::cleanOldUpdates() } } +void SoftwareUpdater::sawRemoteVersion(unsigned int vmaj,unsigned int vmin,unsigned int rev) +{ + const uint64_t tmp = packVersion(vmaj,vmin,rev); + if (tmp > _myVersion) { + Mutex::Lock _l(_lock); + if ((_status == UPDATE_STATUS_IDLE)&&(!_die)&&(ZT_DEFAULTS.updateLatestNfoURL.length())) { + const uint64_t now = Utils::now(); + if ((now - _lastUpdateAttempt) >= ZT_UPDATE_MIN_INTERVAL) { + _lastUpdateAttempt = now; + _status = UPDATE_STATUS_GETTING_NFO; + _r->http->GET(ZT_DEFAULTS.updateLatestNfoURL,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionInfo,this); + } + } + } +} + +void SoftwareUpdater::checkNow() +{ + Mutex::Lock _l(_lock); + if (_status == UPDATE_STATUS_IDLE) { + _lastUpdateAttempt = Utils::now(); + _status = UPDATE_STATUS_GETTING_NFO; + _r->http->GET(ZT_DEFAULTS.updateLatestNfoURL,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionInfo,this); + } +} + const char *SoftwareUpdater::parseNfo( const char *nfoText, unsigned int &vMajor, @@ -127,7 +155,7 @@ bool SoftwareUpdater::validateUpdate( return updateAuthority->second.verify(data,len,signature.data(),(unsigned int)signature.length()); } -void SoftwareUpdater::_cbHandleGetLatestVersionInfo(void *arg,int code,const std::string &url,bool onDisk,const std::string &body) +void SoftwareUpdater::_cbHandleGetLatestVersionInfo(void *arg,int code,const std::string &url,const std::string &body) { SoftwareUpdater *upd = (SoftwareUpdater *)arg; const RuntimeEnvironment *_r = (const RuntimeEnvironment *)upd->_r; @@ -175,14 +203,14 @@ void SoftwareUpdater::_cbHandleGetLatestVersionInfo(void *arg,int code,const std upd->_signedBy = signedBy; upd->_signature = signature; - HttpClient::GET(url,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionBinary,arg); + _r->http->GET(url,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionBinary,arg); } catch ( ... ) { LOG("software update check failed: .nfo file invalid or missing field(s)"); upd->_status = UPDATE_STATUS_IDLE; } } -void SoftwareUpdater::_cbHandleGetLatestVersionBinary(void *arg,int code,const std::string &url,bool onDisk,const std::string &body) +void SoftwareUpdater::_cbHandleGetLatestVersionBinary(void *arg,int code,const std::string &url,const std::string &body) { SoftwareUpdater *upd = (SoftwareUpdater *)arg; const RuntimeEnvironment *_r = (const RuntimeEnvironment *)upd->_r; diff --git a/node/SoftwareUpdater.hpp b/node/SoftwareUpdater.hpp index b687ca30e..d2e19c155 100644 --- a/node/SoftwareUpdater.hpp +++ b/node/SoftwareUpdater.hpp @@ -35,7 +35,6 @@ #include "Constants.hpp" #include "Mutex.hpp" #include "Utils.hpp" -#include "HttpClient.hpp" #include "Defaults.hpp" #include "Address.hpp" @@ -66,21 +65,7 @@ public: * @param vmin Peer's minor version * @param rev Peer's revision */ - inline void sawRemoteVersion(unsigned int vmaj,unsigned int vmin,unsigned int rev) - { - const uint64_t tmp = packVersion(vmaj,vmin,rev); - if (tmp > _myVersion) { - Mutex::Lock _l(_lock); - if ((_status == UPDATE_STATUS_IDLE)&&(!_die)&&(ZT_DEFAULTS.updateLatestNfoURL.length())) { - const uint64_t now = Utils::now(); - if ((now - _lastUpdateAttempt) >= ZT_UPDATE_MIN_INTERVAL) { - _lastUpdateAttempt = now; - _status = UPDATE_STATUS_GETTING_NFO; - HttpClient::GET(ZT_DEFAULTS.updateLatestNfoURL,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionInfo,this); - } - } - } - } + void sawRemoteVersion(unsigned int vmaj,unsigned int vmin,unsigned int rev); /** * Check for updates now regardless of last check time or version @@ -88,15 +73,7 @@ public: * This only starts a check if one is not in progress. Otherwise it does * nothing. */ - inline void checkNow() - { - Mutex::Lock _l(_lock); - if (_status == UPDATE_STATUS_IDLE) { - _lastUpdateAttempt = Utils::now(); - _status = UPDATE_STATUS_GETTING_NFO; - HttpClient::GET(ZT_DEFAULTS.updateLatestNfoURL,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionInfo,this); - } - } + void checkNow(); /** * Check for updates now if it's been longer than ZT_UPDATE_MAX_INTERVAL @@ -167,8 +144,8 @@ public: const std::string &signature); private: - static void _cbHandleGetLatestVersionInfo(void *arg,int code,const std::string &url,bool onDisk,const std::string &body); - static void _cbHandleGetLatestVersionBinary(void *arg,int code,const std::string &url,bool onDisk,const std::string &body); + static void _cbHandleGetLatestVersionInfo(void *arg,int code,const std::string &url,const std::string &body); + static void _cbHandleGetLatestVersionBinary(void *arg,int code,const std::string &url,const std::string &body); const RuntimeEnvironment *_r; const uint64_t _myVersion; diff --git a/selftest.cpp b/selftest.cpp index 7d459774d..25231ba00 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -67,7 +67,7 @@ static unsigned char fuzzbuf[1048576]; static volatile bool webDone = false; static std::string webSha512ShouldBe; -static void testHttpHandler(void *arg,int code,const std::string &url,bool onDisk,const std::string &body) +static void testHttpHandler(void *arg,int code,const std::string &url,const std::string &body) { unsigned char sha[64]; if (code == 200) { @@ -81,46 +81,48 @@ static void testHttpHandler(void *arg,int code,const std::string &url,bool onDis static int testHttp() { + HttpClient http; + webSha512ShouldBe = "221b348c8278ad2063c158fb15927c35dc6bb42880daf130d0574025f88ec350811c34fae38a014b576d3ef5c98af32bb540e68204810db87a51fa9b239ea567"; std::cout << "[http] fetching http://download.zerotier.com/dev/1k ... "; std::cout.flush(); webDone = false; - HttpClient::GET("http://download.zerotier.com/dev/1k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); + http.GET("http://download.zerotier.com/dev/1k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); while (!webDone) Thread::sleep(500); webSha512ShouldBe = "342e1a058332aad2d7a5412c1d9cd4ad02b4038178ca0c3ed9d34e3cf0905c118b684e5d2a935a158195d453d7d69e9c6e201e252620fb53f29611794a5d4b0c"; std::cout << "[http] fetching http://download.zerotier.com/dev/2k ... "; std::cout.flush(); webDone = false; - HttpClient::GET("http://download.zerotier.com/dev/2k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); + http.GET("http://download.zerotier.com/dev/2k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); while (!webDone) Thread::sleep(500); webSha512ShouldBe = "439562e1471dd6bdb558cb680f38dd7742e521497e280cb1456a31f74b9216b7d98145b3896c2f68008e6ac0c1662a4cb70562caeac294c5d01f378b22a21292"; std::cout << "[http] fetching http://download.zerotier.com/dev/4k ... "; std::cout.flush(); webDone = false; - HttpClient::GET("http://download.zerotier.com/dev/4k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); + http.GET("http://download.zerotier.com/dev/4k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); while (!webDone) Thread::sleep(500); webSha512ShouldBe = "fbd3901a9956158b9d290efa1af4fff459d8c03187c98b0e630d10a19fab61940e668652257763973f6cde34f2aa81574f9a50b1979b675b45ddd18d69a4ceb8"; std::cout << "[http] fetching http://download.zerotier.com/dev/8k ... "; std::cout.flush(); webDone = false; - HttpClient::GET("http://download.zerotier.com/dev/8k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); + http.GET("http://download.zerotier.com/dev/8k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); while (!webDone) Thread::sleep(500); webSha512ShouldBe = "098ae593f8c3a962f385f9f008ec2116ad22eea8bc569fc88a06a0193480fdfb27470345c427116d19179fb2a74df21d95fe5f1df575a9f2d10d99595708b765"; std::cout << "[http] fetching http://download.zerotier.com/dev/4m ... "; std::cout.flush(); webDone = false; - HttpClient::GET("http://download.zerotier.com/dev/4m",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); + http.GET("http://download.zerotier.com/dev/4m",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); while (!webDone) Thread::sleep(500); webSha512ShouldBe = ""; std::cout << "[http] fetching http://download.zerotier.com/dev/NOEXIST ... "; std::cout.flush(); webDone = false; - HttpClient::GET("http://download.zerotier.com/dev/NOEXIST",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); + http.GET("http://download.zerotier.com/dev/NOEXIST",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0); while (!webDone) Thread::sleep(500); webSha512ShouldBe = ""; std::cout << "[http] fetching http://1.1.1.1/SHOULD_TIME_OUT ... "; std::cout.flush(); webDone = false; - HttpClient::GET("http://1.1.1.1/SHOULD_TIME_OUT",HttpClient::NO_HEADERS,4,&testHttpHandler,(void *)0); + http.GET("http://1.1.1.1/SHOULD_TIME_OUT",HttpClient::NO_HEADERS,4,&testHttpHandler,(void *)0); while (!webDone) Thread::sleep(500); return 0;