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

108 lines
2.6 KiB
Hare

use ascii;
use bufio;
use encoding::utf8;
use fmt;
use io;
use strings;
// List of HTTP headers.
// TODO: [](str, []str)
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 = {
assert(len(name) >= 1 && len(val) >= 1);
let name = ascii::strlower(name);
append(head, (name, strings::dup(val)));
};
// Sets the value of a given HTTP header, removing any previous values. The name
// 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);
};
// 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 = {
for (let i = 0z; i < len(head); i += 1) {
if (ascii::strcasecmp(head[i].0, name) == 0) {
free(head[i].0);
free(head[i].1);
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);
};
// 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);
};
return new;
};
// 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;
};
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 =>
return protoerr;
};
if (item == "") {
break;
};
let (name, val) = strings::cut(item, ":");
val = strings::trim(val);
if (val == "") {
continue;
};
// TODO: validate field-name
header_add(head, name, val);
};
};