#include <isc/httpd.h>
#include <isc/mem.h>
#include <isc/print.h>
+#include <isc/refcount.h>
#include <isc/socket.h>
#include <isc/string.h>
#include <isc/task.h>
#define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
#define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
#define HTTPD_KEEPALIVE 0x0004 /* Got a Connection: Keep-Alive */
-#define HTTPD_ACCEPT_DEFLATE 0x0008
+#define HTTPD_ACCEPT_DEFLATE 0x0008
+
+#define HTTPD_MAGIC ISC_MAGIC('H', 't', 'p', 'd')
+#define VALID_HTTPD(m) ISC_MAGIC_VALID(m, HTTPD_MAGIC)
+
+#define HTTPDMGR_MAGIC ISC_MAGIC('H', 'p', 'd', 'm')
+#define VALID_HTTPDMGR(m) ISC_MAGIC_VALID(m, HTTPDMGR_MAGIC)
+
/*% http client */
struct isc_httpd {
+ unsigned int magic; /* HTTPD_MAGIC */
+ isc_refcount_t references;
isc_httpdmgr_t *mgr; /*%< our parent */
ISC_LINK(isc_httpd_t) link;
unsigned int state;
/*% lightweight socket manager for httpd output */
struct isc_httpdmgr {
+ unsigned int magic; /* HTTPDMGR_MAGIC */
+ isc_refcount_t references;
isc_mem_t *mctx;
isc_socket_t *sock; /*%< listening socket */
isc_task_t *task; /*%< owning task */
*httpdp = NULL;
+ if (isc_refcount_decrement(&httpd->references) != 1) {
+ return;
+ }
+
LOCK(&httpdmgr->lock);
+ httpd->magic = 0;
+ isc_refcount_destroy(&httpd->references);
isc_socket_detach(&httpd->sock);
ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
ISC_LIST_INIT(httpdmgr->running);
ISC_LIST_INIT(httpdmgr->urls);
+ isc_refcount_init(&httpdmgr->references, 1);
+
/* XXXMLG ignore errors on isc_socket_listen() */
result = isc_socket_listen(sock, SOMAXCONN);
if (result != ISC_R_SUCCESS) {
(void)isc_socket_filter(sock, "httpready");
- result = isc_socket_accept(sock, task, isc_httpd_accept, httpdmgr);
- if (result != ISC_R_SUCCESS)
- goto cleanup;
-
httpdmgr->render_404 = render_404;
httpdmgr->render_500 = render_500;
+ httpdmgr->magic = HTTPDMGR_MAGIC;
+
+ isc_refcount_increment(&httpdmgr->references);
+ result = isc_socket_accept(sock, task, isc_httpd_accept, httpdmgr);
+ if (result != ISC_R_SUCCESS) {
+ (void) isc_refcount_decrement(&httpdmgr->references);
+ goto cleanup;
+ }
*httpdmgrp = httpdmgr;
return (ISC_R_SUCCESS);
cleanup:
+ httpdmgr->magic = 0;
+ isc_refcount_decrement(&httpdmgr->references);
+ isc_refcount_destroy(&httpdmgr->references);
isc_task_detach(&httpdmgr->task);
isc_socket_detach(&httpdmgr->sock);
isc_mem_detach(&httpdmgr->mctx);
static void
httpdmgr_destroy(isc_httpdmgr_t *httpdmgr) {
- isc_mem_t *mctx;
isc_httpdurl_t *url;
ENTER("httpdmgr_destroy");
- LOCK(&httpdmgr->lock);
-
- if (!MSHUTTINGDOWN(httpdmgr)) {
- NOTICE("httpdmgr_destroy not shutting down yet");
- UNLOCK(&httpdmgr->lock);
+ if (isc_refcount_decrement(&httpdmgr->references) != 1) {
return;
}
- /*
- * If all clients are not shut down, don't do anything yet.
- */
- if (!ISC_LIST_EMPTY(httpdmgr->running)) {
- NOTICE("httpdmgr_destroy clients still active");
- UNLOCK(&httpdmgr->lock);
- return;
- }
+ LOCK(&httpdmgr->lock);
+
+ httpdmgr->magic = 0;
+
+ INSIST(MSHUTTINGDOWN(httpdmgr));
+ INSIST(ISC_LIST_EMPTY(httpdmgr->running));
NOTICE("httpdmgr_destroy detaching socket, task, and timermgr");
UNLOCK(&httpdmgr->lock);
isc_mutex_destroy(&httpdmgr->lock);
- if (httpdmgr->ondestroy != NULL)
+ if (httpdmgr->ondestroy != NULL) {
(httpdmgr->ondestroy)(httpdmgr->cb_arg);
-
- mctx = httpdmgr->mctx;
- isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t));
+ }
+ isc_refcount_destroy(&httpdmgr->references);
+ isc_mem_putanddetach(&httpdmgr->mctx, httpdmgr, sizeof(isc_httpdmgr_t));
EXIT("httpdmgr_destroy");
}
httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t));
+ isc_refcount_init(&httpd->references, 1);
+ isc_refcount_increment(&httpdmgr->references);
httpd->mgr = httpdmgr;
ISC_LINK_INIT(httpd, link);
ISC_LIST_APPEND(httpdmgr->running, httpd, link);
isc_buffer_initnull(&httpd->bodybuffer);
httpd->sendbuffer = NULL;
reset_client(httpd);
+ httpd->magic = HTTPD_MAGIC;
r.base = (unsigned char *)httpd->recvbuf;
r.length = HTTP_RECVLEN - 1;
result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone,
httpd);
- /* FIXME!!! */
- POST(result);
+ if (result != ISC_R_SUCCESS) {
+ destroy_client(&httpd);
+ }
NOTICE("accept queued recv on socket");
requeue:
+ isc_refcount_increment(&httpdmgr->references);
result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept,
httpdmgr);
if (result != ISC_R_SUCCESS) {
- /* XXXMLG what to do? Log failure... */
+ int32_t refs = isc_refcount_decrement(&httpdmgr->references);
+ INSIST(refs > 1);
NOTICE("accept could not reaccept due to failure");
}
if (sev->result != ISC_R_SUCCESS) {
NOTICE("recv destroying client");
- destroy_client(&httpd);
goto out;
}
result = process_request(httpd, sev->n);
if (result == ISC_R_NOTFOUND) {
if (httpd->recvlen >= HTTP_RECVLEN - 1) {
- destroy_client(&httpd);
goto out;
}
r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen;
r.length = HTTP_RECVLEN - httpd->recvlen - 1;
- /* check return code? */
- (void)isc_socket_recv(httpd->sock, &r, 1, task,
- isc_httpd_recvdone, httpd);
+ isc_refcount_increment(&httpd->references);
+ result = isc_socket_recv(httpd->sock, &r, 1, task,
+ isc_httpd_recvdone, httpd);
+ if (result != ISC_R_SUCCESS) {
+ uint32_t refs = isc_refcount_decrement(&httpd->references);
+ INSIST(refs > 1);
+ }
goto out;
} else if (result != ISC_R_SUCCESS) {
- destroy_client(&httpd);
goto out;
}
isc_buffer_initnull(&httpd->bodybuffer);
isc_time_now(&now);
isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
+ LOCK(&httpd->mgr->lock);
url = ISC_LIST_HEAD(httpd->mgr->urls);
while (url != NULL) {
if (strcmp(httpd->url, url->url) == 0)
break;
url = ISC_LIST_NEXT(url, link);
}
- if (url == NULL)
+ UNLOCK(&httpd->mgr->lock);
+
+ if (url == NULL) {
result = httpd->mgr->render_404(httpd->url, NULL,
httpd->querystring,
NULL, NULL,
&httpd->bodybuffer,
&httpd->freecb,
&httpd->freecb_arg);
- else
+ } else {
result = url->action(httpd->url, url,
httpd->querystring,
httpd->headers,
&httpd->retcode, &httpd->retmsg,
&httpd->mimetype, &httpd->bodybuffer,
&httpd->freecb, &httpd->freecb_arg);
+ }
if (result != ISC_R_SUCCESS) {
result = httpd->mgr->render_500(httpd->url, url,
httpd->querystring,
#endif
isc_httpd_response(httpd);
- if ((httpd->flags & HTTPD_KEEPALIVE) != 0)
+ if ((httpd->flags & HTTPD_KEEPALIVE) != 0) {
isc_httpd_addheader(httpd, "Connection", "Keep-Alive");
+ }
isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype);
isc_httpd_addheader(httpd, "Date", datebuf);
isc_httpd_addheader(httpd, "Expires", datebuf);
if (is_compressed == true) {
isc_httpd_addheader(httpd, "Content-Encoding", "deflate");
isc_httpd_addheaderuint(httpd, "Content-Length",
- isc_buffer_usedlength(&httpd->compbuffer));
+ isc_buffer_usedlength(&httpd->compbuffer));
} else {
isc_httpd_addheaderuint(httpd, "Content-Length",
isc_buffer_usedlength(&httpd->bodybuffer));
*/
isc_buffer_usedregion(httpd->sendbuffer, &r);
- /* check return code? */
- (void)isc_socket_send(httpd->sock, &r, task,
- isc_httpd_senddone, httpd);
+ isc_refcount_increment(&httpd->references);
+ result = isc_socket_send(httpd->sock, &r, task,
+ isc_httpd_senddone, httpd);
+ if (result != ISC_R_SUCCESS) {
+ uint32_t refs = isc_refcount_decrement(&httpd->references);
+ INSIST(refs > 1);
+ }
out:
+ destroy_client(&httpd);
isc_event_free(&ev);
EXIT("recv");
}
isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
isc_httpdmgr_t *httpdmgr;
isc_httpd_t *httpd;
+
+ REQUIRE(httpdmgrp != NULL);
httpdmgr = *httpdmgrp;
*httpdmgrp = NULL;
+ REQUIRE(VALID_HTTPDMGR(httpdmgr));
ENTER("isc_httpdmgr_shutdown");
UNLOCK(&httpdmgr->lock);
+ httpdmgr_destroy(httpdmgr);
+
EXIT("isc_httpdmgr_shutdown");
}
isc_result_t result;
unsigned int needlen;
+ REQUIRE(VALID_HTTPD(httpd));
+
needlen = strlen(httpd->protocol) + 1; /* protocol + space */
needlen += 3 + 1; /* room for response code, always 3 bytes */
needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */
}
isc_result_t
-isc_httpd_addheader(isc_httpd_t *httpd, const char *name,
- const char *val)
-{
+isc_httpd_addheader(isc_httpd_t *httpd, const char *name, const char *val) {
isc_result_t result;
unsigned int needlen;
+ REQUIRE(VALID_HTTPD(httpd));
+
needlen = strlen(name); /* name itself */
- if (val != NULL)
+ if (val != NULL) {
needlen += 2 + strlen(val); /* :<space> and val */
+ }
needlen += 2; /* CRLF */
while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
isc_httpd_endheaders(isc_httpd_t *httpd) {
isc_result_t result;
+ REQUIRE(VALID_HTTPD(httpd));
+
while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
result = grow_headerspace(httpd);
if (result != ISC_R_SUCCESS)
unsigned int needlen;
char buf[sizeof "18446744073709551616"];
+ REQUIRE(VALID_HTTPD(httpd));
+
snprintf(buf, sizeof(buf), "%d", val);
needlen = strlen(name); /* name itself */
isc_httpd_t *httpd = ev->ev_arg;
isc_region_t r;
isc_socketevent_t *sev = (isc_socketevent_t *)ev;
+ isc_result_t result;
+
+ REQUIRE(VALID_HTTPD(httpd));
ENTER("senddone");
INSIST(ISC_HTTPD_ISSEND(httpd));
}
if (sev->result != ISC_R_SUCCESS) {
- destroy_client(&httpd);
goto out;
}
if ((httpd->flags & HTTPD_CLOSE) != 0) {
- destroy_client(&httpd);
goto out;
}
r.base = (unsigned char *)httpd->recvbuf;
r.length = HTTP_RECVLEN - 1;
- /* check return code? */
- (void)isc_socket_recv(httpd->sock, &r, 1, task,
- isc_httpd_recvdone, httpd);
+
+ isc_refcount_increment(&httpd->references);
+ result = isc_socket_recv(httpd->sock, &r, 1, task,
+ isc_httpd_recvdone, httpd);
+ if (result != ISC_R_SUCCESS) {
+ uint32_t refs = isc_refcount_decrement(&httpd->references);
+ INSIST(refs > 1);
+ }
out:
+ destroy_client(&httpd);
isc_event_free(&ev);
EXIT("senddone");
}
{
isc_httpdurl_t *item;
+ REQUIRE(VALID_HTTPDMGR(httpdmgr));
+
if (url == NULL) {
httpdmgr->render_404 = func;
return (ISC_R_SUCCESS);
isc_time_now(&item->loadtime);
ISC_LINK_INIT(item, link);
+
+ LOCK(&httpdmgr->lock);
ISC_LIST_APPEND(httpdmgr->urls, item, link);
+ UNLOCK(&httpdmgr->lock);
return (ISC_R_SUCCESS);
}