From: Mark Andrews Date: Tue, 26 Oct 2021 04:28:36 +0000 (+1100) Subject: Handle HTTP/1.1 pipelined requests X-Git-Tag: v9.17.20~6^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=49531e45821bbebebbd097337da906042c3e964c;p=thirdparty%2Fbind9.git 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. --- diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index 06aaad39c25..ff6c6e4b677 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -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@ diff --git a/bin/tests/system/statistics/tests.sh b/bin/tests/system/statistics/tests.sh index 4cb80081806..7e0190c6044 100644 --- a/bin/tests/system/statistics/tests.sh +++ b/bin/tests/system/statistics/tests.sh @@ -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 diff --git a/bin/tests/system/statschannel/clean.sh b/bin/tests/system/statschannel/clean.sh index 2e13b1a9a5a..03cb1bb87d1 100644 --- a/bin/tests/system/statschannel/clean.sh +++ b/bin/tests/system/statschannel/clean.sh @@ -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* diff --git a/bin/tests/system/statschannel/tests.sh b/bin/tests/system/statschannel/tests.sh index bbbc8a0c97a..ce145b7c3e7 100644 --- a/bin/tests/system/statschannel/tests.sh +++ b/bin/tests/system/statschannel/tests.sh @@ -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 diff --git a/configure.ac b/configure.ac index fa1dbae1628..17ca8eb60c7 100644 --- a/configure.ac +++ b/configure.ac @@ -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 # diff --git a/lib/isc/httpd.c b/lib/isc/httpd.c index 9596b8e0af6..0787da8c185 100644 --- a/lib/isc/httpd.c +++ b/lib/isc/httpd.c @@ -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