Handle HTTP/1.1 pipelined requests

Check to see whether there are outstanding requests in the
httpd receive buffer after sending the response, and if so,
process them.

Test that pipelined requests are handled by sending multiple
minimal HTTP/1.1 using netcat (nc) and checking that we get
back the same number of responses.
This commit is contained in:
Mark Andrews
2021-10-26 15:28:36 +11:00
committed by Evan Hunt
parent e46c64bf42
commit 49531e4582
6 changed files with 71 additions and 9 deletions

View File

@@ -112,6 +112,9 @@ SHELL=@SHELL@
# CURL will be empty if no program was found by configure
CURL=@CURL@
# NC will be empty if no program was found by configure
NC=@NC@
# XMLLINT will be empty if no program was found by configure
XMLLINT=@XMLLINT@

View File

@@ -175,8 +175,12 @@ echo_i "checking bind9.xsl vs xml ($n)"
if $FEATURETEST --have-libxml2 && [ -x "${CURL}" ] && [ -x "${XSLTPROC}" ] ; then
$DIGCMD +notcp +recurse @10.53.0.3 soa . > dig.out.test$n.1 2>&1
$DIGCMD +notcp +recurse @10.53.0.3 soa example > dig.out.test$n.2 2>&1
${CURL} http://10.53.0.3:${EXTRAPORT1}/xml/v3 > curl.out.${n}.xml 2>/dev/null || ret=1
${CURL} http://10.53.0.3:${EXTRAPORT1}/bind9.xsl > curl.out.${n}.xsl 2>/dev/null || ret=1
# check multiple requests over the same socket
time1=$($PERL -e 'print time(), "\n";')
${CURL} --http1.1 -o curl.out.${n}.xml http://10.53.0.3:${EXTRAPORT1}/xml/v3 \
-o curl.out.${n}.xsl http://10.53.0.3:${EXTRAPORT1}/bind9.xsl 2>/dev/null || ret=1
time2=$($PERL -e 'print time(), "\n";')
test $((time2 - time1)) -lt 5 || ret=1
${DIFF} ${TOP_SRCDIR}/bin/named/bind9.xsl curl.out.${n}.xsl || ret=1
${XSLTPROC} curl.out.${n}.xsl - < curl.out.${n}.xml > xsltproc.out.${n} 2>/dev/null || ret=1
cp curl.out.${n}.xml stats.xml.out || ret=1

View File

@@ -28,3 +28,4 @@ rm -f xml.*mem json.*mem
rm -f xml.*stats json.*stats
rm -f zones zones.out.* zones.json.* zones.xml.* zones.expect.*
rm -rf ./__pycache__
rm -f nc.out*

View File

@@ -374,5 +374,26 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
if [ -x "${NC}" ] ; then
echo_i "Check HTTP/1.1 pipelined requests are handled ($n)"
ret=0
${NC} 10.53.0.3 ${EXTRAPORT1} << EOF > nc.out$n || ret=1
GET /xml/v3/status HTTP/1.1
Host: 10.53.0.3:${EXTRAPORT1}
GET /xml/v3/status HTTP/1.1
Host: 10.53.0.3:${EXTRAPORT1}
Connection: close
EOF
lines=$(grep "^HTTP/1.1" nc.out$n | wc -l)
test $lines = 2 || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
else
echo_i "skipping test as nc not found"
fi
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1

View File

@@ -1256,6 +1256,13 @@ AC_CONFIG_FILES([doc/doxygen/doxygen-input-filter],
AC_PATH_PROG(CURL, curl, curl)
AC_SUBST(CURL)
#
# Look for nc
#
AC_PATH_PROGS(NC, nc, nc)
AC_SUBST(NC)
#
# IDN support using libidn2
#

View File

@@ -403,11 +403,14 @@ process_request(isc_httpd_t *httpd, isc_region_t *region, size_t *buflen) {
len = limit;
}
memmove(httpd->recvbuf + httpd->recvlen, region->base, len);
httpd->recvlen += len;
httpd->recvbuf[httpd->recvlen] = 0;
*buflen = httpd->recvlen;
if (len > 0U) {
memmove(httpd->recvbuf + httpd->recvlen, region->base, len);
httpd->recvlen += len;
httpd->recvbuf[httpd->recvlen] = 0;
isc_region_consume(region, len);
}
httpd->headers = NULL;
*buflen = httpd->recvlen;
/*
* If we don't find a blank line in our buffer, return that we need
@@ -858,10 +861,22 @@ httpd_request(isc_nmhandle_t *handle, isc_result_t eresult,
goto cleanup_readhandle;
}
result = process_request(httpd, region, &buflen);
result = process_request(
httpd, region == NULL ? &(isc_region_t){ NULL, 0 } : region,
&buflen);
if (result == ISC_R_NOTFOUND) {
if (buflen < HTTP_RECVLEN - 1) {
/* don't unref, keep reading */
if (region != NULL) {
/* don't unref, keep reading */
return;
}
/*
* We have been called from httpd_senddone
* and we need to resume reading. Detach
* readhandle before resuming.
*/
isc_nmhandle_detach(&httpd->readhandle);
isc_nm_resumeread(handle);
return;
}
goto cleanup_readhandle;
@@ -950,6 +965,7 @@ httpd_request(isc_nmhandle_t *handle, isc_result_t eresult,
* 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_usedregion(databuffer, &r);
@@ -965,6 +981,7 @@ httpd_request(isc_nmhandle_t *handle, isc_result_t eresult,
}
httpd->recvlen -= httpd->consume;
httpd->consume = 0;
httpd->recvbuf[httpd->recvlen] = 0;
}
/*
@@ -1150,7 +1167,16 @@ httpd_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
}
httpd->state = RECV;
isc_nm_resumeread(handle);
if (httpd->recvlen != 0) {
/*
* Outstanding requests still exist, start processing
* them.
*/
isc_nmhandle_attach(handle, &httpd->readhandle);
httpd_request(handle, ISC_R_SUCCESS, NULL, httpd->mgr);
} else {
isc_nm_resumeread(handle);
}
}
isc_result_t