mirror of
https://github.com/opnsense/src.git
synced 2026-06-09 08:43:19 -04:00
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.
This commit is contained in:
parent
da660b48d7
commit
8f4e4aa5f1
4 changed files with 300 additions and 4 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue