transport: improve identity transport lifetime
This commit is contained in:
parent
159dd0d06f
commit
396d6caa76
|
@ -6,7 +6,7 @@ use net::uri;
|
||||||
use os;
|
use os;
|
||||||
|
|
||||||
export fn main() void = {
|
export fn main() void = {
|
||||||
const client = http::newclient("Hare test client");
|
const client = http::newclient("Hare net::http test client");
|
||||||
defer http::client_finish(&client);
|
defer http::client_finish(&client);
|
||||||
|
|
||||||
const target = match (uri::parse(os::args[1])) {
|
const target = match (uri::parse(os::args[1])) {
|
||||||
|
@ -36,4 +36,5 @@ export fn main() void = {
|
||||||
|
|
||||||
const body = resp.body as *io::stream;
|
const body = resp.body as *io::stream;
|
||||||
io::copy(os::stdout, body)!;
|
io::copy(os::stdout, body)!;
|
||||||
|
io::close(body)!;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,9 @@ use types;
|
||||||
// Performs an HTTP [[request]] with the given [[client]]. The request is
|
// Performs an HTTP [[request]] with the given [[client]]. The request is
|
||||||
// performed synchronously; this function blocks until the server has returned
|
// performed synchronously; this function blocks until the server has returned
|
||||||
// the response status and all HTTP headers associated with the response.
|
// the response status and all HTTP headers associated with the response.
|
||||||
|
//
|
||||||
|
// If the provided [[response]] has a non-null body, the user must pass it to
|
||||||
|
// [[io::close]] before calling [[response_finish]].
|
||||||
export fn do(client: *client, req: *request) (response | error) = {
|
export fn do(client: *client, req: *request) (response | error) = {
|
||||||
assert(req.target.scheme == "http"); // TODO: https
|
assert(req.target.scheme == "http"); // TODO: https
|
||||||
const conn = dial::dial_uri("tcp", req.target)?;
|
const conn = dial::dial_uri("tcp", req.target)?;
|
||||||
|
@ -53,7 +56,7 @@ export fn do(client: *client, req: *request) (response | error) = {
|
||||||
};
|
};
|
||||||
|
|
||||||
let resp = response { ... };
|
let resp = response { ... };
|
||||||
const scan = bufio::newscanner_static(conn, buf);
|
const scan = bufio::newscanner(conn, 512);
|
||||||
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)?;
|
resp.body = new_reader(conn, &resp, &scan)?;
|
||||||
|
|
|
@ -15,7 +15,9 @@ export type response = struct {
|
||||||
body: nullable *io::stream,
|
body: nullable *io::stream,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Frees state associated with an HTTP [[response]].
|
// Frees state associated with an HTTP [[response]]. If the response has a
|
||||||
|
// non-null body, the user must call [[io::close]] prior to calling this
|
||||||
|
// function.
|
||||||
export fn response_finish(resp: *response) void = {
|
export fn response_finish(resp: *response) void = {
|
||||||
header_free(&resp.header);
|
header_free(&resp.header);
|
||||||
free(resp.reason);
|
free(resp.reason);
|
||||||
|
|
|
@ -50,7 +50,7 @@ export type transport = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
fn new_reader(
|
fn new_reader(
|
||||||
conn: io::handle,
|
conn: io::file,
|
||||||
resp: *response,
|
resp: *response,
|
||||||
scan: *bufio::scanner,
|
scan: *bufio::scanner,
|
||||||
) (*io::stream | errors::unsupported | protoerr) = {
|
) (*io::stream | errors::unsupported | protoerr) = {
|
||||||
|
@ -68,8 +68,7 @@ fn new_reader(
|
||||||
return protoerr;
|
return protoerr;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const remain = bufio::scan_buffer(scan);
|
return new_identity_reader(conn, scan, length);
|
||||||
return new_identity_reader(conn, remain, length);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Figure out the semantics for closing the stream
|
// TODO: Figure out the semantics for closing the stream
|
||||||
|
@ -115,33 +114,32 @@ fn new_reader(
|
||||||
|
|
||||||
type identity_reader = struct {
|
type identity_reader = struct {
|
||||||
vtable: io::stream,
|
vtable: io::stream,
|
||||||
conn: io::handle,
|
conn: io::file,
|
||||||
buffer: [os::BUFSZ]u8,
|
scan: *bufio::scanner,
|
||||||
pending: size,
|
src: io::limitstream,
|
||||||
length: size,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const identity_reader_vtable = io::vtable {
|
const identity_reader_vtable = io::vtable {
|
||||||
reader = &identity_read,
|
reader = &identity_read,
|
||||||
|
closer = &identity_close,
|
||||||
...
|
...
|
||||||
};
|
};
|
||||||
|
|
||||||
// Creates a new reader that reads data until the response's Content-Length is
|
// Creates a new reader that reads data until the response's Content-Length is
|
||||||
// reached; i.e. the null Transport-Encoding.
|
// reached; i.e. the null Transport-Encoding.
|
||||||
fn new_identity_reader(
|
fn new_identity_reader(
|
||||||
conn: io::handle,
|
conn: io::file,
|
||||||
buffer: []u8,
|
scan: *bufio::scanner,
|
||||||
content_length: size,
|
content_length: size,
|
||||||
) *io::stream = {
|
) *io::stream = {
|
||||||
let rd = alloc(identity_reader {
|
const scan = alloc(*scan);
|
||||||
|
return alloc(identity_reader {
|
||||||
vtable = &identity_reader_vtable,
|
vtable = &identity_reader_vtable,
|
||||||
conn = conn,
|
conn = conn,
|
||||||
length = content_length,
|
scan = scan,
|
||||||
|
src = io::limitreader(scan, content_length),
|
||||||
...
|
...
|
||||||
});
|
});
|
||||||
rd.buffer[..len(buffer)] = buffer[..];
|
|
||||||
rd.pending = len(buffer);
|
|
||||||
return rd;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn identity_read(
|
fn identity_read(
|
||||||
|
@ -150,34 +148,20 @@ fn identity_read(
|
||||||
) (size | io::EOF | io::error) = {
|
) (size | io::EOF | io::error) = {
|
||||||
let rd = s: *identity_reader;
|
let rd = s: *identity_reader;
|
||||||
assert(rd.vtable == &identity_reader_vtable);
|
assert(rd.vtable == &identity_reader_vtable);
|
||||||
|
return io::read(&rd.src, buf)?;
|
||||||
|
};
|
||||||
|
|
||||||
if (rd.length <= 0) {
|
fn identity_close(s: *io::stream) (void | io::error) = {
|
||||||
return io::EOF;
|
let rd = s: *identity_reader;
|
||||||
};
|
assert(rd.vtable == &identity_reader_vtable);
|
||||||
|
|
||||||
if (rd.pending == 0) {
|
// Flush the remainder of the response in case the caller did not read
|
||||||
let nread = rd.length;
|
// it out entirely
|
||||||
if (nread > len(rd.buffer)) {
|
io::copy(io::empty, &rd.src)?;
|
||||||
nread = len(rd.buffer);
|
|
||||||
};
|
|
||||||
|
|
||||||
match (io::read(rd.conn, rd.buffer[..nread])?) {
|
bufio::finish(rd.scan);
|
||||||
case let n: size =>
|
free(rd.scan);
|
||||||
rd.pending = n;
|
io::close(rd.conn)?;
|
||||||
case io::EOF =>
|
|
||||||
return io::EOF;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
let n = len(buf);
|
|
||||||
if (n > rd.pending) {
|
|
||||||
n = rd.pending;
|
|
||||||
};
|
|
||||||
buf[..n] = rd.buffer[..n];
|
|
||||||
rd.buffer[..len(rd.buffer) - n] = rd.buffer[n..];
|
|
||||||
rd.pending -= n;
|
|
||||||
rd.length -= n;
|
|
||||||
return n;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type chunk_state = enum {
|
type chunk_state = enum {
|
||||||
|
|
Loading…
Reference in a new issue