a79f567f1163027cf20da67fc77c329a26c97a27
[darkstat] / http.c
1 /* darkstat 3
2 * copyright (c) 2001-2008 Emil Mikulic.
3 *
4 * http.c: embedded webserver.
5 * This borrows a lot of code from darkhttpd.
6 *
7 * You may use, modify and redistribute this file under the terms of the
8 * GNU General Public License version 2. (see COPYING.GPL)
9 */
10
11 #include "darkstat.h"
12 #include "http.h"
13 #include "conv.h"
14 #include "hosts_db.h"
15 #include "graph_db.h"
16 #include "err.h"
17 #include "queue.h"
18 #include "now.h"
19
20 #include <sys/uio.h>
21 #include <sys/socket.h>
22 #include <arpa/inet.h>
23 #include <netinet/in.h>
24 #include <netdb.h>
25 #include <assert.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <signal.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <zlib.h>
36
37 static const char mime_type_xml[] = "text/xml";
38 static const char mime_type_html[] = "text/html; charset=us-ascii";
39 static const char mime_type_css[] = "text/css";
40 static const char mime_type_js[] = "text/javascript";
41 static const char encoding_gzip[] =
42 "Vary: Accept-Encoding\r\n"
43 "Content-Encoding: gzip\r\n";
44
45 static const char server[] = PACKAGE_NAME "/" PACKAGE_VERSION;
46 static int idletime = 60;
47 static int sockin = -1; /* socket to accept connections from */
48 #define MAX_REQUEST_LENGTH 4000
49
50 #ifndef min
51 #define min(a,b) (((a) < (b)) ? (a) : (b))
52 #endif
53
54 struct connection {
55 LIST_ENTRY(connection) entries;
56
57 int socket;
58 struct sockaddr_storage client;
59 time_t last_active;
60 enum {
61 RECV_REQUEST, /* receiving request */
62 SEND_HEADER_AND_REPLY, /* try to send header+reply together */
63 SEND_HEADER, /* sending generated header */
64 SEND_REPLY, /* sending reply */
65 DONE /* conn closed, need to remove from queue */
66 } state;
67
68 /* char request[request_length+1] is null-terminated */
69 char *request;
70 size_t request_length;
71 int accept_gzip;
72
73 /* request fields */
74 char *method, *uri, *query; /* query can be NULL */
75
76 char *header;
77 const char *mime_type, *encoding, *header_extra;
78 size_t header_length, header_sent;
79 int header_dont_free, header_only, http_code;
80
81 char *reply;
82 int reply_dont_free;
83 size_t reply_length, reply_sent;
84
85 unsigned int total_sent; /* header + body = total, for logging */
86 };
87
88 static LIST_HEAD(conn_list_head, connection) connlist =
89 LIST_HEAD_INITIALIZER(conn_list_head);
90
91 /* ---------------------------------------------------------------------------
92 * Decode URL by converting %XX (where XX are hexadecimal digits) to the
93 * character it represents. Don't forget to free the return value.
94 */
95 static char *urldecode(const char *url)
96 {
97 size_t i, len = strlen(url);
98 char *out = xmalloc(len+1);
99 int pos;
100
101 for (i=0, pos=0; i<len; i++)
102 {
103 if (url[i] == '%' && i+2 < len &&
104 isxdigit(url[i+1]) && isxdigit(url[i+2]))
105 {
106 /* decode %XX */
107 #define HEX_TO_DIGIT(hex) ( \
108 ((hex) >= 'A' && (hex) <= 'F') ? ((hex)-'A'+10): \
109 ((hex) >= 'a' && (hex) <= 'f') ? ((hex)-'a'+10): \
110 ((hex)-'0') )
111
112 out[pos++] = HEX_TO_DIGIT(url[i+1]) * 16 +
113 HEX_TO_DIGIT(url[i+2]);
114 i += 2;
115
116 #undef HEX_TO_DIGIT
117 }
118 else
119 {
120 /* straight copy */
121 out[pos++] = url[i];
122 }
123 }
124 out[pos] = 0;
125 #if 0
126 /* don't really need to realloc here - it's probably a performance hit */
127 out = xrealloc(out, strlen(out)+1); /* dealloc what we don't need */
128 #endif
129 return (out);
130 }
131
132
133
134 /* ---------------------------------------------------------------------------
135 * Consolidate slashes in-place by shifting parts of the string over repeated
136 * slashes.
137 */
138 static void consolidate_slashes(char *s)
139 {
140 size_t left = 0, right = 0;
141 int saw_slash = 0;
142
143 assert(s != NULL);
144
145 while (s[right] != '\0')
146 {
147 if (saw_slash)
148 {
149 if (s[right] == '/') right++;
150 else
151 {
152 saw_slash = 0;
153 s[left++] = s[right++];
154 }
155 }
156 else
157 {
158 if (s[right] == '/') saw_slash++;
159 s[left++] = s[right++];
160 }
161 }
162 s[left] = '\0';
163 }
164
165
166
167 /* ---------------------------------------------------------------------------
168 * Resolve /./ and /../ in a URI, returing a new, safe URI, or NULL if the URI
169 * is invalid/unsafe. Returned buffer needs to be deallocated.
170 */
171 static char *make_safe_uri(char *uri)
172 {
173 char **elem, *out;
174 unsigned int slashes = 0, elements = 0;
175 size_t urilen, i, j, pos;
176
177 assert(uri != NULL);
178 if (uri[0] != '/')
179 return (NULL);
180 consolidate_slashes(uri);
181 urilen = strlen(uri);
182
183 /* count the slashes */
184 for (i=0, slashes=0; i<urilen; i++)
185 if (uri[i] == '/') slashes++;
186
187 /* make an array for the URI elements */
188 elem = xmalloc(sizeof(*elem) * slashes);
189 for (i=0; i<slashes; i++)
190 elem[i] = (NULL);
191
192 /* split by slashes and build elem[] array */
193 for (i=1; i<urilen;)
194 {
195 /* look for the next slash */
196 for (j=i; j<urilen && uri[j] != '/'; j++)
197 ;
198
199 /* process uri[i,j) */
200 if ((j == i+1) && (uri[i] == '.'))
201 /* "." */;
202 else if ((j == i+2) && (uri[i] == '.') && (uri[i+1] == '.'))
203 {
204 /* ".." */
205 if (elements == 0)
206 {
207 /*
208 * Unsafe string so free elem[]. All its elements are free
209 * at this point.
210 */
211 free(elem);
212 return (NULL);
213 }
214 else
215 {
216 elements--;
217 free(elem[elements]);
218 }
219 }
220 else elem[elements++] = split_string(uri, i, j);
221
222 i = j + 1; /* uri[j] is a slash - move along one */
223 }
224
225 /* reassemble */
226 out = xmalloc(urilen+1); /* it won't expand */
227 pos = 0;
228 for (i=0; i<elements; i++)
229 {
230 size_t delta = strlen(elem[i]);
231
232 assert(pos <= urilen);
233 out[pos++] = '/';
234
235 assert(pos+delta <= urilen);
236 memcpy(out+pos, elem[i], delta);
237 free(elem[i]);
238 pos += delta;
239 }
240 free(elem);
241
242 if ((elements == 0) || (uri[urilen-1] == '/')) out[pos++] = '/';
243 assert(pos <= urilen);
244 out[pos] = '\0';
245
246 #if 0
247 /* don't really need to do this and it's probably a performance hit: */
248 /* shorten buffer if necessary */
249 if (pos != urilen) out = xrealloc(out, strlen(out)+1);
250 #endif
251 return (out);
252 }
253
254 /* ---------------------------------------------------------------------------
255 * Allocate and initialize an empty connection.
256 */
257 static struct connection *new_connection(void)
258 {
259 struct connection *conn = xmalloc(sizeof(*conn));
260
261 conn->socket = -1;
262 memset(&conn->client, '\0', sizeof(conn->client));
263 conn->last_active = now;
264 conn->request = NULL;
265 conn->request_length = 0;
266 conn->accept_gzip = 0;
267 conn->method = NULL;
268 conn->uri = NULL;
269 conn->query = NULL;
270 conn->header = NULL;
271 conn->mime_type = NULL;
272 conn->encoding = "";
273 conn->header_extra = "";
274 conn->header_length = 0;
275 conn->header_sent = 0;
276 conn->header_dont_free = 0;
277 conn->header_only = 0;
278 conn->http_code = 0;
279 conn->reply = NULL;
280 conn->reply_dont_free = 0;
281 conn->reply_length = 0;
282 conn->reply_sent = 0;
283 conn->total_sent = 0;
284
285 /* Make it harmless so it gets garbage-collected if it should, for some
286 * reason, fail to be correctly filled out.
287 */
288 conn->state = DONE;
289
290 return (conn);
291 }
292
293
294
295 /* ---------------------------------------------------------------------------
296 * Accept a connection from sockin and add it to the connection queue.
297 */
298 static void accept_connection(void)
299 {
300 struct sockaddr_storage addrin;
301 socklen_t sin_size;
302 struct connection *conn;
303 char ipaddr[INET6_ADDRSTRLEN], portstr[12];
304 int sock;
305
306 sin_size = (socklen_t)sizeof(addrin);
307 sock = accept(sockin, (struct sockaddr *)&addrin, &sin_size);
308 if (sock == -1)
309 {
310 if (errno == ECONNABORTED || errno == EINTR)
311 {
312 verbosef("accept() failed: %s", strerror(errno));
313 return;
314 }
315 /* else */ err(1, "accept()");
316 }
317
318 fd_set_nonblock(sock);
319
320 /* allocate and initialise struct connection */
321 conn = new_connection();
322 conn->socket = sock;
323 conn->state = RECV_REQUEST;
324 memcpy(&conn->client, &addrin, sizeof(conn->client));
325 LIST_INSERT_HEAD(&connlist, conn, entries);
326
327 getnameinfo((struct sockaddr *) &addrin, sin_size,
328 ipaddr, sizeof(ipaddr), portstr, sizeof(portstr),
329 NI_NUMERICHOST | NI_NUMERICSERV);
330 verbosef("accepted connection from %s:%s", ipaddr, portstr);
331 }
332
333
334
335 /* ---------------------------------------------------------------------------
336 * Log a connection, then cleanly deallocate its internals.
337 */
338 static void free_connection(struct connection *conn)
339 {
340 dverbosef("free_connection(%d)", conn->socket);
341 if (conn->socket != -1) close(conn->socket);
342 if (conn->request != NULL) free(conn->request);
343 if (conn->method != NULL) free(conn->method);
344 if (conn->uri != NULL) free(conn->uri);
345 if (conn->query != NULL) free(conn->query);
346 if (conn->header != NULL && !conn->header_dont_free) free(conn->header);
347 if (conn->reply != NULL && !conn->reply_dont_free) free(conn->reply);
348 }
349
350
351
352 /* ---------------------------------------------------------------------------
353 * Format [when] as an RFC1123 date, stored in the specified buffer. The same
354 * buffer is returned for convenience.
355 */
356 #define DATE_LEN 30 /* strlen("Fri, 28 Feb 2003 00:02:08 GMT")+1 */
357 static char *rfc1123_date(char *dest, const time_t when)
358 {
359 time_t tmp = when;
360 if (strftime(dest, DATE_LEN,
361 "%a, %d %b %Y %H:%M:%S %Z", gmtime(&tmp) ) == 0)
362 errx(1, "strftime() failed [%s]", dest);
363 return (dest);
364 }
365
366
367
368 /* ---------------------------------------------------------------------------
369 * A default reply for any (erroneous) occasion.
370 */
371 static void default_reply(struct connection *conn,
372 const int errcode, const char *errname, const char *format, ...)
373 {
374 char *reason, date[DATE_LEN];
375 va_list va;
376
377 va_start(va, format);
378 xvasprintf(&reason, format, va);
379 va_end(va);
380
381 /* Only really need to calculate the date once. */
382 (void)rfc1123_date(date, now);
383
384 conn->reply_length = xasprintf(&(conn->reply),
385 "<html><head><title>%d %s</title></head><body>\n"
386 "<h1>%s</h1>\n" /* errname */
387 "%s\n" /* reason */
388 "<hr>\n"
389 "Generated by %s on %s\n"
390 "</body></html>\n",
391 errcode, errname, errname, reason, server, date);
392 free(reason);
393
394 conn->header_length = xasprintf(&(conn->header),
395 "HTTP/1.1 %d %s\r\n"
396 "Date: %s\r\n"
397 "Server: %s\r\n"
398 "Content-Length: %d\r\n"
399 "Content-Type: text/html\r\n"
400 "\r\n",
401 errcode, errname, date, server, conn->reply_length);
402
403 conn->http_code = errcode;
404 }
405
406
407
408 /* ---------------------------------------------------------------------------
409 * Parses a single HTTP request field. Returns string from end of [field] to
410 * first \r, \n or end of request string. Returns NULL if [field] can't be
411 * matched.
412 *
413 * You need to remember to deallocate the result.
414 * example: parse_field(conn, "Referer: ");
415 */
416 static char *parse_field(const struct connection *conn, const char *field)
417 {
418 size_t bound1, bound2;
419 char *pos;
420
421 /* find start */
422 pos = strstr(conn->request, field);
423 if (pos == NULL)
424 return (NULL);
425 bound1 = pos - conn->request + strlen(field);
426
427 /* find end */
428 for (bound2 = bound1;
429 conn->request[bound2] != '\r' &&
430 bound2 < conn->request_length; bound2++)
431 ;
432
433 /* copy to buffer */
434 return (split_string(conn->request, bound1, bound2));
435 }
436
437
438
439 /* ---------------------------------------------------------------------------
440 * Parse an HTTP request like "GET /hosts/?sort=in HTTP/1.1" to get the method
441 * (GET), the uri (/hosts/), the query (sort=in) and whether the UA will
442 * accept gzip encoding. Remember to deallocate all these buffers. Query
443 * can be NULL. The method will be returned in uppercase.
444 */
445 static int parse_request(struct connection *conn)
446 {
447 size_t bound1, bound2, mid;
448 char *accept_enc;
449
450 /* parse method */
451 for (bound1 = 0; bound1 < conn->request_length &&
452 conn->request[bound1] != ' '; bound1++)
453 ;
454
455 conn->method = split_string(conn->request, 0, bound1);
456 strntoupper(conn->method, bound1);
457
458 /* parse uri */
459 for (; bound1 < conn->request_length &&
460 conn->request[bound1] == ' '; bound1++)
461 ;
462
463 if (bound1 == conn->request_length)
464 return (0); /* fail */
465
466 for (bound2=bound1+1; bound2 < conn->request_length &&
467 conn->request[bound2] != ' ' &&
468 conn->request[bound2] != '\r'; bound2++)
469 ;
470
471 /* find query string */
472 for (mid=bound1; mid<bound2 && conn->request[mid] != '?'; mid++)
473 ;
474
475 if (conn->request[mid] == '?') {
476 conn->query = split_string(conn->request, mid+1, bound2);
477 bound2 = mid;
478 }
479
480 conn->uri = split_string(conn->request, bound1, bound2);
481
482 /* parse important fields */
483 accept_enc = parse_field(conn, "Accept-Encoding: ");
484 if (accept_enc != NULL) {
485 if (strstr(accept_enc, "gzip") != NULL)
486 conn->accept_gzip = 1;
487 free(accept_enc);
488 }
489 return (1);
490 }
491
492 /* FIXME: maybe we need a smarter way of doing static pages: */
493
494 /* ---------------------------------------------------------------------------
495 * Web interface: static stylesheet.
496 */
497 static void
498 static_style_css(struct connection *conn)
499 {
500 #include "stylecss.h"
501
502 conn->reply = style_css;
503 conn->reply_length = style_css_len;
504 conn->reply_dont_free = 1;
505 conn->mime_type = mime_type_css;
506 }
507
508 /* ---------------------------------------------------------------------------
509 * Web interface: static JavaScript.
510 */
511 static void
512 static_graph_js(struct connection *conn)
513 {
514 #include "graphjs.h"
515
516 conn->reply = graph_js;
517 conn->reply_length = graph_js_len;
518 conn->reply_dont_free = 1;
519 conn->mime_type = mime_type_js;
520 }
521
522 /* ---------------------------------------------------------------------------
523 * gzip a reply, if requested and possible. Don't bother with a minimum
524 * length requirement, I've never seen a page fail to compress.
525 */
526 static void
527 process_gzip(struct connection *conn)
528 {
529 char *buf;
530 size_t len;
531 z_stream zs;
532
533 if (!conn->accept_gzip)
534 return;
535
536 buf = xmalloc(conn->reply_length);
537 len = conn->reply_length;
538
539 zs.zalloc = Z_NULL;
540 zs.zfree = Z_NULL;
541 zs.opaque = Z_NULL;
542
543 if (deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED,
544 15+16, /* 15 = biggest window, 16 = add gzip header+trailer */
545 8 /* default */,
546 Z_DEFAULT_STRATEGY) != Z_OK)
547 return;
548
549 zs.avail_in = conn->reply_length;
550 zs.next_in = (unsigned char *)conn->reply;
551
552 zs.avail_out = conn->reply_length;
553 zs.next_out = (unsigned char *)buf;
554
555 if (deflate(&zs, Z_FINISH) != Z_STREAM_END) {
556 deflateEnd(&zs);
557 free(buf);
558 verbosef("failed to compress %u bytes", (unsigned int)len);
559 return;
560 }
561
562 if (conn->reply_dont_free)
563 conn->reply_dont_free = 0;
564 else
565 free(conn->reply);
566
567 conn->reply = buf;
568 conn->reply_length -= zs.avail_out;
569 conn->encoding = encoding_gzip;
570 deflateEnd(&zs);
571 }
572
573 /* ---------------------------------------------------------------------------
574 * Process a GET/HEAD request
575 */
576 static void process_get(struct connection *conn)
577 {
578 char *decoded_url, *safe_url;
579 char date[DATE_LEN];
580
581 verbosef("http: %s \"%s\" %s", conn->method, conn->uri,
582 (conn->query == NULL)?"":conn->query);
583
584 /* work out path of file being requested */
585 decoded_url = urldecode(conn->uri);
586
587 /* make sure it's safe */
588 safe_url = make_safe_uri(decoded_url);
589 free(decoded_url);
590 if (safe_url == NULL)
591 {
592 default_reply(conn, 400, "Bad Request",
593 "You requested an invalid URI: %s", conn->uri);
594 return;
595 }
596
597 if (strcmp(safe_url, "/") == 0) {
598 struct str *buf = html_front_page();
599 str_extract(buf, &(conn->reply_length), &(conn->reply));
600 conn->mime_type = mime_type_html;
601 }
602 else if (str_starts_with(safe_url, "/hosts/")) {
603 /* FIXME here - make this saner */
604 struct str *buf = html_hosts(safe_url, conn->query);
605 if (buf == NULL) {
606 default_reply(conn, 404, "Not Found",
607 "The page you requested could not be found.");
608 return;
609 }
610 str_extract(buf, &(conn->reply_length), &(conn->reply));
611 conn->mime_type = mime_type_html;
612 }
613 else if (str_starts_with(safe_url, "/graphs.xml")) {
614 struct str *buf = xml_graphs();
615 str_extract(buf, &(conn->reply_length), &(conn->reply));
616 conn->mime_type = mime_type_xml;
617 /* hack around Opera caching the XML */
618 conn->header_extra = "Pragma: no-cache\r\n";
619 }
620 else if (strcmp(safe_url, "/style.css") == 0)
621 static_style_css(conn);
622 else if (strcmp(safe_url, "/graph.js") == 0)
623 static_graph_js(conn);
624 else {
625 default_reply(conn, 404, "Not Found",
626 "The page you requested could not be found.");
627 return;
628 }
629 free(safe_url);
630
631 process_gzip(conn);
632 assert(conn->mime_type != NULL);
633 conn->header_length = xasprintf(&(conn->header),
634 "HTTP/1.1 200 OK\r\n"
635 "Date: %s\r\n"
636 "Server: %s\r\n"
637 "Content-Length: %d\r\n"
638 "Content-Type: %s\r\n"
639 "%s"
640 "%s"
641 "\r\n"
642 ,
643 rfc1123_date(date, now), server,
644 conn->reply_length, conn->mime_type, conn->encoding,
645 conn->header_extra);
646 conn->http_code = 200;
647 }
648
649
650
651 /* ---------------------------------------------------------------------------
652 * Process a request: build the header and reply, advance state.
653 */
654 static void process_request(struct connection *conn)
655 {
656 if (!parse_request(conn))
657 {
658 default_reply(conn, 400, "Bad Request",
659 "You sent a request that the server couldn't understand.");
660 }
661 else if (strcmp(conn->method, "GET") == 0)
662 {
663 process_get(conn);
664 }
665 else if (strcmp(conn->method, "HEAD") == 0)
666 {
667 process_get(conn);
668 conn->header_only = 1;
669 }
670 else
671 {
672 default_reply(conn, 501, "Not Implemented",
673 "The method you specified (%s) is not implemented.",
674 conn->method);
675 }
676
677 /* advance state */
678 if (conn->header_only)
679 conn->state = SEND_HEADER;
680 else
681 conn->state = SEND_HEADER_AND_REPLY;
682
683 /* request not needed anymore */
684 free(conn->request);
685 conn->request = NULL; /* important: don't free it again later */
686 }
687
688
689
690 /* ---------------------------------------------------------------------------
691 * Receiving request.
692 */
693 static void poll_recv_request(struct connection *conn)
694 {
695 #define BUFSIZE 65536
696 char buf[BUFSIZE];
697 ssize_t recvd;
698
699 recvd = recv(conn->socket, buf, BUFSIZE, 0);
700 dverbosef("poll_recv_request(%d) got %d bytes", conn->socket, (int)recvd);
701 if (recvd <= 0)
702 {
703 if (recvd == -1)
704 verbosef("recv(%d) error: %s", conn->socket, strerror(errno));
705 conn->state = DONE;
706 return;
707 }
708 conn->last_active = now;
709 #undef BUFSIZE
710
711 /* append to conn->request */
712 conn->request = xrealloc(conn->request, conn->request_length+recvd+1);
713 memcpy(conn->request+conn->request_length, buf, (size_t)recvd);
714 conn->request_length += recvd;
715 conn->request[conn->request_length] = 0;
716
717 /* process request if we have all of it */
718 if (conn->request_length > 4 &&
719 memcmp(conn->request+conn->request_length-4, "\r\n\r\n", 4) == 0)
720 process_request(conn);
721
722 /* die if it's too long */
723 if (conn->request_length > MAX_REQUEST_LENGTH)
724 {
725 default_reply(conn, 413, "Request Entity Too Large",
726 "Your request was dropped because it was too long.");
727 conn->state = SEND_HEADER;
728 }
729 }
730
731
732
733 /* ---------------------------------------------------------------------------
734 * Try to send header and [a part of the] reply in one packet.
735 */
736 static void poll_send_header_and_reply(struct connection *conn)
737 {
738 ssize_t sent;
739 struct iovec iov[2];
740
741 assert(!conn->header_only);
742 assert(conn->reply_length > 0);
743 assert(conn->header_sent == 0);
744
745 assert(conn->reply_sent == 0);
746
747 /* Fill out iovec */
748 iov[0].iov_base = conn->header;
749 iov[0].iov_len = conn->header_length;
750
751 iov[1].iov_base = conn->reply + conn->reply_sent;
752 iov[1].iov_len = conn->reply_length - conn->reply_sent;
753
754 sent = writev(conn->socket, iov, 2);
755 conn->last_active = now;
756 verbosef("poll_send_header_and_reply(%d) sent %d bytes",
757 conn->socket, (int)sent);
758
759 /* handle any errors (-1) or closure (0) in send() */
760 if (sent < 1) {
761 if (sent == -1)
762 verbosef("writev(%d) error: %s", conn->socket, strerror(errno));
763 conn->state = DONE;
764 return;
765 }
766
767 /* Figure out what we've sent. */
768 conn->total_sent += (unsigned int)sent;
769 if (sent < (ssize_t)conn->header_length) {
770 verbosef("partially sent header");
771 conn->header_sent = sent;
772 conn->state = SEND_HEADER;
773 return;
774 }
775 /* else */
776 conn->header_sent = conn->header_length;
777 sent -= conn->header_length;
778
779 if (conn->reply_sent + sent < conn->reply_length) {
780 verbosef("partially sent reply");
781 conn->reply_sent += sent;
782 conn->state = SEND_REPLY;
783 return;
784 }
785 /* else */
786 conn->reply_sent = conn->reply_length;
787 conn->state = DONE;
788 }
789
790 /* ---------------------------------------------------------------------------
791 * Sending header. Assumes conn->header is not NULL.
792 */
793 static void poll_send_header(struct connection *conn)
794 {
795 ssize_t sent;
796
797 sent = send(conn->socket, conn->header + conn->header_sent,
798 conn->header_length - conn->header_sent, 0);
799 conn->last_active = now;
800 dverbosef("poll_send_header(%d) sent %d bytes", conn->socket, (int)sent);
801
802 /* handle any errors (-1) or closure (0) in send() */
803 if (sent < 1)
804 {
805 if (sent == -1)
806 verbosef("send(%d) error: %s", conn->socket, strerror(errno));
807 conn->state = DONE;
808 return;
809 }
810 conn->header_sent += (unsigned int)sent;
811 conn->total_sent += (unsigned int)sent;
812
813 /* check if we're done sending */
814 if (conn->header_sent == conn->header_length)
815 {
816 if (conn->header_only)
817 conn->state = DONE;
818 else
819 conn->state = SEND_REPLY;
820 }
821 }
822
823
824
825 /* ---------------------------------------------------------------------------
826 * Sending reply.
827 */
828 static void poll_send_reply(struct connection *conn)
829 {
830 ssize_t sent;
831
832 sent = send(conn->socket,
833 conn->reply + conn->reply_sent,
834 conn->reply_length - conn->reply_sent, 0);
835 conn->last_active = now;
836 dverbosef("poll_send_reply(%d) sent %d: [%d-%d] of %d",
837 conn->socket, (int)sent,
838 (int)conn->reply_sent,
839 (int)(conn->reply_sent + sent - 1),
840 (int)conn->reply_length);
841
842 /* handle any errors (-1) or closure (0) in send() */
843 if (sent < 1)
844 {
845 if (sent == -1)
846 verbosef("send(%d) error: %s", conn->socket, strerror(errno));
847 else if (sent == 0)
848 verbosef("send(%d) closure", conn->socket);
849 conn->state = DONE;
850 return;
851 }
852 conn->reply_sent += (unsigned int)sent;
853 conn->total_sent += (unsigned int)sent;
854
855 /* check if we're done sending */
856 if (conn->reply_sent == conn->reply_length) conn->state = DONE;
857 }
858
859
860
861 /* ---------------------------------------------------------------------------
862 * Initialize the sockin global. This is the socket that we accept
863 * connections from. Pass -1 as max_conn for system limit.
864 */
865 void http_init(const char *bindaddr, const unsigned short bindport,
866 const int max_conn)
867 {
868 struct sockaddr_storage addrin;
869 struct addrinfo hints, *ai, *aiptr;
870 char ipaddr[INET6_ADDRSTRLEN], portstr[12];
871 int sockopt, ret;
872
873 memset(&hints, '\0', sizeof(hints));
874 hints.ai_family = AF_UNSPEC;
875 hints.ai_socktype = SOCK_STREAM;
876 hints.ai_flags = AI_PASSIVE;
877 #ifdef AI_ADDRCONFIG
878 hints.ai_flags |= AI_ADDRCONFIG;
879 #endif
880 snprintf(portstr, sizeof(portstr), "%u", bindport);
881
882 if ((ret = getaddrinfo(bindaddr, portstr, &hints, &aiptr)))
883 err(1, "getaddrinfo(): %s", gai_strerror(ret));
884
885 for (ai = aiptr; ai; ai = ai->ai_next) {
886 /* create incoming socket */
887 sockin = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
888 if (sockin == -1)
889 continue;
890
891 /* reuse address */
892 sockopt = 1;
893 if (setsockopt(sockin, SOL_SOCKET, SO_REUSEADDR,
894 &sockopt, sizeof(sockopt)) == -1) {
895 close(sockin);
896 continue;
897 }
898
899 /* Recover address and port strings. */
900 getnameinfo(ai->ai_addr, ai->ai_addrlen, ipaddr, sizeof(ipaddr),
901 NULL, 0, NI_NUMERICHOST);
902
903 /* bind socket */
904 memcpy(&addrin, ai->ai_addr, ai->ai_addrlen);
905 if (bind(sockin, (struct sockaddr *)&addrin, ai->ai_addrlen) == -1) {
906 close(sockin);
907 continue;
908 }
909
910 verbosef("listening on %s:%u", ipaddr, bindport);
911
912 /* listen on socket */
913 if (listen(sockin, max_conn) >= 0)
914 /* Successfully bound and now listening. */
915 break;
916
917 /* Next candidate. */
918 continue;
919 }
920
921 freeaddrinfo(aiptr);
922
923 if (ai == NULL)
924 err(1, "getaddrinfo() unable to locate address");
925
926 /* ignore SIGPIPE */
927 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
928 err(1, "can't ignore SIGPIPE");
929 }
930
931
932
933 /* ---------------------------------------------------------------------------
934 * Set recv/send fd_sets and calculate timeout length.
935 */
936 void
937 http_fd_set(fd_set *recv_set, fd_set *send_set, int *max_fd,
938 struct timeval *timeout, int *need_timeout)
939 {
940 struct connection *conn, *next;
941 int minidle = idletime + 1;
942
943 #define MAX_FD_SET(sock, fdset) do { \
944 FD_SET(sock, fdset); *max_fd = max(*max_fd, sock); } while(0)
945
946 MAX_FD_SET(sockin, recv_set);
947
948 LIST_FOREACH_SAFE(conn, &connlist, entries, next)
949 {
950 int idlefor = now - conn->last_active;
951
952 /* Time out dead connections. */
953 if (idlefor >= idletime)
954 {
955 char ipaddr[INET6_ADDRSTRLEN];
956 getnameinfo((struct sockaddr *) &conn->client, sizeof(conn->client),
957 ipaddr, sizeof(ipaddr), NULL, 0, NI_NUMERICHOST);
958 verbosef("http socket timeout from %s (fd %d)",
959 ipaddr, conn->socket);
960 conn->state = DONE;
961 }
962
963 /* Connections that need a timeout. */
964 if (conn->state != DONE)
965 minidle = min(minidle, (idletime - idlefor));
966
967 switch (conn->state)
968 {
969 case DONE:
970 /* clean out stale connection */
971 LIST_REMOVE(conn, entries);
972 free_connection(conn);
973 free(conn);
974 break;
975
976 case RECV_REQUEST:
977 MAX_FD_SET(conn->socket, recv_set);
978 break;
979
980 case SEND_HEADER_AND_REPLY:
981 case SEND_HEADER:
982 case SEND_REPLY:
983 MAX_FD_SET(conn->socket, send_set);
984 break;
985
986 default: errx(1, "invalid state");
987 }
988 }
989 #undef MAX_FD_SET
990
991 /* Only set timeout if cap hasn't already. */
992 if ((*need_timeout == 0) && (minidle <= idletime)) {
993 *need_timeout = 1;
994 timeout->tv_sec = minidle;
995 timeout->tv_usec = 0;
996 }
997 }
998
999
1000
1001 /* ---------------------------------------------------------------------------
1002 * poll connections that select() says need attention
1003 */
1004 void http_poll(fd_set *recv_set, fd_set *send_set)
1005 {
1006 struct connection *conn;
1007
1008 if (FD_ISSET(sockin, recv_set)) accept_connection();
1009
1010 LIST_FOREACH(conn, &connlist, entries)
1011 switch (conn->state)
1012 {
1013 case RECV_REQUEST:
1014 if (FD_ISSET(conn->socket, recv_set)) poll_recv_request(conn);
1015 break;
1016
1017 case SEND_HEADER_AND_REPLY:
1018 if (FD_ISSET(conn->socket, send_set)) poll_send_header_and_reply(conn);
1019 break;
1020
1021 case SEND_HEADER:
1022 if (FD_ISSET(conn->socket, send_set)) poll_send_header(conn);
1023 break;
1024
1025 case SEND_REPLY:
1026 if (FD_ISSET(conn->socket, send_set)) poll_send_reply(conn);
1027 break;
1028
1029 default: errx(1, "invalid state");
1030 }
1031 }
1032
1033 /* vim:set ts=4 sw=4 et tw=78: */