diff --git a/lime/net/URLLoader.hx b/lime/net/URLLoader.hx index f569740a0..432711380 100644 --- a/lime/net/URLLoader.hx +++ b/lime/net/URLLoader.hx @@ -11,6 +11,14 @@ import js.Browser; import js.Lib; #end +#if lime_curl +import lime.net.curl.CURL; +import lime.net.curl.CURLEasy; +import lime.net.curl.CURLCode; +import lime.net.curl.CURLInfo; +import lime.net.curl.CURLOption; +#end + class URLLoader { @@ -26,13 +34,22 @@ class URLLoader { public var onProgress = new EventInt->Int->Void> (); public var onSecurityError = new EventString->Void> (); + #if lime_curl + private var __curl:CURL; + private var __data:String; + #end public function new (request:URLRequest = null) { bytesLoaded = 0; bytesTotal = 0; dataFormat = URLLoaderDataFormat.TEXT; - + + #if lime_curl + __data = ""; + __curl = CURLEasy.init(); + #end + if (request != null) { load (request); @@ -44,7 +61,9 @@ class URLLoader { public function close ():Void { - + #if lime_curl + CURLEasy.cleanup(__curl); + #end } @@ -60,6 +79,8 @@ class URLLoader { #if js requestUrl (request.url, request.method, request.data, request.formatRequestHeaders ()); + #elseif lime_curl + requestUrl (request.url, request.method, request.data, request.formatRequestHeaders ()); #end } @@ -227,6 +248,144 @@ class URLLoader { }; } + #elseif lime_curl + + private function prepareData(data:Dynamic):ByteArray { + + var uri:ByteArray = new ByteArray(); + if (Std.is (data, ByteArray)) { + + var data:ByteArray = cast data; + uri = data; + + } else if (Std.is (data, URLVariables)) { + + var data:URLVariables = cast data; + var tmp:String = ""; + for (p in Reflect.fields (data)) { + + if (tmp.length != 0) tmp += "&"; + tmp += StringTools.urlEncode (p) + "=" + StringTools.urlEncode (Reflect.field (data, p)); + + } + + uri.writeUTFBytes(tmp); + + + } else { + + if (data != null) { + + uri.writeUTFBytes(Std.string(data)); + + } + + } + + return uri; + + } + + private function requestUrl (url:String, method:URLRequestMethod, data:Dynamic, requestHeaders:Array):Void { + + var uri = prepareData(data); + uri.position = 0; + + __data = ""; + bytesLoaded = 0; + bytesTotal = 0; + + CURLEasy.reset(__curl); + CURLEasy.setopt(__curl, URL, url); + + switch(method) { + case HEAD: + CURLEasy.setopt(__curl, NOBODY, true); + case GET: + CURLEasy.setopt(__curl, HTTPGET, true); + case POST: + CURLEasy.setopt(__curl, POST, true); + CURLEasy.setopt(__curl, READFUNCTION, readFunction.bind(_, uri)); + CURLEasy.setopt(__curl, POSTFIELDSIZE, uri.length); + CURLEasy.setopt(__curl, INFILESIZE, uri.length); + case PUT: + CURLEasy.setopt(__curl, UPLOAD, true); + CURLEasy.setopt(__curl, READFUNCTION, readFunction.bind(_, uri)); + CURLEasy.setopt(__curl, INFILESIZE, uri.length); + case _: + CURLEasy.setopt(__curl, CUSTOMREQUEST, cast method); + CURLEasy.setopt(__curl, READFUNCTION, readFunction.bind(_, uri)); + CURLEasy.setopt(__curl, INFILESIZE, uri.length); + } + + var headers:Array = []; + headers.push("Expect: "); // removes the default cURL value + for (requestHeader in requestHeaders) { + + headers.push('${requestHeader.name}: ${requestHeader.value}'); + + } + + CURLEasy.setopt(__curl, HTTPHEADER, headers); + + CURLEasy.setopt(__curl, PROGRESSFUNCTION, progressFunction); + + CURLEasy.setopt(__curl, WRITEFUNCTION, writeFunction); + CURLEasy.setopt(__curl, HEADERFUNCTION, headerFunction); + + CURLEasy.setopt(__curl, SSL_VERIFYPEER, false); + CURLEasy.setopt(__curl, SSL_VERIFYHOST, false); + CURLEasy.setopt(__curl, USERAGENT, "libcurl-agent/1.0"); + + var result = CURLEasy.perform(__curl); + + + var responseCode = CURLEasy.getinfo(__curl, RESPONSE_CODE); + + if (result == CURLCode.OK) { + /* + switch(dataFormat) { + case BINARY: this.data = __data; + default: this.data = __data.asString(); + } + */ + this.data = __data; + onHTTPStatus.dispatch (this, Std.parseInt(responseCode)); + onComplete.dispatch (this); + } else { + onIOError.dispatch (this, "Problem with curl: " + result); + } + + } + + private function writeFunction (output:String, size:Int, nmemb:Int):Int { + + __data += output; + return size * nmemb; + + } + + private function headerFunction (output:String, size:Int, nmemb:Int):Int { + + // TODO + return size * nmemb; + + } + + private function progressFunction (dltotal:Float, dlnow:Float, uptotal:Float, upnow:Float):Int { + + if(upnow>bytesLoaded || dlnow>bytesTotal) { + if(upnow > bytesLoaded) bytesLoaded = Std.int(upnow); + if(dlnow > bytesTotal) bytesTotal = Std.int(dlnow); + onProgress.dispatch(this, bytesLoaded, bytesTotal); + } + return 0; + } + + private function readFunction(max:Int, input:ByteArray):String { + return input == null ? "" : input.readUTFBytes(Std.int(Math.min(max, input.length - input.position))); + } + #end diff --git a/project/Build.xml b/project/Build.xml index 438e0c3ce..4984da429 100644 --- a/project/Build.xml +++ b/project/Build.xml @@ -24,7 +24,7 @@ - + diff --git a/project/lib/curl b/project/lib/curl index 96d4700e3..10e4ec2b1 160000 --- a/project/lib/curl +++ b/project/lib/curl @@ -1 +1 @@ -Subproject commit 96d4700e32543d44062c5453d32d4135c9820960 +Subproject commit 10e4ec2b11d9da022bea17d6009bec6398198ab9 diff --git a/project/src/net/curl/CURLBindings.cpp b/project/src/net/curl/CURLBindings.cpp index aed4a510d..ccee50e94 100644 --- a/project/src/net/curl/CURLBindings.cpp +++ b/project/src/net/curl/CURLBindings.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace lime { @@ -157,7 +158,7 @@ namespace lime { } - static size_t rw_callback (void *ptr, size_t size, size_t nmemb, void *userp) { + static size_t write_callback (void *ptr, size_t size, size_t nmemb, void *userp) { AutoGCRoot* callback = (AutoGCRoot*)userp; @@ -171,6 +172,37 @@ namespace lime { return val_int (val_call3 (callback->get (), str, alloc_int (size), alloc_int (nmemb))); } + + static size_t read_callback (void *buffer, size_t size, size_t nmemb, void *userp) { + + AutoGCRoot* callback = (AutoGCRoot*)userp; + + size_t bytes = size * nmemb; + const char *input = val_string (val_call1 (callback->get (), alloc_int (bytes))); + size_t length = strlen(input); + + if(length <= bytes) bytes = length; + + memcpy(buffer, input, bytes); + + return bytes; + + } + + static size_t progress_callback (void *userp, double dltotal, double dlnow, double ultotal, double ulnow) { + + AutoGCRoot* callback = (AutoGCRoot*)userp; + + value vals[] = { + alloc_float(dltotal), + alloc_float(dlnow), + alloc_float(ultotal), + alloc_float(ulnow), + }; + + return val_int (val_callN (callback->get (), vals, 4)); + + } value lime_curl_easy_setopt (value handle, value option, value parameter) { @@ -366,12 +398,8 @@ namespace lime { case CURLOPT_OPENSOCKETDATA: case CURLOPT_CLOSESOCKETFUNCTION: case CURLOPT_CLOSESOCKETDATA: - case CURLOPT_PROGRESSFUNCTION: - case CURLOPT_PROGRESSDATA: case CURLOPT_XFERINFOFUNCTION: //case CURLOPT_XFERINFODATA: - case CURLOPT_HEADERFUNCTION: - case CURLOPT_HEADERDATA: case CURLOPT_DEBUGFUNCTION: case CURLOPT_DEBUGDATA: case CURLOPT_SSL_CTX_FUNCTION: @@ -406,13 +434,36 @@ namespace lime { //case CURLOPT_READDATA: //case CURLOPT_WRITEDATA: - + //case CURLOPT_HEADERDATA: + //case CURLOPT_PROGRESSDATA: + case CURLOPT_READFUNCTION: + { + AutoGCRoot* callback = new AutoGCRoot (parameter); + code = curl_easy_setopt (curl, type, read_callback); + curl_easy_setopt (curl, CURLOPT_READDATA, callback); + break; + } case CURLOPT_WRITEFUNCTION: { AutoGCRoot* callback = new AutoGCRoot (parameter); - code = curl_easy_setopt (curl, type, rw_callback); - curl_easy_setopt (curl, type == CURLOPT_READFUNCTION ? CURLOPT_READDATA : CURLOPT_WRITEDATA, callback); + code = curl_easy_setopt (curl, type, write_callback); + curl_easy_setopt (curl, CURLOPT_WRITEDATA, callback); + break; + } + case CURLOPT_HEADERFUNCTION: + { + AutoGCRoot* callback = new AutoGCRoot (parameter); + code = curl_easy_setopt (curl, type, write_callback); + curl_easy_setopt (curl, CURLOPT_HEADERDATA, callback); + break; + } + case CURLOPT_PROGRESSFUNCTION: + { + AutoGCRoot* callback = new AutoGCRoot (parameter); + code = curl_easy_setopt (curl, type, progress_callback); + curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, callback); + curl_easy_setopt (curl, CURLOPT_NOPROGRESS, false); break; }