Suggestion from James Beal: support a '--title' option to override the
[sgt/agedu] / httpd.c
1 /*
2 * httpd.c: implementation of httpd.h.
3 */
4
5 #include "agedu.h"
6 #include "alloc.h"
7 #include "html.h"
8 #include "httpd.h"
9
10 /* --- Logic driving what the web server's responses are. --- */
11
12 enum { /* connctx states */
13 READING_REQ_LINE,
14 READING_HEADERS,
15 DONE
16 };
17
18 struct connctx {
19 const void *t;
20 char *data;
21 int datalen, datasize;
22 char *method, *url, *headers, *auth;
23 int state;
24 };
25
26 /*
27 * Called when a new connection arrives on a listening socket.
28 * Returns a connctx for the new connection.
29 */
30 struct connctx *new_connection(const void *t)
31 {
32 struct connctx *cctx = snew(struct connctx);
33 cctx->t = t;
34 cctx->data = NULL;
35 cctx->datalen = cctx->datasize = 0;
36 cctx->state = READING_REQ_LINE;
37 cctx->method = cctx->url = cctx->headers = cctx->auth = NULL;
38 return cctx;
39 }
40
41 void free_connection(struct connctx *cctx)
42 {
43 sfree(cctx->data);
44 sfree(cctx);
45 }
46
47 static char *http_error(char *code, char *errmsg, char *extraheader,
48 char *errtext, ...)
49 {
50 return dupfmt("HTTP/1.1 %s %s\r\n"
51 "Date: %D\r\n"
52 "Server: " PNAME "\r\n"
53 "Connection: close\r\n"
54 "%s"
55 "Content-Type: text/html; charset=US-ASCII\r\n"
56 "\r\n"
57 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
58 "<HTML><HEAD>\r\n"
59 "<TITLE>%s %s</TITLE>\r\n"
60 "</HEAD><BODY>\r\n"
61 "<H1>%s %s</H1>\r\n"
62 "<P>%s</P>\r\n"
63 "</BODY></HTML>\r\n", code, errmsg,
64 extraheader ? extraheader : "",
65 code, errmsg, code, errmsg, errtext);
66 }
67
68 static char *http_success(char *mimetype, int stuff_cr, char *document)
69 {
70 return dupfmt("HTTP/1.1 200 OK\r\n"
71 "Date: %D\r\n"
72 "Expires: %D\r\n"
73 "Server: " PNAME "\r\n"
74 "Connection: close\r\n"
75 "Content-Type: %s\r\n"
76 "\r\n"
77 "%S", mimetype, stuff_cr, document);
78 }
79
80 /*
81 * Called when data comes in on a connection.
82 *
83 * If this function returns NULL, the platform code continues
84 * reading from the socket. Otherwise, it returns some dynamically
85 * allocated data which the platform code will then write to the
86 * socket before closing it.
87 */
88 char *got_data(struct connctx *ctx, char *data, int length,
89 int magic_access, const char *auth_string,
90 const struct html_config *cfg)
91 {
92 char *line, *p, *q, *r, *z1, *z2, c1, c2;
93 int auth_correct = 0;
94 unsigned long index;
95 char *document, *ret;
96
97 /*
98 * Add the data we've just received to our buffer.
99 */
100 if (ctx->datasize < ctx->datalen + length) {
101 ctx->datasize = (ctx->datalen + length) * 3 / 2 + 4096;
102 ctx->data = sresize(ctx->data, ctx->datasize, char);
103 }
104 memcpy(ctx->data + ctx->datalen, data, length);
105 ctx->datalen += length;
106
107 /*
108 * Gradually process the HTTP request as we receive it.
109 */
110 if (ctx->state == READING_REQ_LINE) {
111 /*
112 * We're waiting for the first line of the input, which
113 * contains the main HTTP request. See if we've got it
114 * yet.
115 */
116
117 line = ctx->data;
118 /*
119 * RFC 2616 section 4.1: `In the interest of robustness,
120 * [...] if the server is reading the protocol stream at
121 * the beginning of a message and receives a CRLF first,
122 * it should ignore the CRLF.'
123 */
124 while (line - ctx->data < ctx->datalen &&
125 (*line == '\r' || *line == '\n'))
126 line++;
127 q = line;
128 while (q - ctx->data < ctx->datalen && *q != '\n')
129 q++;
130 if (q - ctx->data >= ctx->datalen)
131 return NULL; /* not got request line yet */
132
133 /*
134 * We've got the first line of the request. Zero-terminate
135 * and parse it into method, URL and optional HTTP
136 * version.
137 */
138 *q = '\0';
139 ctx->headers = q+1;
140 if (q > line && q[-1] == '\r')
141 *--q = '\0';
142 z1 = z2 = q;
143 c1 = c2 = *q;
144 p = line;
145 while (*p && !isspace((unsigned char)*p)) p++;
146 if (*p) {
147 z1 = p++;
148 c1 = *z1;
149 *z1 = '\0';
150 }
151 while (*p && isspace((unsigned char)*p)) p++;
152 q = p;
153 while (*q && !isspace((unsigned char)*q)) q++;
154 z2 = q++;
155 c2 = *z2;
156 *z2 = '\0';
157 while (*q && isspace((unsigned char)*q)) q++;
158
159 /*
160 * Now `line' points at the method name; p points at the
161 * URL, if any; q points at the HTTP version, if any.
162 */
163
164 /*
165 * There should _be_ a URL, on any request type at all.
166 */
167 if (!*p) {
168 char *ret, *text;
169 /* Restore the request to the way we received it. */
170 *z2 = c2;
171 *z1 = c1;
172 text = dupfmt("<code>" PNAME "</code> received the HTTP request"
173 " \"<code>%h</code>\", which contains no URL.",
174 line);
175 ret = http_error("400", "Bad request", NULL, text);
176 sfree(text);
177 return ret;
178 }
179
180 ctx->method = line;
181 ctx->url = p;
182
183 /*
184 * If there was an HTTP version, we might need to see
185 * headers. Otherwise, the request is done.
186 */
187 if (*q) {
188 ctx->state = READING_HEADERS;
189 } else {
190 ctx->state = DONE;
191 }
192 }
193
194 if (ctx->state == READING_HEADERS) {
195 /*
196 * While we're receiving the HTTP request headers, all we
197 * do is to keep scanning to see if we find two newlines
198 * next to each other.
199 */
200 q = ctx->data + ctx->datalen;
201 for (p = ctx->headers; p < q; p++) {
202 if (*p == '\n' &&
203 ((p+1 < q && p[1] == '\n') ||
204 (p+2 < q && p[1] == '\r' && p[2] == '\n'))) {
205 p[1] = '\0';
206 ctx->state = DONE;
207 break;
208 }
209 }
210 }
211
212 if (ctx->state == DONE) {
213 /*
214 * Now we have the entire HTTP request. Decide what to do
215 * with it.
216 */
217 if (auth_string) {
218 /*
219 * Search the request headers for Authorization.
220 */
221 q = ctx->data + ctx->datalen;
222 for (p = ctx->headers; p < q; p++) {
223 const char *hdr = "Authorization:";
224 int i;
225 for (i = 0; hdr[i]; i++) {
226 if (p >= q || tolower((unsigned char)*p) !=
227 tolower((unsigned char)hdr[i]))
228 break;
229 p++;
230 }
231 if (!hdr[i])
232 break; /* found our header */
233 p = memchr(p, '\n', q - p);
234 if (!p)
235 p = q;
236 }
237 if (p < q) {
238 while (p < q && isspace((unsigned char)*p))
239 p++;
240 r = p;
241 while (p < q && !isspace((unsigned char)*p))
242 p++;
243 if (p < q) {
244 *p++ = '\0';
245 if (!strcasecmp(r, "Basic")) {
246 while (p < q && isspace((unsigned char)*p))
247 p++;
248 r = p;
249 while (p < q && !isspace((unsigned char)*p))
250 p++;
251 if (p < q) {
252 *p++ = '\0';
253 if (!strcmp(r, auth_string))
254 auth_correct = 1;
255 }
256 }
257 }
258 }
259 }
260
261 if (!magic_access && !auth_correct) {
262 if (auth_string) {
263 ret = http_error("401", "Unauthorized",
264 "WWW-Authenticate: Basic realm=\""PNAME"\"\r\n",
265 "\nYou must authenticate to view these pages.");
266 } else {
267 ret = http_error("403", "Forbidden", NULL,
268 "This is a restricted-access set of pages.");
269 }
270 } else {
271 p = ctx->url;
272 if (!html_parse_path(ctx->t, p, cfg, &index)) {
273 ret = http_error("404", "Not Found", NULL,
274 "This is not a valid pathname.");
275 } else {
276 char *canonpath = html_format_path(ctx->t, cfg, index);
277 if (!strcmp(canonpath, p)) {
278 /*
279 * This is a canonical path. Return the document.
280 */
281 document = html_query(ctx->t, index, cfg, 1);
282 if (document) {
283 ret = http_success("text/html", 1, document);
284 sfree(document);
285 } else {
286 ret = http_error("404", "Not Found", NULL,
287 "This is not a valid pathname.");
288 }
289 } else {
290 /*
291 * This is a non-canonical path. Return a redirect
292 * to the right one.
293 *
294 * To do this, we must search the request headers
295 * for Host:, to see what the client thought it
296 * was calling our server.
297 */
298
299 char *host = NULL;
300 q = ctx->data + ctx->datalen;
301 for (p = ctx->headers; p < q; p++) {
302 const char *hdr = "Host:";
303 int i;
304 for (i = 0; hdr[i]; i++) {
305 if (p >= q || tolower((unsigned char)*p) !=
306 tolower((unsigned char)hdr[i]))
307 break;
308 p++;
309 }
310 if (!hdr[i])
311 break; /* found our header */
312 p = memchr(p, '\n', q - p);
313 if (!p)
314 p = q;
315 }
316 if (p < q) {
317 while (p < q && isspace((unsigned char)*p))
318 p++;
319 r = p;
320 while (p < q) {
321 if (*p == '\r' && (p+1 >= q || p[1] == '\n'))
322 break;
323 p++;
324 }
325 host = snewn(p-r+1, char);
326 memcpy(host, r, p-r);
327 host[p-r] = '\0';
328 }
329 if (host) {
330 char *header = dupfmt("Location: http://%s%s\r\n",
331 host, canonpath);
332 ret = http_error("301", "Moved", header,
333 "This is not the canonical form of"
334 " this pathname.");
335 sfree(header);
336 } else {
337 ret = http_error("400", "Bad Request", NULL,
338 "Needed a Host: header to return"
339 " the intended redirection.");
340 }
341 }
342 sfree(canonpath);
343 }
344 }
345 return ret;
346 } else
347 return NULL;
348 }
349
350 /* --- Platform support for running a web server. --- */
351
352 enum { FD_CLIENT, FD_LISTENER, FD_CONNECTION };
353
354 struct fd {
355 int fd;
356 int type;
357 int deleted;
358 char *wdata;
359 int wdatalen, wdatapos;
360 int magic_access;
361 struct connctx *cctx;
362 };
363
364 struct fd *fds = NULL;
365 int nfds = 0, fdsize = 0;
366
367 struct fd *new_fdstruct(int fd, int type)
368 {
369 struct fd *ret;
370
371 if (nfds >= fdsize) {
372 fdsize = nfds * 3 / 2 + 32;
373 fds = sresize(fds, fdsize, struct fd);
374 }
375
376 ret = &fds[nfds++];
377
378 ret->fd = fd;
379 ret->type = type;
380 ret->wdata = NULL;
381 ret->wdatalen = ret->wdatapos = 0;
382 ret->cctx = NULL;
383 ret->deleted = 0;
384 ret->magic_access = 0;
385
386 return ret;
387 }
388
389 int check_owning_uid(int fd, int flip)
390 {
391 struct sockaddr_in sock, peer;
392 socklen_t addrlen;
393 char linebuf[4096], matchbuf[80];
394 FILE *fp;
395
396 addrlen = sizeof(sock);
397 if (getsockname(fd, (struct sockaddr *)&sock, &addrlen)) {
398 fprintf(stderr, "getsockname: %s\n", strerror(errno));
399 exit(1);
400 }
401 addrlen = sizeof(peer);
402 if (getpeername(fd, (struct sockaddr *)&peer, &addrlen)) {
403 if (errno == ENOTCONN) {
404 peer.sin_addr.s_addr = htonl(0);
405 peer.sin_port = htons(0);
406 } else {
407 fprintf(stderr, "getpeername: %s\n", strerror(errno));
408 exit(1);
409 }
410 }
411
412 if (flip) {
413 struct sockaddr_in tmp = sock;
414 sock = peer;
415 peer = tmp;
416 }
417
418 sprintf(matchbuf, "%08X:%04X %08X:%04X",
419 peer.sin_addr.s_addr, ntohs(peer.sin_port),
420 sock.sin_addr.s_addr, ntohs(sock.sin_port));
421 fp = fopen("/proc/net/tcp", "r");
422 if (fp) {
423 while (fgets(linebuf, sizeof(linebuf), fp)) {
424 if (strlen(linebuf) >= 75 &&
425 !strncmp(linebuf+6, matchbuf, strlen(matchbuf))) {
426 fclose(fp);
427 return atoi(linebuf + 75);
428 }
429 }
430 fclose(fp);
431 }
432
433 return -1;
434 }
435
436 void check_magic_access(struct fd *fd)
437 {
438 if (check_owning_uid(fd->fd, 0) == getuid())
439 fd->magic_access = 1;
440 }
441
442 static void base64_encode_atom(unsigned char *data, int n, char *out)
443 {
444 static const char base64_chars[] =
445 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
446
447 unsigned word;
448
449 word = data[0] << 16;
450 if (n > 1)
451 word |= data[1] << 8;
452 if (n > 2)
453 word |= data[2];
454 out[0] = base64_chars[(word >> 18) & 0x3F];
455 out[1] = base64_chars[(word >> 12) & 0x3F];
456 if (n > 1)
457 out[2] = base64_chars[(word >> 6) & 0x3F];
458 else
459 out[2] = '=';
460 if (n > 2)
461 out[3] = base64_chars[word & 0x3F];
462 else
463 out[3] = '=';
464 }
465
466 void run_httpd(const void *t, int authmask, const struct httpd_config *dcfg,
467 const struct html_config *incfg)
468 {
469 int fd, ret;
470 int authtype;
471 char *authstring = NULL;
472 struct sockaddr_in addr;
473 socklen_t addrlen;
474 struct html_config cfg = *incfg;
475
476 /*
477 * Establish the listening socket and retrieve its port
478 * number.
479 */
480 fd = socket(PF_INET, SOCK_STREAM, 0);
481 if (fd < 0) {
482 fprintf(stderr, "socket(PF_INET): %s\n", strerror(errno));
483 exit(1);
484 }
485 memset(&addr, 0, sizeof(addr));
486 addr.sin_family = AF_INET;
487 if (!dcfg->address) {
488 #ifdef RANDOM_LOCALHOST
489 unsigned long ipaddr;
490 srand(0L);
491 ipaddr = 0x7f000000;
492 ipaddr += (1 + rand() % 255) << 16;
493 ipaddr += (1 + rand() % 255) << 8;
494 ipaddr += (1 + rand() % 255);
495 addr.sin_addr.s_addr = htonl(ipaddr);
496 #else
497 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
498 #endif
499 addr.sin_port = htons(0);
500 } else {
501 addr.sin_addr.s_addr = inet_addr(dcfg->address);
502 addr.sin_port = dcfg->port ? htons(dcfg->port) : 0;
503 }
504 addrlen = sizeof(addr);
505 ret = bind(fd, (const struct sockaddr *)&addr, addrlen);
506 #ifdef RANDOM_LOCALHOST
507 if (ret < 0 && errno == EADDRNOTAVAIL && !dcfg->address) {
508 /*
509 * Some systems don't like us binding to random weird
510 * localhost-space addresses. Try again with the official
511 * INADDR_LOOPBACK.
512 */
513 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
514 addr.sin_port = htons(0);
515 ret = bind(fd, (const struct sockaddr *)&addr, addrlen);
516 }
517 #endif
518 if (ret < 0) {
519 fprintf(stderr, "bind: %s\n", strerror(errno));
520 exit(1);
521 }
522 if (listen(fd, 5) < 0) {
523 fprintf(stderr, "listen: %s\n", strerror(errno));
524 exit(1);
525 }
526 addrlen = sizeof(addr);
527 if (getsockname(fd, (struct sockaddr *)&addr, &addrlen)) {
528 fprintf(stderr, "getsockname: %s\n", strerror(errno));
529 exit(1);
530 }
531 if ((authmask & HTTPD_AUTH_MAGIC) &&
532 (check_owning_uid(fd, 1) == getuid())) {
533 authtype = HTTPD_AUTH_MAGIC;
534 if (authmask != HTTPD_AUTH_MAGIC)
535 printf("Using Linux /proc/net magic authentication\n");
536 } else if ((authmask & HTTPD_AUTH_BASIC)) {
537 char username[128], password[128], userpassbuf[259];
538 const char *userpass;
539 const char *rname;
540 unsigned char passbuf[10];
541 int i, j, k, fd;
542
543 authtype = HTTPD_AUTH_BASIC;
544
545 if (authmask != HTTPD_AUTH_BASIC)
546 printf("Using HTTP Basic authentication\n");
547
548 if (dcfg->basicauthdata) {
549 userpass = dcfg->basicauthdata;
550 } else {
551 strcpy(username, PNAME);
552 rname = "/dev/urandom";
553 fd = open(rname, O_RDONLY);
554 if (fd < 0) {
555 int err = errno;
556 rname = "/dev/random";
557 fd = open(rname, O_RDONLY);
558 if (fd < 0) {
559 int err2 = errno;
560 fprintf(stderr, "/dev/urandom: open: %s\n", strerror(err));
561 fprintf(stderr, "/dev/random: open: %s\n", strerror(err2));
562 exit(1);
563 }
564 }
565 for (i = 0; i < 10 ;) {
566 j = read(fd, passbuf + i, 10 - i);
567 if (j <= 0) {
568 fprintf(stderr, "%s: read: %s\n", rname,
569 j < 0 ? strerror(errno) : "unexpected EOF");
570 exit(1);
571 }
572 i += j;
573 }
574 close(fd);
575 for (i = 0; i < 16; i++) {
576 /*
577 * 32 characters out of the 36 alphanumerics gives
578 * me the latitude to discard i,l,o for being too
579 * numeric-looking, and w because it has two too
580 * many syllables and one too many presidential
581 * associations.
582 */
583 static const char chars[32] =
584 "0123456789abcdefghjkmnpqrstuvxyz";
585 int v = 0;
586
587 k = i / 8 * 5;
588 for (j = 0; j < 5; j++)
589 v |= ((passbuf[k+j] >> (i%8)) & 1) << j;
590
591 password[i] = chars[v];
592 }
593 password[i] = '\0';
594
595 sprintf(userpassbuf, "%s:%s", username, password);
596 userpass = userpassbuf;
597
598 printf("Username: %s\nPassword: %s\n", username, password);
599 }
600
601 k = strlen(userpass);
602 authstring = snewn(k * 4 / 3 + 16, char);
603 for (i = j = 0; i < k ;) {
604 int s = k-i < 3 ? k-i : 3;
605 base64_encode_atom((unsigned char *)(userpass+i), s, authstring+j);
606 i += s;
607 j += 4;
608 }
609 authstring[j] = '\0';
610 } else if ((authmask & HTTPD_AUTH_NONE)) {
611 authtype = HTTPD_AUTH_NONE;
612 if (authmask != HTTPD_AUTH_NONE)
613 printf("Web server is unauthenticated\n");
614 } else {
615 fprintf(stderr, PNAME ": authentication method not supported\n");
616 exit(1);
617 }
618 if (ntohs(addr.sin_addr.s_addr) == INADDR_ANY) {
619 printf("Server port: %d\n", ntohs(addr.sin_port));
620 } else if (ntohs(addr.sin_port) == 80) {
621 printf("URL: http://%s/\n", inet_ntoa(addr.sin_addr));
622 } else {
623 printf("URL: http://%s:%d/\n",
624 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
625 }
626 fflush(stdout);
627
628 /*
629 * Now construct an fd structure to hold it.
630 */
631 new_fdstruct(fd, FD_LISTENER);
632
633 if (dcfg->closeoneof) {
634 /*
635 * Read from standard input, and treat EOF as a notification
636 * to exit.
637 */
638 new_fdstruct(0, FD_CLIENT);
639 }
640
641 /*
642 * Now we're ready to run our main loop. Keep looping round on
643 * select.
644 */
645 while (1) {
646 fd_set rfds, wfds;
647 int i, j;
648 SELECT_TYPE_ARG1 maxfd;
649 int ret;
650
651 #define FD_SET_MAX(fd, set, max) \
652 do { FD_SET((fd),(set)); (max) = ((max)<=(fd)?(fd)+1:(max)); } while(0)
653
654 /*
655 * Loop round the fd list putting fds into our select
656 * sets. Also in this loop we remove any that were marked
657 * as deleted in the previous loop.
658 */
659 FD_ZERO(&rfds);
660 FD_ZERO(&wfds);
661 maxfd = 0;
662 for (i = j = 0; j < nfds; j++) {
663
664 if (fds[j].deleted) {
665 sfree(fds[j].wdata);
666 free_connection(fds[j].cctx);
667 continue;
668 }
669 fds[i] = fds[j];
670
671 switch (fds[i].type) {
672 case FD_CLIENT:
673 FD_SET_MAX(fds[i].fd, &rfds, maxfd);
674 break;
675 case FD_LISTENER:
676 FD_SET_MAX(fds[i].fd, &rfds, maxfd);
677 break;
678 case FD_CONNECTION:
679 /*
680 * Always read from a connection socket. Even
681 * after we've started writing, the peer might
682 * still be sending (e.g. because we shamefully
683 * jumped the gun before waiting for the end of
684 * the HTTP request) and so we should be prepared
685 * to read data and throw it away.
686 */
687 FD_SET_MAX(fds[i].fd, &rfds, maxfd);
688 /*
689 * Also attempt to write, if we have data to write.
690 */
691 if (fds[i].wdatapos < fds[i].wdatalen)
692 FD_SET_MAX(fds[i].fd, &wfds, maxfd);
693 break;
694 }
695
696 i++;
697 }
698 nfds = i;
699
700 ret = select(maxfd, SELECT_TYPE_ARG234 &rfds,
701 SELECT_TYPE_ARG234 &wfds, SELECT_TYPE_ARG234 NULL,
702 SELECT_TYPE_ARG5 NULL);
703 if (ret <= 0) {
704 if (ret < 0 && (errno != EINTR)) {
705 fprintf(stderr, "select: %s", strerror(errno));
706 exit(1);
707 }
708 continue;
709 }
710
711 for (i = 0; i < nfds; i++) {
712 switch (fds[i].type) {
713 case FD_CLIENT:
714 if (FD_ISSET(fds[i].fd, &rfds)) {
715 char buf[4096];
716 int ret = read(fds[i].fd, buf, sizeof(buf));
717 if (ret <= 0) {
718 if (ret < 0) {
719 fprintf(stderr, "standard input: read: %s\n",
720 strerror(errno));
721 exit(1);
722 }
723 return;
724 }
725 }
726 break;
727 case FD_LISTENER:
728 if (FD_ISSET(fds[i].fd, &rfds)) {
729 /*
730 * New connection has come in. Accept it.
731 */
732 struct fd *f;
733 struct sockaddr_in addr;
734 socklen_t addrlen = sizeof(addr);
735 int newfd = accept(fds[i].fd, (struct sockaddr *)&addr,
736 &addrlen);
737 if (newfd < 0)
738 break; /* not sure what happened there */
739
740 f = new_fdstruct(newfd, FD_CONNECTION);
741 f->cctx = new_connection(t);
742 if (authtype == HTTPD_AUTH_MAGIC)
743 check_magic_access(f);
744 }
745 break;
746 case FD_CONNECTION:
747 if (FD_ISSET(fds[i].fd, &rfds)) {
748 /*
749 * There's data to be read.
750 */
751 char readbuf[4096];
752 int ret;
753
754 ret = read(fds[i].fd, readbuf, sizeof(readbuf));
755 if (ret <= 0) {
756 /*
757 * This shouldn't happen in a sensible
758 * HTTP connection, so we abandon the
759 * connection if it does.
760 */
761 close(fds[i].fd);
762 fds[i].deleted = 1;
763 break;
764 } else {
765 if (!fds[i].wdata) {
766 /*
767 * If we haven't got an HTTP response
768 * yet, keep processing data in the
769 * hope of acquiring one.
770 */
771 fds[i].wdata = got_data
772 (fds[i].cctx, readbuf, ret,
773 (authtype == HTTPD_AUTH_NONE ||
774 fds[i].magic_access), authstring, &cfg);
775 if (fds[i].wdata) {
776 fds[i].wdatalen = strlen(fds[i].wdata);
777 fds[i].wdatapos = 0;
778 }
779 } else {
780 /*
781 * Otherwise, just drop our read data
782 * on the floor.
783 */
784 }
785 }
786 }
787 if (FD_ISSET(fds[i].fd, &wfds) &&
788 fds[i].wdatapos < fds[i].wdatalen) {
789 /*
790 * The socket is writable, and we have data to
791 * write. Write it.
792 */
793 int ret = write(fds[i].fd, fds[i].wdata + fds[i].wdatapos,
794 fds[i].wdatalen - fds[i].wdatapos);
795 if (ret <= 0) {
796 /*
797 * Shouldn't happen; abandon the connection.
798 */
799 close(fds[i].fd);
800 fds[i].deleted = 1;
801 break;
802 } else {
803 fds[i].wdatapos += ret;
804 if (fds[i].wdatapos == fds[i].wdatalen) {
805 shutdown(fds[i].fd, SHUT_WR);
806 }
807 }
808 }
809 break;
810 }
811 }
812
813 }
814 }