Files
herolib/lib/core/httpconnection/connection_methods.v
Mahmoud Emad ec22a8e0ec refactor: improve installer code
- Refactor installer code for better readability and maintainability.
- Move `dagu_server.vsh` to `examples/virt/daguserver/dagu_server.vsh`.
- Remove unnecessary `println` statements.
- Improve error handling in `zinit_installer_actions.v`.
- Update `zinit_installer_actions.v` startup type to systemd.
- Refactor several factory functions.
2025-02-12 09:02:33 +00:00

212 lines
6.4 KiB
V

// /*
// METHODS NOTES
// * Our target to wrap the default http methods used in V to be cached using redis
// * By default cache enabled in all Request, if you need to disable cache, set req.cache_disable true
// *
// * Flow will be:
// * 1 - Check cache if enabled try to get result from cache
// * 2 - Check result
// * 3 - Do request, if needed
// * 4 - Set in cache if enabled or invalidate cache
// * 5 - Return result
// Suggestion: Send function now enough to do what we want, no need to any post*, get* additional functions
// */
module httpconnection
import x.json2
import net.http
import freeflowuniverse.herolib.data.hjson
import freeflowuniverse.herolib.ui.console
// Build url from Request and httpconnection
fn (mut h HTTPConnection) url(req Request) string {
mut u := '${h.base_url}/${req.prefix.trim('/')}'
if req.id.len > 0 {
u += '/${req.id}'
}
if req.params.len > 0 && req.method != .post {
u += '?${http.url_encode_form_data(req.params)}'
}
return u
}
// Return if request cacheable, depeds on connection cache and request arguments.
fn (h HTTPConnection) is_cacheable(req Request) bool {
return !(h.cache.disable || req.cache_disable) && req.method in h.cache.allowable_methods
}
// Return true if we need to invalidate cache after unsafe method
fn (h HTTPConnection) needs_invalidate(req Request, result_code int) bool {
return !(h.cache.disable || req.cache_disable) && req.method in unsafe_http_methods
&& req.method !in h.cache.allowable_methods && result_code >= 200 && result_code <= 399
}
// Core fucntion to be used in all other function
pub fn (mut h HTTPConnection) send(req_ Request) !Result {
mut result := Result{}
mut response := http.Response{}
mut err_message := ''
mut from_cache := false // used to know if result came from cache
mut req := req_
is_cacheable := h.is_cacheable(req)
// console.print_debug("is cacheable: ${is_cacheable}")
// 1 - Check cache if enabled try to get result from cache
if is_cacheable {
result = h.cache_get(req)!
if result.code != -1 {
from_cache = true
}
}
// 2 - Check result
if result.code in [0, -1] {
// 3 - Do request, if needed
if req.method == .post {
if req.dataformat == .urlencoded && req.data == '' && req.params.len > 0 {
req.data = http.url_encode_form_data(req.params)
}
}
url := h.url(req)
// println("----")
// println(url)
// println(req.data)
// println("----")
mut new_req := http.new_request(req.method, url, req.data)
// joining the header from the HTTPConnection with the one from Request
new_req.header = h.header()
if new_req.header.contains(http.CommonHeader.content_type) {
panic('bug: content_type should not be set as part of default header')
}
match req.dataformat {
.json {
new_req.header.set(http.CommonHeader.content_type, 'application/json')
}
.urlencoded {
new_req.header.set(http.CommonHeader.content_type, 'application/x-www-form-urlencoded')
}
.multipart_form {
new_req.header.set(http.CommonHeader.content_type, 'multipart/form-data')
}
}
if req.debug {
console.print_debug('http request:\n${new_req.str()}')
}
for _ in 0 .. h.retry {
response = new_req.do() or {
err_message = 'Cannot send request:${req}\nerror:${err}'
// console.print_debug(err_message)
continue
}
break
}
if req.debug {
console.print_debug(response.str())
}
if response.status_code == 0 {
return error(err_message)
}
result.code = response.status_code
result.data = response.body
}
// 4 - Set in cache if enabled
if !from_cache && is_cacheable && result.code in h.cache.allowable_codes {
h.cache_set(req, result)!
}
if h.needs_invalidate(req, result.code) {
h.cache_invalidate(req)!
}
// 5 - Return result
return result
}
pub fn (r Result) is_ok() bool {
return r.code >= 200 && r.code <= 399
}
// dict_key string //if the return is a dict, then will take the element out of the dict with the key and process further
pub fn (mut h HTTPConnection) post_json_str(req_ Request) !string {
mut req := req_
req.method = .post
result := h.send(req)!
if result.is_ok() {
mut data_ := result.data
if req.dict_key.len > 0 {
data_ = hjson.json_dict_get_string(data_, false, req.dict_key)!
}
return data_
}
return error('Could not post ${req}\result:\n${result}')
}
// do a request with certain prefix on the already specified url
// parse as json
pub fn (mut h HTTPConnection) get_json_dict(req Request) !map[string]json2.Any {
data_ := h.get(req)!
mut data := map[string]json2.Any{}
data = hjson.json_dict_filter_any(data_, false, [], [])!
return data
}
// dict_key string //if the return is a dict, then will take the element out of the dict with the key and process further
// list_dict_key string //if the output is a list of dicts, then will process each element of the list to take the val with key out of that dict
// e.g. the input is a list of dicts e.g. [{"key":{"name":"kristof@incubaid.com",...},{"key":...}]
pub fn (mut h HTTPConnection) get_json_list(req Request) ![]string {
mut data_ := h.get(req)!
if req.dict_key.len > 0 {
data_ = hjson.json_dict_get_string(data_, false, req.dict_key)!
}
if req.list_dict_key.len > 0 {
return hjson.json_list_dict_get_string(data_, false, req.list_dict_key)!
}
data := hjson.json_list(data_, false)
return data
}
// dict_key string //if the return is a dict, then will take the element out of the dict with the key and process further
pub fn (mut h HTTPConnection) get_json(req Request) !string {
h.default_header.add(.content_language, 'Content-Type: application/json')
mut data_ := h.get(req)!
if req.dict_key.len > 0 {
data_ = hjson.json_dict_get_string(data_, false, req.dict_key)!
}
return data_
}
// Get Request with json data and return response as string
pub fn (mut h HTTPConnection) get(req_ Request) !string {
mut req := req_
req.debug = true
req.method = .get
result := h.send(req)!
return result.data
}
// Delete Request with json data and return response as string
pub fn (mut h HTTPConnection) delete(req_ Request) !string {
mut req := req_
req.method = .delete
result := h.send(req)!
return result.data
}
// performs a multi part form data request
pub fn (mut h HTTPConnection) post_multi_part(req Request, form http.PostMultipartFormConfig) !http.Response {
mut req_form := form
mut header := h.header()
header.set(http.CommonHeader.content_type, 'multipart/form-data')
req_form.header = header
url := h.url(req)
return http.post_multipart_form(url, req_form)!
}