HttpLib — HTTP Client Library

Comprehensive HTTP client for Plan9Basic. RESTful API interactions, file transfers, form uploads, and full HTTP protocol support across all platforms. 92 functions.

CategoryCountDescription
Error Handling4http_error, http_errormsg$, http_strerror$, http_clearerror
Client Management4http_client# (2 overloads), http_free, http_reset#
Configuration11Base URL, timeouts, user-agent, content type, accept, redirects, SSL
Query Parameters4http_param#, http_param$, http_paramremove#, http_paramclear#
Custom Headers5http_header#, http_header$, http_headerremove#, http_headerclear#, http_headercount
Authentication4http_basicauth#, http_bearerauth#, http_customauth#, http_clearauth#
Cookies5http_cookie#, http_cookie$, http_cookieremove#, http_cookieclear#, http_cookiecount
Proxy3http_proxy#, http_proxyauth#, http_clearproxy#
Form Data10Create, fields, files (3 overloads), clear, count, URL-encode, free
Sync HTTP Methods11GET, POST (3), PUT (2), PATCH, DELETE, HEAD, OPTIONS
Async HTTP Methods6GET, POST, PUT, DELETE, PATCH async + http_busy
Response Access20Status, body, headers, cookies, content info, category checks
File Operations4http_download, http_upload$, http_uploadput$, http_postfile$
Encoding Utilities4http_urlencode$, http_urldecode$, http_htmlencode$, http_htmldecode$
Simple Functions3http_simpleget$, http_simplepost$, http_simpledownload

Platform Considerations

PlatformApproachReason
Desktop (Windows, macOS, Linux)Synchronous functionsSimple, blocking — main thread is fine
Mobile (Android, iOS)Asynchronous + pollingNon-blocking to avoid ANR / UI freeze
╯ desktop.bas
' Desktop - synchronous (simple)
let client# = http_client#("https://api.example.com")
let response$ = http_get$(client#, "/data")
println http_body$(client#)
╯ mobile.bas
' Mobile - async with timer polling
http_get_async(client#, "/data")

function OnTimer(sender#) local x
    if http_busy(client#) = 0 then
        timer_enabled#(sender#, 0)
        println http_body$(client#)
    end if
end function

Error Handling

CodeConstantDescription
0ERR_NONENo error
1ERR_INVALID_CLIENTInvalid or null client pointer
2ERR_INVALID_URLMalformed URL
3ERR_CONNECTIONNetwork connection failed
4ERR_TIMEOUTRequest timed out
5ERR_SSLSSL/TLS certificate error
6ERR_INVALID_ARGUMENTInvalid function argument
7ERR_FILEFile read/write error
8ERR_AUTHAuthentication failed
9ERR_INVALID_RESPONSEMalformed response
10ERR_INVALID_FORMInvalid form data pointer
FunctionSignatureDescription
http_error()http_error@Last error code (0–10)
http_errormsg$()http_errormsg$@Detailed error message from last operation
http_strerror$(code)http_strerror$@nDescription for a specific error code
http_clearerror()http_clearerror@Clear error state
╯ plan9basic
let errcode = http_error()
let errmsg$ = http_errormsg$()
let desc$ = http_strerror$(errcode)
let result = http_clearerror()

Client Management

FunctionSignatureDescription
http_client#()http_client#@Create HTTP client (no base URL)
http_client#(baseUrl$)http_client#@$Create client with base URL
http_free(client#)http_free@#Free client resources
http_reset#(client#)http_reset#@#Reset client to defaults
╯ plan9basic
' Create with base URL
let client# = http_client#("https://api.example.com")

' ... use client ...

' Free when done
let x = http_free(client#)

Configuration

All configuration functions return the client pointer for chaining.

FunctionSignatureDescription
http_baseurl#(c#, url$)http_baseurl#@#$Set base URL
http_baseurl$(c#)http_baseurl$@#Get base URL
http_timeout#(c#, ms)http_timeout#@#nSet connection timeout (milliseconds)
http_timeout(c#)http_timeout@#Get connection timeout
http_responsetimeout#(c#, ms)http_responsetimeout#@#nSet response timeout
http_useragent#(c#, ua$)http_useragent#@#$Set User-Agent header
http_contenttype#(c#, ct$)http_contenttype#@#$Set Content-Type header
http_accept#(c#, accept$)http_accept#@#$Set Accept header
http_followredirects#(c#, n)http_followredirects#@#nEnable/disable redirects (1/0)
http_maxredirects#(c#, n)http_maxredirects#@#nSet max redirect count
http_validatessl#(c#, n)http_validatessl#@#nEnable/disable SSL validation (1/0)
╯ plan9basic
let client# = http_client#("https://api.example.com")
let client# = http_timeout#(client#, 30000)
let client# = http_useragent#(client#, "MyApp/1.0")
let client# = http_contenttype#(client#, "application/json")
let client# = http_accept#(client#, "application/json")
let client# = http_followredirects#(client#, 1)
let client# = http_maxredirects#(client#, 5)
let client# = http_validatessl#(client#, 1)

Query Parameters

FunctionSignatureDescription
http_param#(c#, key$, val$)http_param#@#$$Add/update query parameter
http_param$(c#, key$)http_param$@#$Get parameter value
http_paramremove#(c#, key$)http_paramremove#@#$Remove parameter
http_paramclear#(c#)http_paramclear#@#Clear all parameters
╯ plan9basic
let client# = http_param#(client#, "page", "1")
let client# = http_param#(client#, "limit", "20")
' Requests now append ?page=1&limit=20

let val$ = http_param$(client#, "page")  ' "1"
let client# = http_paramremove#(client#, "page")
ⓘ Note: Parameters are automatically URL-encoded and appended to all requests.

Custom Headers

FunctionSignatureDescription
http_header#(c#, name$, val$)http_header#@#$$Set custom header
http_header$(c#, name$)http_header$@#$Get header value
http_headerremove#(c#, name$)http_headerremove#@#$Remove header
http_headerclear#(c#)http_headerclear#@#Clear all custom headers
http_headercount(c#)http_headercount@#Number of custom headers
╯ plan9basic
let client# = http_header#(client#, "X-API-Key", "abc123")
let client# = http_header#(client#, "X-Request-ID", "req-456")
let key$ = http_header$(client#, "X-API-Key")

Authentication

FunctionSignatureDescription
http_basicauth#(c#, user$, pass$)http_basicauth#@#$$Basic authentication (username/password)
http_bearerauth#(c#, token$)http_bearerauth#@#$Bearer token authentication
http_customauth#(c#, value$)http_customauth#@#$Custom Authorization header value
http_clearauth#(c#)http_clearauth#@#Clear authentication
╯ plan9basic
' Basic auth
let client# = http_basicauth#(client#, "username", "password")

' Bearer token
let client# = http_bearerauth#(client#, "eyJhbGciOiJIUzI1NiIs...")

' Custom
let client# = http_customauth#(client#, "ApiKey my-secret-key")

' Clear
let client# = http_clearauth#(client#)

Cookie Management

FunctionSignatureDescription
http_cookie#(c#, name$, val$)http_cookie#@#$$Set request cookie
http_cookie$(c#, name$)http_cookie$@#$Get cookie value
http_cookieremove#(c#, name$)http_cookieremove#@#$Remove cookie
http_cookieclear#(c#)http_cookieclear#@#Clear all cookies
http_cookiecount(c#)http_cookiecount@#Number of cookies
╯ plan9basic
let client# = http_cookie#(client#, "session", "abc123")
let sess$ = http_cookie$(client#, "session")
let count = http_cookiecount(client#)

Proxy Configuration

FunctionSignatureDescription
http_proxy#(c#, host$, port)http_proxy#@#$nSet proxy server
http_proxyauth#(c#, user$, pass$)http_proxyauth#@#$$Set proxy authentication
http_clearproxy#(c#)http_clearproxy#@#Clear proxy settings
╯ plan9basic
let client# = http_proxy#(client#, "proxy.example.com", 8080)
let client# = http_proxyauth#(client#, "proxyuser", "proxypass")

Form Data

Build complex multipart forms with text fields and multiple file attachments.

FunctionSignatureDescription
http_form#()http_form#@Create empty form data
http_formfield#(f#, name$, val$)http_formfield#@#$$Add text field
http_formfile#(f#, name$, path$)http_formfile#@#$$Add file (uses original filename)
http_formfilenamed#(f#, name$, path$, fn$)http_formfilenamed#@#$$$Add file with custom filename
http_formfiletype#(f#, name$, path$, fn$, ct$)http_formfiletype#@#$$$$Add file with name and content type
http_formfieldcount(f#)http_formfieldcount@#Count of text fields
http_formfilecount(f#)http_formfilecount@#Count of files
http_formurlencoded$(f#)http_formurlencoded$@#URL-encoded string (text fields only)
http_formclear#(f#)http_formclear#@#Clear all fields and files
http_formfree(f#)http_formfree@#Free form data
╯ plan9basic
' Build a form with text fields and file
let form# = http_form#()
let form# = http_formfield#(form#, "title", "Report")
let form# = http_formfield#(form#, "department", "Finance")
let form# = http_formfile#(form#, "document", "C:\Reports\Q4.pdf")

' POST as multipart/form-data (supports files)
let resp$ = http_postform$(client#, "/upload", form#)

' POST as URL-encoded (text fields only)
let resp$ = http_postformurl$(client#, "/login", form#)

' PUT as multipart
let resp$ = http_putform$(client#, "/update", form#)

let x = http_formfree(form#)

Synchronous HTTP Methods

Block until request completes. Ideal for desktop platforms.

FunctionSignatureDescription
http_get$(c#, url$)http_get$@#$GET request
http_post$(c#, url$, body$)http_post$@#$$POST with body
http_postform$(c#, url$, form#)http_postform$@#$#POST multipart form (files + fields)
http_postformurl$(c#, url$, form#)http_postformurl$@#$#POST URL-encoded form (text only)
http_postformstr$(c#, url$, data$)http_postformstr$@#$$POST raw form string
http_put$(c#, url$, body$)http_put$@#$$PUT with body
http_putform$(c#, url$, form#)http_putform$@#$#PUT multipart form
http_patch$(c#, url$, body$)http_patch$@#$$PATCH with body
http_delete$(c#, url$)http_delete$@#$DELETE request
http_head(c#, url$)http_head@#$HEAD request (returns status code)
http_options$(c#, url$)http_options$@#$OPTIONS request
╯ plan9basic
let client# = http_client#("https://api.example.com")
let client# = http_contenttype#(client#, "application/json")

' GET
let users$ = http_get$(client#, "/api/users")

' POST
let body$ = "{\"name\":\"John\",\"email\":\"john@test.com\"}"
let result$ = http_post$(client#, "/api/users", body$)

' PUT
let update$ = http_put$(client#, "/api/users/123", "{\"name\":\"Updated\"}")

' DELETE
let del$ = http_delete$(client#, "/api/users/123")

' HEAD
let status = http_head(client#, "/api/resource")
println "Content-Length: "; http_contentlength(client#)

Asynchronous HTTP (Polling)

For mobile platforms. Functions return immediately — poll with http_busy() for completion.

FunctionSignatureDescription
http_get_async(c#, url$)http_get_async@#$Start async GET
http_post_async(c#, url$, body$)http_post_async@#$$Start async POST
http_put_async(c#, url$, body$)http_put_async@#$$Start async PUT
http_delete_async(c#, url$)http_delete_async@#$Start async DELETE
http_patch_async(c#, url$, body$)http_patch_async@#$$Start async PATCH
http_busy(c#)http_busy@#1 = request in progress, 0 = done
╯ async_demo.bas
' Recommended async pattern with timer polling
let client# = Pointer#(0)
let timer# = Pointer#(0)

let frm# = form#("Async Demo")
let lbl# = label#(frm#, "Ready", 10, 10)
let btn# = button#(frm#, "Fetch", 10, 50, 120, 40)
button_onclick#(btn#, "OnFetchClick")

timer# = timer#(frm#, 100)
timer_enabled#(timer#, 0)
timer_ontimer#(timer#, "OnPollTimer")
form_show(frm#)

function OnFetchClick(sender#)
    client# = http_client#("https://httpbin.org")
    label_text#(lbl#, "Loading...")
    http_get_async(client#, "/get")
    timer_enabled#(timer#, 1)
end function

function OnPollTimer(sender#) local x
    if http_busy(client#) = 0 then
        timer_enabled#(timer#, 0)
        if http_ok(client#) <> 0 then
            label_text#(lbl#, "Status: " + str$(http_status(client#)))
        else
            label_text#(lbl#, "Error: " + http_errormsg$())
        end if
        let x = http_free(client#)
    end if
end function
⚠ Warning: On Android/iOS, always use async functions to avoid ANR (Application Not Responding). Desktop can use either sync or async.

Response Access

Status & Body

FunctionSignatureDescription
http_status(c#)http_status@#HTTP status code (200, 404, etc.)
http_statustext$(c#)http_statustext$@#Status text ("OK", "Not Found", etc.)
http_ok(c#)http_ok@#Success? (non-zero if 2xx)
http_isredirect(c#)http_isredirect@#3xx redirect? (1/0)
http_isclienterror(c#)http_isclienterror@#4xx client error? (1/0)
http_isservererror(c#)http_isservererror@#5xx server error? (1/0)
http_body$(c#)http_body$@#Response body as string
http_bodybase64$(c#)http_bodybase64$@#Response body as Base64 (binary content)
http_savebody(c#, path$)http_savebody@#$Save body to file

Response Headers

FunctionSignatureDescription
http_respheader$(c#, name$)http_respheader$@#$Get specific response header
http_respheaders$(c#)http_respheaders$@#All response headers as text
http_respheadercount(c#)http_respheadercount@#Number of response headers
http_respheadername$(c#, idx)http_respheadername$@#nHeader name by 0-based index
http_respheadervalue$(c#, idx)http_respheadervalue$@#nHeader value by 0-based index

Response Cookies & Info

FunctionSignatureDescription
http_respcookie$(c#, name$)http_respcookie$@#$Get specific response cookie
http_respcookies$(c#)http_respcookies$@#All response cookies as text
http_respcookiecount(c#)http_respcookiecount@#Number of response cookies
http_respcontenttype$(c#)http_respcontenttype$@#Response content type
http_contentlength(c#)http_contentlength@#Response content length
http_redirecturl$(c#)http_redirecturl$@#Redirect URL (3xx responses)
╯ plan9basic
if http_ok(client#) <> 0 then
    println "Status: "; http_status(client#); " "; http_statustext$(client#)
    println "Body: "; http_body$(client#)
    println "Content-Type: "; http_respcontenttype$(client#)
    
    ' Iterate headers
    for i = 0 to http_respheadercount(client#) - 1
        println http_respheadername$(client#, i); ": "; http_respheadervalue$(client#, i)
    next
endif

File Operations

FunctionSignatureDescription
http_download(c#, url$, path$)http_download@#$$Download file (non-zero = success)
http_upload$(c#, url$, path$)http_upload$@#$$Upload file as POST (raw body)
http_uploadput$(c#, url$, path$)http_uploadput$@#$$Upload file as PUT (raw body)
http_postfile$(c#, url$, field$, path$)http_postfile$@#$$$Upload as single-file multipart POST
╯ plan9basic
' Download
if http_download(client#, "/files/doc.pdf", "C:\downloads\doc.pdf") <> 0 then
    println "Downloaded!"
else
    println "Failed: "; http_errormsg$()
endif

' Upload
let resp$ = http_upload$(client#, "/upload", "C:\file.bin")
let resp$ = http_postfile$(client#, "/upload", "file", "C:\docs\report.pdf")

Encoding Utilities

FunctionSignatureDescription
http_urlencode$(text$)http_urlencode$@$URL-encode a string
http_urldecode$(text$)http_urldecode$@$URL-decode a string
http_htmlencode$(text$)http_htmlencode$@$HTML-encode (escape < > & etc.)
http_htmldecode$(text$)http_htmldecode$@$HTML-decode entities
╯ plan9basic
let enc$ = http_urlencode$("hello world & more")
' "hello%20world%20%26%20more"

let dec$ = http_urldecode$("hello%20world")
' "hello world"

let html$ = http_htmlencode$("<script>alert('hi')</script>")
' "&lt;script&gt;alert('hi')&lt;/script&gt;"

let back$ = http_htmldecode$("&lt;p&gt;")
' "<p>"

Simple Functions

Quick one-off requests without creating a client.

FunctionSignatureDescription
http_simpleget$(url$)http_simpleget$@$Simple GET request
http_simplepost$(url$, body$)http_simplepost$@$$Simple POST request
http_simpledownload(url$, path$)http_simpledownload@$$Simple file download (1/0)
╯ plan9basic
let response$ = http_simpleget$("https://api.example.com/data")

let body$ = "{\"key\":\"value\"}"
let response$ = http_simplepost$("https://api.example.com/data", body$)

let ok = http_simpledownload("https://example.com/file.zip", "C:\file.zip")

Complete Examples

REST API Client (Desktop)

╯ rest_client.bas
let api# = http_client#("https://jsonplaceholder.typicode.com")
let api# = http_accept#(api#, "application/json")

' GET
let users$ = http_get$(api#, "/users")
if http_ok(api#) <> 0 then
    println "Users: " + users$
end if

' POST
let newUser$ = "{\"name\":\"John\",\"email\":\"john@test.com\"}"
let result$ = http_post$(api#, "/users", newUser$)
println "Created: " + result$

let x = http_free(api#)

File Upload with Form Fields

╯ file_upload.bas
let client# = http_client#("https://upload.example.com")
let client# = http_bearerauth#(client#, myToken$)

let form# = http_form#()
let form# = http_formfield#(form#, "title", "Quarterly Report")
let form# = http_formfield#(form#, "department", "Finance")
let form# = http_formfile#(form#, "document", "C:\Reports\Q4-2024.pdf")

let response$ = http_postform$(client#, "/api/reports/upload", form#)

if http_ok(client#) <> 0 then
    println "Upload complete!"
else
    println "Failed: " + stri$(http_status(client#))
end if

let x = http_formfree(form#)
let x = http_free(client#)

Login with Session Cookie

╯ session_login.bas
let client# = http_client#("https://app.example.com")

let form# = http_form#()
let form# = http_formfield#(form#, "username", "john")
let form# = http_formfield#(form#, "password", "secret123")

let response$ = http_postformurl$(client#, "/login", form#)

if http_ok(client#) <> 0 then
    let sessionId$ = http_respcookie$(client#, "session_id")
    let client# = http_cookie#(client#, "session_id", sessionId$)
    
    let data$ = http_get$(client#, "/api/dashboard")
    println data$
end if

let x = http_formfree(form#)
let x = http_free(client#)

Mobile Async HTTP Tests

╯ mobile_async.bas
' Async HTTP demo for Android/iOS
let client# = Pointer#(0)
let timer# = Pointer#(0)
let testNum = 0

let frm# = form#("Mobile HTTP Demo")
let memo# = memo#(frm#, 10, 10, 380, 350, "")
let btnRun# = button#(frm#, "Run Tests", 10, 370, 120, 40)
button_onclick#(btnRun#, "OnRunClick")

timer# = timer#(frm#, 50)
timer_enabled#(timer#, 0)
timer_ontimer#(timer#, "OnPollTimer")
form_show(frm#)

function OnRunClick(sender#)
    memo_clear#(memo#)
    memo_addline#(memo#, "Starting tests...")
    client# = http_client#("https://httpbin.org")
    testNum = 0
    StartNextTest()
end function

function StartNextTest()
    testNum = testNum + 1
    if testNum = 1 then
        memo_addline#(memo#, "Test 1: GET...")
        http_get_async(client#, "/get")
        timer_enabled#(timer#, 1)
    else if testNum = 2 then
        memo_addline#(memo#, "Test 2: POST...")
        http_post_async(client#, "/post", "{\"test\":true}")
        timer_enabled#(timer#, 1)
    else
        memo_addline#(memo#, "All tests complete!")
        let x = http_free(client#)
    end if
end function

function OnPollTimer(sender#)
    if http_busy(client#) = 0 then
        timer_enabled#(timer#, 0)
        if http_ok(client#) <> 0 then
            memo_addline#(memo#, "  OK: " + str$(http_status(client#)))
        else
            memo_addline#(memo#, "  FAIL: " + http_errormsg$())
        end if
        StartNextTest()
    end if
end function

Quick Reference

Error & Client (8)

FunctionSignatureDescription
http_error()http_error@Last error code
http_errormsg$()http_errormsg$@Error message
http_strerror$(n)http_strerror$@nError code description
http_clearerror()http_clearerror@Clear errors
http_client#() / http_client#(url$)http_client#@[/$]Create client
http_free(c#)http_free@#Free client
http_reset#(c#)http_reset#@#Reset client

Config & Setup (27)

FunctionSignatureDescription
http_baseurl#/$ | http_timeout#/timeoutvariousURL & timeouts
http_useragent# | contenttype# | accept#@#$Headers
http_followredirects# | maxredirects# | validatessl#@#nBehavior
http_param#/$, paramremove#, paramclear#variousQuery params (4)
http_header#/$, headerremove#, headerclear#, headercountvariousCustom headers (5)
http_basicauth# | bearerauth# | customauth# | clearauth#variousAuth (4)
http_cookie#/$, cookieremove#, cookieclear#, cookiecountvariousCookies (5)
http_proxy#, proxyauth#, clearproxy#variousProxy (3)

Form Data (10)

FunctionSignatureDescription
http_form#()http_form#@Create form
http_formfield#(f#, n$, v$)http_formfield#@#$$Add text field
http_formfile#/filenamed#/filetype#variousAdd file (3 overloads)
http_formfieldcount/filecount/formurlencoded$/formclear#/formfreevariousUtilities (5)

HTTP Methods (17)

FunctionSignatureDescription
http_get$/post$/put$/patch$/delete$variousSync methods (5 core)
http_postform$/postformurl$/postformstr$/putform$variousForm methods (4)
http_head/http_options$variousHEAD & OPTIONS (2)
http_get/post/put/delete/patch_asyncvariousAsync methods (5)
http_busy(c#)http_busy@#Check async completion

Response (20), Files (4), Encoding (4), Simple (3)

FunctionSignatureDescription
http_status/statustext$/ok/isredirect/isclienterror/isservererrorvariousStatus checks (6)
http_body$/bodybase64$/savebodyvariousBody access (3)
http_respheader$/respheaders$/respheadercount/name$/value$variousResponse headers (5)
http_respcookie$/respcookies$/respcookiecountvariousResponse cookies (3)
http_respcontenttype$/contentlength/redirecturl$variousOther info (3)
http_download/upload$/uploadput$/postfile$variousFile ops (4)
http_urlencode$/urldecode$/htmlencode$/htmldecode$variousEncoding (4)
http_simpleget$/simplepost$/simpledownloadvariousNo-client shortcuts (3)

92 functions across 15 categories.