Add the --files option, to list individual files in the various
[sgt/agedu] / httpd.c
CommitLineData
70322ae3 1/*
2 * httpd.c: implementation of httpd.h.
3 */
4
bf53e756 5#include "agedu.h"
995db599 6#include "alloc.h"
70322ae3 7#include "html.h"
812e4bf2 8#include "httpd.h"
70322ae3 9
10/* --- Logic driving what the web server's responses are. --- */
11
812e4bf2 12enum { /* connctx states */
13 READING_REQ_LINE,
14 READING_HEADERS,
15 DONE
16};
17
70322ae3 18struct connctx {
19 const void *t;
20 char *data;
21 int datalen, datasize;
812e4bf2 22 char *method, *url, *headers, *auth;
23 int state;
70322ae3 24};
25
26/*
27 * Called when a new connection arrives on a listening socket.
28 * Returns a connctx for the new connection.
29 */
30struct 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;
812e4bf2 36 cctx->state = READING_REQ_LINE;
37 cctx->method = cctx->url = cctx->headers = cctx->auth = NULL;
70322ae3 38 return cctx;
39}
40
41void free_connection(struct connctx *cctx)
42{
43 sfree(cctx->data);
44 sfree(cctx);
45}
46
812e4bf2 47static char *http_error(char *code, char *errmsg, char *extraheader,
48 char *errtext, ...)
70322ae3 49{
50 return dupfmt("HTTP/1.1 %s %s\r\n"
51 "Date: %D\r\n"
bf53e756 52 "Server: " PNAME "\r\n"
70322ae3 53 "Connection: close\r\n"
812e4bf2 54 "%s"
70322ae3 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,
812e4bf2 64 extraheader ? extraheader : "",
70322ae3 65 code, errmsg, code, errmsg, errtext);
66}
67
68static 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"
bf53e756 73 "Server: " PNAME "\r\n"
70322ae3 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 */
812e4bf2 88char *got_data(struct connctx *ctx, char *data, int length,
f2e52893 89 int magic_access, const char *auth_string,
90 const struct html_config *cfg)
70322ae3 91{
812e4bf2 92 char *line, *p, *q, *r, *z1, *z2, c1, c2;
93 int auth_provided = 0, auth_correct = 0;
70322ae3 94 unsigned long index;
95 char *document, *ret;
96
812e4bf2 97 /*
98 * Add the data we've just received to our buffer.
99 */
70322ae3 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 /*
812e4bf2 108 * Gradually process the HTTP request as we receive it.
70322ae3 109 */
812e4bf2 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 */
70322ae3 116
812e4bf2 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 */
70322ae3 132
812e4bf2 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;
bf53e756 172 text = dupfmt("<code>" PNAME "</code> received the HTTP request"
812e4bf2 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 }
70322ae3 192 }
70322ae3 193
812e4bf2 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 }
70322ae3 210 }
211
812e4bf2 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 auth_provided = 1;
239 while (p < q && isspace((unsigned char)*p))
240 p++;
241 r = p;
242 while (p < q && !isspace((unsigned char)*p))
243 p++;
244 if (p < q) {
245 *p++ = '\0';
246 if (!strcasecmp(r, "Basic")) {
247 while (p < q && isspace((unsigned char)*p))
248 p++;
249 r = p;
250 while (p < q && !isspace((unsigned char)*p))
251 p++;
252 if (p < q) {
253 *p++ = '\0';
254 if (!strcmp(r, auth_string))
255 auth_correct = 1;
256 }
257 }
258 }
259 }
260 }
261
262 if (!magic_access && !auth_correct) {
5a830bf8 263 if (auth_string) {
812e4bf2 264 ret = http_error("401", "Unauthorized",
72bd16db 265 "WWW-Authenticate: Basic realm=\""PNAME"\"\r\n",
5a830bf8 266 "\nYou must authenticate to view these pages.");
812e4bf2 267 } else {
268 ret = http_error("403", "Forbidden", NULL,
269 "This is a restricted-access set of pages.");
270 }
70322ae3 271 } else {
14601b5d 272 char *q;
812e4bf2 273 p = ctx->url;
274 p += strspn(p, "/?");
14601b5d 275 index = strtoul(p, &q, 10);
276 if (*q) {
812e4bf2 277 ret = http_error("404", "Not Found", NULL,
14601b5d 278 "This is not a valid pathname index.");
279 } else {
280 document = html_query(ctx->t, index, cfg);
281 if (document) {
282 ret = http_success("text/html", 1, document);
283 sfree(document);
284 } else {
285 ret = http_error("404", "Not Found", NULL,
286 "Pathname index out of range.");
287 }
812e4bf2 288 }
70322ae3 289 }
812e4bf2 290 return ret;
291 } else
292 return NULL;
70322ae3 293}
294
295/* --- Platform support for running a web server. --- */
296
297enum { FD_CLIENT, FD_LISTENER, FD_CONNECTION };
298
299struct fd {
300 int fd;
301 int type;
302 int deleted;
303 char *wdata;
304 int wdatalen, wdatapos;
305 int magic_access;
306 struct connctx *cctx;
307};
308
309struct fd *fds = NULL;
310int nfds = 0, fdsize = 0;
311
312struct fd *new_fdstruct(int fd, int type)
313{
314 struct fd *ret;
315
316 if (nfds >= fdsize) {
317 fdsize = nfds * 3 / 2 + 32;
318 fds = sresize(fds, fdsize, struct fd);
319 }
320
321 ret = &fds[nfds++];
322
323 ret->fd = fd;
324 ret->type = type;
325 ret->wdata = NULL;
326 ret->wdatalen = ret->wdatapos = 0;
327 ret->cctx = NULL;
328 ret->deleted = 0;
329 ret->magic_access = 0;
330
331 return ret;
332}
333
812e4bf2 334int check_owning_uid(int fd, int flip)
70322ae3 335{
336 struct sockaddr_in sock, peer;
337 socklen_t addrlen;
338 char linebuf[4096], matchbuf[80];
339 FILE *fp;
340
341 addrlen = sizeof(sock);
812e4bf2 342 if (getsockname(fd, (struct sockaddr *)&sock, &addrlen)) {
70322ae3 343 fprintf(stderr, "getsockname: %s\n", strerror(errno));
344 exit(1);
345 }
346 addrlen = sizeof(peer);
812e4bf2 347 if (getpeername(fd, (struct sockaddr *)&peer, &addrlen)) {
348 if (errno == ENOTCONN) {
349 peer.sin_addr.s_addr = htonl(0);
350 peer.sin_port = htons(0);
351 } else {
352 fprintf(stderr, "getpeername: %s\n", strerror(errno));
353 exit(1);
354 }
355 }
356
357 if (flip) {
358 struct sockaddr_in tmp = sock;
359 sock = peer;
360 peer = tmp;
70322ae3 361 }
362
363 sprintf(matchbuf, "%08X:%04X %08X:%04X",
364 peer.sin_addr.s_addr, ntohs(peer.sin_port),
365 sock.sin_addr.s_addr, ntohs(sock.sin_port));
366 fp = fopen("/proc/net/tcp", "r");
367 if (fp) {
368 while (fgets(linebuf, sizeof(linebuf), fp)) {
369 if (strlen(linebuf) >= 75 &&
370 !strncmp(linebuf+6, matchbuf, strlen(matchbuf))) {
14601b5d 371 fclose(fp);
812e4bf2 372 return atoi(linebuf + 75);
70322ae3 373 }
374 }
14601b5d 375 fclose(fp);
70322ae3 376 }
812e4bf2 377
378 return -1;
379}
380
381void check_magic_access(struct fd *fd)
382{
383 if (check_owning_uid(fd->fd, 0) == getuid())
384 fd->magic_access = 1;
70322ae3 385}
386
812e4bf2 387static void base64_encode_atom(unsigned char *data, int n, char *out)
388{
389 static const char base64_chars[] =
390 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
391
392 unsigned word;
393
394 word = data[0] << 16;
395 if (n > 1)
396 word |= data[1] << 8;
397 if (n > 2)
398 word |= data[2];
399 out[0] = base64_chars[(word >> 18) & 0x3F];
400 out[1] = base64_chars[(word >> 12) & 0x3F];
401 if (n > 1)
402 out[2] = base64_chars[(word >> 6) & 0x3F];
403 else
404 out[2] = '=';
405 if (n > 2)
406 out[3] = base64_chars[word & 0x3F];
407 else
408 out[3] = '=';
409}
410
1e8d78b9 411void run_httpd(const void *t, int authmask, const struct httpd_config *dcfg,
412 const struct html_config *incfg)
70322ae3 413{
15e73840 414 int fd, ret;
812e4bf2 415 int authtype;
1e8d78b9 416 char *authstring = NULL;
70322ae3 417 struct fd *f;
418 struct sockaddr_in addr;
419 socklen_t addrlen;
f2e52893 420 struct html_config cfg = *incfg;
421
cfe942fb 422 cfg.format = "%.0lu";
70322ae3 423
424 /*
425 * Establish the listening socket and retrieve its port
426 * number.
427 */
428 fd = socket(PF_INET, SOCK_STREAM, 0);
429 if (fd < 0) {
430 fprintf(stderr, "socket(PF_INET): %s\n", strerror(errno));
431 exit(1);
432 }
433 addr.sin_family = AF_INET;
1e8d78b9 434 if (!dcfg->address) {
14557c01 435#ifdef RANDOM_LOCALHOST
436 unsigned long ipaddr;
1e8d78b9 437 srand(0L);
438 ipaddr = 0x7f000000;
439 ipaddr += (1 + rand() % 255) << 16;
440 ipaddr += (1 + rand() % 255) << 8;
441 ipaddr += (1 + rand() % 255);
442 addr.sin_addr.s_addr = htonl(ipaddr);
14557c01 443#else
444 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
445#endif
1e8d78b9 446 addr.sin_port = htons(0);
447 } else {
448 addr.sin_addr.s_addr = inet_addr(dcfg->address);
15e73840 449 addr.sin_port = dcfg->port ? htons(dcfg->port) : 0;
1e8d78b9 450 }
70322ae3 451 addrlen = sizeof(addr);
15e73840 452 ret = bind(fd, (const struct sockaddr *)&addr, addrlen);
14557c01 453#ifdef RANDOM_LOCALHOST
15e73840 454 if (ret < 0 && errno == EADDRNOTAVAIL && !dcfg->address) {
455 /*
456 * Some systems don't like us binding to random weird
457 * localhost-space addresses. Try again with the official
458 * INADDR_LOOPBACK.
459 */
460 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
461 addr.sin_port = htons(0);
462 ret = bind(fd, (const struct sockaddr *)&addr, addrlen);
463 }
14557c01 464#endif
15e73840 465 if (ret < 0) {
70322ae3 466 fprintf(stderr, "bind: %s\n", strerror(errno));
467 exit(1);
468 }
469 if (listen(fd, 5) < 0) {
470 fprintf(stderr, "listen: %s\n", strerror(errno));
471 exit(1);
472 }
473 addrlen = sizeof(addr);
474 if (getsockname(fd, (struct sockaddr *)&addr, &addrlen)) {
475 fprintf(stderr, "getsockname: %s\n", strerror(errno));
476 exit(1);
477 }
812e4bf2 478 if ((authmask & HTTPD_AUTH_MAGIC) &&
479 (check_owning_uid(fd, 1) == getuid())) {
480 authtype = HTTPD_AUTH_MAGIC;
1e8d78b9 481 if (authmask != HTTPD_AUTH_MAGIC)
482 printf("Using Linux /proc/net magic authentication\n");
812e4bf2 483 } else if ((authmask & HTTPD_AUTH_BASIC)) {
1e8d78b9 484 char username[128], password[128], userpassbuf[259];
485 const char *userpass;
812e4bf2 486 const char *rname;
487 unsigned char passbuf[10];
488 int i, j, k, fd;
489
490 authtype = HTTPD_AUTH_BASIC;
491
1e8d78b9 492 if (authmask != HTTPD_AUTH_BASIC)
493 printf("Using HTTP Basic authentication\n");
494
495 if (dcfg->basicauthdata) {
496 userpass = dcfg->basicauthdata;
497 } else {
bf53e756 498 strcpy(username, PNAME);
1e8d78b9 499 rname = "/dev/urandom";
812e4bf2 500 fd = open(rname, O_RDONLY);
501 if (fd < 0) {
1e8d78b9 502 int err = errno;
503 rname = "/dev/random";
504 fd = open(rname, O_RDONLY);
505 if (fd < 0) {
506 int err2 = errno;
507 fprintf(stderr, "/dev/urandom: open: %s\n", strerror(err));
508 fprintf(stderr, "/dev/random: open: %s\n", strerror(err2));
509 exit(1);
510 }
812e4bf2 511 }
1e8d78b9 512 for (i = 0; i < 10 ;) {
513 j = read(fd, passbuf + i, 10 - i);
514 if (j <= 0) {
515 fprintf(stderr, "%s: read: %s\n", rname,
516 j < 0 ? strerror(errno) : "unexpected EOF");
517 exit(1);
518 }
519 i += j;
812e4bf2 520 }
1e8d78b9 521 close(fd);
522 for (i = 0; i < 16; i++) {
523 /*
524 * 32 characters out of the 36 alphanumerics gives
525 * me the latitude to discard i,l,o for being too
526 * numeric-looking, and w because it has two too
527 * many syllables and one too many presidential
528 * associations.
529 */
530 static const char chars[32] =
531 "0123456789abcdefghjkmnpqrstuvxyz";
532 int v = 0;
533
534 k = i / 8 * 5;
535 for (j = 0; j < 5; j++)
536 v |= ((passbuf[k+j] >> (i%8)) & 1) << j;
537
538 password[i] = chars[v];
539 }
540 password[i] = '\0';
541
542 sprintf(userpassbuf, "%s:%s", username, password);
543 userpass = userpassbuf;
812e4bf2 544
1e8d78b9 545 printf("Username: %s\nPassword: %s\n", username, password);
546 }
812e4bf2 547
1e8d78b9 548 k = strlen(userpass);
549 authstring = snewn(k * 4 / 3 + 16, char);
812e4bf2 550 for (i = j = 0; i < k ;) {
551 int s = k-i < 3 ? k-i : 3;
1e8d78b9 552 base64_encode_atom((unsigned char *)(userpass+i), s, authstring+j);
812e4bf2 553 i += s;
554 j += 4;
555 }
1e8d78b9 556 authstring[j] = '\0';
557 } else if ((authmask & HTTPD_AUTH_NONE)) {
812e4bf2 558 authtype = HTTPD_AUTH_NONE;
1e8d78b9 559 if (authmask != HTTPD_AUTH_NONE)
560 printf("Web server is unauthenticated\n");
561 } else {
bf53e756 562 fprintf(stderr, PNAME ": authentication method not supported\n");
1e8d78b9 563 exit(1);
564 }
15e73840 565 if (ntohs(addr.sin_addr.s_addr) == INADDR_ANY) {
566 printf("Server port: %d\n", ntohs(addr.sin_port));
567 } else if (ntohs(addr.sin_port) == 80) {
568 printf("URL: http://%s/\n", inet_ntoa(addr.sin_addr));
569 } else {
570 printf("URL: http://%s:%d/\n",
571 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
812e4bf2 572 }
70322ae3 573
574 /*
575 * Now construct an fd structure to hold it.
576 */
577 f = new_fdstruct(fd, FD_LISTENER);
578
579 /*
580 * Read from standard input, and treat EOF as a notification
581 * to exit.
582 */
583 new_fdstruct(0, FD_CLIENT);
584
585 /*
586 * Now we're ready to run our main loop. Keep looping round on
587 * select.
588 */
589 while (1) {
590 fd_set rfds, wfds;
50e82fdc 591 int i, j;
592 SELECT_TYPE_ARG1 maxfd;
593 int ret;
70322ae3 594
595#define FD_SET_MAX(fd, set, max) \
596 do { FD_SET((fd),(set)); (max) = ((max)<=(fd)?(fd)+1:(max)); } while(0)
597
598 /*
599 * Loop round the fd list putting fds into our select
600 * sets. Also in this loop we remove any that were marked
601 * as deleted in the previous loop.
602 */
603 FD_ZERO(&rfds);
604 FD_ZERO(&wfds);
605 maxfd = 0;
606 for (i = j = 0; j < nfds; j++) {
607
608 if (fds[j].deleted) {
609 sfree(fds[j].wdata);
610 free_connection(fds[j].cctx);
611 continue;
612 }
613 fds[i] = fds[j];
614
615 switch (fds[i].type) {
616 case FD_CLIENT:
15e73840 617 FD_SET_MAX(fds[i].fd, &rfds, maxfd);
618 break;
70322ae3 619 case FD_LISTENER:
620 FD_SET_MAX(fds[i].fd, &rfds, maxfd);
621 break;
622 case FD_CONNECTION:
623 /*
624 * Always read from a connection socket. Even
625 * after we've started writing, the peer might
626 * still be sending (e.g. because we shamefully
627 * jumped the gun before waiting for the end of
628 * the HTTP request) and so we should be prepared
629 * to read data and throw it away.
630 */
631 FD_SET_MAX(fds[i].fd, &rfds, maxfd);
632 /*
633 * Also attempt to write, if we have data to write.
634 */
635 if (fds[i].wdatapos < fds[i].wdatalen)
636 FD_SET_MAX(fds[i].fd, &wfds, maxfd);
637 break;
638 }
639
640 i++;
641 }
642 nfds = i;
643
50e82fdc 644 ret = select(maxfd, SELECT_TYPE_ARG234 &rfds,
645 SELECT_TYPE_ARG234 &wfds, SELECT_TYPE_ARG234 NULL,
646 SELECT_TYPE_ARG5 NULL);
70322ae3 647 if (ret <= 0) {
648 if (ret < 0 && (errno != EINTR)) {
649 fprintf(stderr, "select: %s", strerror(errno));
650 exit(1);
651 }
652 continue;
653 }
654
655 for (i = 0; i < nfds; i++) {
656 switch (fds[i].type) {
657 case FD_CLIENT:
658 if (FD_ISSET(fds[i].fd, &rfds)) {
659 char buf[4096];
660 int ret = read(fds[i].fd, buf, sizeof(buf));
661 if (ret <= 0) {
662 if (ret < 0) {
663 fprintf(stderr, "standard input: read: %s\n",
664 strerror(errno));
665 exit(1);
666 }
667 return;
668 }
669 }
670 break;
671 case FD_LISTENER:
672 if (FD_ISSET(fds[i].fd, &rfds)) {
673 /*
674 * New connection has come in. Accept it.
675 */
676 struct fd *f;
677 struct sockaddr_in addr;
678 socklen_t addrlen = sizeof(addr);
679 int newfd = accept(fds[i].fd, (struct sockaddr *)&addr,
680 &addrlen);
681 if (newfd < 0)
682 break; /* not sure what happened there */
683
684 f = new_fdstruct(newfd, FD_CONNECTION);
685 f->cctx = new_connection(t);
812e4bf2 686 if (authtype == HTTPD_AUTH_MAGIC)
687 check_magic_access(f);
70322ae3 688 }
689 break;
690 case FD_CONNECTION:
691 if (FD_ISSET(fds[i].fd, &rfds)) {
692 /*
693 * There's data to be read.
694 */
695 char readbuf[4096];
696 int ret;
697
698 ret = read(fds[i].fd, readbuf, sizeof(readbuf));
699 if (ret <= 0) {
700 /*
701 * This shouldn't happen in a sensible
702 * HTTP connection, so we abandon the
703 * connection if it does.
704 */
705 close(fds[i].fd);
706 fds[i].deleted = 1;
707 break;
708 } else {
709 if (!fds[i].wdata) {
710 /*
711 * If we haven't got an HTTP response
712 * yet, keep processing data in the
713 * hope of acquiring one.
714 */
812e4bf2 715 fds[i].wdata = got_data
716 (fds[i].cctx, readbuf, ret,
717 (authtype == HTTPD_AUTH_NONE ||
f2e52893 718 fds[i].magic_access), authstring, &cfg);
70322ae3 719 if (fds[i].wdata) {
720 fds[i].wdatalen = strlen(fds[i].wdata);
721 fds[i].wdatapos = 0;
722 }
723 } else {
724 /*
725 * Otherwise, just drop our read data
726 * on the floor.
727 */
728 }
729 }
730 }
731 if (FD_ISSET(fds[i].fd, &wfds) &&
732 fds[i].wdatapos < fds[i].wdatalen) {
733 /*
734 * The socket is writable, and we have data to
735 * write. Write it.
736 */
737 int ret = write(fds[i].fd, fds[i].wdata + fds[i].wdatapos,
738 fds[i].wdatalen - fds[i].wdatapos);
739 if (ret <= 0) {
740 /*
741 * Shouldn't happen; abandon the connection.
742 */
743 close(fds[i].fd);
744 fds[i].deleted = 1;
745 break;
746 } else {
747 fds[i].wdatapos += ret;
748 if (fds[i].wdatapos == fds[i].wdatalen) {
749 shutdown(fds[i].fd, SHUT_WR);
750 }
751 }
752 }
753 break;
754 }
755 }
756
757 }
758}