Edit: I retracted my below questions and wall of text. I leave this as a basic curl usage example.
Edit2: Updated example. Removed cascade of if/else. Reduced lines. More efficient and reusable.
Edit3: Found that since the callback is called from a c procedure, that using c calling convention is a better practice for the callbacks. Not doing that worked in this case, but when I tried more complicated things, it got weird. Added struct to carry data and context into the callback.
Edit4: Expanded example cause I can’t stop faffing with stuff. Sorry. More usage examples. Renamed stuff.
The libcurl.lib file needed for Windows is already in the vendor/curl/lib folder, so should compile straight-away.
On Linux, I had to add the following to compile:
sudo apt install libcurl4-gnutls-dev
sudo apt install libcurl4-openssl-dev
sudo apt install libmbedtls-dev
sudo apt install libz-dev
Example getinmemory from lib_curl examples.
package getinmemory
import "core:os"
import "core:fmt"
import "core:bytes"
import "vendor:curl"
CB_Write_Data :: struct {
data: ^[dynamic]byte,
ctx: ^runtime.Context,
}
cb_write_data_proc :: proc"c"(ndata: rawptr, nsize: uint, nlength: uint, cb_write_data: rawptr) -> uint {
wdata := (^CB_Write_Data)(cb_write_data) // convert CB_Write_Data rawptr
nsize := nsize * nlength // new data to write
wsize := uint(len(wdata.data)) // currently stored callback data
context = wdata.ctx^ // apply cloned context
if resize_dynamic_array(wdata.data, wsize + nsize) != nil { return 0 }
return uint(copy(wdata.data[wsize:], ([^]byte)(ndata)[:nsize]))
}
curl_http :: proc(url: cstring, data: ^[dynamic]byte, progress := false) -> (code: curl.code) {
hcurl := curl.easy_init()
if hcurl == nil { return .E_FAILED_INIT }
defer curl.easy_cleanup(hcurl)
ctx_clone, ctx_err := new_clone(context)
if ctx_err != nil { return .E_FAILED_INIT }
defer free(ctx_clone)
wdata := CB_Write_Data{data, ctx_clone}
curl.easy_setopt(hcurl, .URL, url) or_return
curl.easy_setopt(hcurl, .WRITEFUNCTION, cb_write_data_proc) or_return
curl.easy_setopt(hcurl, .WRITEDATA, &wdata) or_return
curl.easy_setopt(hcurl, .USERAGENT, "libcurl-agent/1.0") or_return
curl.easy_setopt(hcurl, .NOPROGRESS, progress ? 0:1) or_return
curl.easy_perform(hcurl) or_return
return .E_OK
}
main :: proc() {
data := make([dynamic]byte, context.allocator)
defer delete(data)
url := cstring("https://www.example.com/") // use this for testing
//url := cstring("https://wttr.in/Chicago-IL") // use this for funs
//url := cstring("https://wttr.in/moon") // use this for more funs
// global_init once for application and do multiple curl_http within
// Get web data and print to terminal - progress disabled
if curl.global_init(curl.GLOBAL_ALL) == .E_OK {
defer curl.global_cleanup()
if code := curl_http(url, &data, false); code != .E_OK {
fmt.println("curl_http failed with code:", code)
} else {
fmt.printfln("%s", bytes.trim_right(data[:], {'\n', '\r'}))
}
clear(&data)
}
/*
// Download a file and save to current directory - progress enabled
if curl.global_init(curl.GLOBAL_ALL) == .E_OK {
defer curl.global_cleanup()
url_file := cstring("https://sample-files.com/downloads/compressed/zip/large-archive.zip")
if code := curl_http(url_file, &data, true); code != .E_OK {
fmt.println("curl_http failed with code:", code)
} else if file, err := os.open("large-archive.zip", {.Create, .Write}); err == nil {
os.write(file, data[:])
os.close(file)
}
clear(&data)
}
*/
}