# Fetch the Web page at `url`
fun download_html_page: String
do
- var curl = new Curl
-
- var request = new CurlHTTPRequest(url, curl)
+ var request = new CurlHTTPRequest(url)
var response = request.execute
if response isa CurlResponseSuccess then
var body = response.body_str
- curl.destroy
return body
else if response isa CurlResponseFailed then
print "Failed downloading URL '{url}' with: {response.error_msg} ({response.error_code})"
# Download this element to `path`
fun download_to(path: Text)
do
- var curl = new Curl
-
- var request = new CurlHTTPRequest(link, curl)
+ var request = new CurlHTTPRequest(link)
var response = request.download_to_file(path.to_s)
- if response isa CurlFileResponseSuccess then
- curl.destroy
- else if response isa CurlResponseFailed then
+ if response isa CurlResponseFailed then
sys.stderr.write "Failed downloading URL '{link}' with: {response.error_msg} ({response.error_code})\n"
- else abort
+ end
end
# Get an unique identifier for this element, uses `Config::unique_pattern`
# Get the content of the RSS feed at `self`
fun fetch_rss_content: Text
do
- var curl = new Curl
-
if sys.verbose then print "\n# Downloading RSS file from '{self}'"
- var request = new CurlHTTPRequest(to_s, curl)
+ var request = new CurlHTTPRequest(to_s)
var response = request.execute
if response isa CurlResponseSuccess then
var body = response.body_str
- curl.destroy
if sys.verbose then print "Download successful"
return body
else if response isa CurlResponseFailed then
# See the License for the specific language governing permissions and
# limitations under the License.
-# Network functionnalities based on Curl_c module.
+# Curl services: `CurlHTTPRequest` and `CurlMail`
module curl
-import curl_c
+import native_curl
-# Top level of Curl
+redef class Sys
+ # Shared Curl library handle
+ #
+ # Usually, you do not have to use this attribute, it instancied by `CurlHTTPRequest` and `CurlMail`.
+ # But in some cases you may want to finalize it to free some small resources.
+ # However, if Curl services are needed once again, this attribute must be manually set.
+ var curl: Curl = new Curl is lazy, writable
+end
+
+# Curl library handle, it is initialized and released with this class
class Curl
- protected var prim_curl = new CCurl.easy_init
+ super FinalizableOnce
- init
- do
- assert curlInstance:self.prim_curl.is_init else
- print "Curl must be instancied to be used"
- end
- end
+ private var native = new NativeCurl.easy_init
# Check for correct initialization
- fun is_ok: Bool do return self.prim_curl.is_init
+ fun is_ok: Bool do return self.native.is_init
- # Release Curl instance
- fun destroy do self.prim_curl.easy_clean
+ redef fun finalize_once do if is_ok then native.easy_clean
end
# CURL Request
class CurlRequest
- var verbose: Bool = false is writable
- private var curl: nullable Curl = null
+ private var curl: Curl = sys.curl
- # Launch request method
- fun execute: CurlResponse is abstract
+ # Shall this request be verbose?
+ var verbose: Bool = false is writable
# Intern perform method, lowest level of request launching
private fun perform: nullable CurlResponseFailed
var err
- err = self.curl.prim_curl.easy_setopt(new CURLOption.verbose, self.verbose)
+ err = self.curl.native.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
+ err = self.curl.native.easy_perform
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
return null
# CURL HTTP Request
class CurlHTTPRequest
super CurlRequest
- super CCurlCallbacks
- super CurlCallbacksRegisterIntern
+ super NativeCurlCallbacks
var url: String
- var datas: nullable HeaderMap = null is writable
- var headers: nullable HeaderMap = null is writable
+ var datas: nullable HeaderMap is writable
+ var headers: nullable HeaderMap is writable
+ var delegate: nullable CurlCallbacks is writable
# Set the user agent for all following HTTP requests
- fun user_agent=(name: String)
- do
- curl.prim_curl.easy_setopt(new CURLOption.user_agent, name)
- end
-
- init (url: String, curl: nullable Curl) is old_style_init do
- self.url = url
- self.curl = curl
- end
+ var user_agent: nullable String is writable
# Execute HTTP request with settings configured through attribute
- redef fun execute
+ fun execute: CurlResponse
do
if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized")
var err
- err = self.curl.prim_curl.easy_setopt(new CURLOption.follow_location, 1)
+ err = self.curl.native.easy_setopt(new CURLOption.follow_location, 1)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
- err = self.curl.prim_curl.easy_setopt(new CURLOption.url, url)
+ err = self.curl.native.easy_setopt(new CURLOption.url, url)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
+ var user_agent = user_agent
+ if user_agent != null then
+ err = curl.native.easy_setopt(new CURLOption.user_agent, user_agent)
+ if not err.is_ok then return answer_failure(err.to_i, err.to_s)
+ end
+
# Callbacks
- err = self.curl.prim_curl.register_callback(callback_receiver, new CURLCallbackType.header)
+ err = self.curl.native.register_callback_header(callback_receiver)
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)
+ err = self.curl.native.register_callback_body(callback_receiver)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
# HTTP Header
- 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)
+ var headers = self.headers
+ if headers != null then
+ var headers_joined = headers.join_pairs(": ")
+ err = self.curl.native.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
# Datas
- 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)
+ var datas = self.datas
+ if datas != null then
+ var postdatas = datas.to_url_encoded(self.curl)
+ err = self.curl.native.easy_setopt(new CURLOption.postfields, postdatas)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
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 st_code = self.curl.native.easy_getinfo_long(new CURLInfoLong.response_code)
+ if not st_code == null then success_response.status_code = st_code
return success_response
end
var err
- err = self.curl.prim_curl.easy_setopt(new CURLOption.follow_location, 1)
+ err = self.curl.native.easy_setopt(new CURLOption.follow_location, 1)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
- err = self.curl.prim_curl.easy_setopt(new CURLOption.url, url)
+ err = self.curl.native.easy_setopt(new CURLOption.url, url)
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.header)
+ err = self.curl.native.register_callback_header(callback_receiver)
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)
+ err = self.curl.native.register_callback_stream(callback_receiver)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
var opt_name
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
+ success_response.file = new FileWriter.open(opt_name)
+ if not success_response.file.is_writable then
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 st_code = self.curl.native.easy_getinfo_long(new CURLInfoLong.response_code)
+ if not st_code == null then success_response.status_code = st_code
- 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 speed = self.curl.native.easy_getinfo_double(new CURLInfoDouble.speed_download)
+ if not speed == null then success_response.speed_download = speed
- 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 size = self.curl.native.easy_getinfo_double(new CURLInfoDouble.size_download)
+ if not size == null then success_response.size_download = size
- var time = self.curl.prim_curl.easy_getinfo_double(new CURLInfoDouble.total_time)
- if not time == null then success_response.total_time = time.response
+ var time = self.curl.native.easy_getinfo_double(new CURLInfoDouble.total_time)
+ if not time == null then success_response.total_time = time
- success_response.i_file.close
+ success_response.file.close
return success_response
end
end
# CURL Mail Request
-class CurlMailRequest
+#
+# ~~~
+# # Craft mail
+# var mail = new CurlMail("sender@example.org",
+# to=["to@example.org"], cc=["bob@example.org"])
+#
+# mail.headers_body["Content-Type:"] = """text/html; charset="UTF-8""""
+# mail.headers_body["Content-Transfer-Encoding:"] = "quoted-printable"
+#
+# mail.body = "<h1>Here you can write HTML stuff.</h1>"
+# mail.subject = "Hello From My Nit Program"
+#
+# # Set mail server
+# var error = mail.set_outgoing_server("smtps://smtp.example.org:465",
+# "user@example.org", "mypassword")
+# if error != null then
+# print "Mail Server Error: {error}"
+# exit 0
+# end
+#
+# # Send
+# error = mail.execute
+# if error != null then
+# print "Transfer Error: {error}"
+# exit 0
+# end
+# ~~~
+class CurlMail
super CurlRequest
- super CCurlCallbacks
-
- var headers: nullable HeaderMap = null is writable
- var headers_body: nullable HeaderMap = null is writable
- var from: nullable String = null is writable
- var to: nullable Array[String] = null is writable
- var cc: nullable Array[String] = null is writable
- var bcc: nullable Array[String] = null is writable
- var subject: nullable String = "" is writable
- var body: nullable String = "" is writable
- private var supported_outgoing_protocol: Array[String] = ["smtp", "smtps"]
-
- init (curl: nullable Curl) is old_style_init do
- self.curl = curl
- end
+ super NativeCurlCallbacks
+
+ # Address of the sender
+ var from: nullable String is writable
+
+ # Main recipients
+ var to: nullable Array[String] is writable
+
+ # Subject line
+ var subject: nullable String is writable
+
+ # Text content
+ var body: nullable String is writable
+
+ # CC recipients
+ var cc: nullable Array[String] is writable
+
+ # BCC recipients (hidden from other recipients)
+ var bcc: nullable Array[String] is writable
- # Helper method to add conventional space while building entire mail
- private fun add_conventional_space(str: String):String do return "{str}\n" end
+ # HTTP header
+ var headers = new HeaderMap is lazy, writable
+
+ # Content header
+ var headers_body = new HeaderMap is lazy, writable
+
+ # Protocols supported to send mail to a server
+ #
+ # Default value at `["smtp", "smtps"]`
+ var supported_outgoing_protocol: Array[String] = ["smtp", "smtps"]
# Helper method to add pair values to mail content while building it (ex: "To:", "address@mail.com")
- private fun add_pair_to_content(str: String, att: String, val: nullable String):String
+ private fun add_pair_to_content(str: String, att: String, val: nullable String): String
do
if val != null then return "{str}{att}{val}\n"
return "{str}{att}\n"
end
# Helper method to add entire list of pairs to mail content
- private fun add_pairs_to_content(content: String, pairs: HeaderMap):String
+ private fun add_pairs_to_content(content: String, pairs: HeaderMap): String
do
for h_key, h_val in pairs do content = add_pair_to_content(content, h_key, h_val)
return content
end
# Check for host and protocol availability
- private fun is_supported_outgoing_protocol(host: String):CURLCode
+ private fun is_supported_outgoing_protocol(host: String): CURLCode
do
var host_reach = host.split_with("://")
if host_reach.length > 1 and supported_outgoing_protocol.has(host_reach[0]) then return once new CURLCode.ok
# Host & Protocol
err = is_supported_outgoing_protocol(host)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
- err = self.curl.prim_curl.easy_setopt(new CURLOption.url, host)
+
+ err = self.curl.native.easy_setopt(new CURLOption.url, host)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
# Credentials
if not user == null and not pwd == null then
- err = self.curl.prim_curl.easy_setopt(new CURLOption.username, user)
+ err = self.curl.native.easy_setopt(new CURLOption.username, user)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
- err = self.curl.prim_curl.easy_setopt(new CURLOption.password, pwd)
+
+ err = self.curl.native.easy_setopt(new CURLOption.password, pwd)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
end
end
# Execute Mail request with settings configured through attribute
- redef fun execute
+ fun execute: nullable CurlResponseFailed
do
if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized")
- var success_response = new CurlMailResponseSuccess
- var content = ""
+ var lines = new Array[String]
+
# Headers
- if self.headers != null then
- content = add_pairs_to_content(content, self.headers.as(not null))
+ var headers = self.headers
+ if not headers.is_empty then
+ for k, v in headers do lines.add "{k}{v}"
end
# Recipients
- var g_rec = new Array[String]
- if self.to != null and self.to.length > 0 then
- content = add_pair_to_content(content, "To:", self.to.join(","))
- g_rec.append(self.to.as(not null))
+ var all_recipients = new Array[String]
+ var to = self.to
+ if to != null and to.length > 0 then
+ lines.add "To:{to.join(",")}"
+ all_recipients.append to
end
- if self.cc != null and self.cc.length > 0 then
- content = add_pair_to_content(content, "Cc:", self.cc.join(","))
- g_rec.append(self.cc.as(not null))
+
+ var cc = self.cc
+ if cc != null and cc.length > 0 then
+ lines.add "Cc:{cc.join(",")}"
+ all_recipients.append cc
end
- if self.bcc != null and self.bcc.length > 0 then g_rec.append(self.bcc.as(not null))
- if g_rec.length < 1 then return answer_failure(0, "The mail recipients can not be empty")
+ var bcc = self.bcc
+ if bcc != null and bcc.length > 0 then all_recipients.append bcc
- var err
+ if all_recipients.is_empty then return answer_failure(0, "There must be at lease one recipient")
- err = self.curl.prim_curl.easy_setopt(new CURLOption.follow_location, 1)
+ var err = self.curl.native.easy_setopt(new CURLOption.follow_location, 1)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
- err = self.curl.prim_curl.easy_setopt(new CURLOption.mail_rcpt, g_rec.to_curlslist)
+ err = self.curl.native.easy_setopt(new CURLOption.mail_rcpt, all_recipients.to_curlslist)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
# From
- if not self.from == null then
- content = add_pair_to_content(content, "From:", self.from)
- err = self.curl.prim_curl.easy_setopt(new CURLOption.mail_from, self.from.as(not null))
+ var from = self.from
+ if not from == null then
+ lines.add "From:{from}"
+
+ err = self.curl.native.easy_setopt(new CURLOption.mail_from, from)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
end
# Subject
- content = add_pair_to_content(content, "Subject:", self.subject)
+ var subject = self.subject
+ if subject == null then subject = "" # Default
+ lines.add "Subject: {subject}"
# Headers body
- if self.headers_body != null then
- content = add_pairs_to_content(content, self.headers_body.as(not null))
+ var headers_body = self.headers_body
+ if not headers_body.is_empty then
+ for k, v in headers_body do lines.add "{k}{v}"
end
# Body
- content = add_conventional_space(content)
- content = add_pair_to_content(content, "", self.body)
- content = add_conventional_space(content)
- err = self.curl.prim_curl.register_callback(self, once new CURLCallbackType.read)
+ var body = self.body
+ if body == null then body = "" # Default
+
+ lines.add ""
+ lines.add body
+ lines.add ""
+
+ err = self.curl.native.register_callback_read(self)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
- err = self.curl.prim_curl.register_read_datas_callback(self, content)
+
+ var content = lines.join("\n")
+ err = self.curl.native.register_read_datas_callback(self, content)
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
- return success_response
+ return null
end
end
# Callbacks Interface, allow you to manage in your way the different streams
interface CurlCallbacks
- super CCurlCallbacks
-end
-
-# Callbacks attributes
-abstract class CurlCallbacksRegisterIntern
- var delegate: nullable CurlCallbacks = null is writable
+ super NativeCurlCallbacks
end
# Abstract Curl request response
var error_code: Int
var error_msg: String
+
+ redef fun to_s do return "{error_msg} ({error_code})"
end
# Success Abstract Response Success Class
end
end
-# Success Response Class of mail request
-class CurlMailResponseSuccess
- super CurlResponseSuccessIntern
-end
-
# Success Response Class of a downloaded File
class CurlFileResponseSuccess
super CurlResponseSuccessIntern
var status_code = 0
- var speed_download = 0
- var size_download = 0
- var total_time = 0
- private var i_file: nullable OFile = null
+ var speed_download = 0.0
+ var size_download = 0.0
+ var total_time = 0.0
+ private var file: nullable FileWriter = null
# Receive bytes stream from request due to stream callback registering
- redef fun stream_callback(buffer, size, count)
+ redef fun stream_callback(buffer)
do
- self.i_file.write(buffer, size, count)
+ file.write buffer
end
end
-# Pseudo map associating Strings to Strings,
-# each key can have multiple associations
-# and the order of insertion is important.
+# Pseudo map associating `String` to `String` for HTTP exchanges
+#
+# This structure differs from `Map` as each key can have multiple associations
+# and the order of insertion is important to some services.
class HeaderMap
- private var arr = new Array[Couple[String, String]]
+ private var array = new Array[Couple[String, String]]
- fun []=(k, v: String) do arr.add(new Couple[String, String](k, v))
+ # Add a `value` associated to `key`
+ fun []=(key, value: String)
+ do
+ array.add new Couple[String, String](key, value)
+ end
+ # Get a list of the keys associated to `key`
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)
+ for c in array do if c.first == k then res.add c.second
return res
end
+ # Iterate over all the associations in `self`
fun iterator: MapIterator[String, String] do return new HeaderMapIterator(self)
- # Convert Self to a single string used to post http fields
- fun to_url_encoded(curl: CCurl): String
+ # Get `self` as a single string for HTTP POST
+ #
+ # Require: `curl.is_ok`
+ fun to_url_encoded(curl: Curl): String
do
- assert curlNotInitialized: curl.is_init else
- print "to_url_encoded required a valid instance of CCurl Object."
- end
- var str = ""
- var length = self.length
- var i = 0
+ assert curl.is_ok
+
+ var lines = new Array[String]
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
+ if k.length == 0 then continue
+
+ k = curl.native.escape(k)
+ v = curl.native.escape(v)
+ lines.add "{k}={v}"
end
- return str
+ return lines.join("&")
end
# Concatenate couple of 'key value' separated by 'sep' in Array
return col
end
- fun length: Int do return arr.length
- fun is_empty: Bool do return arr.is_empty
+ # Number of values in `self`
+ fun length: Int do return array.length
+
+ # Is this map empty?
+ fun is_empty: Bool do return array.is_empty
end
-class HeaderMapIterator
+private class HeaderMapIterator
super MapIterator[String, String]
- private var iterator: Iterator[Couple[String, String]]
- init(map: HeaderMap) is old_style_init do self.iterator = map.arr.iterator
+ var map: HeaderMap
+ var iterator: Iterator[Couple[String, String]] = map.array.iterator is lazy
redef fun is_ok do return self.iterator.is_ok
redef fun next do self.iterator.next
# See the License for the specific language governing permissions and
# limitations under the License.
-# Sample of the Curl module.
+# Example use of the Curl module
module curl_http
import curl
-# Small class to represent an Http Fetcher
+# Custom delegate to receive callbacks from a Curl transfer
class MyHttpFetcher
super CurlCallbacks
- var curl: Curl
- var our_body: String = ""
+ # Body of the downloaded file
+ var fetched_body = ""
- init(curl: Curl) do self.curl = curl
-
- # Release curl object
- fun destroy do self.curl.destroy
-
- # Header callback
redef fun header_callback(line) do
# We keep this callback silent for testing purposes
- #if not line.has_prefix("Date:") then print "Header_callback : {line}"
+ #if not line.has_prefix("Date:") then print "Header_callback: {line}"
end
- # Body callback
- redef fun body_callback(line) do self.our_body = "{self.our_body}{line}"
+ redef fun body_callback(line) do self.fetched_body += line
- # Stream callback - Cf : No one is registered
- redef fun stream_callback(buffer, size, count) do print "Stream_callback : {buffer} - {size} - {count}"
+ redef fun stream_callback(buffer) do print "Stream_callback: {buffer}"
end
+private fun print_usage do print "Usage: curl_http [POST|GET|GET_FILE] url"
-# Program
if args.length < 2 then
- print "Usage: curl_http <method wished [POST, GET, GET_FILE]> <target url>"
-else
- var curl = new Curl
- var url = args[1]
- var request = new CurlHTTPRequest(url, curl)
+ print_usage
+ exit 1
+end
+
+var url = args[1]
+var request = new CurlHTTPRequest(url)
+request.verbose = false # Set to `true` to debug
+if args[0] == "GET" then
# HTTP Get Request
- if args[0] == "GET" then
- request.verbose = false
- var getResponse = request.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
+ var response = request.execute
+
+ if response isa CurlResponseSuccess then
+ print "Status code: {response.status_code}"
+ print "Body: {response.body_str}"
+ else if response isa CurlResponseFailed then
+ print "Error code: {response.error_code}"
+ print "Error msg: {response.error_msg}"
+ end
+else if args[0] == "POST" then
# HTTP Post Request
- else if args[0] == "POST" then
- var myHttpFetcher = new MyHttpFetcher(curl)
- request.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"
- request.datas = postDatas
- request.verbose = false
- var postResponse = request.execute
-
- print "Our body from the callback : {myHttpFetcher.our_body}"
-
- 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
+ var my_http_fetcher = new MyHttpFetcher
+ request.delegate = my_http_fetcher
+
+ var post_datas = new HeaderMap
+ post_datas["Bugs Bunny"] = "Daffy Duck"
+ post_datas["Batman"] = "Robin likes special characters @#ùà !è§'(\"é&://,;<>∞~*"
+ post_datas["Batman"] = "Yes you can set multiple identical keys, but APACHE will consider only one, the last one"
+ request.datas = post_datas
+ var response = request.execute
+
+ print "Our body from the callback: {my_http_fetcher.fetched_body}"
+
+ if response isa CurlResponseSuccess then
+ print "*** Answer ***"
+ print "Status code: {response.status_code}"
+ print "Body should be empty, because we decided to manage callbacks: {response.body_str.length}"
+ else if response isa CurlResponseFailed then
+ print "Error code: {response.error_code}"
+ print "Error msg: {response.error_msg}"
+ end
+else if args[0] == "GET_FILE" then
# HTTP Get to file Request
- else if args[0] == "GET_FILE" then
- var headers = new HeaderMap
- headers["Accept"] = "Moo"
- request.headers = headers
- request.verbose = false
- var downloadResponse = request.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
- # Program logic
- else
- print "Usage : Method[POST, GET, GET_FILE]"
+ var headers = new HeaderMap
+ headers["Accept"] = "Moo"
+ request.headers = headers
+ var response = request.download_to_file(null)
+
+ if response isa CurlFileResponseSuccess then
+ print "*** Answer ***"
+ print "Status code: {response.status_code}"
+ print "Size downloaded: {response.size_download}"
+ else if response isa CurlResponseFailed then
+ print "Error code: {response.error_code}"
+ print "Error msg: {response.error_msg}"
end
+
+else
+ print_usage
+ exit 1
end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2013 Matthieu Lucas <lucasmatthieu@gmail.com>
-#
-# 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.
-
-# Mail sender sample using the Curl module
-module curl_mail
-
-import curl
-
-var curl = new Curl
-var mail_request = new CurlMailRequest(curl)
-
-# Networks
-var response: nullable CurlResponse = mail_request.set_outgoing_server("smtps://smtp.example.org:465", "user@example.org", "mypassword")
-if response isa CurlResponseFailed then
- print "Error code : {response.error_code}"
- print "Error msg : {response.error_msg}"
-end
-
-# Headers
-mail_request.from = "Billy Bob"
-mail_request.to = ["user@example.org"]
-mail_request.cc = ["bob@example.org"]
-mail_request.bcc = null
-
-var headers_body = new HeaderMap
-headers_body["Content-Type:"] = "text/html; charset=\"UTF-8\""
-headers_body["Content-Transfer-Encoding:"] = "quoted-printable"
-mail_request.headers_body = headers_body
-
-# Content
-mail_request.body = "<h1>Here you can write HTML stuff.</h1>"
-mail_request.subject = "Hello From My Nit Program"
-
-# Others
-mail_request.verbose = false
-
-# Send mail
-response = mail_request.execute
-if response isa CurlResponseFailed then
- print "Error code : {response.error_code}"
- print "Error msg : {response.error_msg}"
-else if response isa CurlMailResponseSuccess then
- print "Mail Sent"
-else
- print "Unknown Curl Response type"
-end
# limitations under the License.
# Binding of C libCurl which allow us to interact with network.
-module curl_c is pkgconfig("libcurl")
+module native_curl is pkgconfig "libcurl"
+
+intrude import standard::file
+import standard
in "C header" `{
#include <curl/curl.h>
- typedef enum {
- CURLcallbackTypeHeader,
- CURLcallbackTypeBody,
- CURLcallbackTypeStream,
- CURLcallbackTypeRead,
- } CURLcallbackType;
-
- typedef struct {
- CCurlCallbacks delegate;
- CURLcallbackType type;
- } CURLCallbackDatas;
-
typedef struct {
char *data;
int len;
#include <stdlib.h>
#include <string.h>
- // 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 = NativeString_to_s_with_copy(line_c);
- CCurlCallbacks_header_callback(datas->delegate, line_o);
- }
- else if(datas->type == CURLcallbackTypeBody){
- char *line_c = (char*)buffer;
- String line_o = NativeString_to_s_with_copy(line_c);
- CCurlCallbacks_body_callback(datas->delegate, line_o);
- }
- else if(datas->type == CURLcallbackTypeStream){
- char *line_c = (char*)buffer;
- String line_o = NativeString_to_s(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;
}
`}
+redef extern class NativeString
+ private fun native_callback_header(size, count: Int, target: NativeCurlCallbacks): Int
+ do
+ target.header_callback to_s_with_length(size*count)
+
+ # FIXME we probably should return a value from the user
+ return count
+ end
+
+ private fun native_callback_body(size, count: Int, target: NativeCurlCallbacks): Int
+ do
+ target.body_callback to_s_with_length(size*count)
+
+ return count
+ end
+
+ private fun native_callback_stream(size, count: Int, target: NativeCurlCallbacks): Int
+ do
+ target.stream_callback to_s_with_length(size*count)
+
+ return count
+ end
+end
+
# CURL Extern Type, reproduce CURL low level behaviors
-extern class CCurl `{ CURL * `}
+extern class NativeCurl `{ CURL * `}
# Constructor, CURL low level initializer
new easy_init `{ return curl_easy_init(); `}
+
# Check for correct initialization
- fun is_init:Bool `{ return (self != NULL); `}
+ fun is_init: Bool `{ return (self != NULL); `}
+
# Easy Clean / Release CURL instance
fun easy_clean `{ curl_easy_cleanup( self ); `}
+
# Perform the transfer described by setted options
- fun easy_perform:CURLCode `{ return curl_easy_perform( self ); `}
- # 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
+ fun easy_perform: CURLCode `{ return curl_easy_perform( self ); `}
+
+ # Set options to tell CURL how to behave. Obj parameter type can be Int, Bool, String, FileWriter, 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)
+ if obj isa Int then return native_setopt_int(opt, obj)
+ if obj isa Bool and obj == true then return native_setopt_int(opt, 1)
+ if obj isa Bool and obj == false then return native_setopt_int(opt, 0)
+ if obj isa String then return native_setopt_string(opt, obj)
+ if obj isa FileWriter then return native_setopt_file(opt, obj._file.as(not null))
+ if obj isa CURLSList then return native_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( self, opt, fl); `}
+
+ # Internal method to set options to CURL using NativeFile parameter.
+ private fun native_setopt_file(opt: CURLOption, file: NativeFile): CURLCode `{
+ return curl_easy_setopt( self, opt, file);
+ `}
+
# Internal method to set options to CURL using Int parameter.
- private fun i_setopt_int(opt: CURLOption, num: Int):CURLCode `{ return curl_easy_setopt( self, opt, num); `}
+ private fun native_setopt_int(opt: CURLOption, num: Int): CURLCode `{ return curl_easy_setopt( self, 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( self, opt, list); `}
+ private fun native_setopt_slist(opt: CURLOption, list: CURLSList): CURLCode `{ return curl_easy_setopt( self, 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 `{
+ private fun native_setopt_string(opt: CURLOption, str: String): CURLCode import String.to_cstring `{
char *rStr = String_to_cstring(str);
return curl_easy_setopt( self, opt, rStr);
`}
+
# Request Chars internal information from the CURL session
- fun easy_getinfo_chars(opt: CURLInfoChars):nullable CURLInfoResponseString
+ fun easy_getinfo_chars(opt: CURLInfoChars): nullable String
do
- var answ = new CURLInfoResponseString
- if not i_getinfo_chars(opt, answ).is_ok then return null
- return answ
+ var answ = new Container[NativeString]("".to_cstring)
+ if not native_getinfo_chars(opt, answ).is_ok then return null
+ if answ.item.address_is_null then return null
+ return answ.item.to_s
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=, NativeString.to_s_with_copy `{
- char *r = NULL;
+ private fun native_getinfo_chars(opt: CURLInfoChars, res: Container[NativeString]): CURLCode
+ import Container[NativeString].item= `{
+ char *r;
CURLcode c = curl_easy_getinfo( self, opt, &r);
- if((c == CURLE_OK) && r != NULL){
- String ro = NativeString_to_s_with_copy(r);
- CURLInfoResponseString_response__assign( res, ro);
- }
+ if (c == CURLE_OK) Container_of_NativeString_item__assign(res, r);
return c;
`}
+
# Request Long internal information from the CURL session
- fun easy_getinfo_long(opt: CURLInfoLong):nullable CURLInfoResponseLong
+ fun easy_getinfo_long(opt: CURLInfoLong): nullable Int
do
- var answ = new CURLInfoResponseLong
- if not i_getinfo_long(opt, answ).is_ok then return null
- return answ
+ var answ = new Container[Int](0)
+ if not native_getinfo_long(opt, answ).is_ok then return null
+ return answ.item
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( self, opt, r);
- if((c == CURLE_OK) && r != NULL) CURLInfoResponseLong_response__assign( res, *r);
- free(r);
+ private fun native_getinfo_long(opt: CURLInfoLong, res: Container[Int]): CURLCode
+ import Container[Int].item= `{
+ long r;
+ CURLcode c = curl_easy_getinfo( self, opt, &r);
+ if (c == CURLE_OK) Container_of_Int_item__assign(res, r);
return c;
`}
+
# Request Double internal information from the CURL session
- fun easy_getinfo_double(opt: CURLInfoDouble):nullable CURLInfoResponseDouble
+ fun easy_getinfo_double(opt: CURLInfoDouble): nullable Float
do
- var answ = new CURLInfoResponseDouble
- if not i_getinfo_double(opt, answ).is_ok then return null
- return answ
+ var answ = new Container[Float](0.0)
+ if not native_getinfo_double(opt, answ).is_ok then return null
+ return answ.item
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( self, opt, r);
- if((c == CURLE_OK) && r != NULL) CURLInfoResponseDouble_response__assign( res, *r);
- free(r);
+ private fun native_getinfo_double(opt: CURLInfoDouble, res: Container[Float]): CURLCode
+ import Container[Float].item= `{
+ double r;
+ CURLcode c = curl_easy_getinfo(self, opt, &r);
+ if (c == CURLE_OK) Container_of_Float_item__assign(res, r);
return c;
`}
+
# Request SList internal information from the CURL session
- fun easy_getinfo_slist(opt: CURLInfoSList):nullable CURLInfoResponseArray
+ fun easy_getinfo_slist(opt: CURLInfoSList): nullable Array[String]
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
+ var answ = new Container[CURLSList](new CURLSList)
+ if not native_getinfo_slist(opt, answ).is_ok then return null
+
+ var native = answ.item
+ var nity = native.to_a
+ native.destroy
+ return nity
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( self, opt, &csl);
- CURLInfoResponseArray_prim_response__assign(res, csl);
- return ce;
+ private fun native_getinfo_slist(opt: CURLInfoSList, res: Container[CURLSList]): CURLCode
+ import Container[CURLSList].item= `{
+ struct curl_slist* csl;
+ CURLcode c = curl_easy_getinfo(self, opt, &csl);
+ if (c == CURLE_OK) Container_of_CURLSList_item__assign(res, csl);
+ return c;
`}
- # 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
+ fun register_read_datas_callback(delegate: NativeCurlCallbacks, datas: String): CURLCode
do
- if datas.length > 0 then return i_register_read_datas_callback(delegate, datas, datas.length)
+ if datas.length > 0 then return native_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 `{
+ private fun native_register_read_datas_callback(delegate: NativeCurlCallbacks, datas: String, size: Int): CURLCode import String.to_cstring `{
CURLCallbackReadDatas *d = NULL;
d = malloc(sizeof(CURLCallbackReadDatas));
d->data = (char*)String_to_cstring(datas);
d->pos = 0;
return curl_easy_setopt( self, 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, NativeString.to_s_with_copy, NativeString.to_s `{
- CURLCallbackDatas *d = malloc(sizeof(CURLCallbackDatas));
- CCurlCallbacks_incr_ref(delegate);
- d->type = cbtype;
- d->delegate = delegate;
+
+ # Register `delegate` to get callbacks about the CURL transfer
+ fun register_callback_header(delegate: NativeCurlCallbacks): CURLCode
+ import NativeString.native_callback_header `{
CURLcode e;
- switch(cbtype){
- case CURLcallbackTypeHeader:
- e = curl_easy_setopt( self, CURLOPT_HEADERFUNCTION, (curl_write_callback)&nit_curl_callback_func);
- if(e != CURLE_OK) return e;
- e = curl_easy_setopt( self, CURLOPT_WRITEHEADER, d);
- break;
- case CURLcallbackTypeBody:
- case CURLcallbackTypeStream:
- e = curl_easy_setopt( self, CURLOPT_WRITEFUNCTION, (curl_write_callback)&nit_curl_callback_func);
- if(e != CURLE_OK) return e;
- e = curl_easy_setopt( self, CURLOPT_WRITEDATA, d);
- break;
- case CURLcallbackTypeRead:
- e = curl_easy_setopt( self, CURLOPT_READFUNCTION, (curl_write_callback)&nit_curl_callback_read_func);
- default:
- break;
- }
+ NativeCurlCallbacks_incr_ref(delegate); // FIXME deallocated these when download completes?
+
+ e = curl_easy_setopt(self, CURLOPT_HEADERFUNCTION, (curl_write_callback)&NativeString_native_callback_header);
+ if(e != CURLE_OK) return e;
+
+ e = curl_easy_setopt(self, CURLOPT_WRITEHEADER, delegate);
return e;
`}
+
+ # Register `delegate` to get callbacks about the CURL transfer
+ fun register_callback_body(delegate: NativeCurlCallbacks): CURLCode
+ import NativeString.native_callback_body `{
+ CURLcode e;
+ NativeCurlCallbacks_incr_ref(delegate);
+
+ e = curl_easy_setopt(self, CURLOPT_WRITEFUNCTION, (curl_write_callback)&NativeString_native_callback_body);
+ if(e != CURLE_OK) return e;
+
+ e = curl_easy_setopt(self, CURLOPT_WRITEDATA, delegate);
+ return e;
+ `}
+
+ # Register `delegate` to get callbacks about the CURL transfer
+ fun register_callback_stream(delegate: NativeCurlCallbacks): CURLCode
+ import NativeString.native_callback_stream `{
+ CURLcode e;
+ NativeCurlCallbacks_incr_ref(delegate);
+
+ e = curl_easy_setopt(self, CURLOPT_WRITEFUNCTION, (curl_write_callback)&NativeString_native_callback_stream);
+ if(e != CURLE_OK) return e;
+
+ e = curl_easy_setopt(self, CURLOPT_WRITEDATA, delegate);
+ return e;
+ `}
+
+ # Register `delegate` to get callbacks about the CURL transfer
+ fun register_callback_read(delegate: NativeCurlCallbacks): CURLCode
+ import NativeString.native_callback_stream `{
+ NativeCurlCallbacks_incr_ref(delegate);
+
+ return curl_easy_setopt(self, CURLOPT_READFUNCTION, (curl_write_callback)&nit_curl_callback_read_func);
+ `}
+
# Convert given string to URL encoded string
- fun escape(url: String):String import String.to_cstring, NativeString.to_s_with_copy `{
+ fun escape(url: String): String import String.to_cstring, NativeString.to_s_with_copy `{
char *orig_url, *encoded_url = NULL;
orig_url = String_to_cstring(url);
encoded_url = curl_easy_escape( self, orig_url, strlen(orig_url));
`}
end
-# FILE Extern type, reproduce basic FILE I/O
-extern class 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 self != 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, self); `}
- # 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(self); `}
- # 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 class CURLCallbackType `{ CURLcallbackType `}
- new header `{ return CURLcallbackTypeHeader; `}
- new body `{ return CURLcallbackTypeBody; `}
- new stream `{ return CURLcallbackTypeStream; `}
- new read `{ return CURLcallbackTypeRead; `}
- fun to_i:Int `{ return self; `}
+interface NativeCurlCallbacks
+ fun header_callback(buffer: String) do end
+ fun body_callback(buffer: String) do end
+ fun stream_callback(buffer: String) do end
end
# CURL Code binding and helpers
new unsupported_protocol `{ return CURLE_UNSUPPORTED_PROTOCOL; `}
new ok `{ return CURLE_OK; `}
new failed_init `{ return CURLE_FAILED_INIT; `}
- fun code:Int `{ return self; `}
- fun is_ok:Bool `{ return self == CURLE_OK; `}
- fun is_valid_protocol:Bool `{ return self == CURLE_UNSUPPORTED_PROTOCOL; `}
- fun is_valid_init:Bool `{ return self == CURLE_FAILED_INIT; `}
- fun to_i:Int do return code end
+
+ fun code: Int `{ return self; `}
+ fun is_ok: Bool `{ return self == CURLE_OK; `}
+ fun is_valid_protocol: Bool `{ return self == CURLE_UNSUPPORTED_PROTOCOL; `}
+ fun is_valid_init: Bool `{ return self == CURLE_FAILED_INIT; `}
+ fun to_i: Int do return code end
redef fun to_s import NativeString.to_s_with_copy `{
char *c = (char*)curl_easy_strerror(self);
return NativeString_to_s_with_copy(c);
extern class 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 (self != NULL); `}
+ fun is_init: Bool `{ return (self != 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(self, (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); `}
+ private fun native_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); `}
+ private fun native_next_reachable(c: CURLSList): Bool `{ return (c != NULL && c->next != NULL); `}
+
# Internal method to get current data
- private fun i_data(c: CURLSList):String import NativeString.to_s `{ return NativeString_to_s(c->data); `}
+ private fun native_data(c: CURLSList): String import NativeString.to_s `{ return NativeString_to_s(c->data); `}
+
# Internal method to get next element
- private fun i_next(c: CURLSList):CURLSList `{ return c->next; `}
+ private fun native_next(c: CURLSList): CURLSList `{ return c->next; `}
+
# Convert current low level List to an Array[String] object
- fun to_a:Array[String]
+ 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)
+ if native_data_reachable(cursor) != true then break
+ r.add(native_data(cursor))
+ cursor = native_next(cursor)
end
return r
end
+
# Release allocated memory
fun destroy `{ curl_slist_free_all(self); `}
end
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
+# Reproduce Enum of available CURL SList information, used for NativeCurl.easy_getinfo
extern class 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
+# Reproduce Enum of available CURL Long information, used for NativeCurl.easy_getinfo
extern class CURLInfoLong `{ CURLINFO `}
new response_code `{ return CURLINFO_RESPONSE_CODE; `}
new header_size `{ return CURLINFO_HEADER_SIZE; `}
new rtsp_cseq_self `{ return CURLINFO_RTSP_CSEQ_RECV; `}
end
-# Reproduce Enum of available CURL Double information, used for CCurl.easy_getinfo
+# Reproduce Enum of available CURL Double information, used for NativeCurl.easy_getinfo
extern class CURLInfoDouble `{ CURLINFO `}
new total_time `{ return CURLINFO_TOTAL_TIME; `}
new namelookup_time `{ return CURLINFO_NAMELOOKUP_TIME; `}
new content_length_upload `{ return CURLINFO_CONTENT_LENGTH_UPLOAD; `}
end
-# Reproduce Enum of available CURL Chars information, used for CCurl.easy_getinfo
+# Reproduce Enum of available CURL Chars information, used for NativeCurl.easy_getinfo
extern class CURLInfoChars `{ CURLINFO `}
new content_type `{ return CURLINFO_CONTENT_TYPE; `}
new effective_url `{ return CURLINFO_EFFECTIVE_URL; `}
new service_unavailable `{ return 503; `}
new gateway_timeout `{ return 504; `}
new http_version_not_supported `{ return 505; `}
- fun to_i:Int `{ return self; `}
+ fun to_i: Int `{ return self; `}
end
-# Reproduce Enum of CURL Options usable, used for CCurl.easy_setopt
+# Reproduce Enum of CURL Options usable, used for NativeCurl.easy_setopt
extern class CURLOption `{ CURLoption `}
# Behavior options
# Display verbose information.
new verbose `{ return CURLOPT_VERBOSE; `}
+
# Include the header in the body output.
new header `{ return CURLOPT_HEADER; `}
+
# Shut off the progress meter.
new no_progress `{ return CURLOPT_NOPROGRESS; `}
+
# Do not install signal handlers.
new no_signal `{ return CURLOPT_NOSIGNAL; `}
+
# Transfer multiple files according to a file name pattern.
new wild_card_match `{ return CURLOPT_WILDCARDMATCH; `}
# Callback for writing data.
new write_function `{ return CURLOPT_WRITEFUNCTION; `}
+
# Data pointer to pass to the write callback.
new write_data `{ return CURLOPT_WRITEDATA; `}
# Accept-Encoding and automatic decompressing data.
new accept_encoding `{ return CURLOPT_ACCEPT_ENCODING; `}
+
# Request Transfer-Encoding.
new transfert_encoding `{ return CURLOPT_TRANSFER_ENCODING; `}
+
# Follow HTTP redirects.
new follow_location `{ return CURLOPT_FOLLOWLOCATION; `}
# Issue a HTTP PUT request.
new put `{ return CURLOPT_PUT; `}
+
# Issue a HTTP POS request.
new post `{ return CURLOPT_POST; `}
+
# Send a POST with this data.
new postfields `{ return CURLOPT_POSTFIELDS; `}
# User-Agent: header.
new user_agent `{ return CURLOPT_USERAGENT; `}
+
# Custom HTTP headers.
new httpheader `{ return CURLOPT_HTTPHEADER; `}
# Address of the sender.
new mail_from `{ return CURLOPT_MAIL_FROM; `}
+
# Address of the recipients.
new mail_rcpt `{ return CURLOPT_MAIL_RCPT; `}
# List only.
new dir_list_only `{ return CURLOPT_DIRLISTONLY; `}
+
# Append to remote file.
new append `{ return CURLOPT_APPEND; `}
# Specific Curl that know hot to talk to the github API
class GithubCurl
- super Curl
# Headers to use on all requests
var header: HeaderMap is noinit
# and check for Github errors.
fun get_and_check(uri: String): nullable Jsonable
do
- var request = new CurlHTTPRequest(uri, self)
+ var request = new CurlHTTPRequest(uri)
request.user_agent = user_agent
request.headers = header
var response = request.execute
# are reported as `GithubError`.
fun get_and_parse(uri: String): nullable Jsonable
do
- var request = new CurlHTTPRequest(uri, self)
+ var request = new CurlHTTPRequest(uri)
request.user_agent = user_agent
request.headers = header
var response = request.execute
# An abstract request that defines most of the standard options for Neo4j REST API
abstract class JsonCurlRequest
- super CurlRequest
- super CCurlCallbacks
- super CurlCallbacksRegisterIntern
-
- # REST API service URL
- var url: String
-
- init (url: String, curl: nullable Curl) do
- self.url = url
- self.curl = curl
-
- init_headers
- end
+ super CurlHTTPRequest
# OAuth token
var auth: nullable String is writable
- # User agent (is used by github to contact devs in case of problems)
- # Eg. "Awesome-Octocat-App"
- var user_agent: nullable String is writable
-
- # HTTP headers to send
- var headers: nullable HeaderMap = null is writable
-
-
# init HTTP headers for Neo4j REST API
protected fun init_headers do
headers = new HeaderMap
if auth != null then
headers["Authorization"] = "token {auth.to_s}"
end
+
+ # User agent (is used by github to contact devs in case of problems)
if user_agent != null then
headers["User-Agent"] = user_agent.to_s
end
redef fun execute do
init_headers
+
if not self.curl.is_ok then
return answer_failure(0, "Curl instance is not correctly initialized")
end
var callback_receiver: CurlCallbacks = success_response
if self.delegate != null then callback_receiver = self.delegate.as(not null)
- var err
-
- err = self.curl.prim_curl.easy_setopt(new CURLOption.follow_location, 1)
+ var err = self.curl.native.easy_setopt(new CURLOption.follow_location, 1)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
- err = self.curl.prim_curl.easy_setopt(new CURLOption.http_version, 1)
+ err = self.curl.native.easy_setopt(new CURLOption.http_version, 1)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
-
- err = self.curl.prim_curl.easy_setopt(new CURLOption.url, url)
+ err = self.curl.native.easy_setopt(new CURLOption.url, url)
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.header)
+ err = self.curl.native.register_callback_header(callback_receiver)
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)
+ err = self.curl.native.register_callback_body(callback_receiver)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
# HTTP Header
if self.headers != null then
var headers_joined = self.headers.join_pairs(": ")
- err = self.curl.prim_curl.easy_setopt(
+ err = self.curl.native.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
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 st_code = self.curl.native.easy_getinfo_long(new CURLInfoLong.response_code)
+ if not st_code == null then success_response.status_code = st_code
return success_response
end
super JsonCurlRequest
redef fun execute_hook do
- var err = self.curl.prim_curl.easy_setopt(new CURLOption.get, true)
+ var err = self.curl.native.easy_setopt(new CURLOption.get, true)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
return null
end
end
redef fun execute_hook do
- var err = self.curl.prim_curl.easy_setopt(new CURLOption.post, true)
+ var err = self.curl.native.easy_setopt(new CURLOption.post, true)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
if self.data != null then
var postdatas = self.data.to_json
- err = self.curl.prim_curl.easy_setopt(new CURLOption.postfields, postdatas)
+ err = self.curl.native.easy_setopt(new CURLOption.postfields, postdatas)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
end
return null
super JsonCurlRequest
redef fun execute_hook do
- var err = self.curl.prim_curl.easy_setopt(new CURLOption.custom_request, "DELETE")
+ var err = self.curl.native.easy_setopt(new CURLOption.custom_request, "DELETE")
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
return null
end
end
redef fun execute_hook do
- var err = self.curl.prim_curl.easy_setopt(new CURLOption.custom_request, "PUT")
+ var err = self.curl.native.easy_setopt(new CURLOption.custom_request, "PUT")
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
if self.data != null then
var postdatas = self.data.to_json
- err = self.curl.prim_curl.easy_setopt(new CURLOption.postfields, postdatas)
+ err = self.curl.native.easy_setopt(new CURLOption.postfields, postdatas)
if not err.is_ok then return answer_failure(err.to_i, err.to_s)
end
return null
# GET JSON data from `url`
fun get(url: String): Jsonable do
- var request = new JsonGET(url, curl)
+ var request = new JsonGET(url)
var response = request.execute
return parse_response(response)
end
# POST `params` to `url`
fun post(url: String, params: Jsonable): Jsonable do
- var request = new JsonPOST(url, curl)
+ var request = new JsonPOST(url)
request.data = params
var response = request.execute
return parse_response(response)
# PUT `params` at `url`
fun put(url: String, params: Jsonable): Jsonable do
- var request = new JsonPUT(url, curl)
+ var request = new JsonPUT(url)
request.data = params
var response = request.execute
return parse_response(response)
# DELETE `url`
fun delete(url: String): Jsonable do
- var request = new JsonDELETE(url, curl)
+ var request = new JsonDELETE(url)
var response = request.execute
return parse_response(response)
end
# Execute the batch and update local nodes
fun execute: List[NeoError] do
- var request = new JsonPOST(client.batch_url, client.curl)
+ var request = new JsonPOST(client.batch_url)
# request.headers["X-Stream"] = "true"
var json_jobs = new JsonArray
for job in jobs.values do json_jobs.add job.to_rest
-Usage: curl_http <method wished [POST, GET, GET_FILE]> <target url>
+Usage: curl_http [POST|GET|GET_FILE] url
-Status code : 200
-Body : <!doctype html>
+Status code: 200
+Body: <!doctype html>
<html>
<head>
<title>Example Domain</title>
-Our body from the callback : <!doctype html>
+Our body from the callback: <!doctype html>
<html>
<head>
<title>Example Domain</title>
</html>
*** Answer ***
-Status code : 200
-Body should be empty, because we decided to manage callbacks : 0
+Status code: 200
+Body should be empty, because we decided to manage callbacks: 0
*** Answer ***
-Status code : 200
-Size downloaded : 1270
+Status code: 200
+Size downloaded: 1270.0
+++ /dev/null
-Error code : 6
-Error msg : Couldn't resolve host name
# 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
var url = "http://example.org/"
-var curl = new CCurl.easy_init
+var curl = new NativeCurl.easy_init
if not curl.is_init then print "failed init"
var error:CURLCode
#error_manager(error)
var cbManager = new CallbackManager
-error = curl.register_callback(cbManager, new CURLCallbackType.body)
+error = curl.register_callback_body(cbManager)
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)
+var info = curl.easy_getinfo_long(new CURLInfoLong.header_size)
assert infoResp:info != null
info = curl.easy_getinfo_long(new CURLInfoLong.response_code)
assert infoResp:info != null
# Double
-var infoDouble: nullable CURLInfoResponseDouble
-infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.total_time)
+var infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.total_time)
assert infoResp:infoDouble != null
infoDouble = curl.easy_getinfo_double(new CURLInfoDouble.namelookup_time)
assert infoResp:infoDouble != null
# String set
-var infoStr:nullable CURLInfoResponseString
-infoStr = curl.easy_getinfo_chars(new CURLInfoChars.content_type)
+var infoStr = curl.easy_getinfo_chars(new CURLInfoChars.content_type)
assert infoResp:infoStr != null
infoStr = curl.easy_getinfo_chars(new CURLInfoChars.effective_url)
assert infoResp:infoStr != null
+# follow_location not set, so returns null
infoStr = curl.easy_getinfo_chars(new CURLInfoChars.redirect_url)
-assert infoResp:infoStr != null
+assert infoStr == null
infoStr = curl.easy_getinfo_chars(new CURLInfoChars.primary_ip)
assert infoResp:infoStr != null
infoStr = curl.easy_getinfo_chars(new CURLInfoChars.local_ip)
assert infoResp:infoStr != null
+# Not connecting to FTP so `null`
infoStr = curl.easy_getinfo_chars(new CURLInfoChars.ftp_entry_path)
-assert infoResp:infoStr != null
+assert infoStr == null
+# opt private not set nor implemented, so returns null
infoStr = curl.easy_getinfo_chars(new CURLInfoChars.private_data)
-assert infoResp:infoStr != null
+assert infoStr == null
+# Not an RTSP connection so `null`
infoStr = curl.easy_getinfo_chars(new CURLInfoChars.rtsp_session_id)
-assert infoResp:infoStr != null
+assert infoStr == null
# CURLSList set
-var infoList:nullable CURLInfoResponseArray
-infoList = curl.easy_getinfo_slist(new CURLInfoSList.ssl_engines)
+var infoList = curl.easy_getinfo_slist(new CURLInfoSList.ssl_engines)
assert infoResp:infoList != null
infoList = curl.easy_getinfo_slist(new CURLInfoSList.cookielist)
hashMapRefined["hello"] = "toto"
hashMapRefined["hello"] = "tata"
hashMapRefined["allo"] = "foo"
-print hashMapRefined.to_url_encoded(new CCurl.easy_init)
+print hashMapRefined.to_url_encoded(sys.curl)