Blob


1 // This is free and unencumbered software released into the public domain.
2 //
3 // Anyone is free to copy, modify, publish, use, compile, sell, or
4 // distribute this software, either in source code form or as a compiled
5 // binary, for any purpose, commercial or non-commercial, and by any
6 // means.
7 //
8 // In jurisdictions that recognize copyright laws, the author or authors
9 // of this software dedicate any and all copyright interest in the
10 // software to the public domain. We make this dedication for the benefit
11 // of the public at large and to the detriment of our heirs and
12 // successors. We intend this dedication to be an overt act of
13 // relinquishment in perpetuity of all present and future rights to this
14 // software under copyright law.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 // OTHER DEALINGS IN THE SOFTWARE.
24 use fmt;
25 use io;
26 use log;
28 export type reswriter = struct {
29 vtable: io::stream,
30 dst: dynstream,
31 code: int,
32 hdrdone: bool, // done with the headers
33 done: bool, // done with the response
34 wrotedone: bool, // written the done chunk
35 chunked: bool, // chunk the reply
36 };
38 const reswriter_vtable: io::vtable = io::vtable {
39 writer = &reswriter_write,
40 closer = &reswriter_close,
41 ...
42 };
44 fn new_reswriter(s: io::handle) reswriter = {
45 return reswriter {
46 vtable = &reswriter_vtable,
47 dst = new_dynstream(s),
48 ...
49 };
50 };
52 fn reswriter_write(s: *io::stream, buf: const []u8) (size | io::error) = {
53 let res = s: *reswriter;
55 if (!res.hdrdone) {
56 if (res.chunked) {
57 header(res, "Transfer-Encoding", "chunked")?;
58 };
59 header(res, "Connection", "closed")?;
61 fmt::fprint(&res.dst, "\r\n")?;
62 res.hdrdone = true;
63 };
65 if (res.chunked) {
66 fmt::fprintf(&res.dst, "{:x}\r\n", len(buf))?;
67 io::write(&res.dst, buf)?;
68 fmt::fprint(&res.dst, "\r\n")?;
69 return len(buf);
70 } else {
71 return io::write(&res.dst, buf)?;
72 };
73 };
75 fn reswriter_flush(res: *reswriter) (void | io::error) = {
76 if (res.chunked && res.done && !res.wrotedone) {
77 fmt::fprint(&res.dst, "0\r\n\r\n")?;
78 res.wrotedone = true;
79 };
80 dynstream_flush(&res.dst)?;
81 };
83 fn reswriter_close(s: *io::stream) (void | io::error) = {
84 let w = s: *reswriter;
86 io::close(&w.dst)?;
87 };
89 export fn reply(
90 req: *request,
91 res: *reswriter,
92 code: int,
93 status: str
94 ) (void | io::error) = {
95 log::printfln("> {} {}", code, status);
97 res.code = code;
99 let version = if (req.version == version::HTTP_1_0)
100 "HTTP/1.0" else "HTTP/1.1";
102 fmt::fprintf(&res.dst, "{} {} {}\r\n", version, code, status)?;
103 };
105 export fn header(
106 res: *reswriter,
107 header: str,
108 value: str
109 ) (void | io::error) = {
110 fmt::fprintf(&res.dst, "{}: {}\r\n", header, value)?;
111 };