2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 06:25:31 +00:00

Merge branch 'ondrej-refactor-isc_httpd' into 'main'

Rewrite isc_httpd using picohttpparser and isc_url_parse

See merge request isc-projects/bind9!6879
This commit is contained in:
Ondřej Surý
2022-10-14 10:23:21 +00:00
11 changed files with 1445 additions and 863 deletions

View File

@@ -1,3 +1,6 @@
5994. [func] Refactor the isc_httpd implementation used in the
statistics channel. [GL !6879]
5993. [cleanup] Store dns_name_t attributes as boolean members of
the structure. Remove DNS_NAMEATTR_* macros.
Fix latent attribute handling bug in RBT. [GL !6902]

View File

@@ -2376,20 +2376,14 @@ wrap_xmlfree(isc_buffer_t *buffer, void *arg) {
}
static isc_result_t
render_xml(uint32_t flags, const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg, const char **mimetype,
isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
render_xml(uint32_t flags, void *arg, unsigned int *retcode,
const char **retmsg, const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
unsigned char *msg = NULL;
int msglen;
named_server_t *server = arg;
isc_result_t result;
UNUSED(url);
UNUSED(urlinfo);
UNUSED(headers);
UNUSED(querystring);
result = generatexml(server, flags, &msglen, &msg);
if (result == ISC_R_SUCCESS) {
@@ -2410,91 +2404,91 @@ render_xml(uint32_t flags, const char *url, isc_httpdurl_t *urlinfo,
}
static isc_result_t
render_xml_all(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_xml_all(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
void **freecb_args) {
return (render_xml(STATS_XML_ALL, url, urlinfo, querystring, headers,
arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_xml(STATS_XML_ALL, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_xml_status(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_xml_status(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_xml(STATS_XML_STATUS, url, urlinfo, querystring, headers,
arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_xml(STATS_XML_STATUS, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_xml_server(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_xml_server(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_xml(STATS_XML_SERVER, url, urlinfo, querystring, headers,
arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_xml(STATS_XML_SERVER, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_xml_zones(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_xml_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_xml(STATS_XML_ZONES, url, urlinfo, querystring, headers,
arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_xml(STATS_XML_ZONES, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_xml_net(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_xml_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
void **freecb_args) {
return (render_xml(STATS_XML_NET, url, urlinfo, querystring, headers,
arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_xml(STATS_XML_NET, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_xml_tasks(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_xml_tasks(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_xml(STATS_XML_TASKS, url, urlinfo, querystring, headers,
arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_xml(STATS_XML_TASKS, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_xml_mem(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_xml_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb,
void **freecb_args) {
return (render_xml(STATS_XML_MEM, url, urlinfo, querystring, headers,
arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_xml(STATS_XML_MEM, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_xml_traffic(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_xml_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_xml(STATS_XML_TRAFFIC, url, urlinfo, querystring,
headers, arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_xml(STATS_XML_TRAFFIC, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
#endif /* HAVE_LIBXML2 */
@@ -3320,10 +3314,9 @@ cleanup:
}
static isc_result_t
render_json(uint32_t flags, const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg, const char **mimetype,
isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
render_json(uint32_t flags, void *arg, unsigned int *retcode,
const char **retmsg, const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
isc_result_t result;
json_object *bindstats = NULL;
named_server_t *server = arg;
@@ -3331,11 +3324,6 @@ render_json(uint32_t flags, const char *url, isc_httpdurl_t *urlinfo,
size_t msglen = 0;
char *p;
UNUSED(url);
UNUSED(urlinfo);
UNUSED(headers);
UNUSED(querystring);
result = generatejson(server, &msglen, &msg, &bindstats, flags);
if (result == ISC_R_SUCCESS) {
*retcode = 200;
@@ -3356,156 +3344,139 @@ render_json(uint32_t flags, const char *url, isc_httpdurl_t *urlinfo,
}
static isc_result_t
render_json_all(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_json_all(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_json(STATS_JSON_ALL, url, urlinfo, querystring, headers,
arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_json(STATS_JSON_ALL, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_json_status(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_json_status(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_json(STATS_JSON_STATUS, url, urlinfo, querystring,
headers, arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_json(STATS_JSON_STATUS, arg, retcode, retmsg, mimetype,
b, freecb, freecb_args));
}
static isc_result_t
render_json_server(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_json_server(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_json(STATS_JSON_SERVER, url, urlinfo, querystring,
headers, arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_json(STATS_JSON_SERVER, arg, retcode, retmsg, mimetype,
b, freecb, freecb_args));
}
static isc_result_t
render_json_zones(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_json_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_json(STATS_JSON_ZONES, url, urlinfo, querystring,
headers, arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_json(STATS_JSON_ZONES, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_json_mem(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_json_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_json(STATS_JSON_MEM, url, urlinfo, querystring, headers,
arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_json(STATS_JSON_MEM, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_json_tasks(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_json_tasks(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_json(STATS_JSON_TASKS, url, urlinfo, querystring,
headers, arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_json(STATS_JSON_TASKS, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_json_net(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_json_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_json(STATS_JSON_NET, url, urlinfo, querystring, headers,
arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_json(STATS_JSON_NET, arg, retcode, retmsg, mimetype, b,
freecb, freecb_args));
}
static isc_result_t
render_json_traffic(const char *url, isc_httpdurl_t *urlinfo,
const char *querystring, const char *headers, void *arg,
unsigned int *retcode, const char **retmsg,
render_json_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo,
void *arg, unsigned int *retcode, const char **retmsg,
const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
return (render_json(STATS_JSON_TRAFFIC, url, urlinfo, querystring,
headers, arg, retcode, retmsg, mimetype, b, freecb,
freecb_args));
UNUSED(httpd);
UNUSED(urlinfo);
return (render_json(STATS_JSON_TRAFFIC, arg, retcode, retmsg, mimetype,
b, freecb, freecb_args));
}
#endif /* HAVE_JSON_C */
static isc_result_t
render_xsl(const char *url, isc_httpdurl_t *urlinfo, const char *querystring,
const char *headers, void *args, unsigned int *retcode,
const char **retmsg, const char **mimetype, isc_buffer_t *b,
isc_httpdfree_t **freecb, void **freecb_args) {
render_xsl(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *args,
unsigned int *retcode, const char **retmsg, const char **mimetype,
isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
isc_result_t result;
char *_headers = NULL;
char *p;
char *p = NULL;
UNUSED(url);
UNUSED(querystring);
UNUSED(httpd);
UNUSED(args);
*freecb = NULL;
*freecb_args = NULL;
*mimetype = "text/xslt+xml";
if (urlinfo->isstatic) {
isc_time_t when;
char *line, *saveptr;
const char *if_modified_since = "If-Modified-Since: ";
_headers = strdup(headers);
if (isc_httpdurl_isstatic(urlinfo)) {
time_t t1, t2;
const isc_time_t *when;
const isc_time_t *loadtime;
if (_headers == NULL) {
when = isc_httpd_if_modified_since(httpd);
if (isc_time_isepoch(when)) {
goto send;
}
saveptr = NULL;
for (line = strtok_r(_headers, "\n", &saveptr); line;
line = strtok_r(NULL, "\n", &saveptr))
{
if (strncasecmp(line, if_modified_since,
strlen(if_modified_since)) == 0) {
time_t t1, t2;
line += strlen(if_modified_since);
result = isc_time_parsehttptimestamp(line,
&when);
if (result != ISC_R_SUCCESS) {
goto send;
}
result = isc_time_secondsastimet(&when, &t1);
if (result != ISC_R_SUCCESS) {
goto send;
}
result = isc_time_secondsastimet(
&urlinfo->loadtime, &t2);
if (result != ISC_R_SUCCESS) {
goto send;
}
if (t1 < t2) {
goto send;
}
*retcode = 304;
*retmsg = "Not modified";
goto end;
}
result = isc_time_secondsastimet(when, &t1);
if (result != ISC_R_SUCCESS) {
goto send;
}
loadtime = isc_httpdurl_loadtime(urlinfo);
result = isc_time_secondsastimet(loadtime, &t2);
if (result != ISC_R_SUCCESS) {
goto send;
}
if (t1 < t2) {
goto send;
}
*retcode = 304;
*retmsg = "Not modified";
goto end;
}
send:
@@ -3515,7 +3486,6 @@ send:
isc_buffer_reinit(b, p, strlen(xslmsg));
isc_buffer_add(b, strlen(xslmsg));
end:
free(_headers);
return (ISC_R_SUCCESS);
}

View File

@@ -1,46 +0,0 @@
#!/usr/bin/perl
#
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
#
# SPDX-License-Identifier: MPL-2.0
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
#
# Send a file to a given address and port using TCP. Used for
# configuring the test server in ans.pl.
#
use IO::File;
use IO::Socket;
@ARGV == 2 or die "usage: send.pl host port\n";
my $host = shift @ARGV;
my $port = shift @ARGV;
my $sock = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $port,
Proto => "tcp",) or die "$!";
#send the file
while ($n = read(STDIN, $buf, 64000)) {
$sock->syswrite($buf, $n);
}
#get the response with with a 15 second timeout
my $rin;
my $rout;
my $n;
do {
$rin = '';
vec($rin, fileno($sock), 1) = 1;
$n = select($rout = $rin, undef, undef, 15);
$n = $sock->sysread($buf, 64000) if ($n > 0);
print STDOUT $buf if ($n > 0);
} while ($n > 0);
$sock->close;

View File

@@ -388,8 +388,8 @@ Host: 10.53.0.3:${EXTRAPORT1}
Connection: close
EOF
lines=$(grep "^HTTP/1.1" nc.out$n | wc -l)
test $lines = 2 || ret=1
lines=$(grep -c "^HTTP/1.1" nc.out$n)
test "$lines" = 2 || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
@@ -415,8 +415,8 @@ Connection: close
{}
EOF
lines=$(grep "^HTTP/1.1" nc.out$n | wc -l)
test $lines = 2 || ret=1
lines=$(grep -c "^HTTP/1.1" nc.out$n)
test "$lines" = 2 || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
@@ -424,29 +424,54 @@ else
echo_i "skipping test as nc not found"
fi
echo_i "Check HTTP with more than 10 headers ($n)"
ret=0
i=0
# build input stream.
printf 'GET /xml/v3/status HTTP/1.1\r\nHost: 10.53.0.3\r\n\r\n' > send.in$n
printf 'GET /xml/v3/status HTTP/1.1\r\nHost: 10.53.0.3\r\n' >> send.in$n
while test $i -lt 11
do
printf 'X-Bloat: VGhlIG1vc3QgY29tbW9uIHJlYXNvbiBmb3IgYmxvYXRpbmcgaXMgaGF2aW5nIGEgbG90IG9mIGdhcyBpbiB5b3VyIGd1dC4gCg==\r\n' >> send.in$n
i=$((i+1))
done
printf '\r\n' >> send.in$n
# send the requests then wait for named to close the socket.
time1=$($PERL -e 'print time(), "\n";')
${NC} 10.53.0.3 ${EXTRAPORT1} < send.in$n > send.out$n
time2=$($PERL -e 'print time(), "\n";')
test $((time2 - time1)) -lt 5 || ret=1
# we expect 1 request to be processed.
lines=$(grep -c "^HTTP/1.1" send.out$n)
test $lines = 1 || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "Check HTTP/1.1 pipelined with truncated stream ($n)"
ret=0
i=0
# build input stream.
cp /dev/null send.in$n
while test $i -lt 500
printf 'GET /xml/v3/status HTTP/1.1\r\nHost: 10.53.0.3\r\n\r\n' > send.in$n
printf 'GET /xml/v3/status HTTP/1.1\r\nHost: 10.53.0.3\r\nX-Bloat:' >> send.in$n
while test $i -lt 5000
do
cat >> send.in$n << EOF
GET /xml/v3/status HTTP/1.1
Host: 10.53.0.3
EOF
printf '%s' "VGhlIG1vc3QgY29tbW9uIHJlYXNvbiBmb3IgYmxvYXRpbmcgaXMgaGF2aW5nIGEgbG90IG9mIGdhcyBpbiB5b3VyIGd1dC4gCg==" >> send.in$n
i=$((i+1))
done
printf '\r\n' >> send.in$n
printf '\r\n' >> send.in$n
# send the requests then wait for named to close the socket.
time1=$($PERL -e 'print time(), "\n";')
${PERL} send64k.pl 10.53.0.3 ${EXTRAPORT1} < send.in$n > send.out$n
${NC} 10.53.0.3 ${EXTRAPORT1} < send.in$n > send.out$n
time2=$($PERL -e 'print time(), "\n";')
test $((time2 - time1)) -lt 5 || ret=1
# we expect 91 of the 500 requests to be processed.
lines=$(grep "^HTTP/1.1" send.out$n | wc -l)
test $lines = 91 || ret=1
# we expect 1 request to be processed.
lines=$(grep -c "^HTTP/1.1" send.out$n)
test $lines = 1 || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))

View File

@@ -177,6 +177,8 @@ libisc_la_SOURCES = \
os.c \
os_p.h \
parseint.c \
picohttpparser.c \
picohttpparser.h \
portset.c \
quota.c \
radix.c \

File diff suppressed because it is too large Load Diff

View File

@@ -23,28 +23,20 @@
#include <isc/task.h>
#include <isc/time.h>
#include <isc/types.h>
/*%
* HTTP urls. These are the URLs we manage, and the function to call to
* provide the data for it. We pass in the base url (so the same function
* can handle multiple requests), and a structure to fill in to return a
* result to the client. We also pass in a pointer to be filled in for
* the data cleanup function.
*/
struct isc_httpdurl {
char *url;
isc_httpdaction_t *action;
void *action_arg;
bool isstatic;
isc_time_t loadtime;
ISC_LINK(isc_httpdurl_t) link;
};
#include <isc/url.h>
#define HTTPD_EVENTCLASS ISC_EVENTCLASS(4300)
#define HTTPD_SHUTDOWN (HTTPD_EVENTCLASS + 0x0001)
#define ISC_HTTPDMGR_SHUTTINGDOWN 0x00000001
typedef isc_result_t(isc_httpdaction_t)(
const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg,
unsigned int *retcode, const char **retmsg, const char **mimetype,
isc_buffer_t *body, isc_httpdfree_t **freecb, void **freecb_args);
typedef bool(isc_httpdclientok_t)(const isc_sockaddr_t *, void *);
isc_result_t
isc_httpdmgr_create(isc_nm_t *nm, isc_mem_t *mctx, isc_sockaddr_t *addr,
isc_httpdclientok_t *client_ok,
@@ -60,3 +52,12 @@ isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url, bool isstatic,
void
isc_httpd_setfinishhook(void (*fn)(void));
bool
isc_httpdurl_isstatic(const isc_httpdurl_t *url);
const isc_time_t *
isc_httpdurl_loadtime(const isc_httpdurl_t *url);
const isc_time_t *
isc_httpd_if_modified_since(const isc_httpd_t *httpd);

View File

@@ -96,14 +96,6 @@ typedef struct isc_nm_http_endpoints isc_nm_http_endpoints_t;
typedef void (*isc_taskaction_t)(isc_task_t *, isc_event_t *);
/* The following cannot be listed alphabetically due to forward reference */
typedef isc_result_t(isc_httpdaction_t)(
const char *url, isc_httpdurl_t *urlinfo, const char *querystring,
const char *headers, void *arg, unsigned int *retcode,
const char **retmsg, const char **mimetype, isc_buffer_t *body,
isc_httpdfree_t **freecb, void **freecb_args);
typedef bool(isc_httpdclientok_t)(const isc_sockaddr_t *, void *);
/*% Resource */
typedef enum {
isc_resource_coresize = 1,

View File

@@ -36,6 +36,7 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <isc/result.h>

722
lib/isc/picohttpparser.c Normal file
View File

@@ -0,0 +1,722 @@
/*
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
* Shigeo Mitsunari
*
* SPDX-License-Identifier: MIT
*
* The software is licensed under either the MIT License (below) or the Perl
* license.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <assert.h>
#include <stddef.h>
#include <string.h>
#ifdef __SSE4_2__
#ifdef _MSC_VER
#include <nmmintrin.h>
#else
#include <x86intrin.h>
#endif
#endif
#include "picohttpparser.h"
#if __GNUC__ >= 3
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
#ifdef _MSC_VER
#define ALIGNED(n) _declspec(align(n))
#else
#define ALIGNED(n) __attribute__((aligned(n)))
#endif
#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)
#define CHECK_EOF() \
if (buf == buf_end) { \
*ret = -2; \
return NULL; \
}
#define EXPECT_CHAR_NO_CHECK(ch) \
if (*buf++ != ch) { \
*ret = -1; \
return NULL; \
}
#define EXPECT_CHAR(ch) \
CHECK_EOF(); \
EXPECT_CHAR_NO_CHECK(ch);
#define ADVANCE_TOKEN(tok, toklen) \
do { \
const char *tok_start = buf; \
static const char ALIGNED(16) \
ranges2[16] = "\000\040\177\177"; \
int found2; \
buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
if (!found2) { \
CHECK_EOF(); \
} \
while (1) { \
if (*buf == ' ') { \
break; \
} else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
if ((unsigned char)*buf < '\040' || \
*buf == '\177') { \
*ret = -1; \
return NULL; \
} \
} \
++buf; \
CHECK_EOF(); \
} \
tok = tok_start; \
toklen = buf - tok_start; \
} while (0)
static const char *token_char_map =
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
"\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
"\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
static const char *
findchar_fast(const char *buf, const char *buf_end, const char *ranges,
size_t ranges_size, int *found) {
*found = 0;
#if __SSE4_2__
if (likely(buf_end - buf >= 16)) {
__m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
size_t left = (buf_end - buf) & ~15;
do {
__m128i b16 = _mm_loadu_si128((const __m128i *)buf);
int r = _mm_cmpestri(ranges16, ranges_size, b16, 16,
_SIDD_LEAST_SIGNIFICANT |
_SIDD_CMP_RANGES |
_SIDD_UBYTE_OPS);
if (unlikely(r != 16)) {
buf += r;
*found = 1;
break;
}
buf += 16;
left -= 16;
} while (likely(left != 0));
}
#else
/* suppress unused parameter warning */
(void)buf_end;
(void)ranges;
(void)ranges_size;
#endif
return buf;
}
static const char *
get_token_to_eol(const char *buf, const char *buf_end, const char **token,
size_t *token_len, int *ret) {
const char *token_start = buf;
#ifdef __SSE4_2__
static const char ALIGNED(16)
ranges1[16] = "\0\010" /* allow HT */
"\012\037" /* allow SP and up to but not including
DEL */
"\177\177"; /* allow chars w. MSB set */
int found;
buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
if (found)
goto FOUND_CTL;
#else
/* find non-printable char within the next 8 bytes, this is the hottest
* code; manually inlined */
while (likely(buf_end - buf >= 8)) {
#define DOIT() \
do { \
if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
goto NonPrintable; \
++buf; \
} while (0)
DOIT();
DOIT();
DOIT();
DOIT();
DOIT();
DOIT();
DOIT();
DOIT();
#undef DOIT
continue;
NonPrintable:
if ((likely((unsigned char)*buf < '\040') &&
likely(*buf != '\011')) ||
unlikely(*buf == '\177'))
{
goto FOUND_CTL;
}
++buf;
}
#endif
for (;; ++buf) {
CHECK_EOF();
if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
if ((likely((unsigned char)*buf < '\040') &&
likely(*buf != '\011')) ||
unlikely(*buf == '\177'))
{
goto FOUND_CTL;
}
}
}
FOUND_CTL:
if (likely(*buf == '\015')) {
++buf;
EXPECT_CHAR('\012');
*token_len = buf - 2 - token_start;
} else if (*buf == '\012') {
*token_len = buf - token_start;
++buf;
} else {
*ret = -1;
return NULL;
}
*token = token_start;
return buf;
}
static const char *
is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) {
int ret_cnt = 0;
buf = last_len < 3 ? buf : buf + last_len - 3;
while (1) {
CHECK_EOF();
if (*buf == '\015') {
++buf;
CHECK_EOF();
EXPECT_CHAR('\012');
++ret_cnt;
} else if (*buf == '\012') {
++buf;
++ret_cnt;
} else {
++buf;
ret_cnt = 0;
}
if (ret_cnt == 2) {
return buf;
}
}
*ret = -2;
return NULL;
}
#define PARSE_INT(valp_, mul_) \
if (*buf < '0' || '9' < *buf) { \
buf++; \
*ret = -1; \
return NULL; \
} \
*(valp_) = (mul_) * (*buf++ - '0');
#define PARSE_INT_3(valp_) \
do { \
int res_ = 0; \
PARSE_INT(&res_, 100) \
*valp_ = res_; \
PARSE_INT(&res_, 10) \
*valp_ += res_; \
PARSE_INT(&res_, 1) \
*valp_ += res_; \
} while (0)
/* returned pointer is always within [buf, buf_end), or null */
static const char *
parse_token(const char *buf, const char *buf_end, const char **token,
size_t *token_len, char next_char, int *ret) {
/* We use pcmpestri to detect non-token characters. This instruction can
* take no more than eight character ranges (8*2*8=128 bits that is the
* size of a SSE register). Due to this restriction, characters `|` and
* `~` are handled in the slow loop. */
static const char ALIGNED(16) ranges[] = "\x00 " /* control chars and up
to SP */
"\"\"" /* 0x22 */
"()" /* 0x28,0x29 */
",," /* 0x2c */
"//" /* 0x2f */
":@" /* 0x3a-0x40 */
"[]" /* 0x5b-0x5d */
"{\xff"; /* 0x7b-0xff */
const char *buf_start = buf;
int found;
buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found);
if (!found) {
CHECK_EOF();
}
while (1) {
if (*buf == next_char) {
break;
} else if (!token_char_map[(unsigned char)*buf]) {
*ret = -1;
return NULL;
}
++buf;
CHECK_EOF();
}
*token = buf_start;
*token_len = buf - buf_start;
return buf;
}
/* returned pointer is always within [buf, buf_end), or null */
static const char *
parse_http_version(const char *buf, const char *buf_end, int *minor_version,
int *ret) {
/* we want at least [HTTP/1.<two chars>] to try to parse */
if (buf_end - buf < 9) {
*ret = -2;
return NULL;
}
EXPECT_CHAR_NO_CHECK('H');
EXPECT_CHAR_NO_CHECK('T');
EXPECT_CHAR_NO_CHECK('T');
EXPECT_CHAR_NO_CHECK('P');
EXPECT_CHAR_NO_CHECK('/');
EXPECT_CHAR_NO_CHECK('1');
EXPECT_CHAR_NO_CHECK('.');
PARSE_INT(minor_version, 1);
return buf;
}
static const char *
parse_headers(const char *buf, const char *buf_end, struct phr_header *headers,
size_t *num_headers, size_t max_headers, int *ret) {
for (;; ++*num_headers) {
CHECK_EOF();
if (*buf == '\015') {
++buf;
EXPECT_CHAR('\012');
break;
} else if (*buf == '\012') {
++buf;
break;
}
if (*num_headers == max_headers) {
*ret = -1;
return NULL;
}
if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
/* parsing name, but do not discard SP before colon, see
* http://www.mozilla.org/security/announce/2006/mfsa2006-33.html
*/
if ((buf = parse_token(buf, buf_end,
&headers[*num_headers].name,
&headers[*num_headers].name_len,
':', ret)) == NULL)
{
return NULL;
}
if (headers[*num_headers].name_len == 0) {
*ret = -1;
return NULL;
}
++buf;
for (;; ++buf) {
CHECK_EOF();
if (!(*buf == ' ' || *buf == '\t')) {
break;
}
}
} else {
headers[*num_headers].name = NULL;
headers[*num_headers].name_len = 0;
}
const char *value;
size_t value_len;
if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len,
ret)) == NULL) {
return NULL;
}
/* remove trailing SPs and HTABs */
const char *value_end = value + value_len;
for (; value_end != value; --value_end) {
const char c = *(value_end - 1);
if (!(c == ' ' || c == '\t')) {
break;
}
}
headers[*num_headers].value = value;
headers[*num_headers].value_len = value_end - value;
}
return buf;
}
static const char *
parse_request(const char *buf, const char *buf_end, const char **method,
size_t *method_len, const char **path, size_t *path_len,
int *minor_version, struct phr_header *headers,
size_t *num_headers, size_t max_headers, int *ret) {
/* skip first empty line (some clients add CRLF after POST content) */
CHECK_EOF();
if (*buf == '\015') {
++buf;
EXPECT_CHAR('\012');
} else if (*buf == '\012') {
++buf;
}
/* parse request line */
if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) ==
NULL) {
return NULL;
}
do {
++buf;
CHECK_EOF();
} while (*buf == ' ');
ADVANCE_TOKEN(*path, *path_len);
do {
++buf;
CHECK_EOF();
} while (*buf == ' ');
if (*method_len == 0 || *path_len == 0) {
*ret = -1;
return NULL;
}
if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) ==
NULL) {
return NULL;
}
if (*buf == '\015') {
++buf;
EXPECT_CHAR('\012');
} else if (*buf == '\012') {
++buf;
} else {
*ret = -1;
return NULL;
}
return parse_headers(buf, buf_end, headers, num_headers, max_headers,
ret);
}
int
phr_parse_request(const char *buf_start, size_t len, const char **method,
size_t *method_len, const char **path, size_t *path_len,
int *minor_version, struct phr_header *headers,
size_t *num_headers, size_t last_len) {
const char *buf = buf_start, *buf_end = buf_start + len;
size_t max_headers = *num_headers;
int r = -1;
*method = NULL;
*method_len = 0;
*path = NULL;
*path_len = 0;
*minor_version = -1;
*num_headers = 0;
/* if last_len != 0, check if the request is complete (a fast
countermeasure againt slowloris */
if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
return r;
}
if ((buf = parse_request(buf, buf_end, method, method_len, path,
path_len, minor_version, headers, num_headers,
max_headers, &r)) == NULL)
{
return r;
}
return (int)(buf - buf_start);
}
static const char *
parse_response(const char *buf, const char *buf_end, int *minor_version,
int *status, const char **msg, size_t *msg_len,
struct phr_header *headers, size_t *num_headers,
size_t max_headers, int *ret) {
/* parse "HTTP/1.x" */
if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) ==
NULL) {
return NULL;
}
/* skip space */
if (*buf != ' ') {
*ret = -1;
return NULL;
}
do {
++buf;
CHECK_EOF();
} while (*buf == ' ');
/* parse status code, we want at least [:digit:][:digit:][:digit:]<other
* char> to try to parse */
if (buf_end - buf < 4) {
*ret = -2;
return NULL;
}
PARSE_INT_3(status);
/* get message including preceding space */
if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
return NULL;
}
if (*msg_len == 0) {
/* ok */
} else if (**msg == ' ') {
/* Remove preceding space. Successful return from
* `get_token_to_eol` guarantees that we would hit something
* other than SP before running past the end of the given
* buffer. */
do {
++*msg;
--*msg_len;
} while (**msg == ' ');
} else {
/* garbage found after status code */
*ret = -1;
return NULL;
}
return parse_headers(buf, buf_end, headers, num_headers, max_headers,
ret);
}
int
phr_parse_response(const char *buf_start, size_t len, int *minor_version,
int *status, const char **msg, size_t *msg_len,
struct phr_header *headers, size_t *num_headers,
size_t last_len) {
const char *buf = buf_start, *buf_end = buf + len;
size_t max_headers = *num_headers;
int r;
*minor_version = -1;
*status = 0;
*msg = NULL;
*msg_len = 0;
*num_headers = 0;
/* if last_len != 0, check if the response is complete (a fast
countermeasure against slowloris */
if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
return r;
}
if ((buf = parse_response(buf, buf_end, minor_version, status, msg,
msg_len, headers, num_headers, max_headers,
&r)) == NULL)
{
return r;
}
return (int)(buf - buf_start);
}
int
phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers,
size_t *num_headers, size_t last_len) {
const char *buf = buf_start, *buf_end = buf + len;
size_t max_headers = *num_headers;
int r;
*num_headers = 0;
/* if last_len != 0, check if the response is complete (a fast
countermeasure against slowloris */
if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
return r;
}
if ((buf = parse_headers(buf, buf_end, headers, num_headers,
max_headers, &r)) == NULL)
{
return r;
}
return (int)(buf - buf_start);
}
enum {
CHUNKED_IN_CHUNK_SIZE,
CHUNKED_IN_CHUNK_EXT,
CHUNKED_IN_CHUNK_DATA,
CHUNKED_IN_CHUNK_CRLF,
CHUNKED_IN_TRAILERS_LINE_HEAD,
CHUNKED_IN_TRAILERS_LINE_MIDDLE
};
static int
decode_hex(int ch) {
if ('0' <= ch && ch <= '9') {
return ch - '0';
} else if ('A' <= ch && ch <= 'F') {
return ch - 'A' + 0xa;
} else if ('a' <= ch && ch <= 'f') {
return ch - 'a' + 0xa;
} else {
return -1;
}
}
ssize_t
phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf,
size_t *_bufsz) {
size_t dst = 0, src = 0, bufsz = *_bufsz;
ssize_t ret = -2; /* incomplete */
while (1) {
switch (decoder->_state) {
case CHUNKED_IN_CHUNK_SIZE:
for (;; ++src) {
int v;
if (src == bufsz)
goto Exit;
if ((v = decode_hex(buf[src])) == -1) {
if (decoder->_hex_count == 0) {
ret = -1;
goto Exit;
}
break;
}
if (decoder->_hex_count == sizeof(size_t) * 2) {
ret = -1;
goto Exit;
}
decoder->bytes_left_in_chunk =
decoder->bytes_left_in_chunk * 16 + v;
++decoder->_hex_count;
}
decoder->_hex_count = 0;
decoder->_state = CHUNKED_IN_CHUNK_EXT;
/* fallthru */
case CHUNKED_IN_CHUNK_EXT:
/* RFC 7230 A.2 "Line folding in chunk extensions is
* disallowed" */
for (;; ++src) {
if (src == bufsz)
goto Exit;
if (buf[src] == '\012')
break;
}
++src;
if (decoder->bytes_left_in_chunk == 0) {
if (decoder->consume_trailer) {
decoder->_state =
CHUNKED_IN_TRAILERS_LINE_HEAD;
break;
} else {
goto Complete;
}
}
decoder->_state = CHUNKED_IN_CHUNK_DATA;
/* fallthru */
case CHUNKED_IN_CHUNK_DATA: {
size_t avail = bufsz - src;
if (avail < decoder->bytes_left_in_chunk) {
if (dst != src)
memmove(buf + dst, buf + src, avail);
src += avail;
dst += avail;
decoder->bytes_left_in_chunk -= avail;
goto Exit;
}
if (dst != src)
memmove(buf + dst, buf + src,
decoder->bytes_left_in_chunk);
src += decoder->bytes_left_in_chunk;
dst += decoder->bytes_left_in_chunk;
decoder->bytes_left_in_chunk = 0;
decoder->_state = CHUNKED_IN_CHUNK_CRLF;
}
/* fallthru */
case CHUNKED_IN_CHUNK_CRLF:
for (;; ++src) {
if (src == bufsz)
goto Exit;
if (buf[src] != '\015')
break;
}
if (buf[src] != '\012') {
ret = -1;
goto Exit;
}
++src;
decoder->_state = CHUNKED_IN_CHUNK_SIZE;
break;
case CHUNKED_IN_TRAILERS_LINE_HEAD:
for (;; ++src) {
if (src == bufsz)
goto Exit;
if (buf[src] != '\015')
break;
}
if (buf[src++] == '\012')
goto Complete;
decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
/* fallthru */
case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
for (;; ++src) {
if (src == bufsz)
goto Exit;
if (buf[src] == '\012')
break;
}
++src;
decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
break;
default:
assert(!"decoder is corrupt");
}
}
Complete:
ret = bufsz - src;
Exit:
if (dst != src)
memmove(buf + dst, buf + src, bufsz - src);
*_bufsz = dst;
return ret;
}
int
phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) {
return decoder->_state == CHUNKED_IN_CHUNK_DATA;
}
#undef CHECK_EOF
#undef EXPECT_CHAR
#undef ADVANCE_TOKEN

100
lib/isc/picohttpparser.h Normal file
View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
* Shigeo Mitsunari
*
* SPDX-License-Identifier: MIT
*
* The software is licensed under either the MIT License (below) or the Perl
* license.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef picohttpparser_h
#define picohttpparser_h
#include <sys/types.h>
#ifdef _MSC_VER
#define ssize_t intptr_t
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* contains name and value of a header (name == NULL if is a continuing line
* of a multiline header */
struct phr_header {
const char *name;
size_t name_len;
const char *value;
size_t value_len;
};
/* returns number of bytes consumed if successful, -2 if request is partial,
* -1 if failed */
int
phr_parse_request(const char *buf, size_t len, const char **method,
size_t *method_len, const char **path, size_t *path_len,
int *minor_version, struct phr_header *headers,
size_t *num_headers, size_t last_len);
/* ditto */
int
phr_parse_response(const char *_buf, size_t len, int *minor_version,
int *status, const char **msg, size_t *msg_len,
struct phr_header *headers, size_t *num_headers,
size_t last_len);
/* ditto */
int
phr_parse_headers(const char *buf, size_t len, struct phr_header *headers,
size_t *num_headers, size_t last_len);
/* should be zero-filled before start */
struct phr_chunked_decoder {
size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
char consume_trailer; /* if trailing headers should be consumed */
char _hex_count;
char _state;
};
/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
* encoding headers. When the function returns without an error, bufsz is
* updated to the length of the decoded data available. Applications should
* repeatedly call the function while it returns -2 (incomplete) every time
* supplying newly arrived data. If the end of the chunked-encoded data is
* found, the function returns a non-negative number indicating the number of
* octets left undecoded, that starts from the offset returned by `*bufsz`.
* Returns -1 on error.
*/
ssize_t
phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf,
size_t *bufsz);
/* returns if the chunked decoder is in middle of chunked data */
int
phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);
#ifdef __cplusplus
}
#endif
#endif