소스 검색

HTTP client works!

Adam Ierymenko 11 년 전
부모
커밋
518410b7e0
3개의 변경된 파일57개의 추가작업 그리고 13개의 파일을 삭제
  1. 16 13
      node/HttpClient.cpp
  2. 5 0
      node/HttpClient.hpp
  3. 36 0
      selftest.cpp

+ 16 - 13
node/HttpClient.cpp

@@ -52,6 +52,8 @@
 
 namespace ZeroTier {
 
+const std::map<std::string,std::string> HttpClient::NO_HEADERS;
+
 #ifdef __UNIX_LIKE__
 
 // The *nix implementation calls 'curl' externally rather than linking to it.
@@ -59,6 +61,10 @@ namespace ZeroTier {
 // provided you don't want to have automatic software updates... or want to
 // do them via another method.
 
+#ifdef __APPLE__
+// TODO: get proxy configuration
+#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" };
@@ -117,11 +123,12 @@ public:
 			headerArgs.back().append(h->second);
 		}
 		for(std::vector<std::string>::iterator h(headerArgs.begin());h!=headerArgs.end();++h) {
-			if (argPtr >= (1024 - 3)) // leave room for terminating NULL
+			if (argPtr >= (1024 - 4)) // leave room for terminating NULL and URL
 				break;
 			curlArgs[argPtr++] = const_cast <char *>("-H");
 			curlArgs[argPtr++] = const_cast <char *>(h->c_str());
 		}
+		curlArgs[argPtr++] = const_cast <char *>(_url.c_str());
 		curlArgs[argPtr] = (char *)0;
 
 		int curlStdout[2];
@@ -166,7 +173,8 @@ public:
 					int n = (int)::read(curlStdout[0],buf,sizeof(buf));
 					if (n > 0)
 						_body.append(buf,n);
-					else break;
+					else if (n < 0)
+						break;
 					if (_body.length() > CURL_MAX_MESSAGE_LENGTH) {
 						::kill(pid,SIGKILL);
 						tooLong = true;
@@ -215,8 +223,8 @@ public:
 						if (!headers.back().length()) {
 							headers.pop_back();
 							break;
-						}
-					} else if (c != '\r') // \r's shouldn't be present but ignore them if they are
+						} else headers.push_back(std::string());
+					} else if (c != '\r')
 						headers.back().push_back(c);
 				}
 				if (headers.empty()||(!headers.front().length())) {
@@ -233,14 +241,6 @@ public:
 					return;
 				}
 				++scPos;
-				size_t msgPos = headers.front().find(' ',scPos);
-				if (msgPos == std::string::npos)
-					msgPos = headers.front().length();
-				if ((msgPos - scPos) != 3) {
-					_handler(_arg,-1,_url,false,"invalid HTTP response (no response code)");
-					delete this;
-					return;
-				}
 				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)");
@@ -251,7 +251,9 @@ public:
 				// Serve up the resulting data to the handler
 				if (rcode == 200)
 					_handler(_arg,rcode,_url,false,_body.substr(idx));
-				else _handler(_arg,rcode,_url,false,headers.front().substr(scPos+3));
+				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)");
 			}
 
 			delete this;
@@ -284,6 +286,7 @@ HttpClient::Request HttpClient::_do(
 	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

+ 5 - 0
node/HttpClient.hpp

@@ -57,6 +57,11 @@ class HttpClient
 public:
 	typedef void * Request;
 
+	/**
+	 * Empty map for convenience use
+	 */
+	static const std::map<std::string,std::string> NO_HEADERS;
+
 	/**
 	 * Request a URL using the GET method
 	 */

+ 36 - 0
selftest.cpp

@@ -52,6 +52,7 @@
 #include "node/C25519.hpp"
 #include "node/Poly1305.hpp"
 #include "node/CertificateOfMembership.hpp"
+#include "node/HttpClient.hpp"
 
 #ifdef __WINDOWS__
 #include <tchar.h>
@@ -63,6 +64,40 @@ using namespace ZeroTier;
 
 static unsigned char fuzzbuf[1048576];
 
+static Condition webDoneCondition;
+static void testHttpHandler(void *arg,int code,const std::string &url,bool onDisk,const std::string &body)
+{
+	if (code == 200)
+		std::cout << "got " << body.length() << " bytes, response code " << code << std::endl;
+	else std::cout << "ERROR " << code << ": " << body << std::endl;
+	webDoneCondition.signal();
+}
+
+static int testHttp()
+{
+	std::cout << "[http] fetching http://download.zerotier.com/dev/1k ... "; std::cout.flush();
+	HttpClient::GET("http://download.zerotier.com/dev/1k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0);
+	webDoneCondition.wait();
+
+	std::cout << "[http] fetching http://download.zerotier.com/dev/2k ... "; std::cout.flush();
+	HttpClient::GET("http://download.zerotier.com/dev/2k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0);
+	webDoneCondition.wait();
+
+	std::cout << "[http] fetching http://download.zerotier.com/dev/4k ... "; std::cout.flush();
+	HttpClient::GET("http://download.zerotier.com/dev/4k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0);
+	webDoneCondition.wait();
+
+	std::cout << "[http] fetching http://download.zerotier.com/dev/8k ... "; std::cout.flush();
+	HttpClient::GET("http://download.zerotier.com/dev/8k",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0);
+	webDoneCondition.wait();
+
+	std::cout << "[http] fetching http://download.zerotier.com/dev/NOEXIST ... "; std::cout.flush();
+	HttpClient::GET("http://download.zerotier.com/dev/NOEXIST",HttpClient::NO_HEADERS,30,&testHttpHandler,(void *)0);
+	webDoneCondition.wait();
+
+	return 0;
+}
+
 static int testCrypto()
 {
 	unsigned char buf1[16384];
@@ -562,6 +597,7 @@ int main(int argc,char **argv)
 
 	srand((unsigned int)time(0));
 
+	r |= testHttp();
 	r |= testCrypto();
 	r |= testPacket();
 	r |= testOther();