use bufio; use encoding::utf8; use errors; use fmt; use io; use net::dial; use net::uri; use net; use os; use strconv; use strings; use types; // Performs an HTTP [[request]] with the given [[client]]. The request is // performed synchronously; this function blocks until the server has returned // the response status and all HTTP headers associated with the response. export fn do(client: *client, req: *request) (response | error) = { assert(req.target.scheme == "http"); // TODO: https const conn = dial::dial_uri("tcp", req.target)?; let buf: [os::BUFSIZ]u8 = [0...]; let file = bufio::buffered(conn, [], buf); bufio::setflush(&file, []); fmt::fprintf(&file, "{} ", req.method)?; // TODO: Support other request-targets than origin-form const target = uri_origin_form(req.target); uri::fmt(&file, &target)?; fmt::fprintf(&file, " HTTP/1.1\r\n")?; write_header(&file, &req.header)?; fmt::fprintf(&file, "\r\n")?; bufio::flush(&file)?; const trans = match (req.transport) { case let t: *transport => yield t; case => yield &client.default_transport; }; // TODO: Implement None assert(trans.request_transport == transport_mode::AUTO); assert(trans.response_transport == transport_mode::AUTO); assert(trans.request_content == content_mode::AUTO); assert(trans.response_content == content_mode::AUTO); match (req.body) { case let body: io::handle => io::copy(conn, body)?; case void => yield; }; let resp = response { ... }; const scan = bufio::newscanner_static(conn, buf); read_statusline(&resp, &scan)?; read_header(&resp.header, &scan)?; resp.body = new_reader(conn, &resp, &scan)?; return resp; }; fn read_statusline( resp: *response, scan: *bufio::scanner, ) (void | error) = { const status = match (bufio::scan_string(scan, "\r\n")) { case let line: const str => yield line; case let err: io::error => return err; case utf8::invalid => return protoerr; case io::EOF => return protoerr; }; const tok = strings::tokenize(status, " "); const version = match (strings::next_token(&tok)) { case let ver: str => yield ver; case void => return protoerr; }; const status = match (strings::next_token(&tok)) { case let status: str => yield status; case void => return protoerr; }; const reason = match (strings::next_token(&tok)) { case let reason: str => yield reason; case void => return protoerr; }; const (_, version) = strings::cut(version, "/"); const (major, minor) = strings::cut(version, "."); resp.version = ( strconv::stou(major)!, strconv::stou(minor)!, ); if (resp.version.0 > 1) { return errors::unsupported; }; resp.status = strconv::stou(status)!; resp.reason = strings::dup(reason); };