Lindenii Project Forge
Warning: Due to various recent migrations, viewing non-HEAD refs may be broken.
/ev/client/http.ha (raw)
use bufio;
use errors;
use ev::dial;
use ev;
use io;
use memio;
use net::http;
use os;
use types;
// A callback for an [[http_do]] operation.
export type http_donecb = fn(
user: nullable *opaque,
r: (
(*ev::file, http::response) |
dial::error | io::EOF | io::error | http::protoerr | errors::unsupported
)
) void;
export fn http_do(
loop: *ev::loop,
reqcli: *http::client,
req: *http::request,
cb: *http_donecb,
user: nullable *opaque
) (ev::req | dial::error | io::error | nomem) = {
const cli = alloc(client {
cb = cb,
user = user,
buf = memio::dynamic(),
...
})?;
http::request_write_internal(&cli.buf, req, reqcli)?;
const req = dial::dial_uri(
loop,
"tcp",
req.target,
&http_dialed,
cli,
)?;
cli.req = req;
return ev::mkreq(&http_do_cancel, cli);
};
export fn http_send(
file: *ev::file,
reqcli: *http::client,
req: *http::request,
cb: *http_donecb,
) (void | io::error) = {
const cli = ev::getuser(file): *client;
http::request_write_internal(&cli.buf, req, reqcli)?;
cli.wbuf = memio::buffer(&cli.buf: *memio::stream);
ev::write(file, &http_write, cli.wbuf);
};
fn http_do_cancel(req: *ev::req) void = {
let cli = req.user: *client;
ev::cancel(&cli.req);
client_close(cli);
};
fn http_dialed(user: nullable *opaque, r: (*ev::file | dial::error)) (void | nomem) = {
const cli = user: *client;
cli.req = ev::req { ... };
const file = match (r) {
case let file: *ev::file => yield file;
case let err: dial::error =>
const cb = cli.cb: *http_donecb;
cb(cli.user, err);
client_close(cli);
return;
};
ev::setuser(file, cli);
cli.wbuf = memio::buffer(&cli.buf: *memio::stream);
ev::write(file, &http_write, cli.wbuf);
};
fn http_write(sock: *ev::file, r: (size | io::error)) (void | nomem) = {
const cli = ev::getuser(sock): *client;
const n = match (r) {
case let err: io::error =>
const cb = cli.cb: *http_donecb;
cb(cli.user, err);
client_close(cli);
return;
case let n: size =>
yield n;
};
static delete(cli.wbuf[..n]);
if (len(cli.wbuf) != 0) {
ev::write(sock, &http_write, cli.wbuf);
return;
};
memio::reset(&cli.buf);
ev::read(sock, &http_read, cli.rbuf);
};
fn http_read(sock: *ev::file, r: (size | io::EOF | io::error)) (void | nomem) = {
const cli = ev::getuser(sock): *client;
const cb = cli.cb: *http_donecb;
const n = match (r) {
case let err: io::error =>
cb(cli.user, err);
client_close(cli);
return;
case io::EOF =>
cb(cli.user, io::EOF);
client_close(cli);
return;
case let n: size =>
yield n;
};
io::seek(&cli.buf, 0, io::whence::END)!;
io::write(&cli.buf, cli.rbuf[..n])!;
io::seek(&cli.buf, 0, io::whence::SET)!;
let scan = bufio::newscanner(&cli.buf, types::SIZE_MAX);
defer bufio::finish(&scan);
let resp = match (http::response_scan(&scan)) {
case let resp: http::response => yield resp;
case io::EOF =>
ev::read(sock, &http_read, cli.rbuf);
return;
case let err: (http::protoerr | errors::unsupported | io::error) =>
cb(cli.user, err);
client_close(cli);
return;
};
defer http::parsed_response_finish(&resp);
memio::reset(&cli.buf);
cb(cli.user, (sock, resp));
};
fn client_close(cli: *client) void = {
if (!(cli.sock is null)) {
ev::close(cli.sock as *ev::file);
};
io::close(&cli.buf)!;
free(cli);
};