Property definitions

curl $ CurlHTTPRequest :: defaultinit
# HTTP request builder
#
# The request itself is sent by either `execute` or `download_to_file`.
# The attributes of this class must be set before calling either of these two methods.
#
# ## Minimal usage example
#
# ~~~
# var request = new CurlHTTPRequest("http://example.org/")
# var response = request.execute
# if response isa CurlResponseSuccess then
#     print "Response status code: {response.status_code}"
#     print response.body_str
# else if response isa CurlResponseFailed then
#     print_error response.error_msg
# end
# ~~~
class CurlHTTPRequest
	super CurlRequest
	super NativeCurlCallbacks

	# Address of the remote resource to request
	var url: String

	# Data for the body of a POST request
	var data: nullable HeaderMap is writable

	# Raw body string
	#
	# Set this value to send raw data instead of the POST formatted `data`.
	#
	# If `data` is set, the body will not be sent.
	var body: nullable String is writable

	# Header content of the request
	var headers: nullable HeaderMap is writable

	# Delegates to customize the behavior when running `execute`
	var delegate: nullable CurlCallbacks is writable

	# Set the user agent for all following HTTP requests
	var user_agent: nullable String is writable

	# Set the Unix domain socket path to use
	#
	# When not null, enables using a Unix domain socket
	# instead of a TCP connection and DNS hostname resolution.
	var unix_socket_path: nullable String is writable

	# The HTTP method, GET by default
	#
	# Must be a capitalized string with request name complying with RFC7231
	var method: String = "GET" is optional, writable

	# Execute HTTP request
	#
	# By default, the response body is returned in an instance of `CurlResponse`.
	# This behavior can be customized by setting a custom `delegate`.
	fun execute: CurlResponse
	do
		# Reset libcurl parameters as the lib is shared and options
		# might affect requests from one another.
		if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized")

		var success_response = new CurlResponseSuccess
		var callback_receiver: CurlCallbacks = success_response
		var err : CURLCode

		# Prepare request
		err = prepare_request(callback_receiver)
		if not err.is_ok then return answer_failure(err.to_i, err.to_s)

		# Perform request
		var err_resp = perform
		if err_resp != null then return err_resp

		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

	# Internal function that sets cURL options and request' parameters
	private fun prepare_request(callback_receiver: CurlCallbacks) : CURLCode
	do
		var err

		# cURL options and delegates
		err = set_curl_options
		if not err.is_ok then return err

		# Callbacks
		err = set_curl_callback(callback_receiver)
		if not err.is_ok then return err

		# HTTP Header
		err = set_curl_http_header
		if not err.is_ok then return err

		# Set HTTP method and body
		err = set_method
		if not err.is_ok then return err
		err = set_body

		return err
	end

	# Set cURL parameters according to assigned HTTP method set in method
	# attribute and body if the method allows it according to RFC7231
	private fun set_method : CURLCode
	do
		var err : CURLCode

		if self.method=="GET" then
			err=self.curl.native.easy_setopt(new CURLOption.get, 1)

		else if self.method=="POST" then
			err=self.curl.native.easy_setopt(new CURLOption.post, 1)

		else if self.method=="HEAD" then
			err=self.curl.native.easy_setopt(new CURLOption.no_body,1)

		else
			err=self.curl.native.easy_setopt(new CURLOption.custom_request,self.method)
		end
		return err
	end

	# Set request's body
	private fun set_body : CURLCode
	do
		var err
		var data = self.data
		var body = self.body

		if data != null then
			var postdatas = data.to_url_encoded(self.curl)
			err = self.curl.native.easy_setopt(new CURLOption.postfields, postdatas)
			if not err.is_ok then return err
		else if body != null then
			err = self.curl.native.easy_setopt(new CURLOption.postfields, body)
			if not err.is_ok then return err
		end
		return new CURLCode.ok
	end

	# Set cURL options
	# such as delegate, follow location, URL, user agent and address family
	private fun set_curl_options : CURLCode
	do
		var err

		err = self.curl.native.easy_setopt(new CURLOption.follow_location, 1)
		if not err.is_ok then return err

		err = self.curl.native.easy_setopt(new CURLOption.url, url)
		if not err.is_ok then return err

		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 err
		end

		var unix_socket_path = unix_socket_path
		if unix_socket_path != null then
			err = self.curl.native.easy_setopt(new CURLOption.unix_socket_path, unix_socket_path)
			if not err.is_ok then return err
		end
		return err
	end

	# Set cURL callback
	private fun set_curl_callback(callback_receiver : CurlCallbacks) : CURLCode
	do
		var err

		if self.delegate != null then callback_receiver = self.delegate.as(not null)

		err = self.curl.native.register_callback_header(callback_receiver)
		if not err.is_ok then return err

		err = self.curl.native.register_callback_body(callback_receiver)
		if not err.is_ok then return err

		return err
	end

	# Set cURL request header according to attribute headers
	private fun set_curl_http_header : CURLCode
	do
		var headers = self.headers
		if headers != null then
			var headers_joined = headers.join_pairs(": ")
			var err = self.curl.native.easy_setopt(new CURLOption.httpheader, headers_joined.to_curlslist)
			if not err.is_ok then return err
		end
		return new CURLCode.ok
	end

	# Download to file given resource
	fun download_to_file(output_file_name: nullable String): CurlResponse
	do
		if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized")

		var success_response = new CurlFileResponseSuccess

		var callback_receiver: CurlCallbacks = success_response
		if self.delegate != null then callback_receiver = self.delegate.as(not null)

		var err

		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.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.native.register_callback_header(callback_receiver)
		if not err.is_ok then return answer_failure(err.to_i, err.to_s)

		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
		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.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.native.easy_getinfo_long(new CURLInfoLong.response_code)
		if not st_code == null then success_response.status_code = st_code

		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.native.easy_getinfo_double(new CURLInfoDouble.size_download)
		if not size == null then success_response.size_download = size

		var time = self.curl.native.easy_getinfo_double(new CURLInfoDouble.total_time)
		if not time == null then success_response.total_time = time

		success_response.file.close

		return success_response
	end
end
lib/curl/curl.nit:77,1--334,3