1
Fork 0
hare-http/net/http/header.ha

108 lines
2.6 KiB
Hare
Raw Permalink Normal View History

use ascii;
use bufio;
use encoding::utf8;
2023-02-09 19:54:21 +00:00
use fmt;
2023-02-09 22:23:05 +00:00
use io;
use strings;
2023-02-09 19:54:21 +00:00
// List of HTTP headers.
// TODO: [](str, []str)
2023-02-09 19:54:21 +00:00
export type header = [](str, str);
// Adds a given HTTP header, which may be added more than once. The name should
// be canonicalized by the caller.
export fn header_add(head: *header, name: str, val: str) void = {
2023-10-07 10:57:24 +00:00
assert(len(name) >= 1 && len(val) >= 1);
let name = ascii::strlower(name);
append(head, (name, strings::dup(val)));
2023-02-09 19:54:21 +00:00
};
// Sets the value of a given HTTP header, removing any previous values. The name
2023-02-09 22:23:05 +00:00
// should be canonicalized by the caller.
export fn header_set(head: *header, name: str, val: str) void = {
header_del(head, name);
header_add(head, name, val);
2023-02-09 19:54:21 +00:00
};
// Removes an HTTP header from a list of [[header]]. If multiple headers match
// the given name, all matching headers are removed.
export fn header_del(head: *header, name: str) void = {
2023-02-09 19:54:21 +00:00
for (let i = 0z; i < len(head); i += 1) {
if (ascii::strcasecmp(head[i].0, name) == 0) {
free(head[i].0);
2023-02-09 22:23:05 +00:00
free(head[i].1);
2023-02-09 19:54:21 +00:00
delete(head[i]);
i -= 1;
};
};
};
// Retrieves a value, or values, from a header. An empty string indicates the
// absence of a header.
export fn header_get(head: *header, name: str) str = {
for (let i = 0z; i < len(head); i += 1) {
const (key, val) = head[i];
if (ascii::strcasecmp(key, name) == 0) {
return val;
};
};
return "";
};
// Frees state associated with an HTTP [[header]].
export fn header_free(head: *header) void = {
for (let i = 0z; i < len(head); i += 1) {
free(head[i].0);
free(head[i].1);
};
free(*head);
};
2023-02-10 13:18:01 +00:00
// Duplicates a set of HTTP headers.
export fn header_dup(head: *header) header = {
let new: header = [];
for (let i = 0z; i < len(head); i += 1) {
const (key, val) = head[i];
header_add(&new, key, val);
2023-02-10 13:18:01 +00:00
};
return new;
};
2023-02-09 19:54:21 +00:00
// Writes a list of HTTP headers to the provided I/O handle in the HTTP wire
// format.
export fn write_header(sink: io::handle, head: *header) (size | io::error) = {
let z = 0z;
for (let i = 0z; i < len(head); i += 1) {
const (name, val) = head[i];
z += fmt::fprintf(sink, "{}: {}\r\n", name, val)?;
};
return z;
};
2023-02-10 09:13:30 +00:00
fn read_header(head: *header, scan: *bufio::scanner) (void | io::error | protoerr) = {
for (true) {
const item = match (bufio::scan_string(scan, "\r\n")) {
case let line: const str =>
yield line;
case io::EOF =>
break;
case let err: io::error =>
return err;
case utf8::invalid =>
2023-02-11 10:03:51 +00:00
return protoerr;
};
if (item == "") {
break;
};
let (name, val) = strings::cut(item, ":");
val = strings::trim(val);
2023-02-11 10:03:51 +00:00
if (val == "") {
continue;
2023-02-11 10:03:51 +00:00
};
// TODO: validate field-name
header_add(head, name, val);
2023-02-10 09:13:30 +00:00
};
};