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:
3
CHANGES
3
CHANGES
@@ -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]
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
@@ -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))
|
||||
|
@@ -177,6 +177,8 @@ libisc_la_SOURCES = \
|
||||
os.c \
|
||||
os_p.h \
|
||||
parseint.c \
|
||||
picohttpparser.c \
|
||||
picohttpparser.h \
|
||||
portset.c \
|
||||
quota.c \
|
||||
radix.c \
|
||||
|
1044
lib/isc/httpd.c
1044
lib/isc/httpd.c
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
@@ -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,
|
||||
|
@@ -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
722
lib/isc/picohttpparser.c
Normal 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
100
lib/isc/picohttpparser.h
Normal 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
|
Reference in New Issue
Block a user