1
Fork 0

Improve support riggings for Transfer-Encoding

This commit is contained in:
Drew DeVault 2023-02-12 11:04:05 +01:00
parent 8edf406c52
commit 0e4277ded9
4 changed files with 98 additions and 30 deletions

View file

@ -44,29 +44,7 @@ export fn do(client: *client, req: *request) (response | error) = {
const scan = bufio::newscanner_static(conn, buf); const scan = bufio::newscanner_static(conn, buf);
read_statusline(&resp, &scan)?; read_statusline(&resp, &scan)?;
read_header(&resp.header, &scan)?; read_header(&resp.header, &scan)?;
resp.body = new_reader(conn, &resp, &scan)?;
const cl = header_get(&resp.header, "Content-Length");
const te = header_get(&resp.header, "Transfer-Encoding");
if (len(cl) > 1 || len(te) > 1) {
return protoerr;
};
if (len(te) == 1) {
abort(); // TODO: Assign transport encoding appropriately
} else {
let length = types::SIZE_MAX;
if (len(cl) == 1) {
length = match (strconv::stoz(cl[0])) {
case let z: size =>
yield z;
case =>
return protoerr;
};
};
const remain = bufio::scan_buffer(&scan);
resp.body = new_identity_reader(conn, remain, length);
};
return resp; return resp;
}; };

View file

@ -1,14 +1,15 @@
use errors;
use io; use io;
use net::dial; use net::dial;
// Errors possible while servicing HTTP requests. Note that these errors are for // Errors possible while servicing HTTP requests. Note that these errors are for
// errors related to the processing of the HTTP connection; semantic HTTP errors // errors related to the processing of the HTTP connection; semantic HTTP errors
// such as [[STATUS_NOTFOUND]] are not handled by this type. // such as [[STATUS_NOTFOUND]] are not handled by this type.
export type error = !(dial::error | io::error | protoerr); export type error = !(dial::error | io::error | errors::unsupported | protoerr);
// An HTTP protocol error occurred, indicating that the remote party is not // An HTTP protocol error occurred, indicating that the remote party is not
// conformant with HTTP semantics. // conformant with HTTP semantics.
export type protoerr = void; export type protoerr = !void;
// Converts an [[error]] to a string. // Converts an [[error]] to a string.
export fn strerror(err: error) const str = { export fn strerror(err: error) const str = {
@ -17,6 +18,8 @@ export fn strerror(err: error) const str = {
return dial::strerror(err); return dial::strerror(err);
case let err: io::error => case let err: io::error =>
return io::strerror(err); return io::strerror(err);
case errors::unsupported =>
return "Unsupported HTTP feature";
case protoerr => case protoerr =>
return "HTTP protocol error"; return "HTTP protocol error";
}; };

View file

@ -35,16 +35,16 @@ export fn header_del(head: *header, name: str) void = {
}; };
}; };
// Retrieves a value, or values, from a header. The empty slice indicates the // Retrieves a value, or values, from a header. An empty string indicates the
// abscence of a header. // absence of a header.
export fn header_get(head: *header, name: str) []str = { export fn header_get(head: *header, name: str) str = {
for (let i = 0z; i < len(head); i += 1) { for (let i = 0z; i < len(head); i += 1) {
const (key, val) = head[i]; const (key, val) = head[i];
if (key == name) { if (key == name) {
return [val]; return val;
}; };
}; };
return []; return "";
}; };
// Frees state associated with an HTTP [[header]]. // Frees state associated with an HTTP [[header]].

View file

@ -1,5 +1,66 @@
use errors;
use bufio;
use io; use io;
use os; use os;
use strconv;
use strings;
use types;
export fn new_reader(
conn: io::handle,
resp: *response,
scan: *bufio::scanner,
) (*io::stream | errors::unsupported | protoerr) = {
// TODO: Content-Encoding support
const cl = header_get(&resp.header, "Content-Length");
const te = header_get(&resp.header, "Transfer-Encoding");
if (cl != "" || te == "") {
let length = types::SIZE_MAX;
if (cl != "") {
length = match (strconv::stoz(cl)) {
case let z: size =>
yield z;
case =>
return protoerr;
};
};
const remain = bufio::scan_buffer(scan);
return new_identity_reader(conn, remain, length);
};
let stream: io::handle = conn;
let buffer: []u8 = bufio::scan_buffer(scan);
const iter = strings::tokenize(te, ",");
for (true) {
const te = match (strings::next_token(&iter)) {
case let tok: str =>
yield strings::trim(tok);
case void =>
break;
};
// XXX: We could add lzw support if someone added it to
// hare-compress
switch (te) {
case "chunked" =>
stream = new_chunked_reader(stream, buffer);
buffer = [];
case "deflate" =>
abort(); // TODO
case "gzip" =>
abort(); // TODO
case =>
return errors::unsupported;
};
};
if (!(stream is *io::stream)) {
// Empty Transfer-Encoding header
return protoerr;
};
return stream as *io::stream;
};
export type identity_reader = struct { export type identity_reader = struct {
vtable: io::stream, vtable: io::stream,
@ -67,3 +128,29 @@ fn identity_read(
rd.length -= n; rd.length -= n;
return n; return n;
}; };
export type chunked_reader = struct {
vtable: io::stream,
conn: io::handle,
buffer: [os::BUFSIZ]u8,
pending: size,
};
fn new_chunked_reader(
conn: io::handle,
buffer: []u8,
) *io::stream = {
abort(); // TODO
};
const chunked_reader_vtable = io::vtable {
reader = &chunked_read,
...
};
fn chunked_read(
s: *io::stream,
buf: []u8,
) (size | io::EOF | io::error) = {
abort(); // TODO
};