Lindenii Project Forge
add httpclient
use ev; use ev::client; use net::uri; use net::http; use ev::dial; use net; use io; use errors; use log; use os; use fmt; type state = struct { loop: *ev::loop, client: *http::client, exit: int, }; export fn main() void = { const loop = ev::newloop()!; defer ev::finish(&loop); const client = http::newclient("Hare net::http test client"); defer http::client_finish(&client); const targ = uri::parse("http://127.0.0.1:8080")!; defer uri::finish(&targ); let req = http::new_request(&client, "GET", &targ)!; let state = state { loop = &loop, client = &client, ... }; const con = match (client::http_do(&loop, &client, &req, &http_done, &state)) { case let req: ev::req => yield req; case let err: dial::error => log::println("dial error:", dial::strerror(err)); return; case let err: io::error => log::println("io error:", io::strerror(err)); return; }; for (ev::dispatch(&loop, -1)!) void; }; fn http_done( user: nullable *opaque, r: ( (*ev::file, http::response) | io::EOF | dial::error | io::error | http::protoerr | errors::unsupported ) ) void = { const state = user: *state; const (file, resp) = match (r) { case let resp: (*ev::file, http::response) => yield resp; case io::EOF => log::println("connection closed"); ev::stop(state.loop); return; case let err: dial::error => log::println("dial error", dial::strerror(err)); ev::stop(state.loop); return; case let err: io::error => log::println("io error:", io::strerror(err)); ev::stop(state.loop); return; case let err: http::protoerr => log::println("http protoerror:", http::strerror(err)); ev::stop(state.loop); return; case let err: errors::unsupported => log::println("errors:", errors::strerror(err)); ev::stop(state.loop); return; }; fmt::printfln("Headers:")!; http::write_header(os::stdout, &resp.header)!; fmt::println()!; io::copy(os::stdout, resp.body)!; ev::close(file); ev::stop(state.loop); };
use ev; use io; use memio; use os; type client = struct { req: ev::req, sock: nullable *ev::file, cb: *opaque, user: nullable *opaque, wbuf: []u8, rbuf: [os::BUFSZ]u8, buf: memio::stream, };
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) = { 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 = { 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 = { 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 = { 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); };