Annotation of embedaddon/ntp/lib/isc/httpd.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2006-2008 Internet Systems Consortium, Inc. ("ISC")
! 3: *
! 4: * Permission to use, copy, modify, and/or distribute this software for any
! 5: * purpose with or without fee is hereby granted, provided that the above
! 6: * copyright notice and this permission notice appear in all copies.
! 7: *
! 8: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
! 9: * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
! 10: * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
! 11: * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
! 12: * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
! 13: * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
! 14: * PERFORMANCE OF THIS SOFTWARE.
! 15: */
! 16:
! 17: /* $Id: httpd.c,v 1.16 2008/08/08 05:06:49 marka Exp $ */
! 18:
! 19: /*! \file */
! 20:
! 21: #include <config.h>
! 22:
! 23: #include <isc/buffer.h>
! 24: #include <isc/httpd.h>
! 25: #include <isc/mem.h>
! 26: #include <isc/socket.h>
! 27: #include <isc/string.h>
! 28: #include <isc/task.h>
! 29: #include <isc/util.h>
! 30:
! 31: #include <string.h>
! 32:
! 33: /*%
! 34: * TODO:
! 35: *
! 36: * o Put in better checks to make certain things are passed in correctly.
! 37: * This includes a magic number for externally-visible structures,
! 38: * checking for NULL-ness before dereferencing, etc.
! 39: * o Make the URL processing external functions which will fill-in a buffer
! 40: * structure we provide, or return an error and we will render a generic
! 41: * page and close the client.
! 42: */
! 43:
! 44: #define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0)
! 45: #define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN)
! 46:
! 47: #ifdef DEBUG_HTTPD
! 48: #define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (0)
! 49: #define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (0)
! 50: #define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (0)
! 51: #else
! 52: #define ENTER(x) do { } while(0)
! 53: #define EXIT(x) do { } while(0)
! 54: #define NOTICE(x) do { } while(0)
! 55: #endif
! 56:
! 57: #define HTTP_RECVLEN 1024
! 58: #define HTTP_SENDGROW 1024
! 59: #define HTTP_SEND_MAXLEN 10240
! 60:
! 61: /*%
! 62: * HTTP urls. These are the URLs we manage, and the function to call to
! 63: * provide the data for it. We pass in the base url (so the same function
! 64: * can handle multiple requests), and a structure to fill in to return a
! 65: * result to the client. We also pass in a pointer to be filled in for
! 66: * the data cleanup function.
! 67: */
! 68: struct isc_httpdurl {
! 69: char *url;
! 70: isc_httpdaction_t *action;
! 71: void *action_arg;
! 72: ISC_LINK(isc_httpdurl_t) link;
! 73: };
! 74:
! 75: #define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
! 76: #define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
! 77:
! 78: /*% http client */
! 79: struct isc_httpd {
! 80: isc_httpdmgr_t *mgr; /*%< our parent */
! 81: ISC_LINK(isc_httpd_t) link;
! 82: unsigned int state;
! 83: isc_socket_t *sock;
! 84:
! 85: /*%
! 86: * Received data state.
! 87: */
! 88: char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
! 89: isc_uint32_t recvlen; /*%< length recv'd */
! 90: unsigned int method;
! 91: char *url;
! 92: char *querystring;
! 93: char *protocol;
! 94:
! 95: /*
! 96: * Flags on the httpd client.
! 97: */
! 98: int flags;
! 99:
! 100: /*%
! 101: * Transmit data state.
! 102: *
! 103: * This is the data buffer we will transmit.
! 104: *
! 105: * This free function pointer is filled in by the rendering function
! 106: * we call. The free function is called after the data is transmitted
! 107: * to the client.
! 108: *
! 109: * The bufflist is the list of buffers we are currently transmitting.
! 110: * The headerdata is where we render our headers to. If we run out of
! 111: * space when rendering a header, we will change the size of our
! 112: * buffer. We will not free it until we are finished, and will
! 113: * allocate an additional HTTP_SENDGROW bytes per header space grow.
! 114: *
! 115: * We currently use two buffers total, one for the headers (which
! 116: * we manage) and another for the client to fill in (which it manages,
! 117: * it provides the space for it, etc) -- we will pass that buffer
! 118: * structure back to the caller, who is responsible for managing the
! 119: * space it may have allocated as backing store for it. This second
! 120: * buffer is bodybuffer, and we only allocate the buffer itself, not
! 121: * the backing store.
! 122: */
! 123: isc_bufferlist_t bufflist;
! 124: char *headerdata; /*%< send header buf */
! 125: unsigned int headerlen; /*%< current header buffer size */
! 126: isc_buffer_t headerbuffer;
! 127:
! 128: const char *mimetype;
! 129: unsigned int retcode;
! 130: const char *retmsg;
! 131: isc_buffer_t bodybuffer;
! 132: isc_httpdfree_t *freecb;
! 133: void *freecb_arg;
! 134: };
! 135:
! 136: /*% lightweight socket manager for httpd output */
! 137: struct isc_httpdmgr {
! 138: isc_mem_t *mctx;
! 139: isc_socket_t *sock; /*%< listening socket */
! 140: isc_task_t *task; /*%< owning task */
! 141: isc_timermgr_t *timermgr;
! 142:
! 143: isc_httpdclientok_t *client_ok; /*%< client validator */
! 144: isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */
! 145: void *cb_arg; /*%< argument for the above */
! 146:
! 147: unsigned int flags;
! 148: ISC_LIST(isc_httpd_t) running; /*%< running clients */
! 149:
! 150: isc_mutex_t lock;
! 151:
! 152: ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */
! 153: isc_httpdaction_t *render_404;
! 154: };
! 155:
! 156: /*%
! 157: * HTTP methods.
! 158: */
! 159: #define ISC_HTTPD_METHODUNKNOWN 0
! 160: #define ISC_HTTPD_METHODGET 1
! 161: #define ISC_HTTPD_METHODPOST 2
! 162:
! 163: /*%
! 164: * Client states.
! 165: *
! 166: * _IDLE The client is not doing anything at all. This state should
! 167: * only occur just after creation, and just before being
! 168: * destroyed.
! 169: *
! 170: * _RECV The client is waiting for data after issuing a socket recv().
! 171: *
! 172: * _RECVDONE Data has been received, and is being processed.
! 173: *
! 174: * _SEND All data for a response has completed, and a reply was
! 175: * sent via a socket send() call.
! 176: *
! 177: * _SENDDONE Send is completed.
! 178: *
! 179: * Badly formatted state table:
! 180: *
! 181: * IDLE -> RECV when client has a recv() queued.
! 182: *
! 183: * RECV -> RECVDONE when recvdone event received.
! 184: *
! 185: * RECVDONE -> SEND if the data for a reply is at hand.
! 186: *
! 187: * SEND -> RECV when a senddone event was received.
! 188: *
! 189: * At any time -> RECV on error. If RECV fails, the client will
! 190: * self-destroy, closing the socket and freeing memory.
! 191: */
! 192: #define ISC_HTTPD_STATEIDLE 0
! 193: #define ISC_HTTPD_STATERECV 1
! 194: #define ISC_HTTPD_STATERECVDONE 2
! 195: #define ISC_HTTPD_STATESEND 3
! 196: #define ISC_HTTPD_STATESENDDONE 4
! 197:
! 198: #define ISC_HTTPD_ISRECV(c) ((c)->state == ISC_HTTPD_STATERECV)
! 199: #define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE)
! 200: #define ISC_HTTPD_ISSEND(c) ((c)->state == ISC_HTTPD_STATESEND)
! 201: #define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE)
! 202:
! 203: /*%
! 204: * Overall magic test that means we're not idle.
! 205: */
! 206: #define ISC_HTTPD_SETRECV(c) ((c)->state = ISC_HTTPD_STATERECV)
! 207: #define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE)
! 208: #define ISC_HTTPD_SETSEND(c) ((c)->state = ISC_HTTPD_STATESEND)
! 209: #define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE)
! 210:
! 211: static void isc_httpd_accept(isc_task_t *, isc_event_t *);
! 212: static void isc_httpd_recvdone(isc_task_t *, isc_event_t *);
! 213: static void isc_httpd_senddone(isc_task_t *, isc_event_t *);
! 214: static void destroy_client(isc_httpd_t **);
! 215: static isc_result_t process_request(isc_httpd_t *, int);
! 216: static void httpdmgr_destroy(isc_httpdmgr_t *);
! 217: static isc_result_t grow_headerspace(isc_httpd_t *);
! 218: static void reset_client(isc_httpd_t *httpd);
! 219: static isc_result_t render_404(const char *, const char *,
! 220: void *,
! 221: unsigned int *, const char **,
! 222: const char **, isc_buffer_t *,
! 223: isc_httpdfree_t **, void **);
! 224:
! 225: static void
! 226: destroy_client(isc_httpd_t **httpdp)
! 227: {
! 228: isc_httpd_t *httpd = *httpdp;
! 229: isc_httpdmgr_t *httpdmgr = httpd->mgr;
! 230:
! 231: *httpdp = NULL;
! 232:
! 233: LOCK(&httpdmgr->lock);
! 234:
! 235: isc_socket_detach(&httpd->sock);
! 236: ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
! 237:
! 238: if (httpd->headerlen > 0)
! 239: isc_mem_put(httpdmgr->mctx, httpd->headerdata,
! 240: httpd->headerlen);
! 241:
! 242: isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
! 243:
! 244: UNLOCK(&httpdmgr->lock);
! 245:
! 246: httpdmgr_destroy(httpdmgr);
! 247: }
! 248:
! 249: isc_result_t
! 250: isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task,
! 251: isc_httpdclientok_t *client_ok,
! 252: isc_httpdondestroy_t *ondestroy, void *cb_arg,
! 253: isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp)
! 254: {
! 255: isc_result_t result;
! 256: isc_httpdmgr_t *httpd;
! 257:
! 258: REQUIRE(mctx != NULL);
! 259: REQUIRE(sock != NULL);
! 260: REQUIRE(task != NULL);
! 261: REQUIRE(tmgr != NULL);
! 262: REQUIRE(httpdp != NULL && *httpdp == NULL);
! 263:
! 264: httpd = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
! 265: if (httpd == NULL)
! 266: return (ISC_R_NOMEMORY);
! 267:
! 268: result = isc_mutex_init(&httpd->lock);
! 269: if (result != ISC_R_SUCCESS) {
! 270: isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
! 271: return (result);
! 272: }
! 273: httpd->mctx = NULL;
! 274: isc_mem_attach(mctx, &httpd->mctx);
! 275: httpd->sock = NULL;
! 276: isc_socket_attach(sock, &httpd->sock);
! 277: httpd->task = NULL;
! 278: isc_task_attach(task, &httpd->task);
! 279: httpd->timermgr = tmgr; /* XXXMLG no attach function? */
! 280: httpd->client_ok = client_ok;
! 281: httpd->ondestroy = ondestroy;
! 282: httpd->cb_arg = cb_arg;
! 283:
! 284: ISC_LIST_INIT(httpd->running);
! 285: ISC_LIST_INIT(httpd->urls);
! 286:
! 287: /* XXXMLG ignore errors on isc_socket_listen() */
! 288: result = isc_socket_listen(sock, SOMAXCONN);
! 289: if (result != ISC_R_SUCCESS) {
! 290: UNEXPECTED_ERROR(__FILE__, __LINE__,
! 291: "isc_socket_listen() failed: %s",
! 292: isc_result_totext(result));
! 293: goto cleanup;
! 294: }
! 295:
! 296: (void)isc_socket_filter(sock, "httpready");
! 297:
! 298: result = isc_socket_accept(sock, task, isc_httpd_accept, httpd);
! 299: if (result != ISC_R_SUCCESS)
! 300: goto cleanup;
! 301:
! 302: httpd->render_404 = render_404;
! 303:
! 304: *httpdp = httpd;
! 305: return (ISC_R_SUCCESS);
! 306:
! 307: cleanup:
! 308: isc_task_detach(&httpd->task);
! 309: isc_socket_detach(&httpd->sock);
! 310: isc_mem_detach(&httpd->mctx);
! 311: isc_mutex_destroy(&httpd->lock);
! 312: isc_mem_put(mctx, httpd, sizeof(isc_httpdmgr_t));
! 313: return (result);
! 314: }
! 315:
! 316: static void
! 317: httpdmgr_destroy(isc_httpdmgr_t *httpdmgr)
! 318: {
! 319: isc_mem_t *mctx;
! 320: isc_httpdurl_t *url;
! 321:
! 322: ENTER("httpdmgr_destroy");
! 323:
! 324: LOCK(&httpdmgr->lock);
! 325:
! 326: if (!MSHUTTINGDOWN(httpdmgr)) {
! 327: NOTICE("httpdmgr_destroy not shutting down yet");
! 328: UNLOCK(&httpdmgr->lock);
! 329: return;
! 330: }
! 331:
! 332: /*
! 333: * If all clients are not shut down, don't do anything yet.
! 334: */
! 335: if (!ISC_LIST_EMPTY(httpdmgr->running)) {
! 336: NOTICE("httpdmgr_destroy clients still active");
! 337: UNLOCK(&httpdmgr->lock);
! 338: return;
! 339: }
! 340:
! 341: NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
! 342:
! 343: isc_socket_detach(&httpdmgr->sock);
! 344: isc_task_detach(&httpdmgr->task);
! 345: httpdmgr->timermgr = NULL;
! 346:
! 347: /*
! 348: * Clear out the list of all actions we know about. Just free the
! 349: * memory.
! 350: */
! 351: url = ISC_LIST_HEAD(httpdmgr->urls);
! 352: while (url != NULL) {
! 353: isc_mem_free(httpdmgr->mctx, url->url);
! 354: ISC_LIST_UNLINK(httpdmgr->urls, url, link);
! 355: isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
! 356: url = ISC_LIST_HEAD(httpdmgr->urls);
! 357: }
! 358:
! 359: UNLOCK(&httpdmgr->lock);
! 360: isc_mutex_destroy(&httpdmgr->lock);
! 361:
! 362: if (httpdmgr->ondestroy != NULL)
! 363: (httpdmgr->ondestroy)(httpdmgr->cb_arg);
! 364:
! 365: mctx = httpdmgr->mctx;
! 366: isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
! 367:
! 368: EXIT("httpdmgr_destroy");
! 369: }
! 370:
! 371: #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
! 372: #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
! 373:
! 374: static isc_result_t
! 375: process_request(isc_httpd_t *httpd, int length)
! 376: {
! 377: char *s;
! 378: char *p;
! 379: int delim;
! 380:
! 381: ENTER("request");
! 382:
! 383: httpd->recvlen += length;
! 384:
! 385: httpd->recvbuf[httpd->recvlen] = 0;
! 386:
! 387: /*
! 388: * If we don't find a blank line in our buffer, return that we need
! 389: * more data.
! 390: */
! 391: s = strstr(httpd->recvbuf, "\r\n\r\n");
! 392: delim = 1;
! 393: if (s == NULL) {
! 394: s = strstr(httpd->recvbuf, "\n\n");
! 395: delim = 2;
! 396: }
! 397: if (s == NULL)
! 398: return (ISC_R_NOTFOUND);
! 399:
! 400: /*
! 401: * Determine if this is a POST or GET method. Any other values will
! 402: * cause an error to be returned.
! 403: */
! 404: if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
! 405: httpd->method = ISC_HTTPD_METHODGET;
! 406: p = httpd->recvbuf + 4;
! 407: } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
! 408: httpd->method = ISC_HTTPD_METHODPOST;
! 409: p = httpd->recvbuf + 5;
! 410: } else {
! 411: return (ISC_R_RANGE);
! 412: }
! 413:
! 414: /*
! 415: * From now on, p is the start of our buffer.
! 416: */
! 417:
! 418: /*
! 419: * Extract the URL.
! 420: */
! 421: s = p;
! 422: while (LENGTHOK(s) && BUFLENOK(s) &&
! 423: (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
! 424: s++;
! 425: if (!LENGTHOK(s))
! 426: return (ISC_R_NOTFOUND);
! 427: if (!BUFLENOK(s))
! 428: return (ISC_R_NOMEMORY);
! 429: *s = 0;
! 430:
! 431: /*
! 432: * Make the URL relative.
! 433: */
! 434: if ((strncmp(p, "http:/", 6) == 0)
! 435: || (strncmp(p, "https:/", 7) == 0)) {
! 436: /* Skip first / */
! 437: while (*p != '/' && *p != 0)
! 438: p++;
! 439: if (*p == 0)
! 440: return (ISC_R_RANGE);
! 441: p++;
! 442: /* Skip second / */
! 443: while (*p != '/' && *p != 0)
! 444: p++;
! 445: if (*p == 0)
! 446: return (ISC_R_RANGE);
! 447: p++;
! 448: /* Find third / */
! 449: while (*p != '/' && *p != 0)
! 450: p++;
! 451: if (*p == 0) {
! 452: p--;
! 453: *p = '/';
! 454: }
! 455: }
! 456:
! 457: httpd->url = p;
! 458: p = s + delim;
! 459: s = p;
! 460:
! 461: /*
! 462: * Now, see if there is a ? mark in the URL. If so, this is
! 463: * part of the query string, and we will split it from the URL.
! 464: */
! 465: httpd->querystring = strchr(httpd->url, '?');
! 466: if (httpd->querystring != NULL) {
! 467: *(httpd->querystring) = 0;
! 468: httpd->querystring++;
! 469: }
! 470:
! 471: /*
! 472: * Extract the HTTP/1.X protocol. We will bounce on anything but
! 473: * HTTP/1.1 for now.
! 474: */
! 475: while (LENGTHOK(s) && BUFLENOK(s) &&
! 476: (*s != '\n' && *s != '\r' && *s != '\0'))
! 477: s++;
! 478: if (!LENGTHOK(s))
! 479: return (ISC_R_NOTFOUND);
! 480: if (!BUFLENOK(s))
! 481: return (ISC_R_NOMEMORY);
! 482: *s = 0;
! 483: if ((strncmp(p, "HTTP/1.0", 8) != 0)
! 484: && (strncmp(p, "HTTP/1.1", 8) != 0))
! 485: return (ISC_R_RANGE);
! 486: httpd->protocol = p;
! 487: p = s + 1;
! 488: s = p;
! 489:
! 490: if (strstr(s, "Connection: close") != NULL)
! 491: httpd->flags |= HTTPD_CLOSE;
! 492:
! 493: if (strstr(s, "Host: ") != NULL)
! 494: httpd->flags |= HTTPD_FOUNDHOST;
! 495:
! 496: /*
! 497: * Standards compliance hooks here.
! 498: */
! 499: if (strcmp(httpd->protocol, "HTTP/1.1") == 0
! 500: && ((httpd->flags & HTTPD_FOUNDHOST) == 0))
! 501: return (ISC_R_RANGE);
! 502:
! 503: EXIT("request");
! 504:
! 505: return (ISC_R_SUCCESS);
! 506: }
! 507:
! 508: static void
! 509: isc_httpd_accept(isc_task_t *task, isc_event_t *ev)
! 510: {
! 511: isc_result_t result;
! 512: isc_httpdmgr_t *httpdmgr = ev->ev_arg;
! 513: isc_httpd_t *httpd;
! 514: isc_region_t r;
! 515: isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev;
! 516: isc_sockaddr_t peeraddr;
! 517:
! 518: ENTER("accept");
! 519:
! 520: LOCK(&httpdmgr->lock);
! 521: if (MSHUTTINGDOWN(httpdmgr)) {
! 522: NOTICE("accept shutting down, goto out");
! 523: goto out;
! 524: }
! 525:
! 526: if (nev->result == ISC_R_CANCELED) {
! 527: NOTICE("accept canceled, goto out");
! 528: goto out;
! 529: }
! 530:
! 531: if (nev->result != ISC_R_SUCCESS) {
! 532: /* XXXMLG log failure */
! 533: NOTICE("accept returned failure, goto requeue");
! 534: goto requeue;
! 535: }
! 536:
! 537: (void)isc_socket_getpeername(nev->newsocket, &peeraddr);
! 538: if (httpdmgr->client_ok != NULL &&
! 539: !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) {
! 540: isc_socket_detach(&nev->newsocket);
! 541: goto requeue;
! 542: }
! 543:
! 544: httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
! 545: if (httpd == NULL) {
! 546: /* XXXMLG log failure */
! 547: NOTICE("accept failed to allocate memory, goto requeue");
! 548: isc_socket_detach(&nev->newsocket);
! 549: goto requeue;
! 550: }
! 551:
! 552: httpd->mgr = httpdmgr;
! 553: ISC_LINK_INIT(httpd, link);
! 554: ISC_LIST_APPEND(httpdmgr->running, httpd, link);
! 555: ISC_HTTPD_SETRECV(httpd);
! 556: httpd->sock = nev->newsocket;
! 557: isc_socket_setname(httpd->sock, "httpd", NULL);
! 558: httpd->flags = 0;
! 559:
! 560: /*
! 561: * Initialize the buffer for our headers.
! 562: */
! 563: httpd->headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
! 564: if (httpd->headerdata == NULL) {
! 565: isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t));
! 566: isc_socket_detach(&nev->newsocket);
! 567: goto requeue;
! 568: }
! 569: httpd->headerlen = HTTP_SENDGROW;
! 570: isc_buffer_init(&httpd->headerbuffer, httpd->headerdata,
! 571: httpd->headerlen);
! 572:
! 573: ISC_LIST_INIT(httpd->bufflist);
! 574:
! 575: isc_buffer_initnull(&httpd->bodybuffer);
! 576: reset_client(httpd);
! 577:
! 578: r.base = (unsigned char *)httpd->recvbuf;
! 579: r.length = HTTP_RECVLEN - 1;
! 580: result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
! 581: httpd);
! 582: NOTICE("accept queued recv on socket");
! 583:
! 584: requeue:
! 585: result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
! 586: httpdmgr);
! 587: if (result != ISC_R_SUCCESS) {
! 588: /* XXXMLG what to do? Log failure... */
! 589: NOTICE("accept could not reaccept due to failure");
! 590: }
! 591:
! 592: out:
! 593: UNLOCK(&httpdmgr->lock);
! 594:
! 595: httpdmgr_destroy(httpdmgr);
! 596:
! 597: isc_event_free(&ev);
! 598:
! 599: EXIT("accept");
! 600: }
! 601:
! 602: static isc_result_t
! 603: render_404(const char *url, const char *querystring,
! 604: void *arg,
! 605: unsigned int *retcode, const char **retmsg,
! 606: const char **mimetype, isc_buffer_t *b,
! 607: isc_httpdfree_t **freecb, void **freecb_args)
! 608: {
! 609: static char msg[] = "No such URL.";
! 610:
! 611: UNUSED(url);
! 612: UNUSED(querystring);
! 613: UNUSED(arg);
! 614:
! 615: *retcode = 404;
! 616: *retmsg = "No such URL";
! 617: *mimetype = "text/plain";
! 618: isc_buffer_reinit(b, msg, strlen(msg));
! 619: isc_buffer_add(b, strlen(msg));
! 620: *freecb = NULL;
! 621: *freecb_args = NULL;
! 622:
! 623: return (ISC_R_SUCCESS);
! 624: }
! 625:
! 626: static void
! 627: isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev)
! 628: {
! 629: isc_region_t r;
! 630: isc_result_t result;
! 631: isc_httpd_t *httpd = ev->ev_arg;
! 632: isc_socketevent_t *sev = (isc_socketevent_t *)ev;
! 633: isc_httpdurl_t *url;
! 634: isc_time_t now;
! 635: char datebuf[32]; /* Only need 30, but safety first */
! 636:
! 637: ENTER("recv");
! 638:
! 639: INSIST(ISC_HTTPD_ISRECV(httpd));
! 640:
! 641: if (sev->result != ISC_R_SUCCESS) {
! 642: NOTICE("recv destroying client");
! 643: destroy_client(&httpd);
! 644: goto out;
! 645: }
! 646:
! 647: result = process_request(httpd, sev->n);
! 648: if (result == ISC_R_NOTFOUND) {
! 649: if (httpd->recvlen >= HTTP_RECVLEN - 1) {
! 650: destroy_client(&httpd);
! 651: goto out;
! 652: }
! 653: r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
! 654: r.length = HTTP_RECVLEN - httpd->recvlen - 1;
! 655: result = isc_socket_recv(httpd->sock, &r, 1, task,
! 656: isc_httpd_recvdone, httpd);
! 657: goto out;
! 658: } else if (result != ISC_R_SUCCESS) {
! 659: destroy_client(&httpd);
! 660: goto out;
! 661: }
! 662:
! 663: ISC_HTTPD_SETSEND(httpd);
! 664:
! 665: /*
! 666: * XXXMLG Call function here. Provide an add-header function
! 667: * which will append the common headers to a response we generate.
! 668: */
! 669: isc_buffer_initnull(&httpd->bodybuffer);
! 670: isc_time_now(&now);
! 671: isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
! 672: url = ISC_LIST_HEAD(httpd->mgr->urls);
! 673: while (url != NULL) {
! 674: if (strcmp(httpd->url, url->url) == 0)
! 675: break;
! 676: url = ISC_LIST_NEXT(url, link);
! 677: }
! 678: if (url == NULL)
! 679: result = httpd->mgr->render_404(httpd->url, httpd->querystring,
! 680: NULL,
! 681: &httpd->retcode,
! 682: &httpd->retmsg,
! 683: &httpd->mimetype,
! 684: &httpd->bodybuffer,
! 685: &httpd->freecb,
! 686: &httpd->freecb_arg);
! 687: else
! 688: result = url->action(httpd->url, httpd->querystring,
! 689: url->action_arg,
! 690: &httpd->retcode, &httpd->retmsg,
! 691: &httpd->mimetype, &httpd->bodybuffer,
! 692: &httpd->freecb, &httpd->freecb_arg);
! 693: if (result != ISC_R_SUCCESS) {
! 694: destroy_client(&httpd);
! 695: goto out;
! 696: }
! 697:
! 698: isc_httpd_response(httpd);
! 699: isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
! 700: isc_httpd_addheader(httpd, "Date", datebuf);
! 701: isc_httpd_addheader(httpd, "Expires", datebuf);
! 702: isc_httpd_addheader(httpd, "Last-Modified", datebuf);
! 703: isc_httpd_addheader(httpd, "Pragma: no-cache", NULL);
! 704: isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
! 705: isc_httpd_addheader(httpd, "Server: libisc", NULL);
! 706: isc_httpd_addheaderuint(httpd, "Content-Length",
! 707: isc_buffer_usedlength(&httpd->bodybuffer));
! 708: isc_httpd_endheaders(httpd); /* done */
! 709:
! 710: ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link);
! 711: /*
! 712: * Link the data buffer into our send queue, should we have any data
! 713: * rendered into it. If no data is present, we won't do anything
! 714: * with the buffer.
! 715: */
! 716: if (isc_buffer_length(&httpd->bodybuffer) > 0)
! 717: ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link);
! 718:
! 719: result = isc_socket_sendv(httpd->sock, &httpd->bufflist, task,
! 720: isc_httpd_senddone, httpd);
! 721:
! 722: out:
! 723: isc_event_free(&ev);
! 724: EXIT("recv");
! 725: }
! 726:
! 727: void
! 728: isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp)
! 729: {
! 730: isc_httpdmgr_t *httpdmgr;
! 731: isc_httpd_t *httpd;
! 732: httpdmgr = *httpdmgrp;
! 733: *httpdmgrp = NULL;
! 734:
! 735: ENTER("isc_httpdmgr_shutdown");
! 736:
! 737: LOCK(&httpdmgr->lock);
! 738:
! 739: MSETSHUTTINGDOWN(httpdmgr);
! 740:
! 741: isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL);
! 742:
! 743: httpd = ISC_LIST_HEAD(httpdmgr->running);
! 744: while (httpd != NULL) {
! 745: isc_socket_cancel(httpd->sock, httpdmgr->task,
! 746: ISC_SOCKCANCEL_ALL);
! 747: httpd = ISC_LIST_NEXT(httpd, link);
! 748: }
! 749:
! 750: UNLOCK(&httpdmgr->lock);
! 751:
! 752: EXIT("isc_httpdmgr_shutdown");
! 753: }
! 754:
! 755: static isc_result_t
! 756: grow_headerspace(isc_httpd_t *httpd)
! 757: {
! 758: char *newspace;
! 759: unsigned int newlen;
! 760: isc_region_t r;
! 761:
! 762: newlen = httpd->headerlen + HTTP_SENDGROW;
! 763: if (newlen > HTTP_SEND_MAXLEN)
! 764: return (ISC_R_NOSPACE);
! 765:
! 766: newspace = isc_mem_get(httpd->mgr->mctx, newlen);
! 767: if (newspace == NULL)
! 768: return (ISC_R_NOMEMORY);
! 769: isc_buffer_region(&httpd->headerbuffer, &r);
! 770: isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
! 771:
! 772: isc_mem_put(httpd->mgr->mctx, r.base, r.length);
! 773:
! 774: return (ISC_R_SUCCESS);
! 775: }
! 776:
! 777: isc_result_t
! 778: isc_httpd_response(isc_httpd_t *httpd)
! 779: {
! 780: isc_result_t result;
! 781: unsigned int needlen;
! 782:
! 783: needlen = strlen(httpd->protocol) + 1; /* protocol + space */
! 784: needlen += 3 + 1; /* room for response code, always 3 bytes */
! 785: needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */
! 786:
! 787: if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
! 788: result = grow_headerspace(httpd);
! 789: if (result != ISC_R_SUCCESS)
! 790: return (result);
! 791: }
! 792:
! 793: sprintf(isc_buffer_used(&httpd->headerbuffer), "%s %03d %s\r\n",
! 794: httpd->protocol, httpd->retcode, httpd->retmsg);
! 795: isc_buffer_add(&httpd->headerbuffer, needlen);
! 796:
! 797: return (ISC_R_SUCCESS);
! 798: }
! 799:
! 800: isc_result_t
! 801: isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
! 802: const char *val)
! 803: {
! 804: isc_result_t result;
! 805: unsigned int needlen;
! 806:
! 807: needlen = strlen(name); /* name itself */
! 808: if (val != NULL)
! 809: needlen += 2 + strlen(val); /* :<space> and val */
! 810: needlen += 2; /* CRLF */
! 811:
! 812: if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
! 813: result = grow_headerspace(httpd);
! 814: if (result != ISC_R_SUCCESS)
! 815: return (result);
! 816: }
! 817:
! 818: if (val != NULL)
! 819: sprintf(isc_buffer_used(&httpd->headerbuffer),
! 820: "%s: %s\r\n", name, val);
! 821: else
! 822: sprintf(isc_buffer_used(&httpd->headerbuffer),
! 823: "%s\r\n", name);
! 824:
! 825: isc_buffer_add(&httpd->headerbuffer, needlen);
! 826:
! 827: return (ISC_R_SUCCESS);
! 828: }
! 829:
! 830: isc_result_t
! 831: isc_httpd_endheaders(isc_httpd_t *httpd)
! 832: {
! 833: isc_result_t result;
! 834:
! 835: if (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
! 836: result = grow_headerspace(httpd);
! 837: if (result != ISC_R_SUCCESS)
! 838: return (result);
! 839: }
! 840:
! 841: sprintf(isc_buffer_used(&httpd->headerbuffer), "\r\n");
! 842: isc_buffer_add(&httpd->headerbuffer, 2);
! 843:
! 844: return (ISC_R_SUCCESS);
! 845: }
! 846:
! 847: isc_result_t
! 848: isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
! 849: isc_result_t result;
! 850: unsigned int needlen;
! 851: char buf[sizeof "18446744073709551616"];
! 852:
! 853: sprintf(buf, "%d", val);
! 854:
! 855: needlen = strlen(name); /* name itself */
! 856: needlen += 2 + strlen(buf); /* :<space> and val */
! 857: needlen += 2; /* CRLF */
! 858:
! 859: if (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
! 860: result = grow_headerspace(httpd);
! 861: if (result != ISC_R_SUCCESS)
! 862: return (result);
! 863: }
! 864:
! 865: sprintf(isc_buffer_used(&httpd->headerbuffer),
! 866: "%s: %s\r\n", name, buf);
! 867:
! 868: isc_buffer_add(&httpd->headerbuffer, needlen);
! 869:
! 870: return (ISC_R_SUCCESS);
! 871: }
! 872:
! 873: static void
! 874: isc_httpd_senddone(isc_task_t *task, isc_event_t *ev)
! 875: {
! 876: isc_httpd_t *httpd = ev->ev_arg;
! 877: isc_region_t r;
! 878: isc_result_t result;
! 879: isc_socketevent_t *sev = (isc_socketevent_t *)ev;
! 880:
! 881: ENTER("senddone");
! 882: INSIST(ISC_HTTPD_ISSEND(httpd));
! 883:
! 884: /*
! 885: * First, unlink our header buffer from the socket's bufflist. This
! 886: * is sort of an evil hack, since we know our buffer will be there,
! 887: * and we know it's address, so we can just remove it directly.
! 888: */
! 889: NOTICE("senddone unlinked header");
! 890: ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link);
! 891:
! 892: /*
! 893: * We will always want to clean up our receive buffer, even if we
! 894: * got an error on send or we are shutting down.
! 895: *
! 896: * We will pass in the buffer only if there is data in it. If
! 897: * there is no data, we will pass in a NULL.
! 898: */
! 899: if (httpd->freecb != NULL) {
! 900: isc_buffer_t *b = NULL;
! 901: if (isc_buffer_length(&httpd->bodybuffer) > 0)
! 902: b = &httpd->bodybuffer;
! 903: httpd->freecb(b, httpd->freecb_arg);
! 904: NOTICE("senddone free callback performed");
! 905: }
! 906: if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) {
! 907: ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link);
! 908: NOTICE("senddone body buffer unlinked");
! 909: }
! 910:
! 911: if (sev->result != ISC_R_SUCCESS) {
! 912: destroy_client(&httpd);
! 913: goto out;
! 914: }
! 915:
! 916: if ((httpd->flags & HTTPD_CLOSE) != 0) {
! 917: destroy_client(&httpd);
! 918: goto out;
! 919: }
! 920:
! 921: ISC_HTTPD_SETRECV(httpd);
! 922:
! 923: NOTICE("senddone restarting recv on socket");
! 924:
! 925: reset_client(httpd);
! 926:
! 927: r.base = (unsigned char *)httpd->recvbuf;
! 928: r.length = HTTP_RECVLEN - 1;
! 929: result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
! 930: httpd);
! 931:
! 932: out:
! 933: isc_event_free(&ev);
! 934: EXIT("senddone");
! 935: }
! 936:
! 937: static void
! 938: reset_client(isc_httpd_t *httpd)
! 939: {
! 940: /*
! 941: * Catch errors here. We MUST be in RECV mode, and we MUST NOT have
! 942: * any outstanding buffers. If we have buffers, we have a leak.
! 943: */
! 944: INSIST(ISC_HTTPD_ISRECV(httpd));
! 945: INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link));
! 946: INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link));
! 947:
! 948: httpd->recvbuf[0] = 0;
! 949: httpd->recvlen = 0;
! 950: httpd->method = ISC_HTTPD_METHODUNKNOWN;
! 951: httpd->url = NULL;
! 952: httpd->querystring = NULL;
! 953: httpd->protocol = NULL;
! 954: httpd->flags = 0;
! 955:
! 956: isc_buffer_clear(&httpd->headerbuffer);
! 957: isc_buffer_invalidate(&httpd->bodybuffer);
! 958: }
! 959:
! 960: isc_result_t
! 961: isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url,
! 962: isc_httpdaction_t *func, void *arg)
! 963: {
! 964: isc_httpdurl_t *item;
! 965:
! 966: if (url == NULL) {
! 967: httpdmgr->render_404 = func;
! 968: return (ISC_R_SUCCESS);
! 969: }
! 970:
! 971: item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
! 972: if (item == NULL)
! 973: return (ISC_R_NOMEMORY);
! 974:
! 975: item->url = isc_mem_strdup(httpdmgr->mctx, url);
! 976: if (item->url == NULL) {
! 977: isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t));
! 978: return (ISC_R_NOMEMORY);
! 979: }
! 980:
! 981: item->action = func;
! 982: item->action_arg = arg;
! 983: ISC_LINK_INIT(item, link);
! 984: ISC_LIST_APPEND(httpdmgr->urls, item, link);
! 985:
! 986: return (ISC_R_SUCCESS);
! 987: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>