From d125eac288d0e149f6d1ef8d875f829bc0f525cd Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alexis=20Laferri=C3=A8re?= Date: Mon, 29 Jul 2013 14:18:43 -0400 Subject: [PATCH] lib: intro curl module by Matthieu Lucas MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexis Laferrière --- examples/curl_http.nit | 129 +++++++++ lib/curl/curl.nit | 312 ++++++++++++++++++++++ lib/curl/curl_c.nit | 668 ++++++++++++++++++++++++++++++++++++++++++++++ lib/curl/curl_c.nit.args | 1 + tests/sav/curl_http.sav | 1 + tests/sav/test_curl.sav | 64 +++++ tests/test_curl.nit | 244 +++++++++++++++++ 7 files changed, 1419 insertions(+) create mode 100644 examples/curl_http.nit create mode 100644 lib/curl/curl.nit create mode 100644 lib/curl/curl_c.nit create mode 100644 lib/curl/curl_c.nit.args create mode 100644 tests/sav/curl_http.sav create mode 100644 tests/sav/test_curl.sav create mode 100644 tests/test_curl.nit diff --git a/examples/curl_http.nit b/examples/curl_http.nit new file mode 100644 index 0000000..50c46e5 --- /dev/null +++ b/examples/curl_http.nit @@ -0,0 +1,129 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2013 Matthieu Lucas +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Sample of the Curl module. +module curl_http + +import curl + +# Small class to represent an Http Fetcher +class MyHttpFetcher + super CurlCallbacks + + var curl: Curl + + init do self.curl = new Curl + + # Release curl object + fun destroy do self.curl.destroy + + fun request(url: String): nullable CurlRequest do return self.curl.http_request(url) + + # Header callback + redef fun header_callback(line: String) do print "Header_callback : {line}" + + # Body callback + redef fun body_callback(line: String) do print "Body_callback : {line}" + + # Stream callback - Cf : No one is registered + redef fun stream_callback(buffer: String, size: Int, count: Int) do print "Stream_callback : {buffer} - {size} - {count}" +end + + +# Program +if args.length < 2 then + print "Usage {sys.program_name} " +else + + var myHttpFetcher = new MyHttpFetcher + var url = args[1] + + # HTTP Get Request + if args[0] == "GET" then + var getContentRequest = myHttpFetcher.request(url) + if getContentRequest isa CurlHTTPRequest then + + getContentRequest.verbose = false + var getResponse = getContentRequest.execute + + if getResponse isa CurlResponseSuccess then + print "Status code : {getResponse.status_code}" + print "Body : {getResponse.body_str}" + else if getResponse isa CurlResponseFailed then + print "Error code : {getResponse.error_code}" + print "Error msg : {getResponse.error_msg}" + end + + else + print "Wrong init with Curl HTTP request" + end + + # HTTP Post Request + else if args[0] == "POST" then + var postContentRequest = myHttpFetcher.request(url) + if postContentRequest isa CurlHTTPRequest then + + postContentRequest.delegate = myHttpFetcher + var postDatas = new HeaderMap + postDatas["Bugs Bunny"] = "Daffy Duck" + postDatas["Batman"] = "Robin likes special characters @#ùà!è§'(\"é&://,;<>∞~*" + postDatas["Batman"] = "Yes you can set multiple identical keys, but APACHE will consider only once, the last one" + postContentRequest.datas = postDatas + postContentRequest.verbose = false + var postResponse = postContentRequest.execute + + if postResponse isa CurlResponseSuccess then + print "*** Answer ***" + print "Status code : {postResponse.status_code}" + print "Body should be empty, because we decided to manage callbacks : {postResponse.body_str.length}" + else if postResponse isa CurlResponseFailed then + print "Error code : {postResponse.error_code}" + print "Error msg : {postResponse.error_msg}" + end + + else + print "Wrong init with Curl HTTP request" + end + + # HTTP Get to file Request + else if args[0] == "GET_FILE" then + var downloadFileRequest = myHttpFetcher.request(url) + if downloadFileRequest isa CurlHTTPRequest then + + var headers = new HeaderMap + headers["Accept"] = "Moo" + downloadFileRequest.headers = headers + downloadFileRequest.verbose = true + var downloadResponse = downloadFileRequest.download_to_file(null) + + if downloadResponse isa CurlFileResponseSuccess then + print "*** Answer ***" + print "Status code : {downloadResponse.status_code}" + print "Size downloaded : {downloadResponse.size_download}" + else if downloadResponse isa CurlResponseFailed then + print "Error code : {downloadResponse.error_code}" + print "Error msg : {downloadResponse.error_msg}" + end + + else + print "Wrong init with Curl HTTP request" + end + + # Program logic + else + print "Usage : Method[POST, GET, GET_FILE]" + end +end diff --git a/lib/curl/curl.nit b/lib/curl/curl.nit new file mode 100644 index 0000000..323e660 --- /dev/null +++ b/lib/curl/curl.nit @@ -0,0 +1,312 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2013 Matthieu Lucas +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Network functionnalities based on Curl_c module. +module curl + +import curl_c + +# Top level of Curl +class Curl + protected var prim_curl: CCurl + + init + do + self.prim_curl = new CCurl.easy_init + assert curlInstance:self.prim_curl.is_init else + print "Curl must be instancied to be used" + end + end + + # Check for correct initialization + fun is_ok: Bool do return self.prim_curl.is_init + + # Get an HTTP Request object to perform your own + fun http_request(url: String): nullable CurlRequest + do + var err: CURLCode + err = self.prim_curl.easy_setopt(new CURLOption.follow_location, 1) + if not err.is_ok then return null + + err = self.prim_curl.easy_setopt(new CURLOption.url, url) + if not err.is_ok then return null + + return new CurlHTTPRequest(url, self) + end + + # Release Curl instance + fun destroy do self.prim_curl.easy_clean +end + +# CURL Request +class CurlRequest + super CCurlCallbacks + + var url: String + var headers: nullable HeaderMap writable = null + var datas: nullable HeaderMap writable = null + var delegate: nullable CurlCallbacks writable = null + var verbose: Bool writable = false + private var curl: nullable Curl + + # Launch request method + fun execute: CurlResponse is abstract + + # Intern perform method, lowest level of request launching + private fun perform: nullable CurlResponse + do + if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized") + + var err: CURLCode + + if self.datas != null then + var postdatas = self.datas.to_url_encoded(self.curl.prim_curl) + err = self.curl.prim_curl.easy_setopt(new CURLOption.postfields, postdatas) + if not err.is_ok then return answer_failure(err.to_i, err.to_s) + end + + if self.headers != null then + var headers_joined = self.headers.join_pairs(": ") + err = self.curl.prim_curl.easy_setopt(new CURLOption.httpheader, headers_joined.to_curlslist) + if not err.is_ok then return answer_failure(err.to_i, err.to_s) + end + + err = self.curl.prim_curl.easy_setopt(new CURLOption.verbose, self.verbose) + if not err.is_ok then return answer_failure(err.to_i, err.to_s) + + err = self.curl.prim_curl.easy_perform + if not err.is_ok then return answer_failure(err.to_i, err.to_s) + + return null + end + + # Intern method with return a failed answer with given code and message + private fun answer_failure(error_code: Int, error_msg: String): CurlResponse + do + return new CurlResponseFailed(error_code, error_msg) + end +end + +# CURL HTTP Request +class CurlHTTPRequest + super CurlRequest + + # Execute HTTP request with settings configured through attribute + redef fun execute: CurlResponse + do + if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized") + + var success_response: CurlResponseSuccess = new CurlResponseSuccess + + var callback_receiver: CurlCallbacks = success_response + if self.delegate != null then callback_receiver = self.delegate.as(not null) + + var err: CURLCode + + err = self.curl.prim_curl.register_callback(callback_receiver, new CURLCallbackType.header) + if not err.is_ok then return answer_failure(err.to_i, err.to_s) + + err = self.curl.prim_curl.register_callback(callback_receiver, new CURLCallbackType.body) + if not err.is_ok then return answer_failure(err.to_i, err.to_s) + + var err_resp = perform + if err_resp != null then return err_resp + + var st_code = self.curl.prim_curl.easy_getinfo_long(new CURLInfoLong.response_code) + if not st_code == null then success_response.status_code = st_code.response + + return success_response + end + + # Download to file given resource + fun download_to_file(output_file_name: nullable String): CurlResponse + do + var success_response: CurlFileResponseSuccess = new CurlFileResponseSuccess + + var callback_receiver: CurlCallbacks = success_response + if self.delegate != null then callback_receiver = self.delegate.as(not null) + + var err: CURLCode + err = self.curl.prim_curl.register_callback(callback_receiver, new CURLCallbackType.header) + if not err.is_ok then return answer_failure(err.to_i, err.to_s) + + err = self.curl.prim_curl.register_callback(callback_receiver, new CURLCallbackType.stream) + if not err.is_ok then return answer_failure(err.to_i, err.to_s) + + var opt_name:nullable String + if not output_file_name == null then + opt_name = output_file_name + else if not self.url.substring(self.url.length-1, self.url.length) == "/" then + opt_name = self.url.basename("") + else + return answer_failure(0, "Unable to extract file name, please specify one") + end + + success_response.i_file = new OFile.open(opt_name.to_cstring) + if not success_response.i_file.is_valid then + success_response.i_file.close + return answer_failure(0, "Unable to create associated file") + end + + var err_resp = perform + if err_resp != null then return err_resp + + var st_code = self.curl.prim_curl.easy_getinfo_long(new CURLInfoLong.response_code) + if not st_code == null then success_response.status_code = st_code.response + + var speed = self.curl.prim_curl.easy_getinfo_double(new CURLInfoDouble.speed_download) + if not speed == null then success_response.speed_download = speed.response + + var size = self.curl.prim_curl.easy_getinfo_double(new CURLInfoDouble.size_download) + if not size == null then success_response.size_download = size.response + + var time = self.curl.prim_curl.easy_getinfo_double(new CURLInfoDouble.total_time) + if not time == null then success_response.total_time = time.response + + success_response.i_file.close + + return success_response + end +end + +# Callbacks Interface, allow you to manage in your way the different streams +interface CurlCallbacks + super CCurlCallbacks +end + +# Abstract Curl request response +abstract class CurlResponse +end + +# Failed Response Class returned when errors during configuration are raised +class CurlResponseFailed + super CurlResponse + + var error_code: Int + var error_msg: String + + init (err_code: Int, err_msg: String) + do + self.error_code = err_code + self.error_msg = err_msg + end +end + +# Success Abstract Response Success Class +abstract class CurlResponseSuccessIntern + super CurlCallbacks + super CurlResponse + + var headers: HashMap[String, String] = new HashMap[String, String] + var status_code: Int = 0 + + # Receive headers from request due to headers callback registering + redef fun header_callback(line: String) + do + var splitted = line.split_with(':') + if splitted.length > 1 then + var key = splitted.shift + self.headers[key] = splitted.to_s + end + end +end + +# Success Response Class of a basic response +class CurlResponseSuccess + super CurlResponseSuccessIntern + + var body_str: String = "" + + # Receive body from request due to body callback registering + redef fun body_callback(line: String) + do + self.body_str = "{self.body_str}{line}" + end +end + +# Success Response Class of a downloaded File +class CurlFileResponseSuccess + super CurlResponseSuccessIntern + + var speed_download: Int = 0 + var size_download: Int = 0 + var total_time: Int = 0 + private var i_file: nullable OFile = null + + # Receive bytes stream from request due to stream callback registering + redef fun stream_callback(buffer: String, size: Int, count: Int) + do + self.i_file.write(buffer, size, count) + end +end + +# Pseudo map associating Strings to Strings, +# each key can have multiple associations +# and the order of insertion is important. +class HeaderMap + private var arr = new Array[Couple[String, String]] + + fun []=(k, v: String) do arr.add(new Couple[String, String](k, v)) + + fun [](k: String): Array[String] + do + var res = new Array[String] + for c in arr do if c.first == k then res.add(c.second) + return res + end + + fun iterate !each(k, v: String) + do + var i = arr.iterator + while i.is_ok do + var item = i.item + each(item.first, item.second) + i.next + end + end + + # Convert Self to a single string used to post http fields + fun to_url_encoded(curl: CCurl): String + do + assert curlNotInitialized: curl.is_init else + print "to_url_encoded required a valid instance of CCurl Object." + end + var str: String = "" + var length = self.length + var i = 0 + for k, v in self do + if k.length > 0 then + k = curl.escape(k) + v = curl.escape(v) + str = "{str}{k}={v}" + if i < length-1 then str = "{str}&" + end + i += 1 + end + return str + end + + # Concatenate couple of 'key value' separated by 'sep' in Array + fun join_pairs(sep: String): Array[String] + do + var col = new Array[String] + for k, v in self do col.add("{k}{sep}{v}") + return col + end + + fun length: Int do return arr.length + fun is_empty: Bool do return arr.is_empty +end diff --git a/lib/curl/curl_c.nit b/lib/curl/curl_c.nit new file mode 100644 index 0000000..61525a6 --- /dev/null +++ b/lib/curl/curl_c.nit @@ -0,0 +1,668 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2013 Matthieu Lucas +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Binding of C libCurl which allow us to interact with network. +module curl_c + +import pipeline + +in "C header" `{ + #include + #include + #include + + typedef enum { + CURLcallbackTypeHeader, + CURLcallbackTypeBody, + CURLcallbackTypeStream, + CURLcallbackTypeRead, + } CURLcallbackType; + + typedef struct { + CCurlCallbacks delegate; + CURLcallbackType type; + } CURLCallbackDatas; + + typedef struct { + char *data; + int len; + int pos; + } CURLCallbackReadDatas; +`} + +in "C body" `{ + // Callbacks method for Header, Body, Stream. + size_t nit_curl_callback_func(void *buffer, size_t size, size_t count, CURLCallbackDatas *datas){ + if(datas->type == CURLcallbackTypeHeader){ + char *line_c = (char*)buffer; + String line_o = new_String_copy_from_native(line_c); + CCurlCallbacks_header_callback(datas->delegate, line_o); + } + else if(datas->type == CURLcallbackTypeBody){ + char *line_c = (char*)buffer; + String line_o = new_String_copy_from_native(line_c); + CCurlCallbacks_body_callback(datas->delegate, line_o); + } + else if(datas->type == CURLcallbackTypeStream){ + char *line_c = (char*)buffer; + String line_o = new_String_from_cstring(line_c); + CCurlCallbacks_stream_callback(datas->delegate, line_o, size, count); + } + return count; + } + // Callback method to read datas from buffer. + size_t nit_curl_callback_read_func(void *buffer, size_t size, size_t count, CURLCallbackReadDatas *datas){ + int len = datas->len - datas->pos; + if(len > size * count) len = size * count; + memcpy(buffer, datas->data + datas->pos, len); + datas->pos += len; + return len; + } +`} + +# CURL Extern Type, reproduce CURL low level behaviors +extern CCurl `{ CURL * `} + # Constructor, CURL low level initializer + new easy_init `{ return curl_easy_init(); `} + # Check for correct initialization + fun is_init:Bool `{ return (recv != NULL); `} + # Easy Clean / Release CURL instance + fun easy_clean `{ curl_easy_cleanup( recv ); `} + # Perform the transfer described by setted options + fun easy_perform:CURLCode `{ return curl_easy_perform( recv ); `} + # Set options to tell CURL how to behave. Obj parameter type can be Int, Bool, String, OFile, CURLSList. + fun easy_setopt(opt: CURLOption, obj: Object):CURLCode + do + if obj isa Int then return i_setopt_int(opt, obj) + if obj isa Bool and obj == true then return i_setopt_int(opt, 1) + if obj isa Bool and obj == false then return i_setopt_int(opt, 0) + if obj isa String then return i_setopt_string(opt, obj) + if obj isa OFile then return i_setopt_file(opt, obj) + if obj isa CURLSList then return i_setopt_slist(opt, obj) + return once new CURLCode.unknown_option + end + # Internal method to set options to CURL using OFile parameter. + private fun i_setopt_file(opt: CURLOption, fl: OFile):CURLCode `{ return curl_easy_setopt( recv, opt, fl); `} + # Internal method to set options to CURL using Int parameter. + private fun i_setopt_int(opt: CURLOption, num: Int):CURLCode `{ return curl_easy_setopt( recv, opt, num); `} + # Internal method to set options to CURL using CURLSList parameter. + private fun i_setopt_slist(opt: CURLOption, list: CURLSList):CURLCode `{ return curl_easy_setopt( recv, opt, list); `} + # Internal method to set options to CURL using String parameter. + private fun i_setopt_string(opt: CURLOption, str: String):CURLCode import String::to_cstring `{ + char *rStr = String_to_cstring(str); + return curl_easy_setopt( recv, opt, rStr); + `} + # Request Chars internal information from the CURL session + fun easy_getinfo_chars(opt: CURLInfoChars):nullable CURLInfoResponseString + do + var answ = new CURLInfoResponseString + if not i_getinfo_chars(opt, answ).is_ok then return null + return answ + end + # Internal method used to get String object information initially knowns as C Chars type + private fun i_getinfo_chars(opt: CURLInfoChars, res: CURLInfoResponseString):CURLCode import CURLInfoResponseString::response= `{ + char *r = NULL; + CURLcode c = curl_easy_getinfo( recv, opt, &r); + if((c == CURLE_OK) && r != NULL){ + String ro = new_String_copy_from_native(r); + CURLInfoResponseString_response__assign( res, ro); + } + return c; + `} + # Request Long internal information from the CURL session + fun easy_getinfo_long(opt: CURLInfoLong):nullable CURLInfoResponseLong + do + var answ = new CURLInfoResponseLong + if not i_getinfo_long(opt, answ).is_ok then return null + return answ + end + # Internal method used to get Int object information initially knowns as C Long type + private fun i_getinfo_long(opt: CURLInfoLong, res: CURLInfoResponseLong):CURLCode import CURLInfoResponseLong::response= `{ + long *r = NULL; + r = malloc(sizeof(long)); + CURLcode c = curl_easy_getinfo( recv, opt, r); + if((c == CURLE_OK) && r != NULL) CURLInfoResponseLong_response__assign( res, *r); + free(r); + return c; + `} + # Request Double internal information from the CURL session + fun easy_getinfo_double(opt: CURLInfoDouble):nullable CURLInfoResponseDouble + do + var answ = new CURLInfoResponseDouble + if not i_getinfo_double(opt, answ).is_ok then return null + return answ + end + # Internal method used to get Int object information initially knowns as C Double type + private fun i_getinfo_double(opt: CURLInfoDouble, res: CURLInfoResponseDouble):CURLCode import CURLInfoResponseDouble::response= `{ + double *r = NULL; + r = malloc(sizeof(double)); + CURLcode c = curl_easy_getinfo( recv, opt, r); + if((c == CURLE_OK) && r != NULL) CURLInfoResponseDouble_response__assign( res, *r); + free(r); + return c; + `} + # Request SList internal information from the CURL session + fun easy_getinfo_slist(opt: CURLInfoSList):nullable CURLInfoResponseArray + do + var answ = new CURLInfoResponseArray + if not i_getinfo_slist(opt, answ).is_ok then return null + answ.response = answ.prim_response.to_a + answ.prim_response.destroy + return answ + end + # Internal method used to get Array[String] object information initially knowns as C SList type + private fun i_getinfo_slist(opt: CURLInfoSList, res: CURLInfoResponseArray):CURLCode import CURLInfoResponseArray::prim_response=`{ + struct curl_slist* csl = NULL; + CURLcode ce = curl_easy_getinfo( recv, opt, &csl); + CURLInfoResponseArray_prim_response__assign(res, csl); + return ce; + `} + # Register delegate to get callbacks about the CURL transfer performed + fun register_callback(delegate: CCurlCallbacks, cbtype: CURLCallbackType):CURLCode + do + if once [new CURLCallbackType.header, new CURLCallbackType.body, new CURLCallbackType.stream, new CURLCallbackType.read].has(cbtype) then + return i_register_callback(delegate, cbtype) + end + return once new CURLCode.unknown_option + end + # Register delegate to read datas from given buffer + fun register_read_datas_callback(delegate: CCurlCallbacks, datas: String):CURLCode + do + if datas.length > 0 then return i_register_read_datas_callback(delegate, datas, datas.length) + return once new CURLCode.unknown_option + end + # Internal method used to configure read callback + private fun i_register_read_datas_callback(delegate: CCurlCallbacks, datas: String, size: Int):CURLCode import String::to_cstring, String::copy_from_native `{ + CURLCallbackReadDatas *d = NULL; + d = malloc(sizeof(CURLCallbackReadDatas)); + d->data = (char*)String_to_cstring(datas); + d->len = size; + d->pos = 0; + return curl_easy_setopt( recv, CURLOPT_READDATA, d); + + `} + # Internal method used to configure callbacks in terms of given type + private fun i_register_callback(delegate: CCurlCallbacks, cbtype: CURLCallbackType):CURLCode is extern import CCurlCallbacks::header_callback, CCurlCallbacks::body_callback, CCurlCallbacks::stream_callback `{ + CURLCallbackDatas *d = malloc(sizeof(CURLCallbackDatas)); + CCurlCallbacks_incr_ref(delegate); + d->type = cbtype; + d->delegate = delegate; + CURLcode e; + switch(cbtype){ + case CURLcallbackTypeHeader: + e = curl_easy_setopt( recv, CURLOPT_HEADERFUNCTION, &nit_curl_callback_func); + if(e != CURLE_OK) return e; + e = curl_easy_setopt( recv, CURLOPT_WRITEHEADER, d); + break; + case CURLcallbackTypeBody: + case CURLcallbackTypeStream: + e = curl_easy_setopt( recv, CURLOPT_WRITEFUNCTION, &nit_curl_callback_func); + if(e != CURLE_OK) return e; + e = curl_easy_setopt( recv, CURLOPT_WRITEDATA, d); + break; + case CURLcallbackTypeRead: + e = curl_easy_setopt( recv, CURLOPT_READFUNCTION, &nit_curl_callback_read_func); + default: + break; + } + return e; + `} + # Convert given string to URL encoded string + fun escape(url: String):String `{ + char *orig_url, *encoded_url = NULL; + orig_url = String_to_cstring(url); + encoded_url = curl_easy_escape( recv, orig_url, strlen(orig_url)); + String b_url = new_String_copy_from_native(encoded_url); + curl_free(encoded_url); + return b_url; + `} +end + +# FILE Extern type, reproduce basic FILE I/O +extern OFile `{ FILE* `} + # Open / Create a file from given name + new open(str: NativeString) `{ return fopen(str, "wb"); `} + # Check for File validity + fun is_valid:Bool `{ return recv != NULL; `} + # Internal method to write to the current file + private fun n_write(buffer: NativeString, size: Int, count: Int):Int `{ return fwrite(buffer, size, count, recv); `} + # Write datas to the current file + fun write(buffer: String, size: Int, count: Int):Int + do + if is_valid == true then return n_write(buffer.to_cstring, size, count) + return 0 + end + # Internal method to close the current file + private fun n_close:Int `{ return fclose(recv); `} + # Close the current file + fun close:Bool + do + if is_valid == true then return n_close == 0 + return false + end +end + +# Interface for internal information callbacks methods +interface CCurlCallbacks + fun header_callback(line: String) is abstract + fun body_callback(line: String) is abstract + fun stream_callback(buffer: String, size: Int, count: Int) is abstract +end + +# Extern Type to reproduce Enum of available Callback type +extern CURLCallbackType `{ CURLcallbackType `} + new header `{ return CURLcallbackTypeHeader; `} + new body `{ return CURLcallbackTypeBody; `} + new stream `{ return CURLcallbackTypeStream; `} + new read `{ return CURLcallbackTypeRead; `} + fun to_i:Int `{ return recv; `} +end + +# CURL Code binding and helpers +extern CURLCode `{ CURLcode `} + new unknown_option `{ return CURLE_UNKNOWN_OPTION; `} + new unsupported_protocol `{ return CURLE_UNSUPPORTED_PROTOCOL; `} + new ok `{ return CURLE_OK; `} + new failed_init `{ return CURLE_FAILED_INIT; `} + fun code:Int `{ return recv; `} + fun is_ok:Bool `{ return recv == CURLE_OK; `} + fun is_valid_protocol:Bool `{ return recv == CURLE_UNSUPPORTED_PROTOCOL; `} + fun is_valid_init:Bool `{ return recv == CURLE_FAILED_INIT; `} + fun to_i:Int do return code end + redef fun to_s `{ + char *c = (char*)curl_easy_strerror(recv); + return new_String_copy_from_native(c); + `} +end + +# Extern Type of the Linked list type of CURL +extern CURLSList `{ struct curl_slist * `} + # Empty constructor which allow us to avoid the use of Nit NULLABLE type + private new `{ return NULL; `} + # Constructor allow us to get list instancied by appending an element inside. + new with_str(s: String) import String::to_cstring `{ + struct curl_slist *l = NULL; + l = curl_slist_append(l, String_to_cstring(s)); + return l; + `} + # Check for initialization + fun is_init:Bool `{ return (recv != NULL); `} + # Append an element in the linked list + fun append(key: String) import String::to_cstring `{ + char *k = String_to_cstring(key); + curl_slist_append(recv, (char*)k); + `} + # Internal method to check for reachability of current data + private fun i_data_reachable(c: CURLSList):Bool `{ return (c != NULL && c->data != NULL); `} + # Internal method to check for reachability of next element + private fun i_next_reachable(c: CURLSList):Bool `{ return (c != NULL && c->next != NULL); `} + # Internal method to get current data + private fun i_data(c: CURLSList):String `{ return new_String_from_cstring(c->data); `} + # Internal method to get next element + private fun i_next(c: CURLSList):CURLSList `{ return c->next; `} + # Convert current low level List to an Array[String] object + fun to_a:Array[String] + do + var r = new Array[String] + var cursor = self + loop + if i_data_reachable(cursor) != true then break + r.add(i_data(cursor)) + cursor = i_next(cursor) + end + return r + end + # Release allocated memory + fun destroy `{ curl_slist_free_all(recv); `} +end + +redef class Collection[E] + # Convert Collection[String] to CURLSList + fun to_curlslist: CURLSList + do + assert collectionItemType: self isa Collection[String] else + print "Collection item must be strings." + end + var primList = new CURLSList.with_str(self.first) + for s in self.skip_head(1) do primList.append(s) + return primList + end +end + +# Array Response type of CCurl::easy_getinfo method +class CURLInfoResponseArray + var response:Array[String] = new Array[String] + private var prim_response:CURLSList = new CURLSList +end + +# Long Response type of CCurl::easy_getinfo method +class CURLInfoResponseLong + var response:Int=0 +end + +# Double Response type of CCurl::easy_getinfo method +class CURLInfoResponseDouble + var response:Int=0 +end + +# String Response type of CCurl:easy_getinfo method +class CURLInfoResponseString + var response:String = "" +end + +# Reproduce Enum of available CURL SList information, used for CCurl::easy_getinfo +extern CURLInfoSList `{ CURLINFO `} + new ssl_engines `{ return CURLINFO_SSL_ENGINES; `} + new cookielist `{ return CURLINFO_COOKIELIST; `} +end + +# Reproduce Enum of available CURL Long information, used for CCurl::easy_getinfo +extern CURLInfoLong `{ CURLINFO `} + new response_code `{ return CURLINFO_RESPONSE_CODE; `} + new header_size `{ return CURLINFO_HEADER_SIZE; `} + new http_connectcode `{ return CURLINFO_HTTP_CONNECTCODE; `} + new filetime `{ return CURLINFO_FILETIME; `} + new redirect_count `{ return CURLINFO_REDIRECT_COUNT; `} + new request_size `{ return CURLINFO_REQUEST_SIZE; `} + new ssl_verifyresult `{ return CURLINFO_SSL_VERIFYRESULT; `} + new httpauth_avail `{ return CURLINFO_HTTPAUTH_AVAIL; `} + new proxyauth_avail `{ return CURLINFO_PROXYAUTH_AVAIL; `} + new os_errno `{ return CURLINFO_OS_ERRNO; `} + new num_connects `{ return CURLINFO_NUM_CONNECTS; `} + new primary_port `{ return CURLINFO_PRIMARY_PORT; `} + new local_port `{ return CURLINFO_LOCAL_PORT; `} + new lastsocket `{ return CURLINFO_LASTSOCKET; `} + new condition_unmet `{ return CURLINFO_CONDITION_UNMET; `} + new rtsp_client_cseq `{ return CURLINFO_RTSP_CLIENT_CSEQ; `} + new rtsp_server_cseq `{ return CURLINFO_RTSP_SERVER_CSEQ; `} + new rtsp_cseq_recv `{ return CURLINFO_RTSP_CSEQ_RECV; `} +end + +# Reproduce Enum of available CURL Double information, used for CCurl::easy_getinfo +extern CURLInfoDouble `{ CURLINFO `} + new total_time `{ return CURLINFO_TOTAL_TIME; `} + new namelookup_time `{ return CURLINFO_NAMELOOKUP_TIME; `} + new connect_time `{ return CURLINFO_CONNECT_TIME; `} + new appconnect_time `{ return CURLINFO_APPCONNECT_TIME; `} + new pretransfer_time `{ return CURLINFO_PRETRANSFER_TIME; `} + new starttransfer_time `{ return CURLINFO_STARTTRANSFER_TIME; `} + new redirect_time `{ return CURLINFO_REDIRECT_TIME; `} + new size_upload `{ return CURLINFO_SIZE_UPLOAD; `} + new size_download `{ return CURLINFO_SIZE_DOWNLOAD; `} + new speed_download `{ return CURLINFO_SPEED_DOWNLOAD; `} + new speed_upload `{ return CURLINFO_SPEED_UPLOAD; `} + new content_length_download `{ return CURLINFO_CONTENT_LENGTH_DOWNLOAD; `} + new content_length_upload `{ return CURLINFO_CONTENT_LENGTH_UPLOAD; `} +end + +# Reproduce Enum of available CURL Chars information, used for CCurl::easy_getinfo +extern CURLInfoChars `{ CURLINFO `} + new content_type `{ return CURLINFO_CONTENT_TYPE; `} + new effective_url `{ return CURLINFO_EFFECTIVE_URL; `} + new redirect_url `{ return CURLINFO_REDIRECT_URL; `} + new primary_ip `{ return CURLINFO_PRIMARY_IP; `} + new local_ip `{ return CURLINFO_LOCAL_IP; `} + new ftp_entry_path `{ return CURLINFO_FTP_ENTRY_PATH; `} + new rtsp_session_id `{ return CURLINFO_RTSP_SESSION_ID; `} + new private_data `{ return CURLINFO_PRIVATE; `} +end + +# Reproduce Enum of HTTP Status Code +extern CURLStatusCode `{ int `} + new proceed `{ return 100; `} + new switching_protocols `{ return 101; `} + new ok `{ return 200; `} + new created `{ return 201; `} + new accepted `{ return 202; `} + new non_authoritative_information `{ return 203; `} + new no_content `{ return 204; `} + new reset_content `{ return 205; `} + new partial_content `{ return 206; `} + new multiple_choices `{ return 300; `} + new moved_permanently `{ return 301; `} + new moved_temporarily `{ return 302; `} + new see_other `{ return 303; `} + new not_modified `{ return 304; `} + new use_proxy `{ return 305; `} + new bad_request `{ return 400; `} + new unauthorized `{ return 401; `} + new payment_required `{ return 402; `} + new forbidden `{ return 403; `} + new not_found `{ return 404; `} + new method_not_allowed `{ return 405; `} + new not_acceptable `{ return 406; `} + new proxy_authentication_required `{ return 407; `} + new request_timeout `{ return 408; `} + new conflict `{ return 409; `} + new gone `{ return 410; `} + new length_required `{ return 411; `} + new precondition_failed `{ return 412; `} + new request_entity_too_large `{ return 413; `} + new request_uri_too_large `{ return 414; `} + new unsupported_media_type `{ return 415; `} + new internal_server_error `{ return 500; `} + new not_implemented `{ return 501; `} + new bad_gateway `{ return 502; `} + new service_unavailable `{ return 503; `} + new gateway_timeout `{ return 504; `} + new http_version_not_supported `{ return 505; `} + fun to_i:Int `{ return recv; `} +end + +# Reproduce Enum of CURL Options usable, used for CCurl::easy_setopt +extern CURLOption `{ CURLoption `} + new write_function `{ return CURLOPT_WRITEFUNCTION; `} + new write_data `{ return CURLOPT_WRITEDATA; `} +# new `{ return CURLOPT_FILE; `} + new url `{ return CURLOPT_URL; `} +# new `{ return CURLOPT_PORT; `} +# new `{ return CURLOPT_PROXY; `} +# new `{ return CURLOPT_USERPWD; `} +# new `{ return CURLOPT_PROXYUSERPWD; `} +# new `{ return CURLOPT_RANGE; `} +# new `{ return CURLOPT_INFILE; `} +# new `{ return CURLOPT_ERRORBUFFER; `} +# new `{ return CURLOPT_WRITEFUNCTION; `} +# new `{ return CURLOPT_READFUNCTION; `} +# new `{ return CURLOPT_TIMEOUT; `} +# new `{ return CURLOPT_INFILESIZE; `} + new postfields `{ return CURLOPT_POSTFIELDS; `} +# new `{ return CURLOPT_REFERER; `} +# new `{ return CURLOPT_FTPPORT; `} +# new `{ return CURLOPT_USERAGENT; `} +# new `{ return CURLOPT_LOW_SPEED_LIMIT; `} +# new `{ return CURLOPT_LOW_SPEED_TIME; `} +# new `{ return CURLOPT_RESUME_FROM; `} +# new `{ return CURLOPT_COOKIE; `} + new httpheader `{ return CURLOPT_HTTPHEADER; `} +# new `{ return CURLOPT_HTTPPOST; `} +# new `{ return CURLOPT_SSLCERT; `} +# new `{ return CURLOPT_KEYPASSWD; `} +# new `{ return CURLOPT_CRLF; `} +# new `{ return CURLOPT_QUOTE; `} +# new `{ return CURLOPT_WRITEHEADER; `} +# new `{ return CURLOPT_COOKIEFILE; `} +# new `{ return CURLOPT_SSLVERSION; `} +# new `{ return CURLOPT_TIMECONDITION; `} +# new `{ return CURLOPT_TIMEVALUE; `} +# new `{ return CURLOPT_CUSTOMREQUEST; `} +# new `{ return CURLOPT_STDERR; `} +# new `{ return CURLOPT_POSTQUOTE; `} +# new `{ return CURLOPT_WRITEINFO; `} /* DEPRECATED, do not use! */ + new verbose `{ return CURLOPT_VERBOSE; `} # talk a lot + new header `{ return CURLOPT_HEADER; `} # throw the header out too + new no_progress `{ return CURLOPT_NOPROGRESS; `} # shut off the progress meter + new no_body `{ return CURLOPT_NOBODY; `} # use HEAD to get http document + new fail_on_error `{ return CURLOPT_FAILONERROR; `} # no output on http error codes >= 300 + new upload `{ return CURLOPT_UPLOAD; `} # this is an upload + new post `{ return CURLOPT_POST; `} # HTTP POST method + new dir_list_only `{ return CURLOPT_DIRLISTONLY; `} # bare names when listing directories + new append `{ return CURLOPT_APPEND; `} # Append instead of overwrite on upload! +# new `{ return CURLOPT_NETRC; `} + new follow_location `{ return CURLOPT_FOLLOWLOCATION; `} # use Location: Luke! + new transfert_text `{ return CURLOPT_TRANSFERTEXT; `} # transfer data in text/ASCII format + new put `{ return CURLOPT_PUT; `} # HTTP PUT */ +# new `{ return CURLOPT_PROGRESSFUNCTION; `} +# new `{ return CURLOPT_PROGRESSDATA; `} +# new `{ return CURLOPT_AUTOREFERER; `} +# new `{ return CURLOPT_PROXYPORT; `} +# new `{ return CURLOPT_POSTFIELDSIZE; `} +# new `{ return CURLOPT_HTTPPROXYTUNNEL; `} +# new `{ return CURLOPT_INTERFACE; `} +# new `{ return CURLOPT_KRBLEVEL; `} +# new `{ return CURLOPT_SSL_VERIFYPEER; `} +# new `{ return CURLOPT_CAINFO; `} +# new `{ return CURLOPT_MAXREDIRS; `} +# new `{ return CURLOPT_FILETIME; `} +# new `{ return CURLOPT_TELNETOPTIONS; `} +# new `{ return CURLOPT_MAXCONNECTS; `} +# new `{ return CURLOPT_CLOSEPOLICY; `} /* DEPRECATED, do not use! */ +# new `{ return CURLOPT_FRESH_CONNECT; `} +# new `{ return CURLOPT_FORBID_REUSE; `} +# new `{ return CURLOPT_RANDOM_FILE; `} +# new `{ return CURLOPT_EGDSOCKET; `} +# new `{ return CURLOPT_CONNECTTIMEOUT; `} +# new `{ return CURLOPT_HEADERFUNCTION; `} +# new `{ return CURLOPT_HTTPGET; `} +# new `{ return CURLOPT_SSL_VERIFYHOST; `} +# new `{ return CURLOPT_COOKIEJAR; `} +# new `{ return CURLOPT_SSL_CIPHER_LIST; `} +# new `{ return CURLOPT_HTTP_VERSION; `} +# new `{ return CURLOPT_FTP_USE_EPSV; `} +# new `{ return CURLOPT_SSLCERTTYPE; `} +# new `{ return CURLOPT_SSLKEY; `} +# new `{ return CURLOPT_SSLKEYTYPE; `} +# new `{ return CURLOPT_SSLENGINE; `} +# new `{ return CURLOPT_SSLENGINE_DEFAULT; `} +# new `{ return CURLOPT_DNS_USE_GLOBAL_CACHE; `} /* DEPRECATED, do not use! */ +# new `{ return CURLOPT_DNS_CACHE_TIMEOUT; `} +# new `{ return CURLOPT_PREQUOTE; `} +# new `{ return CURLOPT_DEBUGFUNCTION; `} +# new `{ return CURLOPT_DEBUGDATA; `} +# new `{ return CURLOPT_COOKIESESSION; `} +# new `{ return CURLOPT_CAPATH; `} +# new `{ return CURLOPT_BUFFERSIZE; `} +# new `{ return CURLOPT_NOSIGNAL; `} +# new `{ return CURLOPT_SHARE; `} +# new `{ return CURLOPT_PROXYTYPE; `} +# new `{ return CURLOPT_ACCEPT_ENCODING; `} +# new `{ return CURLOPT_PRIVATE; `} +# new `{ return CURLOPT_HTTP200ALIASES; `} +# new `{ return CURLOPT_UNRESTRICTED_AUTH; `} +# new `{ return CURLOPT_FTP_USE_EPRT; `} +# new `{ return CURLOPT_HTTPAUTH; `} +# new `{ return CURLOPT_SSL_CTX_FUNCTION; `} +# new `{ return CURLOPT_SSL_CTX_DATA; `} +# new `{ return CURLOPT_FTP_CREATE_MISSING_DIRS; `} +# new `{ return CURLOPT_PROXYAUTH; `} +# new `{ return CURLOPT_FTP_RESPONSE_TIMEOUT; `} +# new `{ return CURLOPT_IPRESOLVE; `} +# new `{ return CURLOPT_MAXFILESIZE; `} +# new `{ return CURLOPT_INFILESIZE_LARGE; `} +# new `{ return CURLOPT_RESUME_FROM_LARGE; `} +# new `{ return CURLOPT_MAXFILESIZE_LARGE; `} +# new `{ return CURLOPT_NETRC_FILE; `} +# new `{ return CURLOPT_USE_SSL; `} +# new `{ return CURLOPT_POSTFIELDSIZE_LARGE; `} +# new `{ return CURLOPT_TCP_NODELAY; `} +# new `{ return CURLOPT_FTPSSLAUTH; `} +# new `{ return CURLOPT_IOCTLFUNCTION; `} +# new `{ return CURLOPT_IOCTLDATA; `} +# new `{ return CURLOPT_FTP_ACCOUNT; `} +# new `{ return CURLOPT_COOKIELIST; `} +# new `{ return CURLOPT_IGNORE_CONTENT_LENGTH; `} +# new `{ return CURLOPT_FTP_SKIP_PASV_IP; `} +# new `{ return CURLOPT_FTP_FILEMETHOD; `} +# new `{ return CURLOPT_LOCALPORT; `} +# new `{ return CURLOPT_LOCALPORTRANGE; `} +# new `{ return CURLOPT_CONNECT_ONLY; `} +# new `{ return CURLOPT_CONV_FROM_NETWORK_FUNCTION; `} +# new `{ return CURLOPT_CONV_TO_NETWORK_FUNCTION; `} +# new `{ return CURLOPT_CONV_FROM_UTF8_FUNCTION; `} +# new `{ return CURLOPT_MAX_SEND_SPEED_LARGE; `} +# new `{ return CURLOPT_MAX_RECV_SPEED_LARGE; `} +# new `{ return CURLOPT_FTP_ALTERNATIVE_TO_USER; `} +# new `{ return CURLOPT_SOCKOPTFUNCTION; `} +# new `{ return CURLOPT_SOCKOPTDATA; `} +# new `{ return CURLOPT_SSL_SESSIONID_CACHE; `} +# new `{ return CURLOPT_SSH_AUTH_TYPES; `} +# new `{ return CURLOPT_SSH_PUBLIC_KEYFILE; `} +# new `{ return CURLOPT_SSH_PRIVATE_KEYFILE; `} +# new `{ return CURLOPT_FTP_SSL_CCC; `} +# new `{ return CURLOPT_TIMEOUT_MS; `} +# new `{ return CURLOPT_CONNECTTIMEOUT_MS; `} +# new `{ return CURLOPT_HTTP_TRANSFER_DECODING; `} +# new `{ return CURLOPT_HTTP_CONTENT_DECODING; `} +# new `{ return CURLOPT_NEW_FILE_PERMS; `} +# new `{ return CURLOPT_NEW_DIRECTORY_PERMS; `} +# new `{ return CURLOPT_POSTREDIR; `} +# new `{ return CURLOPT_SSH_HOST_PUBLIC_KEY_MD5; `} +# new `{ return CURLOPT_OPENSOCKETFUNCTION; `} +# new `{ return CURLOPT_OPENSOCKETDATA; `} +# new `{ return CURLOPT_COPYPOSTFIELDS; `} +# new `{ return CURLOPT_PROXY_TRANSFER_MODE; `} +# new `{ return CURLOPT_SEEKFUNCTION; `} +# new `{ return CURLOPT_SEEKDATA; `} +# new `{ return CURLOPT_CRLFILE; `} +# new `{ return CURLOPT_ISSUERCERT; `} +# new `{ return CURLOPT_ADDRESS_SCOPE; `} +# new `{ return CURLOPT_CERTINFO; `} + new username `{ return CURLOPT_USERNAME; `} + new password `{ return CURLOPT_PASSWORD; `} +# new `{ return CURLOPT_PROXYUSERNAME; `} +# new `{ return CURLOPT_PROXYPASSWORD; `} +# new `{ return CURLOPT_NOPROXY; `} +# new `{ return CURLOPT_TFTP_BLKSIZE; `} +# new `{ return CURLOPT_SOCKS5_GSSAPI_SERVICE; `} +# new `{ return CURLOPT_SOCKS5_GSSAPI_NEC; `} +# new `{ return CURLOPT_PROTOCOLS; `} +# new `{ return CURLOPT_REDIR_PROTOCOLS; `} +# new `{ return CURLOPT_SSH_KNOWNHOSTS; `} +# new `{ return CURLOPT_SSH_KEYFUNCTION; `} +# new `{ return CURLOPT_SSH_KEYDATA; `} + new mail_from `{ return CURLOPT_MAIL_FROM; `} + new mail_rcpt `{ return CURLOPT_MAIL_RCPT; `} +# new `{ return CURLOPT_FTP_USE_PRET; `} +# new `{ return CURLOPT_RTSP_REQUEST; `} +# new `{ return CURLOPT_RTSP_SESSION_ID; `} +# new `{ return CURLOPT_RTSP_STREAM_URI; `} +# new `{ return CURLOPT_RTSP_TRANSPORT; `} +# new `{ return CURLOPT_RTSP_CLIENT_CSEQ; `} +# new `{ return CURLOPT_RTSP_SERVER_CSEQ; `} +# new `{ return CURLOPT_INTERLEAVEDATA; `} +# new `{ return CURLOPT_INTERLEAVEFUNCTION; `} +# new `{ return CURLOPT_WILDCARDMATCH; `} +# new `{ return CURLOPT_CHUNK_BGN_FUNCTION; `} +# new `{ return CURLOPT_CHUNK_END_FUNCTION; `} +# new `{ return CURLOPT_FNMATCH_FUNCTION; `} +# new `{ return CURLOPT_CHUNK_DATA; `} +# new `{ return CURLOPT_FNMATCH_DATA; `} +# new `{ return CURLOPT_RESOLVE; `} +# new `{ return CURLOPT_TLSAUTH_USERNAME; `} +# new `{ return CURLOPT_TLSAUTH_PASSWORD; `} +# new `{ return CURLOPT_TLSAUTH_TYPE; `} +# new `{ return CURLOPT_TRANSFER_ENCODING; `} +# new `{ return CURLOPT_CLOSESOCKETFUNCTION; `} +# new `{ return CURLOPT_CLOSESOCKETDATA; `} +# new `{ return CURLOPT_GSSAPI_DELEGATION; `} +# new `{ return CURLOPT_DNS_SERVERS; `} +# new `{ return CURLOPT_ACCEPTTIMEOUT_MS; `} +# new `{ return CURLOPT_TCP_KEEPALIVE; `} +# new `{ return CURLOPT_TCP_KEEPIDLE; `} +# new `{ return CURLOPT_TCP_KEEPINTVL; `} +# new `{ return CURLOPT_SSL_OPTIONS; `} +# new `{ return CURLOPT_MAIL_AUTH; `} +end diff --git a/lib/curl/curl_c.nit.args b/lib/curl/curl_c.nit.args new file mode 100644 index 0000000..bfd2c46 --- /dev/null +++ b/lib/curl/curl_c.nit.args @@ -0,0 +1 @@ +--cc-lib-name curl diff --git a/tests/sav/curl_http.sav b/tests/sav/curl_http.sav new file mode 100644 index 0000000..649c9c1 --- /dev/null +++ b/tests/sav/curl_http.sav @@ -0,0 +1 @@ +Usage ./out/curl_http.bin diff --git a/tests/sav/test_curl.sav b/tests/sav/test_curl.sav new file mode 100644 index 0000000..f171aeb --- /dev/null +++ b/tests/sav/test_curl.sav @@ -0,0 +1,64 @@ +* About to connect() to example.org port 80 (#0) +* Trying 192.0.43.10... +* connected +* Connected to example.org (192.0.43.10) port 80 (#0) +> GET / HTTP/1.1 +Host: example.org +Accept: */* + +* additional stuff not fine transfer.c:1037: 0 0 +* HTTP 1.0, assume close after body +< HTTP/1.0 302 Found +< Location: http://example.iana.org +< Server: BigIP +* HTTP/1.0 connection set to keep alive! +< Connection: Keep-Alive +< Content-Length: 0 +< +* Connection #0 to host example.org left intact +GetinfoLong:: Header size: 115 +GetinfoLong:: Response code: 302 +GetinfoLong:: http_connectcode: 0 +GetinfoLong:: filetime: -1 +GetinfoLong:: redirect_count: 0 +GetinfoLong:: request_size: 50 +GetinfoLong:: ssl_verifyresult: 0 +GetinfoLong:: httpauth_avail: 0 +GetinfoLong:: proxyauth_avail: 0 +GetinfoLong:: os_errno: 0 +GetinfoLong:: primary_port: 80 +GetinfoLong:: num_connects: 1 +GetinfoLong:: local_port: 44959 +GetinfoLong:: lastsocket: 4 +GetinfoLong:: condition_unmet: 0 +GetinfoLong:: rtsp_client_cseq: 0 +GetinfoLong:: rtsp_server_cseq: 0 +GetinfoLong:: rtsp_cseq_recv: 0 +GetinfoDouble:: total_time: 0 +GetinfoDouble:: namelookup_time: 0 +GetinfoDouble:: connect_time: 0 +GetinfoDouble:: appconnect_time: 0 +GetinfoDouble:: pretransfer_time: 0 +GetinfoDouble:: starttransfer_time: 0 +GetinfoDouble:: redirect_time: 0 +GetinfoDouble:: size_upload: 0 +GetinfoDouble:: size_download: 0 +GetinfoDouble:: speed_download: 0 +GetinfoDouble:: speed_upload: 0 +GetinfoDouble:: content_length_download: 0 +GetinfoDouble:: content_length_upload: 0 +GetinfoStr:: Content type: +GetinfoStr:: Effective url: http://example.org/ +GetinfoStr:: Redirect url: http://example.iana.org +GetinfoStr:: primary_ip not empty: true +GetinfoStr:: local_ip not empty: true +GetinfoStr:: ftp_entry_path: +GetinfoStr:: private_data: +GetinfoStr:: rtsp_session_id: +GetSList:: ssl_engines: +GetSList:: cookielist: +CURLSList to array - content: tititotototo2toto3toto4toto9 +CURLSList to array - length: 6 +Array to CURLSList - content: tatatata2 +Array to CURLSList - length: 2 +hello=toto&hello=tata&allo=foo diff --git a/tests/test_curl.nit b/tests/test_curl.nit new file mode 100644 index 0000000..bc89868 --- /dev/null +++ b/tests/test_curl.nit @@ -0,0 +1,244 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2013 Matthieu Lucas +# Copyright 2013 Alexis Laferrière +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +module test_curl + +import curl + +fun error_manager(err: CURLCode) do if not err.is_ok then print err + +var url = "http://example.org/" + +var curl = new CCurl.easy_init +if not curl.is_init then print "failed init" + +var error:CURLCode +error = curl.easy_setopt(new CURLOption.url, url) +error_manager(error) + +error = curl.easy_setopt(new CURLOption.verbose, 1) +error_manager(error) + +error = curl.easy_perform +error_manager(error) + +# Long set +var info:nullable CURLInfoResponseLong +info = curl.easy_getinfo_long(new CURLInfoLong.header_size) +assert infoResp:info != null +print "GetinfoLong:: Header size: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.response_code) +assert infoResp:info != null +print "GetinfoLong:: Response code: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.http_connectcode) +assert infoResp:info != null +print "GetinfoLong:: http_connectcode: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.filetime) +assert infoResp:info != null +print "GetinfoLong:: filetime: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.redirect_count) +assert infoResp:info != null +print "GetinfoLong:: redirect_count: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.request_size) +assert infoResp:info != null +print "GetinfoLong:: request_size: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.ssl_verifyresult) +assert infoResp:info != null +print "GetinfoLong:: ssl_verifyresult: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.httpauth_avail) +assert infoResp:info != null +print "GetinfoLong:: httpauth_avail: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.proxyauth_avail) +assert infoResp:info != null +print "GetinfoLong:: proxyauth_avail: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.os_errno) +assert infoResp:info != null +print "GetinfoLong:: os_errno: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.primary_port) +assert infoResp:info != null +print "GetinfoLong:: primary_port: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.num_connects) +assert infoResp:info != null +print "GetinfoLong:: num_connects: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.local_port) +assert infoResp:info != null +print "GetinfoLong:: local_port: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.lastsocket) +assert infoResp:info != null +print "GetinfoLong:: lastsocket: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.condition_unmet) +assert infoResp:info != null +print "GetinfoLong:: condition_unmet: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.rtsp_client_cseq) +assert infoResp:info != null +print "GetinfoLong:: rtsp_client_cseq: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.rtsp_server_cseq) +assert infoResp:info != null +print "GetinfoLong:: rtsp_server_cseq: " + info.response.to_s + +info = curl.easy_getinfo_long(new CURLInfoLong.rtsp_cseq_recv) +assert infoResp:info != null +print "GetinfoLong:: rtsp_cseq_recv: " + info.response.to_s + +# Double +var infoDouble: nullable CURLInfoResponseDouble +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.total_time) +assert infoResp:infoDouble != null +print "GetinfoDouble:: total_time: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.namelookup_time) +assert infoResp:infoDouble != null +print "GetinfoDouble:: namelookup_time: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.connect_time) +assert infoResp:infoDouble != null +print "GetinfoDouble:: connect_time: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.appconnect_time) +assert infoResp:infoDouble != null +print "GetinfoDouble:: appconnect_time: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.pretransfer_time) +assert infoResp:infoDouble != null +print "GetinfoDouble:: pretransfer_time: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.starttransfer_time) +assert infoResp:infoDouble != null +print "GetinfoDouble:: starttransfer_time: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.redirect_time) +assert infoResp:infoDouble != null +print "GetinfoDouble:: redirect_time: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.size_upload) +assert infoResp:infoDouble != null +print "GetinfoDouble:: size_upload: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.size_download) +assert infoResp:infoDouble != null +print "GetinfoDouble:: size_download: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.speed_download) +assert infoResp:infoDouble != null +print "GetinfoDouble:: speed_download: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.speed_upload) +assert infoResp:infoDouble != null +print "GetinfoDouble:: speed_upload: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.content_length_download) +assert infoResp:infoDouble != null +print "GetinfoDouble:: content_length_download: " + infoDouble.response.to_s + +infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.content_length_upload) +assert infoResp:infoDouble != null +print "GetinfoDouble:: content_length_upload: " + infoDouble.response.to_s + +# String set +var infoStr:nullable CURLInfoResponseString +infoStr = curl.easy_getinfo_chars(new CURLInfoChars.content_type) +assert infoResp:infoStr != null +print "GetinfoStr:: Content type: " + infoStr.response + +infoStr = curl.easy_getinfo_chars(new CURLInfoChars.effective_url) +assert infoResp:infoStr != null +print "GetinfoStr:: Effective url: " + infoStr.response + +infoStr = curl.easy_getinfo_chars(new CURLInfoChars.redirect_url) +assert infoResp:infoStr != null +print "GetinfoStr:: Redirect url: " + infoStr.response + +infoStr = curl.easy_getinfo_chars(new CURLInfoChars.primary_ip) +assert infoResp:infoStr != null +print( "GetinfoStr:: primary_ip not empty: " + (not infoStr.response.is_empty).to_s) + +infoStr = curl.easy_getinfo_chars(new CURLInfoChars.local_ip) +assert infoResp:infoStr != null +print( "GetinfoStr:: local_ip not empty: " + (not infoStr.response.is_empty).to_s) + +infoStr = curl.easy_getinfo_chars(new CURLInfoChars.ftp_entry_path) +assert infoResp:infoStr != null +print "GetinfoStr:: ftp_entry_path: " + infoStr.response + +infoStr = curl.easy_getinfo_chars(new CURLInfoChars.private_data) +assert infoResp:infoStr != null +print "GetinfoStr:: private_data: " + infoStr.response + +infoStr = curl.easy_getinfo_chars(new CURLInfoChars.rtsp_session_id) +assert infoResp:infoStr != null +print "GetinfoStr:: rtsp_session_id: " + infoStr.response + +# CURLSList set +var infoList:nullable CURLInfoResponseArray +infoList = curl.easy_getinfo_slist(new CURLInfoSList.ssl_engines) +assert infoResp:infoList != null +print "GetSList:: ssl_engines: " + infoList.response.to_s + +infoList = curl.easy_getinfo_slist(new CURLInfoSList.cookielist) +assert infoResp:infoList != null +print "GetSList:: cookielist: " + infoList.response.to_s + +# CURLSList to Array +var mailList = new CURLSList.with_str("titi") +mailList.append("toto") +mailList.append("toto2") +mailList.append("toto3") +mailList.append("toto4") +mailList.append("toto9") +if mailList.is_init then + var content = mailList.to_a + print "CURLSList to array - content: {content.to_s}" + print "CURLSList to array - length: {content.length.to_s}" + mailList.destroy +else + print "CURLSList to array: CURLSList wrong init" +end + +# CURLSList from Array +var mailRecipientsArray = new Array[String] +mailRecipientsArray.add("tata") +mailRecipientsArray.add("tata2") +var mailRecipientsList: CURLSList = mailRecipientsArray.to_curlslist +if mailRecipientsList.is_init then + print "Array to CURLSList - content: {mailRecipientsList.to_a.to_s}" + print "Array to CURLSList - length: {mailRecipientsList.to_a.length.to_s}" + mailRecipientsList.destroy +else + print "CURLSList to array: CURLSList wrong init" +end + +# HashMap Refines +var hashMapRefined = new HeaderMap +hashMapRefined["hello"] = "toto" +hashMapRefined["hello"] = "tata" +hashMapRefined["allo"] = "foo" +print hashMapRefined.to_url_encoded(new CCurl.easy_init) -- 1.7.9.5