2021-01-18 13:20:06 +01:00
|
|
|
// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
|
2019-06-23 04:21:30 +02:00
|
|
|
// Use of this source code is governed by an MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
2019-06-22 20:20:28 +02:00
|
|
|
module http
|
|
|
|
|
2019-08-06 14:43:09 +02:00
|
|
|
import net.urllib
|
|
|
|
|
2019-08-17 14:50:47 +02:00
|
|
|
const (
|
2020-05-16 16:12:23 +02:00
|
|
|
max_redirects = 4
|
2020-01-16 18:16:11 +01:00
|
|
|
content_type_default = 'text/plain'
|
2020-10-15 15:17:52 +02:00
|
|
|
bufsize = 1536
|
2019-08-17 14:50:47 +02:00
|
|
|
)
|
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// FetchConfig holds configurations of fetch
|
2020-01-16 18:16:11 +01:00
|
|
|
pub struct FetchConfig {
|
|
|
|
pub mut:
|
2021-08-17 08:16:59 +02:00
|
|
|
url string
|
2020-07-27 21:20:05 +02:00
|
|
|
method Method
|
2021-04-09 18:17:33 +02:00
|
|
|
header Header
|
2020-04-09 14:03:47 +02:00
|
|
|
data string
|
|
|
|
params map[string]string
|
|
|
|
cookies map[string]string
|
2020-05-20 08:58:57 +02:00
|
|
|
user_agent string = 'v.http'
|
2020-09-09 15:34:41 +02:00
|
|
|
verbose bool
|
2021-09-01 00:43:35 +02:00
|
|
|
//
|
2021-09-09 18:55:49 +02:00
|
|
|
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
|
2020-01-16 18:16:11 +01:00
|
|
|
}
|
|
|
|
|
2020-10-15 15:17:52 +02:00
|
|
|
pub fn new_request(method Method, url_ string, data string) ?Request {
|
2021-09-08 04:11:02 +02:00
|
|
|
url := if method == .get && !url_.contains('?') { url_ + '?' + data } else { url_ }
|
2020-10-15 15:17:52 +02:00
|
|
|
// println('new req() method=$method url="$url" dta="$data"')
|
2020-05-10 02:07:15 +02:00
|
|
|
return Request{
|
2020-07-27 21:20:05 +02:00
|
|
|
method: method
|
2020-05-10 02:07:15 +02:00
|
|
|
url: url
|
2020-12-22 08:32:32 +01:00
|
|
|
data: data
|
|
|
|
/*
|
|
|
|
headers: {
|
2020-05-18 05:10:56 +02:00
|
|
|
'Accept-Encoding': 'compress'
|
|
|
|
}
|
2020-12-22 08:32:32 +01:00
|
|
|
*/
|
2020-05-10 02:07:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// get sends a GET HTTP request to the URL
|
2020-01-16 18:16:11 +01:00
|
|
|
pub fn get(url string) ?Response {
|
2021-08-17 08:16:59 +02:00
|
|
|
return fetch(method: .get, url: url)
|
2020-01-14 23:19:50 +01:00
|
|
|
}
|
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// post sends a POST HTTP request to the URL with a string data
|
2020-10-15 15:17:52 +02:00
|
|
|
pub fn post(url string, data string) ?Response {
|
2021-08-17 08:16:59 +02:00
|
|
|
return fetch(
|
|
|
|
method: .post
|
|
|
|
url: url
|
2021-01-26 15:43:10 +01:00
|
|
|
data: data
|
2021-06-06 00:51:49 +02:00
|
|
|
header: new_header(key: .content_type, value: http.content_type_default)
|
2021-01-26 15:43:10 +01:00
|
|
|
)
|
2020-01-14 23:19:50 +01:00
|
|
|
}
|
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// post_json sends a POST HTTP request to the URL with a JSON data
|
2020-10-15 15:17:52 +02:00
|
|
|
pub fn post_json(url string, data string) ?Response {
|
2021-08-17 08:16:59 +02:00
|
|
|
return fetch(
|
|
|
|
method: .post
|
|
|
|
url: url
|
2021-01-26 15:43:10 +01:00
|
|
|
data: data
|
2021-06-06 00:51:49 +02:00
|
|
|
header: new_header(key: .content_type, value: 'application/json')
|
2021-01-26 15:43:10 +01:00
|
|
|
)
|
2020-06-30 14:36:11 +02:00
|
|
|
}
|
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// post_form sends a POST HTTP request to the URL with X-WWW-FORM-URLENCODED data
|
2020-01-16 18:16:11 +01:00
|
|
|
pub fn post_form(url string, data map[string]string) ?Response {
|
2021-08-17 08:16:59 +02:00
|
|
|
return fetch(
|
|
|
|
method: .post
|
|
|
|
url: url
|
2021-06-06 00:51:49 +02:00
|
|
|
header: new_header(key: .content_type, value: 'application/x-www-form-urlencoded')
|
2021-01-26 15:43:10 +01:00
|
|
|
data: url_encode_form_data(data)
|
|
|
|
)
|
2020-01-14 23:19:50 +01:00
|
|
|
}
|
|
|
|
|
2021-09-25 09:09:49 +02:00
|
|
|
[params]
|
2021-09-16 06:34:07 +02:00
|
|
|
pub struct PostMultipartFormConfig {
|
2021-09-25 09:09:49 +02:00
|
|
|
pub mut:
|
|
|
|
form map[string]string
|
|
|
|
files map[string][]FileData
|
|
|
|
header Header
|
2021-09-16 06:34:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
2021-09-25 09:09:49 +02:00
|
|
|
mut header := conf.header
|
|
|
|
header.set(.content_type, 'multipart/form-data; boundary="$boundary"')
|
2021-09-16 06:34:07 +02:00
|
|
|
return fetch(
|
|
|
|
method: .post
|
|
|
|
url: url
|
2021-09-25 09:09:49 +02:00
|
|
|
header: header
|
2021-09-16 06:34:07 +02:00
|
|
|
data: body
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// put sends a PUT HTTP request to the URL with a string data
|
2020-10-15 15:17:52 +02:00
|
|
|
pub fn put(url string, data string) ?Response {
|
2021-08-17 08:16:59 +02:00
|
|
|
return fetch(
|
|
|
|
method: .put
|
|
|
|
url: url
|
2021-01-26 15:43:10 +01:00
|
|
|
data: data
|
2021-06-06 00:51:49 +02:00
|
|
|
header: new_header(key: .content_type, value: http.content_type_default)
|
2021-01-26 15:43:10 +01:00
|
|
|
)
|
2020-01-14 23:19:50 +01:00
|
|
|
}
|
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// patch sends a PATCH HTTP request to the URL with a string data
|
2020-10-15 15:17:52 +02:00
|
|
|
pub fn patch(url string, data string) ?Response {
|
2021-08-17 08:16:59 +02:00
|
|
|
return fetch(
|
|
|
|
method: .patch
|
|
|
|
url: url
|
2021-01-26 15:43:10 +01:00
|
|
|
data: data
|
2021-06-06 00:51:49 +02:00
|
|
|
header: new_header(key: .content_type, value: http.content_type_default)
|
2021-01-26 15:43:10 +01:00
|
|
|
)
|
2019-07-29 19:18:26 +02:00
|
|
|
}
|
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// head sends a HEAD HTTP request to the URL
|
2020-01-16 18:16:11 +01:00
|
|
|
pub fn head(url string) ?Response {
|
2021-08-17 08:16:59 +02:00
|
|
|
return fetch(method: .head, url: url)
|
2019-06-22 20:20:28 +02:00
|
|
|
}
|
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// delete sends a DELETE HTTP request to the URL
|
2020-01-16 18:16:11 +01:00
|
|
|
pub fn delete(url string) ?Response {
|
2021-08-17 08:16:59 +02:00
|
|
|
return fetch(method: .delete, url: url)
|
2020-01-03 22:07:58 +01:00
|
|
|
}
|
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// fetch sends an HTTP request to the URL with the given method and configurations
|
2021-08-17 08:16:59 +02:00
|
|
|
pub fn fetch(config FetchConfig) ?Response {
|
|
|
|
if config.url == '' {
|
2020-01-16 18:16:11 +01:00
|
|
|
return error('http.fetch: empty url')
|
2019-07-29 19:18:26 +02:00
|
|
|
}
|
2021-08-17 08:16:59 +02:00
|
|
|
url := build_url_from_fetch(config) or { return error('http.fetch: invalid url $config.url') }
|
2020-01-16 18:16:11 +01:00
|
|
|
req := Request{
|
2020-07-27 21:20:05 +02:00
|
|
|
method: config.method
|
2019-07-13 14:10:15 +02:00
|
|
|
url: url
|
2021-08-17 08:16:59 +02:00
|
|
|
data: config.data
|
2021-04-09 18:17:33 +02:00
|
|
|
header: config.header
|
2020-01-16 18:16:11 +01:00
|
|
|
cookies: config.cookies
|
2020-01-29 04:05:37 +01:00
|
|
|
user_agent: config.user_agent
|
2019-06-22 20:20:28 +02:00
|
|
|
user_ptr: 0
|
2020-01-16 18:16:11 +01:00
|
|
|
verbose: config.verbose
|
2021-09-01 00:43:35 +02:00
|
|
|
validate: config.validate
|
|
|
|
verify: config.verify
|
|
|
|
cert: config.cert
|
|
|
|
cert_key: config.cert_key
|
2021-09-09 18:55:49 +02:00
|
|
|
in_memory_verification: config.in_memory_verification
|
2019-06-22 20:20:28 +02:00
|
|
|
}
|
2020-10-15 15:17:52 +02:00
|
|
|
res := req.do() ?
|
2020-01-16 18:16:11 +01:00
|
|
|
return res
|
2019-06-22 20:20:28 +02:00
|
|
|
}
|
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// get_text sends a GET HTTP request to the URL and returns the text content of the response
|
2019-07-31 22:10:28 +02:00
|
|
|
pub fn get_text(url string) string {
|
2021-08-17 08:16:59 +02:00
|
|
|
resp := fetch(url: url, method: .get) or { return '' }
|
2019-10-24 18:44:49 +02:00
|
|
|
return resp.text
|
|
|
|
}
|
2019-07-31 22:10:28 +02:00
|
|
|
|
2021-03-01 10:48:38 +01:00
|
|
|
// url_encode_form_data converts mapped data to an URL encoded string
|
2020-01-16 18:16:11 +01:00
|
|
|
pub fn url_encode_form_data(data map[string]string) string {
|
2020-04-26 13:49:31 +02:00
|
|
|
mut pieces := []string{}
|
2020-07-05 15:27:37 +02:00
|
|
|
for key_, value_ in data {
|
|
|
|
key := urllib.query_escape(key_)
|
|
|
|
value := urllib.query_escape(value_)
|
2020-01-16 18:16:11 +01:00
|
|
|
pieces << '$key=$value'
|
|
|
|
}
|
|
|
|
return pieces.join('&')
|
|
|
|
}
|
|
|
|
|
2021-08-17 08:16:59 +02:00
|
|
|
[deprecated: 'use fetch()']
|
|
|
|
fn fetch_with_method(method Method, _config FetchConfig) ?Response {
|
2020-01-16 18:16:11 +01:00
|
|
|
mut config := _config
|
|
|
|
config.method = method
|
2021-08-17 08:16:59 +02:00
|
|
|
return fetch(config)
|
2020-01-16 18:16:11 +01:00
|
|
|
}
|
|
|
|
|
2021-08-17 08:16:59 +02:00
|
|
|
fn build_url_from_fetch(config FetchConfig) ?string {
|
|
|
|
mut url := urllib.parse(config.url) ?
|
2021-02-17 05:19:25 +01:00
|
|
|
if config.params.len == 0 {
|
2020-01-16 18:16:11 +01:00
|
|
|
return url.str()
|
|
|
|
}
|
2021-02-17 05:19:25 +01:00
|
|
|
mut pieces := []string{cap: config.params.len}
|
|
|
|
for key, val in config.params {
|
|
|
|
pieces << '$key=$val'
|
2020-01-16 18:16:11 +01:00
|
|
|
}
|
|
|
|
mut query := pieces.join('&')
|
|
|
|
if url.raw_query.len > 1 {
|
|
|
|
query = url.raw_query + '&' + query
|
|
|
|
}
|
|
|
|
url.raw_query = query
|
|
|
|
return url.str()
|
|
|
|
}
|
|
|
|
|
2021-10-02 14:19:32 +02:00
|
|
|
[deprecated: 'unescape_url is deprecated, use urllib.query_unescape() instead']
|
2019-08-06 05:54:47 +02:00
|
|
|
pub fn unescape_url(s string) string {
|
2019-10-24 18:44:49 +02:00
|
|
|
panic('http.unescape_url() was replaced with urllib.query_unescape()')
|
2019-08-06 05:54:47 +02:00
|
|
|
}
|
|
|
|
|
2021-10-02 14:19:32 +02:00
|
|
|
[deprecated: 'escape_url is deprecated, use urllib.query_escape() instead']
|
2019-08-06 05:54:47 +02:00
|
|
|
pub fn escape_url(s string) string {
|
2019-10-24 18:44:49 +02:00
|
|
|
panic('http.escape_url() was replaced with urllib.query_escape()')
|
2019-08-06 05:54:47 +02:00
|
|
|
}
|
|
|
|
|
2021-10-02 14:19:32 +02:00
|
|
|
[deprecated: 'unescape is deprecated, use urllib.query_escape() instead']
|
2019-08-06 05:54:47 +02:00
|
|
|
pub fn unescape(s string) string {
|
2019-10-24 18:44:49 +02:00
|
|
|
panic('http.unescape() was replaced with http.unescape_url()')
|
2019-08-06 05:54:47 +02:00
|
|
|
}
|
|
|
|
|
2021-10-02 14:19:32 +02:00
|
|
|
[deprecated: 'escape is deprecated, use urllib.query_unescape() instead']
|
2019-08-06 05:54:47 +02:00
|
|
|
pub fn escape(s string) string {
|
2019-10-24 18:44:49 +02:00
|
|
|
panic('http.escape() was replaced with http.escape_url()')
|
2019-08-06 05:54:47 +02:00
|
|
|
}
|