Improve support riggings for Transfer-Encoding
This commit is contained in:
parent
8edf406c52
commit
0e4277ded9
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
};
|
};
|
||||||
|
|
|
@ -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]].
|
||||||
|
|
|
@ -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
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue