221 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			221 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			V
		
	
	
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
 | 
						|
// Use of this source code is governed by an MIT license
 | 
						|
// that can be found in the LICENSE file.
 | 
						|
module http
 | 
						|
 | 
						|
import net.urllib
 | 
						|
 | 
						|
const (
 | 
						|
	max_redirects        = 4
 | 
						|
	content_type_default = 'text/plain'
 | 
						|
	bufsize              = 1536
 | 
						|
)
 | 
						|
 | 
						|
// FetchConfig holds configurations of fetch
 | 
						|
pub struct FetchConfig {
 | 
						|
pub mut:
 | 
						|
	url        string
 | 
						|
	method     Method
 | 
						|
	header     Header
 | 
						|
	data       string
 | 
						|
	params     map[string]string
 | 
						|
	cookies    map[string]string
 | 
						|
	user_agent string = 'v.http'
 | 
						|
	verbose    bool
 | 
						|
	//
 | 
						|
	validate               bool   // set this to true, if you want to stop requests, when their certificates are found to be invalid
 | 
						|
	verify                 string // the path to a rootca.pem file, containing trusted CA certificate(s)
 | 
						|
	cert                   string // the path to a cert.pem file, containing client certificate(s) for the request
 | 
						|
	cert_key               string // the path to a key.pem file, containing private keys for the client certificate(s)
 | 
						|
	in_memory_verification bool   // if true, verify, cert, and cert_key are read from memory, not from a file
 | 
						|
	allow_redirect         bool = true // whether to allow redirect
 | 
						|
}
 | 
						|
 | 
						|
pub fn new_request(method Method, url_ string, data string) ?Request {
 | 
						|
	url := if method == .get && !url_.contains('?') { url_ + '?' + data } else { url_ }
 | 
						|
	// println('new req() method=$method url="$url" dta="$data"')
 | 
						|
	return Request{
 | 
						|
		method: method
 | 
						|
		url: url
 | 
						|
		data: data
 | 
						|
		/*
 | 
						|
		headers: {
 | 
						|
			'Accept-Encoding': 'compress'
 | 
						|
		}
 | 
						|
		*/
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// get sends a GET HTTP request to the URL
 | 
						|
pub fn get(url string) ?Response {
 | 
						|
	return fetch(method: .get, url: url)
 | 
						|
}
 | 
						|
 | 
						|
// post sends a POST HTTP request to the URL with a string data
 | 
						|
pub fn post(url string, data string) ?Response {
 | 
						|
	return fetch(
 | 
						|
		method: .post
 | 
						|
		url: url
 | 
						|
		data: data
 | 
						|
		header: new_header(key: .content_type, value: http.content_type_default)
 | 
						|
	)
 | 
						|
}
 | 
						|
 | 
						|
// post_json sends a POST HTTP request to the URL with a JSON data
 | 
						|
pub fn post_json(url string, data string) ?Response {
 | 
						|
	return fetch(
 | 
						|
		method: .post
 | 
						|
		url: url
 | 
						|
		data: data
 | 
						|
		header: new_header(key: .content_type, value: 'application/json')
 | 
						|
	)
 | 
						|
}
 | 
						|
 | 
						|
// post_form sends a POST HTTP request to the URL with X-WWW-FORM-URLENCODED data
 | 
						|
pub fn post_form(url string, data map[string]string) ?Response {
 | 
						|
	return fetch(
 | 
						|
		method: .post
 | 
						|
		url: url
 | 
						|
		header: new_header(key: .content_type, value: 'application/x-www-form-urlencoded')
 | 
						|
		data: url_encode_form_data(data)
 | 
						|
	)
 | 
						|
}
 | 
						|
 | 
						|
[params]
 | 
						|
pub struct PostMultipartFormConfig {
 | 
						|
pub mut:
 | 
						|
	form   map[string]string
 | 
						|
	files  map[string][]FileData
 | 
						|
	header Header
 | 
						|
}
 | 
						|
 | 
						|
// post_multipart_form sends a POST HTTP request to the URL with multipart form data
 | 
						|
pub fn post_multipart_form(url string, conf PostMultipartFormConfig) ?Response {
 | 
						|
	body, boundary := multipart_form_body(conf.form, conf.files)
 | 
						|
	mut header := conf.header
 | 
						|
	header.set(.content_type, 'multipart/form-data; boundary="$boundary"')
 | 
						|
	return fetch(
 | 
						|
		method: .post
 | 
						|
		url: url
 | 
						|
		header: header
 | 
						|
		data: body
 | 
						|
	)
 | 
						|
}
 | 
						|
 | 
						|
// put sends a PUT HTTP request to the URL with a string data
 | 
						|
pub fn put(url string, data string) ?Response {
 | 
						|
	return fetch(
 | 
						|
		method: .put
 | 
						|
		url: url
 | 
						|
		data: data
 | 
						|
		header: new_header(key: .content_type, value: http.content_type_default)
 | 
						|
	)
 | 
						|
}
 | 
						|
 | 
						|
// patch sends a PATCH HTTP request to the URL with a string data
 | 
						|
pub fn patch(url string, data string) ?Response {
 | 
						|
	return fetch(
 | 
						|
		method: .patch
 | 
						|
		url: url
 | 
						|
		data: data
 | 
						|
		header: new_header(key: .content_type, value: http.content_type_default)
 | 
						|
	)
 | 
						|
}
 | 
						|
 | 
						|
// head sends a HEAD HTTP request to the URL
 | 
						|
pub fn head(url string) ?Response {
 | 
						|
	return fetch(method: .head, url: url)
 | 
						|
}
 | 
						|
 | 
						|
// delete sends a DELETE HTTP request to the URL
 | 
						|
pub fn delete(url string) ?Response {
 | 
						|
	return fetch(method: .delete, url: url)
 | 
						|
}
 | 
						|
 | 
						|
// fetch sends an HTTP request to the URL with the given method and configurations
 | 
						|
pub fn fetch(config FetchConfig) ?Response {
 | 
						|
	if config.url == '' {
 | 
						|
		return error('http.fetch: empty url')
 | 
						|
	}
 | 
						|
	url := build_url_from_fetch(config) or { return error('http.fetch: invalid url $config.url') }
 | 
						|
	req := Request{
 | 
						|
		method: config.method
 | 
						|
		url: url
 | 
						|
		data: config.data
 | 
						|
		header: config.header
 | 
						|
		cookies: config.cookies
 | 
						|
		user_agent: config.user_agent
 | 
						|
		user_ptr: 0
 | 
						|
		verbose: config.verbose
 | 
						|
		validate: config.validate
 | 
						|
		verify: config.verify
 | 
						|
		cert: config.cert
 | 
						|
		cert_key: config.cert_key
 | 
						|
		in_memory_verification: config.in_memory_verification
 | 
						|
		allow_redirect: config.allow_redirect
 | 
						|
	}
 | 
						|
	res := req.do() ?
 | 
						|
	return res
 | 
						|
}
 | 
						|
 | 
						|
// get_text sends a GET HTTP request to the URL and returns the text content of the response
 | 
						|
pub fn get_text(url string) string {
 | 
						|
	resp := fetch(url: url, method: .get) or { return '' }
 | 
						|
	return resp.text
 | 
						|
}
 | 
						|
 | 
						|
// url_encode_form_data converts mapped data to an URL encoded string
 | 
						|
pub fn url_encode_form_data(data map[string]string) string {
 | 
						|
	mut pieces := []string{}
 | 
						|
	for key_, value_ in data {
 | 
						|
		key := urllib.query_escape(key_)
 | 
						|
		value := urllib.query_escape(value_)
 | 
						|
		pieces << '$key=$value'
 | 
						|
	}
 | 
						|
	return pieces.join('&')
 | 
						|
}
 | 
						|
 | 
						|
[deprecated: 'use fetch()']
 | 
						|
fn fetch_with_method(method Method, _config FetchConfig) ?Response {
 | 
						|
	mut config := _config
 | 
						|
	config.method = method
 | 
						|
	return fetch(config)
 | 
						|
}
 | 
						|
 | 
						|
fn build_url_from_fetch(config FetchConfig) ?string {
 | 
						|
	mut url := urllib.parse(config.url) ?
 | 
						|
	if config.params.len == 0 {
 | 
						|
		return url.str()
 | 
						|
	}
 | 
						|
	mut pieces := []string{cap: config.params.len}
 | 
						|
	for key, val in config.params {
 | 
						|
		pieces << '$key=$val'
 | 
						|
	}
 | 
						|
	mut query := pieces.join('&')
 | 
						|
	if url.raw_query.len > 1 {
 | 
						|
		query = url.raw_query + '&' + query
 | 
						|
	}
 | 
						|
	url.raw_query = query
 | 
						|
	return url.str()
 | 
						|
}
 | 
						|
 | 
						|
[deprecated: 'unescape_url is deprecated, use urllib.query_unescape() instead']
 | 
						|
pub fn unescape_url(s string) string {
 | 
						|
	panic('http.unescape_url() was replaced with urllib.query_unescape()')
 | 
						|
}
 | 
						|
 | 
						|
[deprecated: 'escape_url is deprecated, use urllib.query_escape() instead']
 | 
						|
pub fn escape_url(s string) string {
 | 
						|
	panic('http.escape_url() was replaced with urllib.query_escape()')
 | 
						|
}
 | 
						|
 | 
						|
[deprecated: 'unescape is deprecated, use urllib.query_escape() instead']
 | 
						|
pub fn unescape(s string) string {
 | 
						|
	panic('http.unescape() was replaced with http.unescape_url()')
 | 
						|
}
 | 
						|
 | 
						|
[deprecated: 'escape is deprecated, use urllib.query_unescape() instead']
 | 
						|
pub fn escape(s string) string {
 | 
						|
	panic('http.escape() was replaced with http.escape_url()')
 | 
						|
}
 |