2 * This is free and unencumbered software released into the public domain.
4 * Anyone is free to copy, modify, publish, use, compile, sell, or
5 * distribute this software, either in source code form or as a compiled
6 * binary, for any purpose, commercial or non-commercial, and by any
9 * In jurisdictions that recognize copyright laws, the author or authors
10 * of this software dedicate any and all copyright interest in the
11 * software to the public domain. We make this dedication for the benefit
12 * of the public at large and to the detriment of our heirs and
13 * successors. We intend this dedication to be an overt act of
14 * relinquishment in perpetuity of all present and future rights to this
15 * software under copyright law.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
45 #define nitems(x) (sizeof(x)/sizeof(x[0]))
49 http_init(struct client *clt, int fd)
51 memset(clt, 0, sizeof(*clt));
52 if (bufio_init(&clt->bio) == -1)
54 bufio_set_fd(&clt->bio, fd);
59 http_parse(struct client *clt)
61 struct buffer *rbuf = &clt->bio.rbuf;
62 struct request *req = &clt->req;
65 char *frag, *query, *http, *line;
66 const char *errstr, *m;
68 while (!clt->reqdone) {
69 endln = memmem(rbuf->buf, rbuf->len, "\r\n", 2);
76 if (endln == rbuf->buf)
79 len = endln - rbuf->buf + 2;
80 while (len > 0 && (line[len - 1] == '\r' ||
81 line[len - 1] == '\n' || line[len - 1] == ' ' ||
82 line[len - 1] == '\t'))
86 if (clt->req.method == METHOD_UNKNOWN) {
87 if (!strncmp("GET ", line, 4)) {
88 req->method = METHOD_GET;
90 } else if (!strncmp("POST ", line, 5)) {
91 req->method = METHOD_POST;
98 if ((http = strchr(line, ' ')) == NULL)
103 if ((query = strchr(line, '?')))
105 if ((frag = strchr(line, '#')))
108 clt->req.path = xstrdup(line);
110 if (!strcmp(http, "HTTP/1.0"))
111 clt->req.version = HTTP_1_0;
112 else if (!strcmp(http, "HTTP/1.1")) {
113 clt->req.version = HTTP_1_1;
116 log_warnx("unknown http version %s", http);
121 line = http; /* so that no header below matches */
124 if (!strncasecmp(line, "Content-Length:", 15)) {
126 line += strspn(line, " \t");
127 clt->req.clen = strtonum(line, 0, LONG_MAX,
130 log_warnx("content-length is %s: %s",
137 buf_drain(rbuf, endln - rbuf->buf + 2);
140 if (req->method == METHOD_GET)
142 else if (req->method == METHOD_POST)
146 log_debug("< %s %s HTTP/%s", m, req->path,
147 req->version == HTTP_1_1 ? "1.1" : "1.0");
153 http_read(struct client *clt)
155 struct request *req = &clt->req;
159 if (req->clen > sizeof(clt->buf) - 1) {
160 log_warnx("POST has more data then what can be accepted");
164 /* clients may have sent more data than advertised */
165 if (req->clen < clt->len)
168 left = req->clen - clt->len;
171 nr = bufio_drain(&clt->bio, clt->buf + clt->len, left);
179 clt->buf[clt->len] = '\0';
180 while (clt->len > 0 && (clt->buf[clt->len - 1] == '\r' ||
181 (clt->buf[clt->len - 1] == '\n')))
182 clt->buf[--clt->len] = '\0';
188 http_reply(struct client *clt, int code, const char *reason, const char *ctype)
190 const char *version, *location = NULL;
193 log_debug("> %d %s", code, reason);
195 if (code >= 300 && code < 400) {
197 ctype = "text/html;charset=UTF-8";
200 version = "HTTP/1.1";
201 if (clt->req.version == HTTP_1_0)
202 version = "HTTP/1.0";
204 r = bufio_compose_fmt(&clt->bio, "%s %d %s\r\n"
205 "Connection: close\r\n"
206 "Cache-Control: no-store\r\n"
211 version, code, reason,
212 ctype == NULL ? "" : "Content-Type: ",
213 ctype == NULL ? "" : ctype,
214 ctype == NULL ? "" : "\r\n",
215 location == NULL ? "" : "Location: ",
216 location == NULL ? "" : location,
217 location == NULL ? "" : "\r\n",
218 clt->chunked ? "Transfer-Encoding: chunked\r\n" : "");
224 bufio_set_chunked(&clt->bio, clt->chunked);
227 if (http_writes(clt, "<a href='") == -1 ||
228 http_htmlescape(clt, location) == -1 ||
229 http_writes(clt, "'>") == -1 ||
230 http_htmlescape(clt, reason) == -1 ||
231 http_writes(clt, "</a>") == -1)
239 http_flush(struct client *clt)
247 if (bufio_compose(&clt->bio, clt->buf, clt->len) == -1) {
258 http_write(struct client *clt, const char *d, size_t len)
266 avail = sizeof(clt->buf) - clt->len;
270 memcpy(clt->buf + clt->len, d, avail);
274 if (clt->len == sizeof(clt->buf)) {
275 if (http_flush(clt) == -1)
284 http_writes(struct client *clt, const char *str)
286 return http_write(clt, str, strlen(str));
290 http_fmt(struct client *clt, const char *fmt, ...)
297 r = vasprintf(&str, fmt, ap);
301 log_warn("vasprintf");
306 r = http_write(clt, str, r);
312 http_urlescape(struct client *clt, const char *str)
317 for (; *str; ++str) {
318 if (iscntrl((unsigned char)*str) ||
319 isspace((unsigned char)*str) ||
320 *str == '\'' || *str == '"' || *str == '\\') {
321 r = snprintf(tmp, sizeof(tmp), "%%%2X",
322 (unsigned char)*str);
323 if (r < 0 || (size_t)r >= sizeof(tmp)) {
324 log_warn("snprintf failed");
328 if (http_write(clt, tmp, r) == -1)
330 } else if (http_write(clt, str, 1) == -1)
338 http_htmlescape(struct client *clt, const char *str)
342 for (; *str; ++str) {
345 r = http_writes(clt, "<");
348 r = http_writes(clt, ">");
351 r = http_writes(clt, ">");
354 r = http_writes(clt, """);
357 r = http_writes(clt, "'");
360 r = http_write(clt, str, 1);
372 http_close(struct client *clt)
376 if (clt->len != 0 && http_flush(clt) == -1)
378 if (bufio_compose(&clt->bio, NULL, 0) == -1)
380 return (clt->err ? -1 : 0);
384 http_free(struct client *clt)
387 free(clt->req.ctype);
389 bufio_free(&clt->bio);