From 8f4e4aa5f1519c84c0dc115687069397dc809079 Mon Sep 17 00:00:00 2001 From: Alfred Perlstein Date: Thu, 15 Jun 2000 18:18:43 +0000 Subject: [PATCH] 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. --- sys/kern/uipc_sockbuf.c | 149 +++++++++++++++++++++++++++++++++++++++- sys/kern/uipc_socket.c | 4 ++ sys/kern/uipc_socket2.c | 149 +++++++++++++++++++++++++++++++++++++++- sys/sys/socket.h | 2 + 4 files changed, 300 insertions(+), 4 deletions(-) diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c index 6184c233782..baeb5c7e3df 100644 --- a/sys/kern/uipc_sockbuf.c +++ b/sys/kern/uipc_sockbuf.c @@ -53,6 +53,15 @@ #include /* for aio_swake proto */ #include +/* 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 b172d46b4df..e83b7827c75 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 6184c233782..baeb5c7e3df 100644 --- a/sys/kern/uipc_socket2.c +++ b/sys/kern/uipc_socket2.c @@ -53,6 +53,15 @@ #include /* for aio_swake proto */ #include +/* 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/sys/socket.h b/sys/sys/socket.h index b71e206c61c..630edcad1c4 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -70,6 +70,8 @@ typedef u_int32_t socklen_t; #define SO_OOBINLINE 0x0100 /* leave received OOB data in line */ #define SO_REUSEPORT 0x0200 /* allow local address & port reuse */ #define SO_TIMESTAMP 0x0400 /* timestamp received dgram traffic */ +#define SO_DELAYACCEPT 0x1000 /* delay accept until data arrived */ +#define SO_HTTPACCEPT 0x2000 /* delay accept until http request */ /* * Additional options, not kept in so_options.