use errors; use bufio; use io; use os; use strconv; use strings; use types; // Configures the transport mode. // // For clients, if set to "none", no transport decoding is performed on the // response body, and the user must read the Transport-Encoding header and // decode it themselves. If set to "auto", [[response]].body will be decoded // according to the response's Transport-Encoding header. Most users will want // this to be set to "auto". // // For servers, if set to "auto", net::http will examine the Transport-Encoding // header and apply the appropriate transport encoding. If set to "none", the // response body will be sent directly to the client with no further processing. export type transport_mode = enum { AUTO = 0, NONE, }; // Configures the content mode. // // For clients, if set to "none", no content decoding is performed on the // response body, and the user must read the Content-Encoding header and decode // it themselves. If set to "auto", [[response]].body will be decoded according // to the response's Content-Encoding header. Most users will want this to be // set to "auto". // // For servers, if set to "auto", net::http will examine the Content-Encoding // header and apply the appropriate content encoding. If set to "none", the // response body will be sent directly to the client with no further processing. export type content_mode = enum { AUTO = 0, NONE, }; // Describes an HTTP [[client]]'s transport configuration for a given request. export type transport = struct { // Desired Transport-Encoding configuration, see [[transport_mode]] for // details. transport: transport_mode, // Desired Content-Encoding configuration, see [[content_mode]] for // details. content: content_mode, }; 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 { vtable: io::stream, conn: io::handle, buffer: [os::BUFSIZ]u8, pending: size, length: size, }; const identity_reader_vtable = io::vtable { reader = &identity_read, ... }; // Creates a new reader that reads data until the response's Content-Length is // reached; i.e. the null Transport-Encoding. fn new_identity_reader( conn: io::handle, buffer: []u8, content_length: size, ) *io::stream = { let rd = alloc(identity_reader { vtable = &identity_reader_vtable, conn = conn, length = content_length, ... }); rd.buffer[..len(buffer)] = buffer[..]; rd.pending = len(buffer); return rd; }; fn identity_read( s: *io::stream, buf: []u8, ) (size | io::EOF | io::error) = { let rd = s: *identity_reader; assert(rd.vtable == &identity_reader_vtable); if (rd.length <= 0) { return io::EOF; }; if (rd.pending == 0) { let nread = rd.length; if (nread > len(rd.buffer)) { nread = len(rd.buffer); }; match (io::read(rd.conn, rd.buffer[..nread])?) { case let n: size => rd.pending = n; 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; }; 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 };