--- /dev/null	Sat Nov 22 13:16:58 2014
+++ new/usr/src/uts/common/inet/sockmods/httpfilt.c	Sat Nov 22 13:16:58 2014
@@ -0,0 +1,280 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2012, OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2014 Ryan Zezeski
+ */
+
+/*
+ * This file implements a socketfilter used to defer HTTP connections.
+ * To defer a connection means to delay the return of accept(3SOCKET)
+ * until the entire HTTP request is ready (or majority in case of PUT
+ * and POST). This filter may be applied automatically or
+ * programmatically through the use of soconfig(1M) and
+ * setsockopt(3SOCKET).
+ *
+ * The point of this filter is to defer connections, not parse them.
+ * It is up to the webserver to service the request. This filter
+ * accepts both valid an invalid HTTP requests. A valid HTTP request
+ * is deferred until the CRLF sequence that separates the headers from
+ * the optional body is seen. An invalid request is one that doesn't
+ * start with a valid HTTP verb. In either case the connection will be
+ * accepted once MAX_DEFERRED_BYTES have been seen.
+ *
+ * This filter does its best to be accommodating to non-standard HTTP
+ * verbs, instead of restricting deferment to a small subset of verbs.
+ * It accepts a subset of the BNF specified in RFC2616: any sequence
+ * of capital ASCII letters, hyphen, or underscore.
+ *
+ * [A-Z_-]+
+ *
+ * This means that the filter can be tricked into thinking a request
+ * is HTTP when it is not. E.g. a request of 'GET ...' where <...>
+ * contains no LFCR or LFLF sequence. In this case, if the max bytes
+ * are never reached, the connection will stay deferred until either
+ * the client closes or the socketfilter system feels pressure and
+ * schedules this connection for close (see so_newconn() and
+ * sof_sonode_drop_deferred()).
+ *
+ * Note that req_fsm() looks for a sequence of LFCR or LFLF to
+ * indicate a complete request. This is a deliberate deviation from
+ * strict CRLFCRLF. Not all clients respect RFC2616. Popular
+ * webservers, such as nginx, will accept just LF, with no need for
+ * CR. The sequences LFCR and LFLF accept strict and non-strict
+ * clients.
+ */
+
+#include <sys/kmem.h>
+#include <sys/systm.h>
+#include <sys/stropts.h>
+#include <sys/strsun.h>
+#include <sys/socketvar.h>
+#include <sys/sockfilter.h>
+#include <sys/note.h>
+#include <sys/taskq.h>
+
+#define	HTTPFILT_MODULE		"httpfilt"
+#define	MAX_DEFERRED_BYTES	8192
+
+enum req_state { REQS_START, REQS_VERB, REQS_HEAD, REQS_CRLF, REQS_END };
+
+/*
+ * This structure is created during initialization and then passed to
+ * all callbacks so that state may be kept as message blocks arrive.
+ * This information determines when a connection will be accepted at
+ * the user socket level.
+ */
+typedef struct httpf {
+	size_t		httpf_bytes_in;	/* bytes read */
+	enum req_state	httpf_rs;	/* request state */
+} httpf_t;
+
+static struct modlmisc httpf_modlmisc = {
+	&mod_miscops,
+	"Kernel HTTP socket filter"
+};
+
+static struct modlinkage httpf_modlinkage = {
+	MODREV_1,
+	&httpf_modlmisc,
+	NULL
+};
+
+/*
+ * An FSM to determine how long the socket shall remain deferred.
+ * Given the current state and char, return the next state. The
+ * REQS_END indicates that the socket should be accepted.
+ *
+ *
+ *     +------------+           A request must start with an HTTP verb.
+ *  +--|    START   |   	This FSM will accept any verb consisting
+ *  |  +------------+		of all capital ASCII letters, hyphen, and
+ *  |        | [A-Z_-]          underscore. Any violation will move the
+ *  |        |                  FSM into the END state.
+ *  |        |    [A-Z_-]
+ *  |        |    +----+
+ * [^A-Z_-]  V    V    |
+ *  |  +------------+  |
+ *  +--|    VERB    |--+
+ *  |  +------------+
+ *  |   |
+ *  |   | ' '     [^\n]
+ *  |   |         +----+
+ *  |   V         V    |
+ *  |  +------------+  |       	This state transverses the rest of the
+ *  |  |    HEAD    |--+	the request line and all the headers.
+ *  |  +------------+		A line feed potentially indicates the
+ *  |   |          ^		end of the headers and thus the CRLF
+ *  |   | \n       | [^\r\n]	state is entered.
+ *  |   |          |
+ *  |   V          |
+ *  |  +------------+           If an LFCR or LFLF sequence is seen then
+ *  |  |    CRLF    |		all headers have been seen and the
+ *  |  +------------+		end of the request has been reached (the
+ *  |   |			optional body is not considered for
+ *  |   | [\r\n]		deferment). If the end of the request
+ *  |   |			has not been reached then return to
+ *  |   V			HEAD.
+ *  |  +------------+
+ *  +->|    END     |		Either the end of the request has been
+ *     +------------+		reached or it's not an HTTP request at
+ *				all. In any case the connection is
+ *                              accepted.
+ */
+static int
+req_fsm(enum req_state s, char ch)
+{
+	switch (s) {
+	case REQS_START:
+		if ((ch >= 'A' && ch <= 'Z') || ch == '-' || ch == '_')
+			return (REQS_VERB);
+		else
+			return (REQS_END);
+
+	case REQS_VERB:
+		if ((ch >= 'A' && ch <= 'Z') || ch == '-' || ch == '_')
+			return (REQS_VERB);
+		else if (ch == ' ')
+			return (REQS_HEAD);
+		else
+			return (REQS_END);
+
+	case REQS_HEAD:
+		return (ch == '\n' ? REQS_CRLF : REQS_HEAD);
+
+	case REQS_CRLF:
+		return ((ch == '\r' || ch == '\n') ? REQS_END : REQS_HEAD);
+
+	case REQS_END:
+		/*
+		 * Should never get here, make the compiler happy.
+		 */
+		return (REQS_END);
+	}
+
+	/* Make the compiler happy. */
+	return (REQS_END);
+}
+
+/*
+ * Return 0 to continue processing, 1 to accept the connection.
+ */
+static int
+httpf_process_input(httpf_t *httpf, mblk_t *mp)
+{
+	int i, dlen = MBLKL(mp);
+
+	for (i = 0; i < dlen; i++, httpf->httpf_bytes_in++) {
+		httpf->httpf_rs = req_fsm(httpf->httpf_rs, mp->b_rptr[i]);
+		if (httpf->httpf_rs == REQS_END)
+			return (1);
+	}
+
+	if (httpf->httpf_bytes_in >= MAX_DEFERRED_BYTES)
+		return (1);
+
+	return (0);
+}
+
+static sof_rval_t
+httpf_attach_passive_cb(sof_handle_t handle, sof_handle_t ph,
+    void *parg, struct sockaddr *laddr, socklen_t laddrlen,
+    struct sockaddr *faddr, socklen_t faddrlen, void **cookiep)
+{
+	httpf_t *new;
+
+	_NOTE(ARGUNUSED(handle, ph, parg, faddr, faddrlen, laddr, laddrlen));
+
+	new = kmem_zalloc(sizeof (httpf_t), KM_NOSLEEP | KM_NORMALPRI);
+	if (new == NULL)
+		return (SOF_RVAL_ENOMEM);
+
+	new->httpf_bytes_in = 0;
+	new->httpf_rs = REQS_START;
+	*cookiep = new;
+
+	return (SOF_RVAL_DEFER);
+}
+
+static void
+httpf_detach_cb(sof_handle_t handle, void *cookie, cred_t *cr)
+{
+	httpf_t *httpf = (httpf_t *)cookie;
+
+	_NOTE(ARGUNUSED(handle, cr));
+
+	if (httpf == NULL)
+		return;
+
+	kmem_free(httpf, sizeof (httpf_t));
+}
+
+static mblk_t *
+httpf_data_in_cb(sof_handle_t handle, void *cookie, mblk_t *mp, int flags,
+    size_t *lenp)
+{
+	httpf_t	*httpf = cookie;
+
+	_NOTE(ARGUNUSED(flags, lenp));
+
+	if (httpf == NULL) {
+		sof_bypass(handle);
+		return (mp);
+	}
+
+	if (mp == NULL)
+		return (mp);
+
+	if (httpf_process_input(httpf, mp) == 1) {
+		sof_newconn_ready(handle);
+		sof_bypass(handle);
+	}
+
+	return (mp);
+}
+
+static sof_ops_t httpf_ops = {
+	.sofop_attach_passive = httpf_attach_passive_cb,
+	.sofop_detach = httpf_detach_cb,
+	.sofop_data_in = httpf_data_in_cb,
+};
+
+int
+_init(void)
+{
+	int err;
+
+	err = sof_register(SOF_VERSION, HTTPFILT_MODULE, &httpf_ops, 0);
+	if (err != 0)
+		return (err);
+	if ((err = mod_install(&httpf_modlinkage)) != 0)
+		(void) sof_unregister(HTTPFILT_MODULE);
+
+	return (err);
+}
+
+int
+_fini(void)
+{
+	int error;
+
+	if ((error = sof_unregister(HTTPFILT_MODULE)) != 0)
+		return (error);
+
+	return (mod_remove(&httpf_modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&httpf_modlinkage, modinfop));
+}
