summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild.sh2
-rw-r--r--cmd/main.ha25
-rw-r--r--curl.ha70
-rw-r--r--internal/curl/README1
-rw-r--r--internal/curl/ccurl/ccurl.ha47
-rw-r--r--internal/curl/curl.ha74
6 files changed, 148 insertions, 71 deletions
diff --git a/build.sh b/build.sh
index 90df2ad..95b7e58 100755
--- a/build.sh
+++ b/build.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-hare build $(pkg-config --libs-only-L --libs-only-l libcurl) curl.ha
+hare build $(pkg-config --libs-only-L --libs-only-l libcurl) -o hacurl cmd/main.ha
diff --git a/cmd/main.ha b/cmd/main.ha
new file mode 100644
index 0000000..95d1cad
--- /dev/null
+++ b/cmd/main.ha
@@ -0,0 +1,25 @@
+use fmt;
+use os;
+use internal::curl;
+use memio;
+use io;
+
+export fn main() void = {
+ if (len(os::args) != 2) {
+ fmt::fatalf("USAGE: {} <url>", os::args[0]);
+ };
+ let resp = curl::newresponse();
+ defer curl::closeresponse(resp)!;
+ match (curl::get(os::args[1], &resp)) {
+ case void =>
+ yield;
+ case let err: curl::setopterr =>
+ fmt::fatalf("setopt returned {} instead of 0", err: int);
+ case let err: curl::performerr =>
+ fmt::fatalf("perform returned {} instead of 0", err: int);
+ case let err: curl::getinfoerr =>
+ fmt::fatalf("getinfo returned {} instead of 0", err: int);
+ };
+ fmt::print(memio::string(&resp.data)!)!;
+ fmt::printfln("status: {}, size: {}", resp.status, resp.sz)!;
+};
diff --git a/curl.ha b/curl.ha
deleted file mode 100644
index 4c0c56d..0000000
--- a/curl.ha
+++ /dev/null
@@ -1,70 +0,0 @@
-use types::c;
-use fmt;
-use os;
-
-export type CURL = opaque;
-export type CURLcode = int;
-export type CURLINFO = int;
-
-def CURLOPT_URL = 10002;
-
-def CURLINFO_LONG = 0x200000;
-def CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2;
-
-export @symbol("curl_easy_init") fn curl_easy_init() nullable *CURL;
-export @symbol("curl_easy_cleanup") fn curl_easy_cleanup(handle: nullable *CURL) void;
-export @symbol("curl_easy_setopt") fn curl_easy_setopt(
- handle: nullable *CURL,
- option: int,
- parameter: const *c::char
-) CURLcode;
-
-export @symbol("curl_easy_perform") fn curl_easy_perform(easy_handle: nullable *CURL) CURLcode;
-export @symbol("curl_easy_getinfo") fn curl_easy_getinfo(
- easy_handle: nullable *CURL,
- info: CURLINFO,
- code: nullable *c::long
-) CURLcode;
-
-type curlerror = !(setopterr | performerr | getinfoerr);
-
-type setopterr = !int;
-type performerr = !int;
-type getinfoerr = !int;
-
-fn get(url: const str) (void | curlerror) = {
- let c = curl_easy_init();
- defer curl_easy_cleanup(c);
- let c_url = c::fromstr(url);
- defer free(c_url);
- let res = curl_easy_setopt(c, CURLOPT_URL, c_url);
- if (res != 0) {
- return res: setopterr;
- };
- res = curl_easy_perform(c);
- if (res != 0) {
- return res: performerr;
- };
- let rc: c::long = 0;
- res = curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &rc);
- if (res != 0) {
- return res: getinfoerr;
- };
- fmt::println(rc)!;
-};
-
-export fn main() void = {
- if (len(os::args) != 2) {
- fmt::fatalf("USAGE: {} <url>", os::args[0]);
- };
- match (get(os::args[1])) {
- case void =>
- yield;
- case let err: setopterr =>
- fmt::fatalf("setopt returned {} instead of 0", err: int);
- case let err: performerr =>
- fmt::fatalf("perform returned {} instead of 0", err: int);
- case let err: getinfoerr =>
- fmt::fatalf("getinfo returned {} instead of 0", err: int);
- };
-};
diff --git a/internal/curl/README b/internal/curl/README
new file mode 100644
index 0000000..8c56760
--- /dev/null
+++ b/internal/curl/README
@@ -0,0 +1 @@
+Basic hare bindings for libcurl's C API. Must be compiled with hare build $(pkg-config --libs-only-L --libs-only-l libcurl) ... to add the proper linker flags.
diff --git a/internal/curl/ccurl/ccurl.ha b/internal/curl/ccurl/ccurl.ha
new file mode 100644
index 0000000..3e44d97
--- /dev/null
+++ b/internal/curl/ccurl/ccurl.ha
@@ -0,0 +1,47 @@
+use types::c;
+
+export type CURL = opaque;
+export type CURLcode = int;
+export type CURLINFO = int;
+
+export def CURLOPT_URL = 10002;
+export def CURLOPT_WRITEFUNCTION = 20011;
+export def CURLOPT_WRITEDATA = 10001;
+
+export def CURLINFO_LONG = 0x200000;
+export def CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2;
+
+export @symbol("curl_easy_init") fn curl_easy_init() nullable *CURL;
+export @symbol("curl_easy_cleanup") fn curl_easy_cleanup(handle: nullable *CURL) void;
+
+export type write_callback = fn(ptr: *c::char, sz: c::ssize, nmemb: c::ssize, userdata: *opaque) c::ssize;
+export @symbol("curl_easy_setopt") fn curl_easy_setopt(
+ handle: nullable *CURL,
+ option: int,
+ parameter: const *c::char,
+) CURLcode;
+
+export @symbol("curl_easy_setopt") fn curl_easy_setopt_writefunction(
+ handle: nullable *CURL,
+ option: int,
+ parameter: *write_callback,
+) CURLcode;
+
+export @symbol("curl_easy_setopt") fn curl_easy_setopt_writedata(
+ handle: nullable *CURL,
+ option: int,
+ parameter: *opaque,
+) CURLcode;
+
+//export @symbol("curl_easy_setopt") fn curl_easy_setopt(
+// handle: nullable *CURL,
+// option: int,
+// parameter: (const *c::char | nullable opaque)
+//) CURLcode;
+
+export @symbol("curl_easy_perform") fn curl_easy_perform(easy_handle: nullable *CURL) CURLcode;
+export @symbol("curl_easy_getinfo") fn curl_easy_getinfo(
+ easy_handle: nullable *CURL,
+ info: CURLINFO,
+ code: nullable *c::long
+) CURLcode;
diff --git a/internal/curl/curl.ha b/internal/curl/curl.ha
new file mode 100644
index 0000000..148164a
--- /dev/null
+++ b/internal/curl/curl.ha
@@ -0,0 +1,74 @@
+use types::c;
+use fmt;
+use os;
+use memio;
+use strings;
+use io;
+use internal::curl::ccurl;
+
+export type curlerror = !(setopterr | performerr | getinfoerr);
+
+export type setopterr = !int;
+export type performerr = !int;
+export type getinfoerr = !int;
+
+// A HTTP response
+export type response = struct {
+ // Body
+ data: memio::stream,
+ // HTTP status
+ status: i64,
+ // Size of the body
+ sz: i64,
+};
+
+// Creates a response and returns it. Caller must call [[closeresponse]] to
+// close the underlying stream.
+export fn newresponse() response = {
+ return response {
+ data = memio::dynamic(),
+ status = 0,
+ sz = 0,
+ };
+};
+
+// Closes the stream associated to the response created by [[newresponse]].
+export fn closeresponse(resp: response) (void | io::error) = {
+ io::close(&resp.data)?;
+};
+
+fn cb(ptr: *c::char, sz: c::ssize, nmemb: c::ssize, userdata: *opaque) c::ssize = {
+ let s = c::tostr(ptr)!;
+ //fmt::printfln("FOOKL: {}", s)!;
+ let resp = userdata: *response;
+ let realsize = nmemb * sz;
+ let b = strings::toutf8(s);
+ memio::concat(&resp.data, strings::fromutf8_unsafe(b[..realsize]))!;
+ resp.sz += sz * nmemb;
+ return sz * nmemb;
+};
+
+// Makes a HTTP request to the given url and fills resp with the body, HTTP
+// status and body size.
+export fn get(url: const str, resp: *response) (void | curlerror) = {
+ let c = ccurl::curl_easy_init();
+ defer ccurl::curl_easy_cleanup(c);
+ let c_url = c::fromstr(url);
+ defer free(c_url);
+ let res = ccurl::curl_easy_setopt(c, ccurl::CURLOPT_URL, c_url);
+ if (res != 0) {
+ return res: setopterr;
+ };
+ res = ccurl::curl_easy_setopt_writefunction(c, ccurl::CURLOPT_WRITEFUNCTION, &cb);
+ res = ccurl::curl_easy_setopt_writedata(c, ccurl::CURLOPT_WRITEDATA, resp);
+ res = ccurl::curl_easy_perform(c);
+ if (res != 0) {
+ return res: performerr;
+ };
+ let rc: c::long = 0;
+ res = ccurl::curl_easy_getinfo(c, ccurl::CURLINFO_RESPONSE_CODE, &rc);
+ if (res != 0) {
+ return res: getinfoerr;
+ };
+ resp.status = rc;
+};