// This is free and unencumbered software released into the public domain. // // Anyone is free to copy, modify, publish, use, compile, sell, or // distribute this software, either in source code form or as a compiled // binary, for any purpose, commercial or non-commercial, and by any // means. // // In jurisdictions that recognize copyright laws, the author or authors // of this software dedicate any and all copyright interest in the // software to the public domain. We make this dedication for the benefit // of the public at large and to the detriment of our heirs and // successors. We intend this dedication to be an overt act of // relinquishment in perpetuity of all present and future rights to this // software under copyright law. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. use fmt; use io; use log; export type reswriter = struct { vtable: io::stream, dst: dynstream, code: int, hdrdone: bool, // done with the headers done: bool, // done with the response wrotedone: bool, // written the done chunk chunked: bool, // chunk the reply }; const reswriter_vtable: io::vtable = io::vtable { writer = &reswriter_write, closer = &reswriter_close, ... }; fn new_reswriter(s: io::handle) reswriter = { return reswriter { vtable = &reswriter_vtable, dst = new_dynstream(s), ... }; }; fn reswriter_write(s: *io::stream, buf: const []u8) (size | io::error) = { let res = s: *reswriter; if (!res.hdrdone) { if (res.chunked) { header(res, "Transfer-Encoding", "chunked")?; }; header(res, "Connection", "closed")?; fmt::fprint(&res.dst, "\r\n")?; res.hdrdone = true; }; if (res.chunked) { fmt::fprintf(&res.dst, "{:x}\r\n", len(buf))?; io::write(&res.dst, buf)?; fmt::fprint(&res.dst, "\r\n")?; return len(buf); } else { return io::write(&res.dst, buf)?; }; }; fn reswriter_flush(res: *reswriter) (void | io::error) = { if (res.done && !res.wrotedone) { fmt::fprint(&res.dst, "0\r\n\r\n")?; res.wrotedone = true; }; dynstream_flush(&res.dst)?; }; fn reswriter_close(s: *io::stream) (void | io::error) = { let w = s: *reswriter; io::close(&w.dst)?; }; export fn reply( req: *request, res: *reswriter, code: int, status: str ) (void | io::error) = { log::printfln("> {} {}", code, status); res.code = code; let version = if (req.version == version::HTTP_1_0) "HTTP/1.0" else "HTTP/1.1"; fmt::fprintf(&res.dst, "{} {} {}\r\n", version, code, status)?; }; export fn header( res: *reswriter, header: str, value: str ) (void | io::error) = { fmt::fprintf(&res.dst, "{}: {}\r\n", header, value)?; };