Add client struct and request helpers
This commit is contained in:
parent
a0bfaef6ba
commit
4bb0cd5c7e
|
@ -4,18 +4,14 @@ use net::uri;
|
|||
use os;
|
||||
|
||||
export fn main() void = {
|
||||
const req = &http::request {
|
||||
method = http::GET,
|
||||
target = &uri::uri {
|
||||
scheme = "http",
|
||||
host = ip::LOCAL_V6,
|
||||
path = "/",
|
||||
...
|
||||
},
|
||||
body = void,
|
||||
const client = http::newclient("Hare test client");
|
||||
defer http::client_finish(&client);
|
||||
|
||||
const req = http::get(&client, &uri::uri {
|
||||
scheme = "http",
|
||||
host = ip::LOCAL_V6,
|
||||
path = "/",
|
||||
...
|
||||
};
|
||||
http::add_header(&req.header, "User-Agent", "Hare test client");
|
||||
http::add_header(&req.header, "Content-Length", "100");
|
||||
});
|
||||
http::write_header(os::stdout, &req.header)!;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
use fmt;
|
||||
use io;
|
||||
use net::ip;
|
||||
use net::uri;
|
||||
use strconv;
|
||||
use strings;
|
||||
|
||||
export type client = struct {
|
||||
default_header: header,
|
||||
};
|
||||
|
||||
// Creates a new HTTP [[client]] with the provided User-Agent string, which is
|
||||
// borrowed from the caller. Pass the return value to [[client_finish]] to free
|
||||
// resourfces associated with the HTTP client after use.
|
||||
export fn newclient(ua: str) client = {
|
||||
let client = client { ... };
|
||||
client_add_header(&client, "User-Agent", ua);
|
||||
return client;
|
||||
};
|
||||
|
||||
// Frees resources associated with an HTTP [[client]].
|
||||
export fn client_finish(client: *client) void = {
|
||||
for (let i = 0z; i < len(client.default_header); i += 1) {
|
||||
free(client.default_header[i].1);
|
||||
};
|
||||
free(client.default_header);
|
||||
};
|
||||
|
||||
// Adds a default header which is included on all HTTP requests using a given
|
||||
// [[client]].
|
||||
export fn client_add_header(client: *client, name: str, val: str) void = {
|
||||
add_header(&client.default_header, name, val);
|
||||
};
|
||||
|
||||
fn new_request(client: *client, method: str, target: *uri::uri) request = {
|
||||
let req = request {
|
||||
method = method,
|
||||
target = alloc(uri::dup(target)),
|
||||
header = alloc(client.default_header...),
|
||||
body = void,
|
||||
};
|
||||
if (req.target.port == 0) {
|
||||
switch (req.target.scheme) {
|
||||
case "http" =>
|
||||
req.target.port = 80;
|
||||
case "https" =>
|
||||
req.target.port = 443;
|
||||
};
|
||||
};
|
||||
|
||||
let host = match (req.target.host) {
|
||||
case let host: str =>
|
||||
yield host;
|
||||
case let ip: ip::addr =>
|
||||
yield ip::string(ip);
|
||||
};
|
||||
|
||||
if (req.target.scheme == "http" && req.target.port != 80) {
|
||||
host = fmt::asprintf("{}:{}", host, req.target.port);
|
||||
} else if (target.scheme == "https" && target.port != 443) {
|
||||
host = fmt::asprintf("{}:{}", host, req.target.port);
|
||||
} else {
|
||||
host = strings::dup(host);
|
||||
};
|
||||
|
||||
append(req.header, ("Host", host));
|
||||
|
||||
return req;
|
||||
};
|
||||
|
||||
fn new_request_body(
|
||||
client: *client,
|
||||
method: str,
|
||||
target: *uri::uri,
|
||||
body: io::handle,
|
||||
) request = {
|
||||
let req = new_request(client, method, target);
|
||||
req.body = body;
|
||||
request_set_content_length(&req, body);
|
||||
return req;
|
||||
};
|
||||
|
||||
fn request_set_content_length(req: *request, body: io::handle) void = {
|
||||
const prev = match (io::seek(body, 0, io::whence::CUR)) {
|
||||
case let off: io::off =>
|
||||
yield off;
|
||||
case io::error =>
|
||||
return;
|
||||
};
|
||||
const ln = io::seek(body, 0, io::whence::END)!;
|
||||
io::seek(body, prev, io::whence::SET)!;
|
||||
add_header(&req.header, "Content-Length", strconv::ztos(ln: size));
|
||||
};
|
||||
|
||||
// Prepares a new HTTP GET request for the given client and fills in the default
|
||||
// headers, such as User-Agent and Host. Provide the return value to [[do]] to
|
||||
// execute the request and free associated resources, or use [[request_finish]]
|
||||
// to free resources without executing the request.
|
||||
//
|
||||
// The URI parameter is borrowed from the caller for the lifetime of the request
|
||||
// object.
|
||||
export fn get(client: *client, target: *uri::uri) request = {
|
||||
return new_request(client, GET, target);
|
||||
};
|
||||
|
||||
// Prepares a new HTTP HEAD request for the given client and fills in the
|
||||
// default headers, such as User-Agent and Host. Provide the return value to
|
||||
// [[do]] to execute the request and free associated resources, or use
|
||||
// [[request_finish]] to free resources without executing the request.
|
||||
//
|
||||
// The URI parameter is borrowed from the caller for the lifetime of the request
|
||||
// object.
|
||||
export fn head(client: *client, target: *uri::uri) request = {
|
||||
return new_request(client, HEAD, target);
|
||||
};
|
||||
|
||||
// Prepares a new HTTP POST request for the given client and fills in the
|
||||
// default headers, such as User-Agent and Host. Provide the return value to
|
||||
// [[do]] to execute the request and free associated resources, or use
|
||||
// [[request_finish]] to free resources without executing the request.
|
||||
//
|
||||
// If the provided I/O handle is seekable, the Content-Length header is added
|
||||
// automatically.
|
||||
//
|
||||
// The URI parameter is borrowed from the caller for the lifetime of the request
|
||||
// object.
|
||||
export fn post(client: *client, target: *uri::uri, body: io::handle) request = {
|
||||
return new_request_body(client, POST, target, body);
|
||||
};
|
||||
|
||||
// Prepares a new HTTP PUT request for the given client and fills in the
|
||||
// default headers, such as User-Agent and Host. Provide the return value to
|
||||
// [[do]] to execute the request and free associated resources, or use
|
||||
// [[request_finish]] to free resources without executing the request.
|
||||
//
|
||||
// If the provided I/O handle is seekable, the Content-Length header is added
|
||||
// automatically.
|
||||
//
|
||||
// The URI parameter is borrowed from the caller for the lifetime of the request
|
||||
// object.
|
||||
export fn put(client: *client, target: *uri::uri, body: io::handle) request = {
|
||||
return new_request_body(client, POST, target, body);
|
||||
};
|
|
@ -1,19 +1,20 @@
|
|||
use io;
|
||||
use fmt;
|
||||
use io;
|
||||
use strings;
|
||||
|
||||
// List of HTTP headers.
|
||||
export type header = [](str, str);
|
||||
|
||||
// Adds a given HTTP header, which may be added more than once. The provided
|
||||
// name and value are borrowed from the caller. The provided header name should
|
||||
// be canonicalized by the caller.
|
||||
// Adds a given HTTP header, which may be added more than once. The value is
|
||||
// duplicated, but the name is borrowed from the caller. The name should be
|
||||
// canonicalized by the caller.
|
||||
export fn add_header(head: *header, name: str, val: str) void = {
|
||||
append(head, (name, val));
|
||||
append(head, (name, strings::dup(val)));
|
||||
};
|
||||
|
||||
// Sets the value of a given HTTP header, removing any previous values. The
|
||||
// provided name and value are borrowed from the caller. The provided header
|
||||
// name should be canonicalized by the caller.
|
||||
// value is duplicated, but the name is borrowed from the caller. The name
|
||||
// should be canonicalized by the caller.
|
||||
export fn set_header(head: *header, name: str, val: str) void = {
|
||||
del_header(head, name);
|
||||
add_header(head, name, val);
|
||||
|
@ -24,6 +25,7 @@ export fn set_header(head: *header, name: str, val: str) void = {
|
|||
export fn del_header(head: *header, name: str) void = {
|
||||
for (let i = 0z; i < len(head); i += 1) {
|
||||
if (head[i].0 == name) {
|
||||
free(head[i].1);
|
||||
delete(head[i]);
|
||||
i -= 1;
|
||||
};
|
||||
|
|
|
@ -18,3 +18,14 @@ export type request = struct {
|
|||
// I/O reader for the request body.
|
||||
body: (io::handle | void),
|
||||
};
|
||||
|
||||
// Frees state associated with an HTTP [[request]].
|
||||
export fn request_finish(req: *request) void = {
|
||||
for (let i = 0z; i < len(req.header); i += 1) {
|
||||
free(req.header[i].1);
|
||||
};
|
||||
free(req.header);
|
||||
|
||||
uri::finish(req.target);
|
||||
free(req.target);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue