Skip to content

core.httpconnection #

HTTPConnection Module

The HTTPConnection module provides a robust HTTP client implementation with support for JSON handling, custom headers, retries, and caching.

Features

  • Generic JSON methods for type-safe requests
  • Custom header support
  • Built-in retry mechanism
  • Cache configuration
  • URL encoding support

Basic Usage

import freeflowuniverse.herolib.core.httpconnection

// Create a new HTTP connection
mut conn := HTTPConnection{
    base_url: 'https://api.example.com'
    retry: 5  // number of retries for failed requests
}

important: How to use it on a management class e.g. an installe or a client

// e.g. HetznerManager is the object on which we want to have an http client

pub fn (mut h HetznerManager) connection() !&httpconnection.HTTPConnection {
    mut c := h.conn or {
        mut c2 := httpconnection.new(
            name: 'hetzner_${h.name}'
            url:   h.baseurl
            cache: true
            retry: 3
        )!
        c2.basic_auth(h.user, h.password)
        c2
    }

    return c
}

Examples

GET Request with JSON Response

// Define your data structure
struct User {
    id    int
    name  string
    email string
}

// Make a GET request and decode JSON response
user := conn.get_json_generic[User](
    method: .get
    prefix: 'users/1'
    dataformat: .urlencoded
)!

GET Request for List of Items

// Get a list of items and decode each one
users := conn.get_json_list_generic[User](
    method: .get
    prefix: 'users'
    list_dict_key: 'users'  // if response is wrapped in a key
    dataformat: .urlencoded
)!

POST Request with JSON Data

// Create new resource with POST
new_user := conn.post_json_generic[User](
    method: .post
    prefix: 'users'
    dataformat: .urlencoded
    params: {
        'name': 'John Doe'
        'email': 'john@example.com'
    }
)!

Real-World Example: SSH Key Management

Here's a practical example inspired by SSH key management in a cloud API:

// Define the SSH key structure
struct SSHKey {
pub mut:
    name        string
    fingerprint string
    type_       string    @[json: 'type']
    size        int
    created_at  string
    data        string
}

// Get all SSH keys
fn get_ssh_keys(mut conn HTTPConnection) ![]SSHKey {
    return conn.get_json_list_generic[SSHKey](
        method: .get
        prefix: 'key'
        list_dict_key: 'key'
        dataformat: .urlencoded
    )!
}

// Create a new SSH key
fn create_ssh_key(mut conn HTTPConnection, name string, key_data string) !SSHKey {
    return conn.post_json_generic[SSHKey](
        method: .post
        prefix: 'key'
        dataformat: .urlencoded
        params: {
            'name': name
            'data': key_data
        }
    )!
}

// Delete an SSH key
fn delete_ssh_key(mut conn HTTPConnection, fingerprint string) ! {
    conn.delete(
        method: .delete
        prefix:'key/${fingerprint}'
        dataformat: .urlencoded
    )!
}

Custom Headers

You can set default headers for all requests or specify headers for individual requests:

import net.http { Header }

// Set default headers for all requests
conn.default_header = http.new_header(
    key: .authorization
    value: 'Bearer your-token-here'
)

// Add custom headers for specific request
response := conn.get_json(
    method: .get
    prefix: 'protected/resource'
    header: http.new_header(
        key: .content_type
        value: 'application/json'
    )
)!

Error Handling

The module uses V's built-in error handling. All methods that can fail return a Result type:

// Handle potential errors
user := conn.get_json_generic[User](
    method: .get
    prefix: 'users/1'
) or {
    println('Error: ${err}')
    return
}

Cache Configuration

The module supports caching of responses. Configure caching behavior through the CacheConfig struct:

mut conn := HTTPConnection{
    base_url: 'https://api.example.com'
    cache: CacheConfig{
        enabled: true
        // Add other cache configuration as needed
    }
}

fn new #

fn new(args HTTPConnectionArgs) !&HTTPConnection

enum DataFormat #

enum DataFormat {
	json           // application/json
	urlencoded     //
	multipart_form //
}

struct CacheConfig #

struct CacheConfig {
pub mut:
	key               string // as used to identity in redis
	allowable_methods []Method = [.get, .head]
	allowable_codes   []int    = default_cacheable_codes
	disable           bool     = true // default cache is not working
	expire_after      int      = 3600 // default expire_after is 1h
	match_headers     bool // cache the request header to be matched later
}

struct HTTPConnection #

@[heap]
struct HTTPConnection {
pub mut:
	redis          Redis @[str: skip]
	base_url       string // the base url
	default_header Header
	cache          CacheConfig
	retry          int = 5
}

fn (HTTPConnection) basic_auth #

fn (mut conn HTTPConnection) basic_auth(username string, password string)

fn (HTTPConnection) cache_drop #

fn (mut h HTTPConnection) cache_drop() !

drop full cache for specific cache_key

fn (HTTPConnection) delete #

fn (mut h HTTPConnection) delete(req_ Request) !string

Delete Request with json data and return response as string

fn (HTTPConnection) delete_json_generic #

fn (mut h HTTPConnection) delete_json_generic[T](req Request) !T

TODO

fn (HTTPConnection) get #

fn (mut h HTTPConnection) get(req_ Request) !string

Get Request with json data and return response as string

fn (HTTPConnection) get_json #

fn (mut h HTTPConnection) get_json(req Request) !string

dict_key string //if the return is a dict, then will take the element out of the dict with the key and process further

fn (HTTPConnection) get_json_dict #

fn (mut h HTTPConnection) get_json_dict(req Request) !map[string]json2.Any

do a request with certain prefix on the already specified url parse as json

fn (HTTPConnection) get_json_generic #

fn (mut h HTTPConnection) get_json_generic[T](req Request) !T

fn (HTTPConnection) get_json_list #

fn (mut h HTTPConnection) get_json_list(req Request) ![]string

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":...}]

fn (HTTPConnection) get_json_list_generic #

fn (mut h HTTPConnection) get_json_list_generic[T](req Request) ![]T

fn (HTTPConnection) post_json_generic #

fn (mut h HTTPConnection) post_json_generic[T](req Request) !T

fn (HTTPConnection) post_json_str #

fn (mut h HTTPConnection) post_json_str(req_ Request) !string

dict_key string //if the return is a dict, then will take the element out of the dict with the key and process further

fn (HTTPConnection) post_multi_part #

fn (mut h HTTPConnection) post_multi_part(req Request, form http.PostMultipartFormConfig) !http.Response

performs a multi part form data request

fn (HTTPConnection) put_json_generic #

fn (mut h HTTPConnection) put_json_generic[T](req Request) !T

TODO

fn (HTTPConnection) send #

fn (mut h HTTPConnection) send(req_ Request) !Result

Core fucntion to be used in all other function

struct HTTPConnectionArgs #

@[params]
struct HTTPConnectionArgs {
pub:
	name  string @[required]
	url   string @[required]
	cache bool
	retry int = 1
}

struct Request #

@[params]
struct Request {
pub mut:
	method        Method
	prefix        string
	id            string
	params        map[string]string
	data          string
	cache_disable bool // do not put this default on true, this is set on the connection, this is here to be overruled in specific cases
	header        ?Header
	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
	debug         bool
	dataformat    DataFormat
}

struct Result #

struct Result {
pub mut:
	code int
	data string
}

fn (Result) is_ok #

fn (r Result) is_ok() bool