}
static isc_result_t
-render_xml(uint32_t flags, const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg, const char **mimetype,
- isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
+render_xml(uint32_t flags, void *arg, unsigned int *retcode,
+ const char **retmsg, const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
unsigned char *msg = NULL;
int msglen;
named_server_t *server = arg;
isc_result_t result;
- UNUSED(url);
- UNUSED(urlinfo);
- UNUSED(headers);
- UNUSED(querystring);
-
result = generatexml(server, flags, &msglen, &msg);
if (result == ISC_R_SUCCESS) {
}
static isc_result_t
-render_xml_all(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_xml_all(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
void **freecb_args) {
- return (render_xml(STATS_XML_ALL, url, urlinfo, querystring, headers,
- arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_ALL, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_xml_status(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_xml_status(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_xml(STATS_XML_STATUS, url, urlinfo, querystring, headers,
- arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_STATUS, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_xml_server(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_xml_server(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_xml(STATS_XML_SERVER, url, urlinfo, querystring, headers,
- arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_SERVER, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_xml_zones(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_xml_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_xml(STATS_XML_ZONES, url, urlinfo, querystring, headers,
- arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_ZONES, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_xml_net(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_xml_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
void **freecb_args) {
- return (render_xml(STATS_XML_NET, url, urlinfo, querystring, headers,
- arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_NET, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_xml_tasks(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_xml_tasks(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_xml(STATS_XML_TASKS, url, urlinfo, querystring, headers,
- arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_TASKS, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_xml_mem(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_xml_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
void **freecb_args) {
- return (render_xml(STATS_XML_MEM, url, urlinfo, querystring, headers,
- arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_MEM, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_xml_traffic(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_xml_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_xml(STATS_XML_TRAFFIC, url, urlinfo, querystring,
- headers, arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_xml(STATS_XML_TRAFFIC, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
#endif /* HAVE_LIBXML2 */
}
static isc_result_t
-render_json(uint32_t flags, const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg, const char **mimetype,
- isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
+render_json(uint32_t flags, void *arg, unsigned int *retcode,
+ const char **retmsg, const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args) {
isc_result_t result;
json_object *bindstats = NULL;
named_server_t *server = arg;
size_t msglen = 0;
char *p;
- UNUSED(url);
- UNUSED(urlinfo);
- UNUSED(headers);
- UNUSED(querystring);
-
result = generatejson(server, &msglen, &msg, &bindstats, flags);
if (result == ISC_R_SUCCESS) {
*retcode = 200;
}
static isc_result_t
-render_json_all(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_json_all(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_json(STATS_JSON_ALL, url, urlinfo, querystring, headers,
- arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_ALL, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_json_status(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_json_status(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_json(STATS_JSON_STATUS, url, urlinfo, querystring,
- headers, arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_STATUS, arg, retcode, retmsg, mimetype,
+ b, freecb, freecb_args));
}
static isc_result_t
-render_json_server(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_json_server(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_json(STATS_JSON_SERVER, url, urlinfo, querystring,
- headers, arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_SERVER, arg, retcode, retmsg, mimetype,
+ b, freecb, freecb_args));
}
static isc_result_t
-render_json_zones(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_json_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_json(STATS_JSON_ZONES, url, urlinfo, querystring,
- headers, arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_ZONES, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_json_mem(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_json_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_json(STATS_JSON_MEM, url, urlinfo, querystring, headers,
- arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_MEM, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_json_tasks(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_json_tasks(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_json(STATS_JSON_TASKS, url, urlinfo, querystring,
- headers, arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_TASKS, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_json_net(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_json_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_json(STATS_JSON_NET, url, urlinfo, querystring, headers,
- arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_NET, arg, retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
}
static isc_result_t
-render_json_traffic(const char *url, isc_httpdurl_t *urlinfo,
- const char *querystring, const char *headers, void *arg,
- unsigned int *retcode, const char **retmsg,
+render_json_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
+ void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
- return (render_json(STATS_JSON_TRAFFIC, url, urlinfo, querystring,
- headers, arg, retcode, retmsg, mimetype, b, freecb,
- freecb_args));
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ return (render_json(STATS_JSON_TRAFFIC, arg, retcode, retmsg, mimetype,
+ b, freecb, freecb_args));
}
#endif /* HAVE_JSON_C */
static isc_result_t
-render_xsl(const char *url, isc_httpdurl_t *urlinfo, const char *querystring,
- const char *headers, void *args, unsigned int *retcode,
- const char **retmsg, const char **mimetype, isc_buffer_t *b,
- isc_httpdfree_t **freecb, void **freecb_args) {
+render_xsl(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *args,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
isc_result_t result;
- char *_headers = NULL;
- char *p;
+ char *p = NULL;
- UNUSED(url);
- UNUSED(querystring);
+ UNUSED(httpd);
UNUSED(args);
*freecb = NULL;
*freecb_args = NULL;
*mimetype = "text/xslt+xml";
- if (urlinfo->isstatic) {
- isc_time_t when;
- char *line, *saveptr;
- const char *if_modified_since = "If-Modified-Since: ";
- _headers = strdup(headers);
+ if (isc_httpdurl_isstatic(urlinfo)) {
+ time_t t1, t2;
+ const isc_time_t *when;
+ const isc_time_t *loadtime;
+
+ when = isc_httpd_if_modified_since(httpd);
- if (_headers == NULL) {
+ if (isc_time_isepoch(when)) {
goto send;
}
- saveptr = NULL;
- for (line = strtok_r(_headers, "\n", &saveptr); line;
- line = strtok_r(NULL, "\n", &saveptr))
- {
- if (strncasecmp(line, if_modified_since,
- strlen(if_modified_since)) == 0) {
- time_t t1, t2;
- line += strlen(if_modified_since);
- result = isc_time_parsehttptimestamp(line,
- &when);
- if (result != ISC_R_SUCCESS) {
- goto send;
- }
-
- result = isc_time_secondsastimet(&when, &t1);
- if (result != ISC_R_SUCCESS) {
- goto send;
- }
+ result = isc_time_secondsastimet(when, &t1);
+ if (result != ISC_R_SUCCESS) {
+ goto send;
+ }
- result = isc_time_secondsastimet(
- &urlinfo->loadtime, &t2);
- if (result != ISC_R_SUCCESS) {
- goto send;
- }
+ loadtime = isc_httpdurl_loadtime(urlinfo);
- if (t1 < t2) {
- goto send;
- }
+ result = isc_time_secondsastimet(loadtime, &t2);
+ if (result != ISC_R_SUCCESS) {
+ goto send;
+ }
- *retcode = 304;
- *retmsg = "Not modified";
- goto end;
- }
+ if (t1 < t2) {
+ goto send;
}
+
+ *retcode = 304;
+ *retmsg = "Not modified";
+ goto end;
}
send:
isc_buffer_reinit(b, p, strlen(xslmsg));
isc_buffer_add(b, strlen(xslmsg));
end:
- free(_headers);
return (ISC_R_SUCCESS);
}
/*! \file */
+#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include <isc/sockaddr.h>
#include <isc/string.h>
#include <isc/time.h>
+#include <isc/url.h>
#include <isc/util.h>
+#include "netmgr/netmgr-int.h"
+#include "picohttpparser.h"
+
#ifdef HAVE_ZLIB
#include <zlib.h>
#endif /* ifdef HAVE_ZLIB */
} \
} while (0)
-#define HTTP_RECVLEN 4096
-#define HTTP_SENDGROW 1024
-#define HTTP_SEND_MAXLEN 10240
+/*
+ * Size the recv buffer to hold at maximum two full buffers from isc_nm_read(),
+ * so we don't have to handle the truncation.
+ */
+#define HTTP_RECVLEN ISC_NETMGR_TCP_RECVBUF_SIZE * 2
+#define HTTP_SENDLEN ISC_NETMGR_TCP_RECVBUF_SIZE
+#define HTTP_HEADERS_NUM 10
#define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */
#define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */
#define VALID_HTTPDMGR(m) ISC_MAGIC_VALID(m, HTTPDMGR_MAGIC)
/*%
- * Client states.
- *
- * _RECV The client is waiting for data after starting a read.
- * _SEND All data for a response has completed, and a reply was
- * sent via a send call.
+ * HTTP methods.
*/
-
-typedef enum { RECV, SEND } state_t;
+typedef enum { METHOD_UNKNOWN = 0, METHOD_GET = 1, METHOD_POST = 2 } method_t;
/*%
- * HTTP methods.
+ * HTTP urls. These are the URLs we manage, and the function to call to
+ * provide the data for it. We pass in the base url (so the same function
+ * can handle multiple requests), and a structure to fill in to return a
+ * result to the client. We also pass in a pointer to be filled in for
+ * the data cleanup function.
*/
-typedef enum { METHOD_UNKNOWN = 0, METHOD_GET = 1, METHOD_POST = 2 } method_t;
+struct isc_httpdurl {
+ char *url;
+ isc_httpdaction_t *action;
+ void *action_arg;
+ bool isstatic;
+ isc_time_t loadtime;
+ ISC_LINK(isc_httpdurl_t) link;
+};
/*% http client */
struct isc_httpd {
isc_nmhandle_t *handle; /* Permanent pointer to handle */
isc_nmhandle_t *readhandle; /* Waiting for a read callback */
- isc_nmhandle_t *sendhandle; /* Waiting for a send callback */
- state_t state;
int flags;
/*%
* Received data state.
*/
char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
- uint32_t recvlen; /*%< length recv'd */
- uint32_t consume; /*%< length of last command */
- char *headers; /*%< set in process_request() */
- bool truncated;
- method_t method;
- char *url;
- char *querystring;
- char *protocol;
-
- /*%
- * Transmit data state.
- *
- * This is the data buffer we will transmit.
- *
- * This free function pointer is filled in by the rendering function
- * we call. The free function is called after the data is transmitted
- * to the client.
- *
- * The bufflist is the list of buffers we are currently transmitting.
- * The headerbuffer is where we render our headers to. If we run out
- * of space when rendering a header, we will change the size of our
- * buffer. We will not free it until we are finished, and will
- * allocate an additional HTTP_SENDGROW bytes per header space grow.
- *
- * We currently use three buffers total, one for the headers (which
- * we manage), another for the client to fill in (which it manages,
- * it provides the space for it, etc) -- we will pass that buffer
- * structure back to the caller, who is responsible for managing the
- * space it may have allocated as backing store for it. This second
- * buffer is bodybuffer, and we only allocate the buffer itself, not
- * the backing store.
- * The third buffer is compbuffer, managed by us, that contains the
- * compressed HTTP data, if compression is used.
- */
- isc_buffer_t headerbuffer;
- isc_buffer_t compbuffer;
- isc_buffer_t *sendbuffer;
+ size_t recvlen; /*%< length recv'd */
+ size_t consume; /*%< length of last command */
- const char *mimetype;
- unsigned int retcode;
- const char *retmsg;
- isc_buffer_t bodybuffer;
- isc_httpdfree_t *freecb;
- void *freecb_arg;
+ method_t method;
+ int minor_version;
+ const char *path;
+ isc_url_parser_t up;
+ isc_time_t if_modified_since;
};
struct isc_httpdmgr {
isc_httpdaction_t *render_500;
};
+typedef struct isc_httpd_sendreq {
+ isc_mem_t *mctx;
+ isc_httpd_t *httpd;
+ isc_nmhandle_t *handle;
+
+ /*%
+ * Transmit data state.
+ *
+ * This is the data buffer we will transmit.
+ *
+ * This free function pointer is filled in by the rendering function
+ * we call. The free function is called after the data is transmitted
+ * to the client.
+ *
+ * We currently use three buffers total:
+ *
+ * sendbuffer - gets filled as we gather the data
+ *
+ * bodybuffer - for the client to fill in (which it manages, it provides
+ * the space for it, etc) -- we will pass that buffer structure back to
+ * the caller, who is responsible for managing the space it may have
+ * allocated as backing store for it. we only allocate the buffer
+ * itself, not the backing store.
+ *
+ * compbuffer - managed by us, that contains the compressed HTTP data,
+ * if compression is used.
+ */
+ isc_buffer_t *sendbuffer;
+ isc_buffer_t *compbuffer;
+
+ isc_buffer_t bodybuffer;
+
+ const char *mimetype;
+ unsigned int retcode;
+ const char *retmsg;
+ isc_httpdfree_t *freecb;
+ void *freecb_arg;
+
+} isc_httpd_sendreq_t;
+
static isc_result_t
httpd_newconn(isc_nmhandle_t *, isc_result_t, void *);
static void
static void
httpd_put(void *);
-static isc_result_t
-httpd_addheader(isc_httpd_t *, const char *, const char *);
-static isc_result_t
-httpd_addheaderuint(isc_httpd_t *, const char *, int);
-static isc_result_t
-httpd_endheaders(isc_httpd_t *);
-static isc_result_t
-httpd_response(isc_httpd_t *);
+static void
+httpd_addheader(isc_httpd_sendreq_t *, const char *, const char *);
+static void
+httpd_addheaderuint(isc_httpd_sendreq_t *, const char *, int);
+static void
+httpd_endheaders(isc_httpd_sendreq_t *);
+static void
+httpd_response(isc_httpd_t *, isc_httpd_sendreq_t *);
static isc_result_t
-process_request(isc_httpd_t *, isc_region_t *, size_t *);
-static isc_result_t
-grow_headerspace(isc_httpd_t *);
+process_request(isc_httpd_t *, size_t);
static isc_httpdaction_t render_404;
static isc_httpdaction_t render_500;
static void
httpdmgr_detach(isc_httpdmgr_t **);
-static void
-free_buffer(isc_mem_t *mctx, isc_buffer_t *buffer) {
- isc_region_t r;
-
- isc_buffer_region(buffer, &r);
- if (r.base != NULL) {
- isc_mem_put(mctx, r.base, r.length);
- }
-
- isc_buffer_initnull(buffer);
-}
-
isc_result_t
isc_httpdmgr_create(isc_nm_t *nm, isc_mem_t *mctx, isc_sockaddr_t *addr,
isc_httpdclientok_t *client_ok,
isc_mem_putanddetach(&httpdmgr->mctx, httpdmgr, sizeof(isc_httpdmgr_t));
}
-#define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
-#define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
+static bool
+name_match(const struct phr_header *header, const char *match) {
+ size_t match_len = strlen(match);
+ if (match_len != header->name_len) {
+ return (false);
+ }
+ return (strncasecmp(header->name, match, match_len) == 0);
+}
-/*
- * Look for the given header in headers.
- * If value is specified look for it terminated with a character in eov.
- * If fvalue is specified and the header was found, then *fvalue will point to
- * the found header's value.
- */
static bool
-have_header(isc_httpd_t *httpd, const char *header, const char *value,
- const char *eov, const char **fvalue) {
- char *cr, *nl, *h;
- size_t hlen, vlen = 0;
-
- h = httpd->headers;
- hlen = strlen(header);
- if (value != NULL) {
- INSIST(eov != NULL);
- vlen = strlen(value);
+value_match(const struct phr_header *header, const char *match) {
+ size_t match_len = strlen(match);
+ size_t limit;
+
+ if (match_len > header->value_len) {
+ return (false);
}
- for (;;) {
- if (strncasecmp(h, header, hlen) != 0) {
- /*
- * Skip to next line;
- */
- cr = strchr(h, '\r');
- if (cr != NULL && cr[1] == '\n') {
- cr++;
- }
- nl = strchr(h, '\n');
+ limit = header->value_len - match_len + 1;
- /* last header? */
- h = cr;
- if (h == NULL || (nl != NULL && nl < h)) {
- h = nl;
- }
- if (h == NULL) {
- return (false);
+ for (size_t i = 0; i < limit; i++) {
+ if (isspace(header->value[i])) {
+ while (i < limit && isspace(header->value[i])) {
+ i++;
}
- h++;
continue;
}
- /*
- * Skip optional leading white space.
- */
- h += hlen;
- while (*h == ' ' || *h == '\t') {
- h++;
- }
-
- /*
- * Set the found value.
- */
- if (fvalue != NULL) {
- *fvalue = h;
- }
-
- if (value == NULL) {
- return (true);
- }
-
- /*
- * Terminate token search on NULL or EOL.
- */
- while (*h != 0 && *h != '\r' && *h != '\n') {
- if (strncasecmp(h, value, vlen) == 0) {
- if (strchr(eov, h[vlen]) != NULL) {
- return (true);
- /*
- * Skip to next token.
- */
- }
- }
+ if (strncasecmp(&header->value[i], match, match_len) == 0) {
+ i += match_len;
/*
- * Skip to next token.
+ * Sanity check; f.e. for 'deflate' match only
+ * 'deflate[,;]', but not 'deflateyou'
*/
- h += strcspn(h, eov);
- if (h[0] == '\r' && h[1] == '\n') {
- h++;
- }
- if (h[0] != 0) {
- h++;
+ if (i == header->value_len || header->value[i] == ',' ||
+ header->value[i] == ';')
+ {
+ return (true);
}
}
- return (false);
+ while (i < limit && header->value[i] != ',') {
+ i++;
+ }
}
+ return (false);
}
static isc_result_t
-process_request(isc_httpd_t *httpd, isc_region_t *region, size_t *buflen) {
- char *s = NULL, *p = NULL, *urlend = NULL;
- const char *content_length = NULL;
- size_t limit = sizeof(httpd->recvbuf) - httpd->recvlen - 1;
- size_t len = region->length;
- int delim;
- bool truncated = false;
-
- if (len > limit) {
- len = limit;
- truncated = true;
- }
+process_request(isc_httpd_t *httpd, size_t last_len) {
+ int pret;
+ const char *method = NULL;
+ size_t method_len = 0;
+ const char *path;
+ size_t path_len = 0;
+ struct phr_header headers[HTTP_HEADERS_NUM];
+ size_t num_headers;
+ isc_result_t result;
- if (len > 0U) {
- if (httpd->truncated) {
- return (ISC_R_NOSPACE);
- }
- memmove(httpd->recvbuf + httpd->recvlen, region->base, len);
- httpd->recvlen += len;
- httpd->recvbuf[httpd->recvlen] = 0;
- isc_region_consume(region, len);
- }
- if (truncated) {
- httpd->truncated = true;
- }
- httpd->headers = NULL;
- *buflen = httpd->recvlen;
+ num_headers = ARRAY_SIZE(headers);
- /*
- * If we don't find a blank line in our buffer, return that we need
- * more data.
- */
- s = strstr(httpd->recvbuf, "\r\n\r\n");
- delim = 2;
- if (s == NULL) {
- s = strstr(httpd->recvbuf, "\n\n");
- delim = 1;
- if (s == NULL) {
- return (httpd->truncated ? ISC_R_NOSPACE
- : ISC_R_NOTFOUND);
- }
- httpd->consume = s + 2 - httpd->recvbuf;
- } else {
- httpd->consume = s + 4 - httpd->recvbuf;
+ pret = phr_parse_request(httpd->recvbuf, httpd->recvlen, &method,
+ &method_len, &path, &path_len,
+ &httpd->minor_version, headers, &num_headers,
+ last_len);
+
+ if (pret == -1) {
+ /* Parse Error */
+ return (ISC_R_UNEXPECTED);
+ } else if (pret == -2) {
+ /* Need more data */
+ return (ISC_R_NOMORE);
}
- /*
- * NULL terminate the request at the blank line.
- */
- s[delim] = 0;
+ INSIST(pret > 0);
+
+ httpd->consume = pret;
/*
* Determine if this is a POST or GET method. Any other values will
* cause an error to be returned.
*/
- if (strncmp(httpd->recvbuf, "GET ", 4) == 0) {
+ if (strncmp(method, "GET ", method_len) == 0) {
httpd->method = METHOD_GET;
- p = httpd->recvbuf + 4;
- } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) {
+ } else if (strncmp(method, "POST ", method_len) == 0) {
httpd->method = METHOD_POST;
- p = httpd->recvbuf + 5;
} else {
return (ISC_R_RANGE);
}
/*
- * From now on, p is the start of our buffer.
+ * Parse the URL
*/
-
- /*
- * Extract the URL.
- */
- s = p;
- while (LENGTHOK(s) && BUFLENOK(s) &&
- (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' '))
- {
- s++;
- }
- if (!LENGTHOK(s)) {
- return (ISC_R_NOTFOUND);
- }
- if (!BUFLENOK(s)) {
- return (ISC_R_NOMEMORY);
+ result = isc_url_parse(path, path_len, 0, &httpd->up);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
}
- urlend = s;
+ httpd->path = path;
- /*
- * Make the URL relative.
- */
- if (strncmp(p, "http://", 7) == 0 || strncmp(p, "https://", 8) == 0) {
- /* Skip first '/' */
- while (*p != '/' && *p != 0) {
- p++;
- }
- if (*p == 0) {
- return (ISC_R_RANGE);
- }
- p++;
- /* Skip second '/' */
- while (*p != '/' && *p != 0) {
- p++;
- }
- if (*p == 0) {
- return (ISC_R_RANGE);
- }
- p++;
- /* Find third '/' */
- while (*p != '/' && *p != 0) {
- p++;
- }
- if (*p == 0) {
- p--;
- *p = '/';
- }
- }
+ ssize_t content_len = 0;
+ bool keep_alive = false;
- httpd->url = p;
- p = s + 1;
- s = p;
+ isc_time_set(&httpd->if_modified_since, 0, 0);
- /*
- * Now, see if there is a question mark in the URL. If so, this is
- * part of the query string, and we will split it from the URL.
- */
- httpd->querystring = strchr(httpd->url, '?');
- if (httpd->querystring != NULL) {
- *(httpd->querystring) = 0;
- httpd->querystring++;
- }
+ for (size_t i = 0; i < num_headers; i++) {
+ struct phr_header *header = &headers[i];
- /*
- * Extract the HTTP/1.X protocol. We will bounce on anything but
- * HTTP/1.0 or HTTP/1.1 for now.
- */
- while (LENGTHOK(s) && BUFLENOK(s) &&
- (*s != '\n' && *s != '\r' && *s != '\0')) {
- s++;
- }
- if (!LENGTHOK(s)) {
- return (ISC_R_NOTFOUND);
- }
- if (!BUFLENOK(s)) {
- return (ISC_R_NOMEMORY);
+ if (name_match(header, "Content-Length")) {
+ char *endptr;
+ content_len = (size_t)strtoul(header->value, &endptr,
+ 10);
+ /* Consistency check, if we consumed all numbers */
+ if ((header->value + header->value_len) != endptr) {
+ return (ISC_R_RANGE);
+ }
+ } else if (name_match(header, "Connection")) {
+ if (value_match(header, "close")) {
+ httpd->flags |= HTTPD_CLOSE;
+ } else if (value_match(header, "keep-alive")) {
+ keep_alive = true;
+ }
+ } else if (name_match(header, "Host")) {
+ httpd->flags |= HTTPD_FOUNDHOST;
+ } else if (name_match(header, "Accept-Encoding")) {
+ if (value_match(header, "deflate")) {
+ httpd->flags |= HTTPD_ACCEPT_DEFLATE;
+ }
+ } else if (name_match(header, "If-Modified-Since")) {
+ char timestamp[ISC_FORMATHTTPTIMESTAMP_SIZE + 1];
+ memmove(timestamp, header->value, header->value_len);
+ timestamp[header->value_len] = 0;
+
+ /* Ignore the value if it can't be parsed */
+ (void)isc_time_parsehttptimestamp(
+ timestamp, &httpd->if_modified_since);
+ }
}
+
/*
- * Check that we have the expected eol delimiter.
+ * The Content-Length is optional in an HTTP request.
+ * For a GET the length must be zero.
*/
- if (strncmp(s, delim == 1 ? "\n" : "\r\n", delim) != 0) {
- return (ISC_R_RANGE);
+ if (httpd->method == METHOD_GET && content_len != 0) {
+ return (ISC_R_BADNUMBER);
}
- *s = 0;
- if ((strncmp(p, "HTTP/1.0", 8) != 0) &&
- (strncmp(p, "HTTP/1.1", 8) != 0)) {
- return (ISC_R_RANGE);
- }
- httpd->protocol = p;
- p = s + delim; /* skip past eol */
- s = p;
-
- httpd->headers = s;
-
- if (!have_header(httpd, "Content-Length:", NULL, NULL, &content_length))
- {
- /* Require a Content-Length header for POST requests. */
- if (httpd->method == METHOD_POST) {
- return (ISC_R_BADNUMBER);
- }
- } else {
- INSIST(content_length != NULL);
-
- size_t clen = (size_t)strtoul(content_length, NULL, 10);
- if (clen == ULONG_MAX) {
- /* Invalid number in the header value. */
- return (ISC_R_BADNUMBER);
- }
- if (httpd->recvlen < httpd->consume + clen) {
- /* The request data isn't complete yet. */
- return (ISC_R_NOTFOUND);
- }
- /* Consume the request's data, which we do not use. */
- httpd->consume += clen;
+ if (content_len == (ssize_t)ULONG_MAX) {
+ /* Invalid number in the header value. */
+ return (ISC_R_BADNUMBER);
}
-
- if (have_header(httpd, "Connection:", "close", ", \t\r\n", NULL)) {
- httpd->flags |= HTTPD_CLOSE;
+ if (httpd->consume + content_len > httpd->recvlen) {
+ /* The request data isn't complete yet. */
+ return (ISC_R_NOMORE);
}
- if (have_header(httpd, "Host:", NULL, NULL, NULL)) {
- httpd->flags |= HTTPD_FOUNDHOST;
- }
+ /* Consume the request's data, which we do not use. */
+ httpd->consume += content_len;
- if (strncmp(httpd->protocol, "HTTP/1.0", 8) == 0) {
- if (have_header(httpd, "Connection:", "Keep-Alive", ", \t\r\n",
- NULL)) {
+ switch (httpd->minor_version) {
+ case 0:
+ if (keep_alive == true) {
httpd->flags |= HTTPD_KEEPALIVE;
} else {
httpd->flags |= HTTPD_CLOSE;
}
- }
-
- /*
- * Check for Accept-Encoding:
- */
-#ifdef HAVE_ZLIB
- if (have_header(httpd, "Accept-Encoding:", "deflate", ";, \t\r\n",
- NULL)) {
- httpd->flags |= HTTPD_ACCEPT_DEFLATE;
- }
-#endif /* ifdef HAVE_ZLIB */
-
- /*
- * Standards compliance hooks here.
- */
- if (strcmp(httpd->protocol, "HTTP/1.1") == 0 &&
- ((httpd->flags & HTTPD_FOUNDHOST) == 0))
- {
- return (ISC_R_RANGE);
+ break;
+ case 1:
+ if ((httpd->flags & HTTPD_FOUNDHOST) == 0) {
+ return (ISC_R_RANGE);
+ }
+ break;
+ default:
+ return (ISC_R_UNEXPECTED);
}
/*
* the next read will overwrite this one instead of appending
* to the buffer.
*/
- *urlend = 0;
return (ISC_R_SUCCESS);
}
httpd->recvbuf[0] = 0;
httpd->recvlen = 0;
httpd->consume = 0;
- httpd->truncated = false;
- httpd->headers = NULL;
httpd->method = METHOD_UNKNOWN;
- httpd->url = NULL;
- httpd->querystring = NULL;
- httpd->protocol = NULL;
httpd->flags = 0;
- isc_buffer_clear(&httpd->headerbuffer);
- isc_buffer_clear(&httpd->compbuffer);
- isc_buffer_invalidate(&httpd->bodybuffer);
+ httpd->minor_version = -1;
+ httpd->path = NULL;
+ httpd->up = (isc_url_parser_t){ 0 };
+ isc_time_set(&httpd->if_modified_since, 0, 0);
+}
+
+static void
+isc__httpd_sendreq_free(isc_httpd_sendreq_t *req) {
+ /* Clean up buffers */
+
+ isc_buffer_free(&req->sendbuffer);
+
+ isc_mem_putanddetach(&req->mctx, req, sizeof(*req));
+}
+
+static isc_httpd_sendreq_t *
+isc__httpd_sendreq_new(isc_httpd_t *httpd) {
+ isc_httpdmgr_t *httpdmgr = httpd->mgr;
+ isc_httpd_sendreq_t *req;
+
+ REQUIRE(VALID_HTTPDMGR(httpdmgr));
+
+ req = isc_mem_get(httpdmgr->mctx, sizeof(*req));
+ *req = (isc_httpd_sendreq_t){ 0 };
+
+ isc_mem_attach(httpdmgr->mctx, &req->mctx);
+
+ /*
+ * Initialize the buffer for our headers.
+ */
+ isc_buffer_allocate(req->mctx, &req->sendbuffer, HTTP_SENDLEN);
+ isc_buffer_clear(req->sendbuffer);
+ isc_buffer_setautorealloc(req->sendbuffer, true);
+
+ isc_buffer_initnull(&req->bodybuffer);
+
+ return (req);
}
static void
httpd->magic = 0;
httpd->mgr = NULL;
- free_buffer(mgr->mctx, &httpd->headerbuffer);
- free_buffer(mgr->mctx, &httpd->compbuffer);
-
isc_mem_put(mgr->mctx, httpd, sizeof(*httpd));
httpdmgr_detach(&mgr);
static void
new_httpd(isc_httpdmgr_t *httpdmgr, isc_nmhandle_t *handle) {
isc_httpd_t *httpd = NULL;
- char *headerdata = NULL;
REQUIRE(VALID_HTTPDMGR(httpdmgr));
INSIST(httpd->handle == handle);
}
- /*
- * Initialize the buffer for our headers.
- */
- headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW);
- isc_buffer_init(&httpd->headerbuffer, headerdata, HTTP_SENDGROW);
- isc_buffer_clear(&httpd->headerbuffer);
-
- isc_buffer_initnull(&httpd->compbuffer);
- isc_buffer_clear(&httpd->compbuffer);
-
- isc_buffer_initnull(&httpd->bodybuffer);
-
ISC_LINK_INIT(httpd, link);
httpd->magic = HTTPD_MAGIC;
- httpd->state = RECV;
LOCK(&httpdmgr->lock);
ISC_LIST_APPEND(httpdmgr->running, httpd, link);
}
static isc_result_t
-render_404(const char *url, isc_httpdurl_t *urlinfo, const char *querystring,
- const char *headers, void *arg, unsigned int *retcode,
- const char **retmsg, const char **mimetype, isc_buffer_t *b,
- isc_httpdfree_t **freecb, void **freecb_args) {
+render_404(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
static char msg[] = "No such URL.\r\n";
- UNUSED(url);
+ UNUSED(httpd);
UNUSED(urlinfo);
- UNUSED(querystring);
- UNUSED(headers);
UNUSED(arg);
*retcode = 404;
}
static isc_result_t
-render_500(const char *url, isc_httpdurl_t *urlinfo, const char *querystring,
- const char *headers, void *arg, unsigned int *retcode,
- const char **retmsg, const char **mimetype, isc_buffer_t *b,
- isc_httpdfree_t **freecb, void **freecb_args) {
+render_500(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
static char msg[] = "Internal server failure.\r\n";
- UNUSED(url);
+ UNUSED(httpd);
UNUSED(urlinfo);
- UNUSED(querystring);
- UNUSED(headers);
UNUSED(arg);
*retcode = 500;
}
#ifdef HAVE_ZLIB
-/*%<
- * Reallocates compbuffer to size; does nothing if compbuffer is already
- * larger than size.
- */
-static void
-alloc_compspace(isc_httpd_t *httpd, unsigned int size) {
- char *newspace = NULL;
- isc_region_t r;
-
- if (size <= isc_buffer_length(&httpd->compbuffer)) {
- return;
- }
-
- isc_buffer_region(&httpd->compbuffer, &r);
- newspace = isc_mem_get(httpd->mgr->mctx, size);
- isc_buffer_reinit(&httpd->compbuffer, newspace, size);
-
- if (r.base != NULL) {
- isc_mem_put(httpd->mgr->mctx, r.base, r.length);
- }
-}
-
/*%<
* Tries to compress httpd->bodybuffer to httpd->compbuffer, extending it
* if necessary.
* data would be larger than input data
*/
static isc_result_t
-httpd_compress(isc_httpd_t *httpd) {
+httpd_compress(isc_httpd_sendreq_t *req) {
z_stream zstr;
int ret, inputlen;
* We're setting output buffer size to input size so it fails if the
* compressed data size would be bigger than the input size.
*/
- inputlen = isc_buffer_usedlength(&httpd->bodybuffer);
- alloc_compspace(httpd, inputlen);
- isc_buffer_clear(&httpd->compbuffer);
+ inputlen = isc_buffer_usedlength(&req->bodybuffer);
+ if (inputlen == 0) {
+ return (ISC_R_FAILURE);
+ }
+
+ isc_buffer_allocate(req->mctx, &req->compbuffer, inputlen);
+ isc_buffer_clear(req->compbuffer);
zstr = (z_stream){
.total_in = inputlen,
.avail_out = inputlen,
.avail_in = inputlen,
- .next_in = isc_buffer_base(&httpd->bodybuffer),
- .next_out = isc_buffer_base(&httpd->compbuffer),
+ .next_in = isc_buffer_base(&req->bodybuffer),
+ .next_out = isc_buffer_base(req->compbuffer),
};
ret = deflateInit(&zstr, Z_DEFAULT_COMPRESSION);
}
deflateEnd(&zstr);
if (ret == Z_STREAM_END) {
- isc_buffer_add(&httpd->compbuffer, zstr.total_out);
+ isc_buffer_add(req->compbuffer, zstr.total_out);
return (ISC_R_SUCCESS);
} else {
+ isc_buffer_free(&req->compbuffer);
return (ISC_R_FAILURE);
}
}
isc_result_t result;
isc_httpd_t *httpd = NULL;
isc_httpdmgr_t *mgr = (isc_httpdmgr_t *)arg;
- isc_buffer_t *databuffer = NULL;
isc_httpdurl_t *url = NULL;
isc_time_t now;
isc_region_t r;
bool is_compressed = false;
char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
- size_t buflen = 0;
+ size_t limit = 0;
+ size_t last_len;
httpd = isc_nmhandle_getdata(handle);
+ REQUIRE(VALID_HTTPD(httpd));
+
REQUIRE(httpd->handle == handle);
if (eresult != ISC_R_SUCCESS) {
goto cleanup_readhandle;
}
- REQUIRE(httpd->state == RECV);
+ REQUIRE(region != NULL);
- result = process_request(
- httpd, region == NULL ? &(isc_region_t){ NULL, 0 } : region,
- &buflen);
- if (result == ISC_R_NOTFOUND) {
- if (buflen < HTTP_RECVLEN - 1) {
- if (region != NULL) {
- /* don't unref, keep reading */
- return;
- }
+ last_len = httpd->recvlen;
+
+ if (httpd->recvlen + region->length > sizeof(httpd->recvbuf)) {
+ goto cleanup_readhandle;
+ }
+
+ /* Store the received data into the recvbuf */
+ REQUIRE(httpd->recvlen + region->length < sizeof(httpd->recvbuf));
+ memmove(httpd->recvbuf + httpd->recvlen, region->base, region->length);
+ httpd->recvlen += region->length;
+again:
+ result = process_request(httpd, last_len);
+
+ if (result == ISC_R_NOMORE) {
+ limit = sizeof(httpd->recvbuf) - httpd->recvlen;
+ if (region->length > limit) {
/*
- * We must have been called from httpd_senddone (as
- * ISC_R_NOTFOUND is not returned from netmgr) and we
- * need to resume reading.
+ * We need more data, but we don't have space to store
+ * it
*/
- isc_nm_read(httpd->readhandle, httpd_request,
- httpd->mgr);
- return;
+ goto cleanup_readhandle;
}
- goto cleanup_readhandle;
- } else if (result != ISC_R_SUCCESS) {
+
+ memmove(httpd->recvbuf + httpd->recvlen, region->base,
+ region->length);
+
+ /* Just wait for more data */
+ return;
+ }
+
+ if (result != ISC_R_SUCCESS) {
goto cleanup_readhandle;
}
- isc_buffer_initnull(&httpd->bodybuffer);
isc_time_now(&now);
isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
+ const char *path = "/";
+ size_t path_len = 1;
+
+ if (httpd->up.field_set & (1 << ISC_UF_PATH)) {
+ path = &httpd->path[httpd->up.field_data[ISC_UF_PATH].off];
+ path_len = httpd->up.field_data[ISC_UF_PATH].len;
+ }
+
LOCK(&mgr->lock);
url = ISC_LIST_HEAD(mgr->urls);
while (url != NULL) {
- if (strcmp(httpd->url, url->url) == 0) {
+ if (strncmp(path, url->url, path_len) == 0) {
break;
}
url = ISC_LIST_NEXT(url, link);
}
UNLOCK(&mgr->lock);
+ isc_httpd_sendreq_t *req = isc__httpd_sendreq_new(httpd);
+
if (url == NULL) {
- result = mgr->render_404(
- httpd->url, NULL, httpd->querystring, NULL, NULL,
- &httpd->retcode, &httpd->retmsg, &httpd->mimetype,
- &httpd->bodybuffer, &httpd->freecb, &httpd->freecb_arg);
+ result = mgr->render_404(httpd, NULL, NULL, &req->retcode,
+ &req->retmsg, &req->mimetype,
+ &req->bodybuffer, &req->freecb,
+ &req->freecb_arg);
} else {
- result = url->action(httpd->url, url, httpd->querystring,
- httpd->headers, url->action_arg,
- &httpd->retcode, &httpd->retmsg,
- &httpd->mimetype, &httpd->bodybuffer,
- &httpd->freecb, &httpd->freecb_arg);
+ result = url->action(httpd, url, url->action_arg, &req->retcode,
+ &req->retmsg, &req->mimetype,
+ &req->bodybuffer, &req->freecb,
+ &req->freecb_arg);
}
if (result != ISC_R_SUCCESS) {
- result = mgr->render_500(
- httpd->url, url, httpd->querystring, NULL, NULL,
- &httpd->retcode, &httpd->retmsg, &httpd->mimetype,
- &httpd->bodybuffer, &httpd->freecb, &httpd->freecb_arg);
+ result = mgr->render_500(httpd, url, NULL, &req->retcode,
+ &req->retmsg, &req->mimetype,
+ &req->bodybuffer, &req->freecb,
+ &req->freecb_arg);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
}
#ifdef HAVE_ZLIB
if ((httpd->flags & HTTPD_ACCEPT_DEFLATE) != 0) {
- result = httpd_compress(httpd);
+ result = httpd_compress(req);
if (result == ISC_R_SUCCESS) {
is_compressed = true;
}
}
#endif /* ifdef HAVE_ZLIB */
- httpd_response(httpd);
+ httpd_response(httpd, req);
if ((httpd->flags & HTTPD_KEEPALIVE) != 0) {
- httpd_addheader(httpd, "Connection", "Keep-Alive");
+ httpd_addheader(req, "Connection", "Keep-Alive");
}
- httpd_addheader(httpd, "Content-Type", httpd->mimetype);
- httpd_addheader(httpd, "Date", datebuf);
- httpd_addheader(httpd, "Expires", datebuf);
+ httpd_addheader(req, "Content-Type", req->mimetype);
+ httpd_addheader(req, "Date", datebuf);
+ httpd_addheader(req, "Expires", datebuf);
if (url != NULL && url->isstatic) {
char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
isc_time_formathttptimestamp(&url->loadtime, loadbuf,
sizeof(loadbuf));
- httpd_addheader(httpd, "Last-Modified", loadbuf);
- httpd_addheader(httpd, "Cache-Control: public", NULL);
+ httpd_addheader(req, "Last-Modified", loadbuf);
+ httpd_addheader(req, "Cache-Control: public", NULL);
} else {
- httpd_addheader(httpd, "Last-Modified", datebuf);
- httpd_addheader(httpd, "Pragma: no-cache", NULL);
- httpd_addheader(httpd, "Cache-Control: no-cache", NULL);
+ httpd_addheader(req, "Last-Modified", datebuf);
+ httpd_addheader(req, "Pragma: no-cache", NULL);
+ httpd_addheader(req, "Cache-Control: no-cache", NULL);
}
- httpd_addheader(httpd, "Server: libisc", NULL);
+ httpd_addheader(req, "Server: libisc", NULL);
if (is_compressed) {
- httpd_addheader(httpd, "Content-Encoding", "deflate");
- httpd_addheaderuint(httpd, "Content-Length",
- isc_buffer_usedlength(&httpd->compbuffer));
+ httpd_addheader(req, "Content-Encoding", "deflate");
+ httpd_addheaderuint(req, "Content-Length",
+ isc_buffer_usedlength(req->compbuffer));
} else {
- httpd_addheaderuint(httpd, "Content-Length",
- isc_buffer_usedlength(&httpd->bodybuffer));
+ httpd_addheaderuint(req, "Content-Length",
+ isc_buffer_usedlength(&req->bodybuffer));
}
- httpd_endheaders(httpd); /* done */
+ httpd_endheaders(req); /* done */
/*
* Append either the compressed or the non-compressed response body to
* the response headers and store the result in httpd->sendbuffer.
*/
- isc_buffer_dup(mgr->mctx, &httpd->sendbuffer, &httpd->headerbuffer);
- isc_buffer_clear(&httpd->headerbuffer);
- isc_buffer_setautorealloc(httpd->sendbuffer, true);
- databuffer = (is_compressed ? &httpd->compbuffer : &httpd->bodybuffer);
- isc_buffer_putmem(httpd->sendbuffer, isc_buffer_base(databuffer),
- isc_buffer_usedlength(databuffer));
+ if (is_compressed) {
+ isc_buffer_putmem(req->sendbuffer,
+ isc_buffer_base(req->compbuffer),
+ isc_buffer_usedlength(req->compbuffer));
+ isc_buffer_free(&req->compbuffer);
+ } else {
+ isc_buffer_putmem(req->sendbuffer,
+ isc_buffer_base(&req->bodybuffer),
+ isc_buffer_usedlength(&req->bodybuffer));
+ }
+
+ /* Free the bodybuffer */
+ if (req->freecb != NULL && isc_buffer_length(&req->bodybuffer) > 0) {
+ req->freecb(&req->bodybuffer, req->freecb_arg);
+ }
/* Consume the request from the recv buffer. */
- if (httpd->consume != 0U) {
- INSIST(httpd->consume <= httpd->recvlen);
- if (httpd->consume < httpd->recvlen) {
- memmove(httpd->recvbuf, httpd->recvbuf + httpd->consume,
- httpd->recvlen - httpd->consume);
- }
- httpd->recvlen -= httpd->consume;
- httpd->consume = 0;
- httpd->recvbuf[httpd->recvlen] = 0;
+ INSIST(httpd->consume != 0);
+ INSIST(httpd->consume <= httpd->recvlen);
+ if (httpd->consume < httpd->recvlen) {
+ memmove(httpd->recvbuf, httpd->recvbuf + httpd->consume,
+ httpd->recvlen - httpd->consume);
}
+ httpd->recvlen -= httpd->consume;
+ httpd->consume = 0;
+ last_len = 0;
+
+ /*
+ * We don't need to attach to httpd here because it gets only cleaned
+ * when the last handle has been detached
+ */
+ req->httpd = httpd;
/*
* Determine total response size.
*/
- isc_buffer_usedregion(httpd->sendbuffer, &r);
+ isc_buffer_usedregion(req->sendbuffer, &r);
- isc_nm_read_stop(httpd->handle);
- httpd->state = SEND;
+ isc_nmhandle_attach(httpd->handle, &req->handle);
+ isc_nm_send(httpd->handle, &r, httpd_senddone, req);
+
+ if ((httpd->flags & HTTPD_CLOSE) != 0) {
+ goto cleanup_readhandle;
+ }
+
+ /*
+ * The request was successfully completed;
+ */
+ if (httpd->recvlen > 0) {
+ goto again;
+ }
- isc_nmhandle_attach(httpd->handle, &httpd->sendhandle);
- isc_nm_send(httpd->sendhandle, &r, httpd_senddone, httpd);
return;
cleanup_readhandle:
+ isc_nm_read_stop(httpd->readhandle);
isc_nmhandle_detach(&httpd->readhandle);
}
httpdmgr_detach(&httpdmgr);
}
-static isc_result_t
-grow_headerspace(isc_httpd_t *httpd) {
- char *newspace = NULL;
- unsigned int newlen;
- isc_region_t r;
-
- isc_buffer_region(&httpd->headerbuffer, &r);
- newlen = r.length + HTTP_SENDGROW;
- if (newlen > HTTP_SEND_MAXLEN) {
- return (ISC_R_NOSPACE);
- }
-
- newspace = isc_mem_get(httpd->mgr->mctx, newlen);
-
- isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen);
-
- isc_mem_put(httpd->mgr->mctx, r.base, r.length);
-
- return (ISC_R_SUCCESS);
-}
-
-static isc_result_t
-httpd_response(isc_httpd_t *httpd) {
+static void
+httpd_response(isc_httpd_t *httpd, isc_httpd_sendreq_t *req) {
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 */
+ result = isc_buffer_printf(req->sendbuffer, "HTTP/1.%u %03u %s\r\n",
+ httpd->minor_version, req->retcode,
+ req->retmsg);
- while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
- result = grow_headerspace(httpd);
- if (result != ISC_R_SUCCESS) {
- return (result);
- }
- }
-
- return (isc_buffer_printf(&httpd->headerbuffer, "%s %03u %s\r\n",
- httpd->protocol, httpd->retcode,
- httpd->retmsg));
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
}
-static isc_result_t
-httpd_addheader(isc_httpd_t *httpd, const char *name, const char *val) {
+static void
+httpd_addheader(isc_httpd_sendreq_t *req, 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) {
- needlen += 2 + strlen(val); /* :<space> and val */
- }
- needlen += 2; /* CRLF */
-
- while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
- result = grow_headerspace(httpd);
- if (result != ISC_R_SUCCESS) {
- return (result);
- }
- }
if (val != NULL) {
- return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n",
- name, val));
+ result = isc_buffer_printf(req->sendbuffer, "%s: %s\r\n", name,
+ val);
} else {
- return (isc_buffer_printf(&httpd->headerbuffer, "%s\r\n",
- name));
+ result = isc_buffer_printf(req->sendbuffer, "%s\r\n", name);
}
+
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
}
-static isc_result_t
-httpd_endheaders(isc_httpd_t *httpd) {
+static void
+httpd_endheaders(isc_httpd_sendreq_t *req) {
isc_result_t result;
- REQUIRE(VALID_HTTPD(httpd));
+ result = isc_buffer_printf(req->sendbuffer, "\r\n");
- while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) {
- result = grow_headerspace(httpd);
- if (result != ISC_R_SUCCESS) {
- return (result);
- }
- }
-
- return (isc_buffer_printf(&httpd->headerbuffer, "\r\n"));
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
}
-static isc_result_t
-httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) {
+static void
+httpd_addheaderuint(isc_httpd_sendreq_t *req, const char *name, int val) {
isc_result_t result;
- unsigned int needlen;
- char buf[sizeof "18446744073709551616"];
-
- REQUIRE(VALID_HTTPD(httpd));
- snprintf(buf, sizeof(buf), "%d", val);
+ result = isc_buffer_printf(req->sendbuffer, "%s: %d\r\n", name, val);
- needlen = strlen(name); /* name itself */
- needlen += 2 + strlen(buf); /* :<space> and val */
- needlen += 2; /* CRLF */
-
- while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) {
- result = grow_headerspace(httpd);
- if (result != ISC_R_SUCCESS) {
- return (result);
- }
- }
-
- return (isc_buffer_printf(&httpd->headerbuffer, "%s: %s\r\n", name,
- buf));
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
}
static void
httpd_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
- isc_httpd_t *httpd = (isc_httpd_t *)arg;
+ isc_httpd_sendreq_t *req = (isc_httpd_sendreq_t *)arg;
+ isc_httpd_t *httpd = req->httpd;
REQUIRE(VALID_HTTPD(httpd));
- REQUIRE(httpd->handle == handle);
-
- /* Clean up buffers */
- isc_buffer_free(&httpd->sendbuffer);
- if (httpd->freecb != NULL && isc_buffer_length(&httpd->bodybuffer) > 0)
- {
- httpd->freecb(&httpd->bodybuffer, httpd->freecb_arg);
- }
-
- isc_nmhandle_detach(&httpd->sendhandle);
-
- if (result != ISC_R_SUCCESS) {
- goto cleanup_readhandle;
- }
- if ((httpd->flags & HTTPD_CLOSE) != 0) {
- goto cleanup_readhandle;
+ if (result != ISC_R_SUCCESS && httpd->readhandle != NULL) {
+ isc_nm_read_stop(httpd->readhandle);
+ isc_nmhandle_close(httpd->readhandle);
+ isc_nmhandle_detach(&httpd->readhandle);
}
- REQUIRE(httpd->state == SEND);
-
- httpd->state = RECV;
- httpd->sendhandle = NULL;
+ isc_nmhandle_detach(&handle);
- if (httpd->recvlen != 0) {
- /*
- * Outstanding requests still exist, start processing
- * them.
- */
- httpd_request(httpd->handle, ISC_R_SUCCESS, NULL, httpd->mgr);
- } else if (!httpd->truncated) {
- isc_nm_read(httpd->readhandle, httpd_request, httpd->mgr);
- } else {
- /* Truncated request, don't resume */
- goto cleanup_readhandle;
- }
-
- return;
-
-cleanup_readhandle:
- isc_nmhandle_detach(&httpd->readhandle);
+ isc__httpd_sendreq_free(req);
}
isc_result_t
UNUSED(fn);
#endif /* ENABLE_AFL */
}
+
+bool
+isc_httpdurl_isstatic(const isc_httpdurl_t *url) {
+ return (url->isstatic);
+}
+
+const isc_time_t *
+isc_httpdurl_loadtime(const isc_httpdurl_t *url) {
+ return (&url->loadtime);
+}
+
+const isc_time_t *
+isc_httpd_if_modified_since(const isc_httpd_t *httpd) {
+ return ((const isc_time_t *)&httpd->if_modified_since);
+}