aboutsummaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authorAlfred Perlstein <alfred@FreeBSD.org>2000-06-15 18:18:43 +0000
committerAlfred Perlstein <alfred@FreeBSD.org>2000-06-15 18:18:43 +0000
commit8f4e4aa5f1519c84c0dc115687069397dc809079 (patch)
tree47af6f814c7947cc81170649ca54d3a85892aa56 /sys/kern
parentda660b48d78242004459a9683d187a9cfeb874f2 (diff)
downloadsrc-8f4e4aa5f1519c84c0dc115687069397dc809079.tar.gz
src-8f4e4aa5f1519c84c0dc115687069397dc809079.zip
add socketoptions DELAYACCEPT and HTTPACCEPT which will not allow an accept()
until the incoming connection has either data waiting or what looks like a HTTP request header already in the socketbuffer. This ought to reduce the context switch time and overhead for processing requests. The initial idea and code for HTTPACCEPT came from Yahoo engineers and has been cleaned up and a more lightweight DELAYACCEPT for non-http servers has been added Reviewed by: silence on hackers.
Notes
Notes: svn path=/head/; revision=61714
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/uipc_sockbuf.c149
-rw-r--r--sys/kern/uipc_socket.c4
-rw-r--r--sys/kern/uipc_socket2.c149
3 files changed, 298 insertions, 4 deletions
diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c
index 6184c233782e..baeb5c7e3dff 100644
--- a/sys/kern/uipc_sockbuf.c
+++ b/sys/kern/uipc_sockbuf.c
@@ -53,6 +53,15 @@
#include <sys/aio.h> /* for aio_swake proto */
#include <sys/event.h>
+/* callbacks/functions for delay accept routines */
+/* socket needs _any_ data to be accepted */
+static void sohasdata(struct socket *so, void *arg, int waitflag);
+/* check for GET/POST */
+static void sohashttpgetorpost(struct socket *so, void *arg, int waitflag);
+/* check for end of HTTP request */
+static void soishttpconnected(struct socket *so, void *arg, int waitflag);
+static char sbindex(struct mbuf **mp, int *begin, int end);
+
int maxsockets;
/*
@@ -102,15 +111,151 @@ soisconnecting(so)
so->so_state |= SS_ISCONNECTING;
}
+static char
+sbindex(struct mbuf **mp, int *begin, int end)
+{
+ struct mbuf *m = *mp;
+ int diff = end - *begin + 1;
+
+ while (m->m_len < diff) {
+ *begin += m->m_len;
+ diff -= m->m_len;
+ if (m->m_next) {
+ m = m->m_next;
+ } else if (m->m_nextpkt) {
+ m = m->m_nextpkt;
+ } else {
+ panic("sbindex: not enough data");
+ }
+ }
+ *mp = m;
+ return *(mtod(m, char *) + diff - 1);
+}
+
+static void
+sohashttpgetorpost(struct socket *so, void *arg, int waitflag)
+{
+
+ if ((so->so_state & SS_CANTRCVMORE) == 0) {
+ struct mbuf *m;
+
+ if (so->so_rcv.sb_cc < 6)
+ return;
+ m = so->so_rcv.sb_mb;
+ if (bcmp(mtod(m, char *), "GET ", 4) == 0 ||
+ bcmp(mtod(m, char *), "POST ", 5) == 0) {
+ soishttpconnected(so, arg, waitflag);
+ }
+ }
+
+ so->so_upcall = NULL;
+ so->so_rcv.sb_flags &= ~SB_UPCALL;
+ soisconnected(so);
+ return;
+}
+
+static void
+soishttpconnected(struct socket *so, void *arg, int waitflag)
+{
+ char a, b, c;
+ struct mbuf *y, *z;
+
+ if ((so->so_state & SS_CANTRCVMORE) == 0) {
+ /* seek to end and keep track of next to last mbuf */
+ y = so->so_rcv.sb_mb;
+ while (y->m_nextpkt)
+ y = y->m_nextpkt;
+ z = y;
+ while (y->m_next) {
+ z = y;
+ y = y->m_next;
+ }
+
+ if (z->m_len + y->m_len > 2) {
+ int index = y->m_len - 1;
+
+ c = *(mtod(y, char *) + index--);
+ switch (index) {
+ case -1:
+ y = z;
+ index = y->m_len - 1;
+ b = *(mtod(y, char *) + index--);
+ break;
+ case 0:
+ b = *(mtod(y, char *) + index--);
+ y = z;
+ index = y->m_len - 1;
+ break;
+ default:
+ b = *(mtod(y, char *) + index--);
+ break;
+ }
+ a = *(mtod(y, char *) + index--);
+ } else {
+ int begin = 0;
+ int end = so->so_rcv.sb_cc - 3;
+
+ y = so->so_rcv.sb_mb;
+ a = sbindex(&y, &begin, end++);
+ b = sbindex(&y, &begin, end++);
+ c = sbindex(&y, &begin, end++);
+ }
+
+ if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
+ /* we have all request headers */
+ goto done;
+ } else {
+ /* still need more data */
+ so->so_upcall = soishttpconnected;
+ so->so_rcv.sb_flags |= SB_UPCALL;
+ return;
+ }
+ }
+
+done:
+ so->so_upcall = NULL;
+ so->so_rcv.sb_flags &= ~SB_UPCALL;
+ soisconnected(so);
+ return;
+}
+
+static void
+sohasdata(struct socket *so, void *arg, int waitflag)
+{
+
+ if (!soreadable(so)) {
+ return;
+ }
+
+ so->so_upcall = NULL;
+ so->so_rcv.sb_flags &= ~SB_UPCALL;
+ soisconnected(so);
+ return;
+}
+
void
soisconnected(so)
- register struct socket *so;
+ struct socket *so;
{
- register struct socket *head = so->so_head;
+ struct socket *head = so->so_head;
+ int s;
so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING);
so->so_state |= SS_ISCONNECTED;
if (head && (so->so_state & SS_INCOMP)) {
+ if ((so->so_options & (SO_DELAYACCEPT|SO_HTTPACCEPT)) != 0) {
+ s = splnet();
+ if (!soreadable(so)) {
+ if ((so->so_options & SO_DELAYACCEPT) != 0)
+ so->so_upcall = sohasdata;
+ else
+ so->so_upcall = sohashttpgetorpost;
+ so->so_rcv.sb_flags |= SB_UPCALL;
+ splx(s);
+ return;
+ }
+ splx(s);
+ }
TAILQ_REMOVE(&head->so_incomp, so, so_list);
head->so_incqlen--;
so->so_state &= ~SS_INCOMP;
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index b172d46b4df1..e83b7827c75f 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -1050,6 +1050,8 @@ sosetopt(so, sopt)
case SO_REUSEPORT:
case SO_OOBINLINE:
case SO_TIMESTAMP:
+ case SO_DELAYACCEPT:
+ case SO_HTTPACCEPT:
error = sooptcopyin(sopt, &optval, sizeof optval,
sizeof optval);
if (error)
@@ -1215,6 +1217,8 @@ sogetopt(so, sopt)
case SO_BROADCAST:
case SO_OOBINLINE:
case SO_TIMESTAMP:
+ case SO_DELAYACCEPT:
+ case SO_HTTPACCEPT:
optval = so->so_options & sopt->sopt_name;
integer:
error = sooptcopyout(sopt, &optval, sizeof optval);
diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c
index 6184c233782e..baeb5c7e3dff 100644
--- a/sys/kern/uipc_socket2.c
+++ b/sys/kern/uipc_socket2.c
@@ -53,6 +53,15 @@
#include <sys/aio.h> /* for aio_swake proto */
#include <sys/event.h>
+/* callbacks/functions for delay accept routines */
+/* socket needs _any_ data to be accepted */
+static void sohasdata(struct socket *so, void *arg, int waitflag);
+/* check for GET/POST */
+static void sohashttpgetorpost(struct socket *so, void *arg, int waitflag);
+/* check for end of HTTP request */
+static void soishttpconnected(struct socket *so, void *arg, int waitflag);
+static char sbindex(struct mbuf **mp, int *begin, int end);
+
int maxsockets;
/*
@@ -102,15 +111,151 @@ soisconnecting(so)
so->so_state |= SS_ISCONNECTING;
}
+static char
+sbindex(struct mbuf **mp, int *begin, int end)
+{
+ struct mbuf *m = *mp;
+ int diff = end - *begin + 1;
+
+ while (m->m_len < diff) {
+ *begin += m->m_len;
+ diff -= m->m_len;
+ if (m->m_next) {
+ m = m->m_next;
+ } else if (m->m_nextpkt) {
+ m = m->m_nextpkt;
+ } else {
+ panic("sbindex: not enough data");
+ }
+ }
+ *mp = m;
+ return *(mtod(m, char *) + diff - 1);
+}
+
+static void
+sohashttpgetorpost(struct socket *so, void *arg, int waitflag)
+{
+
+ if ((so->so_state & SS_CANTRCVMORE) == 0) {
+ struct mbuf *m;
+
+ if (so->so_rcv.sb_cc < 6)
+ return;
+ m = so->so_rcv.sb_mb;
+ if (bcmp(mtod(m, char *), "GET ", 4) == 0 ||
+ bcmp(mtod(m, char *), "POST ", 5) == 0) {
+ soishttpconnected(so, arg, waitflag);
+ }
+ }
+
+ so->so_upcall = NULL;
+ so->so_rcv.sb_flags &= ~SB_UPCALL;
+ soisconnected(so);
+ return;
+}
+
+static void
+soishttpconnected(struct socket *so, void *arg, int waitflag)
+{
+ char a, b, c;
+ struct mbuf *y, *z;
+
+ if ((so->so_state & SS_CANTRCVMORE) == 0) {
+ /* seek to end and keep track of next to last mbuf */
+ y = so->so_rcv.sb_mb;
+ while (y->m_nextpkt)
+ y = y->m_nextpkt;
+ z = y;
+ while (y->m_next) {
+ z = y;
+ y = y->m_next;
+ }
+
+ if (z->m_len + y->m_len > 2) {
+ int index = y->m_len - 1;
+
+ c = *(mtod(y, char *) + index--);
+ switch (index) {
+ case -1:
+ y = z;
+ index = y->m_len - 1;
+ b = *(mtod(y, char *) + index--);
+ break;
+ case 0:
+ b = *(mtod(y, char *) + index--);
+ y = z;
+ index = y->m_len - 1;
+ break;
+ default:
+ b = *(mtod(y, char *) + index--);
+ break;
+ }
+ a = *(mtod(y, char *) + index--);
+ } else {
+ int begin = 0;
+ int end = so->so_rcv.sb_cc - 3;
+
+ y = so->so_rcv.sb_mb;
+ a = sbindex(&y, &begin, end++);
+ b = sbindex(&y, &begin, end++);
+ c = sbindex(&y, &begin, end++);
+ }
+
+ if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
+ /* we have all request headers */
+ goto done;
+ } else {
+ /* still need more data */
+ so->so_upcall = soishttpconnected;
+ so->so_rcv.sb_flags |= SB_UPCALL;
+ return;
+ }
+ }
+
+done:
+ so->so_upcall = NULL;
+ so->so_rcv.sb_flags &= ~SB_UPCALL;
+ soisconnected(so);
+ return;
+}
+
+static void
+sohasdata(struct socket *so, void *arg, int waitflag)
+{
+
+ if (!soreadable(so)) {
+ return;
+ }
+
+ so->so_upcall = NULL;
+ so->so_rcv.sb_flags &= ~SB_UPCALL;
+ soisconnected(so);
+ return;
+}
+
void
soisconnected(so)
- register struct socket *so;
+ struct socket *so;
{
- register struct socket *head = so->so_head;
+ struct socket *head = so->so_head;
+ int s;
so->so_state &= ~(SS_ISCONNECTING|SS_ISDISCONNECTING|SS_ISCONFIRMING);
so->so_state |= SS_ISCONNECTED;
if (head && (so->so_state & SS_INCOMP)) {
+ if ((so->so_options & (SO_DELAYACCEPT|SO_HTTPACCEPT)) != 0) {
+ s = splnet();
+ if (!soreadable(so)) {
+ if ((so->so_options & SO_DELAYACCEPT) != 0)
+ so->so_upcall = sohasdata;
+ else
+ so->so_upcall = sohashttpgetorpost;
+ so->so_rcv.sb_flags |= SB_UPCALL;
+ splx(s);
+ return;
+ }
+ splx(s);
+ }
TAILQ_REMOVE(&head->so_incomp, so, so_list);
head->so_incqlen--;
so->so_state &= ~SS_INCOMP;