mirror of
https://github.com/monitoring-plugins/monitoring-plugins.git
synced 2026-06-09 08:42:17 -04:00
added -M<m> age option for document age, using picohttpparser from h2o (maybe handy
later to make a more robust header condition checker?)
This commit is contained in:
parent
bbec77c7ec
commit
9960c56e5e
5 changed files with 912 additions and 7 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -202,6 +202,7 @@ NP-VERSION-FILE
|
|||
/plugins/negate
|
||||
/plugins/stamp-h*
|
||||
/plugins/urlize
|
||||
/plugins/libpicohttpparser.a
|
||||
|
||||
# /plugins/t/
|
||||
/plugins/t/*.tmp
|
||||
|
|
|
|||
|
|
@ -44,11 +44,13 @@ EXTRA_DIST = t tests
|
|||
|
||||
PLUGINHDRS = common.h
|
||||
|
||||
noinst_LIBRARIES = libnpcommon.a
|
||||
noinst_LIBRARIES = libnpcommon.a libpicohttpparser.a
|
||||
|
||||
libnpcommon_a_SOURCES = utils.c netutils.c sslutils.c runcmd.c \
|
||||
popen.c utils.h netutils.h popen.h common.h runcmd.c runcmd.h
|
||||
|
||||
libpicohttpparser_a_SOURCES = picohttpparser.c
|
||||
|
||||
BASEOBJS = libnpcommon.a ../lib/libmonitoringplug.a ../gl/libgnu.a
|
||||
NETOBJS = $(BASEOBJS) $(EXTRA_NETOBLS)
|
||||
NETLIBS = $(NETOBJS) $(SOCKETLIBS)
|
||||
|
|
@ -71,7 +73,7 @@ check_apt_LDADD = $(BASEOBJS)
|
|||
check_cluster_LDADD = $(BASEOBJS)
|
||||
check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS)
|
||||
check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLINCLUDE)
|
||||
check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS)
|
||||
check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) libpicohttpparser.a
|
||||
check_dbi_LDADD = $(NETLIBS) $(DBILIBS)
|
||||
check_dig_LDADD = $(NETLIBS)
|
||||
check_disk_LDADD = $(BASEOBJS)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ const char *progname = "check_curl";
|
|||
const char *copyright = "2006-2017";
|
||||
const char *email = "devel@monitoring-plugins.org";
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
|
@ -47,6 +49,8 @@ const char *email = "devel@monitoring-plugins.org";
|
|||
#include "curl/curl.h"
|
||||
#include "curl/easy.h"
|
||||
|
||||
#include "picohttpparser.h"
|
||||
|
||||
#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch))
|
||||
|
||||
#define DEFAULT_BUFFER_SIZE 2048
|
||||
|
|
@ -73,7 +77,7 @@ typedef struct {
|
|||
int http_code; /* HTTP return code as in RFC 2145 */
|
||||
int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see
|
||||
* http://support.microsoft.com/kb/318380/en-us */
|
||||
char *msg; /* the human readable message */
|
||||
const char *msg; /* the human readable message */
|
||||
char *first_line; /* a copy of the first line */
|
||||
} curlhelp_statusline;
|
||||
|
||||
|
|
@ -142,6 +146,7 @@ char *client_privkey = NULL;
|
|||
char *ca_cert = NULL;
|
||||
X509 *cert = NULL;
|
||||
int no_body = FALSE;
|
||||
int maximum_age = -1;
|
||||
int address_family = AF_UNSPEC;
|
||||
|
||||
int process_arguments (int, char**);
|
||||
|
|
@ -156,6 +161,9 @@ void curlhelp_freebuffer (curlhelp_curlbuf*);
|
|||
int curlhelp_parse_statusline (const char*, curlhelp_statusline *);
|
||||
void curlhelp_free_statusline (curlhelp_statusline *);
|
||||
char *perfd_time_ssl (double microsec);
|
||||
char *get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header);
|
||||
static time_t parse_time_string (const char *string);
|
||||
int check_document_dates (const curlhelp_curlbuf *, char (*msg)[DEFAULT_BUFFER_SIZE]);
|
||||
|
||||
void remove_newlines (char *);
|
||||
void test_file (char *);
|
||||
|
|
@ -391,7 +399,7 @@ check_http (void)
|
|||
/* Curl errors, result in critical Nagios state */
|
||||
if (res != CURLE_OK) {
|
||||
remove_newlines (errbuf);
|
||||
snprintf (msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s\n"),
|
||||
snprintf (msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"),
|
||||
server_port, res, curl_easy_strerror(res));
|
||||
die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
|
||||
}
|
||||
|
|
@ -506,6 +514,10 @@ check_http (void)
|
|||
status_line.http_code, status_line.msg, code);
|
||||
}
|
||||
|
||||
if (maximum_age >= 0) {
|
||||
result = max_state_alt(check_document_dates(&header_buf, &msg), result);
|
||||
}
|
||||
|
||||
/* Page and Header content checks go here */
|
||||
|
||||
if (strlen (header_expect)) {
|
||||
|
|
@ -638,6 +650,7 @@ process_arguments (int argc, char **argv)
|
|||
{"useragent", required_argument, 0, 'A'},
|
||||
{"header", required_argument, 0, 'k'},
|
||||
{"no-body", no_argument, 0, 'N'},
|
||||
{"max-age", required_argument, 0, 'M'},
|
||||
{"content-type", required_argument, 0, 'T'},
|
||||
{"pagesize", required_argument, 0, 'm'},
|
||||
{"invert-regex", no_argument, NULL, INVERT_REGEX},
|
||||
|
|
@ -665,7 +678,7 @@ process_arguments (int argc, char **argv)
|
|||
}
|
||||
|
||||
while (1) {
|
||||
c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:p:d:e:s:R:r:u:f:C:J:K:S::m:NE", longopts, &option);
|
||||
c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:p:d:e:s:R:r:u:f:C:J:K:S::m:M:NE", longopts, &option);
|
||||
if (c == -1 || c == EOF || c == 1)
|
||||
break;
|
||||
|
||||
|
|
@ -962,6 +975,26 @@ process_arguments (int argc, char **argv)
|
|||
case 'N': /* no-body */
|
||||
no_body = TRUE;
|
||||
break;
|
||||
case 'M': /* max-age */
|
||||
{
|
||||
int L = strlen(optarg);
|
||||
if (L && optarg[L-1] == 'm')
|
||||
maximum_age = atoi (optarg) * 60;
|
||||
else if (L && optarg[L-1] == 'h')
|
||||
maximum_age = atoi (optarg) * 60 * 60;
|
||||
else if (L && optarg[L-1] == 'd')
|
||||
maximum_age = atoi (optarg) * 60 * 60 * 24;
|
||||
else if (L && (optarg[L-1] == 's' ||
|
||||
isdigit (optarg[L-1])))
|
||||
maximum_age = atoi (optarg);
|
||||
else {
|
||||
fprintf (stderr, "unparsable max-age: %s\n", optarg);
|
||||
exit (STATE_WARNING);
|
||||
}
|
||||
if (verbose >= 2)
|
||||
printf ("* Maximal age of document set to %d seconds\n", maximum_age);
|
||||
}
|
||||
break;
|
||||
case 'E': /* show extended perfdata */
|
||||
show_extended_perfdata = TRUE;
|
||||
break;
|
||||
|
|
@ -1090,6 +1123,9 @@ print_help (void)
|
|||
printf (" %s\n", "-N, --no-body");
|
||||
printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
|
||||
printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
|
||||
printf (" %s\n", "-M, --max-age=SECONDS");
|
||||
printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
|
||||
printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
|
||||
printf (" %s\n", "-T, --content-type=STRING");
|
||||
printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
|
||||
printf (" %s\n", "-l, --linespan");
|
||||
|
|
@ -1181,7 +1217,7 @@ print_usage (void)
|
|||
printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-E] [-a auth]\n");
|
||||
printf (" [-f <ok|warning|critcal|follow>]\n");
|
||||
printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
|
||||
printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N]\n");
|
||||
printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
|
||||
printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
|
||||
printf (" [-T <content-type>] [-j method]\n", progname);
|
||||
printf ("\n");
|
||||
|
|
@ -1351,7 +1387,164 @@ remove_newlines (char *s)
|
|||
*p = ' ';
|
||||
}
|
||||
|
||||
char *perfd_time_ssl (double elapsed_time_ssl)
|
||||
char *
|
||||
perfd_time_ssl (double elapsed_time_ssl)
|
||||
{
|
||||
return fperfdata ("time_ssl", elapsed_time_ssl, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
|
||||
}
|
||||
|
||||
char *
|
||||
get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header)
|
||||
{
|
||||
for( int i = 0; i < nof_headers; i++ ) {
|
||||
if( strncasecmp( header, headers[i].name, max( headers[i].name_len, 4 ) ) == 0 ) {
|
||||
return strndup( headers[i].value, headers[i].value_len );
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static time_t
|
||||
parse_time_string (const char *string)
|
||||
{
|
||||
struct tm tm;
|
||||
time_t t;
|
||||
memset (&tm, 0, sizeof(tm));
|
||||
|
||||
/* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
|
||||
|
||||
if (isupper (string[0]) && /* Tue */
|
||||
islower (string[1]) &&
|
||||
islower (string[2]) &&
|
||||
',' == string[3] &&
|
||||
' ' == string[4] &&
|
||||
(isdigit(string[5]) || string[5] == ' ') && /* 25 */
|
||||
isdigit (string[6]) &&
|
||||
' ' == string[7] &&
|
||||
isupper (string[8]) && /* Dec */
|
||||
islower (string[9]) &&
|
||||
islower (string[10]) &&
|
||||
' ' == string[11] &&
|
||||
isdigit (string[12]) && /* 2001 */
|
||||
isdigit (string[13]) &&
|
||||
isdigit (string[14]) &&
|
||||
isdigit (string[15]) &&
|
||||
' ' == string[16] &&
|
||||
isdigit (string[17]) && /* 02: */
|
||||
isdigit (string[18]) &&
|
||||
':' == string[19] &&
|
||||
isdigit (string[20]) && /* 59: */
|
||||
isdigit (string[21]) &&
|
||||
':' == string[22] &&
|
||||
isdigit (string[23]) && /* 03 */
|
||||
isdigit (string[24]) &&
|
||||
' ' == string[25] &&
|
||||
'G' == string[26] && /* GMT */
|
||||
'M' == string[27] && /* GMT */
|
||||
'T' == string[28]) {
|
||||
|
||||
tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0');
|
||||
tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0');
|
||||
tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
|
||||
tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
|
||||
tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
|
||||
!strncmp (string+8, "Feb", 3) ? 1 :
|
||||
!strncmp (string+8, "Mar", 3) ? 2 :
|
||||
!strncmp (string+8, "Apr", 3) ? 3 :
|
||||
!strncmp (string+8, "May", 3) ? 4 :
|
||||
!strncmp (string+8, "Jun", 3) ? 5 :
|
||||
!strncmp (string+8, "Jul", 3) ? 6 :
|
||||
!strncmp (string+8, "Aug", 3) ? 7 :
|
||||
!strncmp (string+8, "Sep", 3) ? 8 :
|
||||
!strncmp (string+8, "Oct", 3) ? 9 :
|
||||
!strncmp (string+8, "Nov", 3) ? 10 :
|
||||
!strncmp (string+8, "Dec", 3) ? 11 :
|
||||
-1);
|
||||
tm.tm_year = ((1000 * (string[12]-'0') +
|
||||
100 * (string[13]-'0') +
|
||||
10 * (string[14]-'0') +
|
||||
(string[15]-'0'))
|
||||
- 1900);
|
||||
|
||||
tm.tm_isdst = 0; /* GMT is never in DST, right? */
|
||||
|
||||
if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
This is actually wrong: we need to subtract the local timezone
|
||||
offset from GMT from this value. But, that's ok in this usage,
|
||||
because we only comparing these two GMT dates against each other,
|
||||
so it doesn't matter what time zone we parse them in.
|
||||
*/
|
||||
|
||||
t = mktime (&tm);
|
||||
if (t == (time_t) -1) t = 0;
|
||||
|
||||
if (verbose) {
|
||||
const char *s = string;
|
||||
while (*s && *s != '\r' && *s != '\n')
|
||||
fputc (*s++, stdout);
|
||||
printf (" ==> %lu\n", (unsigned long) t);
|
||||
}
|
||||
|
||||
return t;
|
||||
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
check_document_dates (const curlhelp_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE])
|
||||
{
|
||||
char *server_date = NULL;
|
||||
char *document_date = NULL;
|
||||
int date_result = STATE_OK;
|
||||
curlhelp_statusline status_line;
|
||||
struct phr_header headers[255];
|
||||
size_t nof_headers = 255;
|
||||
size_t msglen;
|
||||
|
||||
int res = phr_parse_response (header_buf->buf, header_buf->buflen,
|
||||
&status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
|
||||
headers, &nof_headers, 0);
|
||||
|
||||
server_date = get_header_value (headers, nof_headers, "date");
|
||||
document_date = get_header_value (headers, nof_headers, "last-modified");
|
||||
|
||||
if (!server_date || !*server_date) {
|
||||
snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg);
|
||||
date_result = max_state_alt(STATE_UNKNOWN, date_result);
|
||||
} else if (!document_date || !*document_date) {
|
||||
snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg);
|
||||
date_result = max_state_alt(STATE_CRITICAL, date_result);
|
||||
} else {
|
||||
time_t srv_data = parse_time_string (server_date);
|
||||
time_t doc_data = parse_time_string (document_date);
|
||||
if (srv_data <= 0) {
|
||||
snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
|
||||
date_result = max_state_alt(STATE_CRITICAL, date_result);
|
||||
} else if (doc_data <= 0) {
|
||||
snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
|
||||
date_result = max_state_alt(STATE_CRITICAL, date_result);
|
||||
} else if (doc_data > srv_data + 30) {
|
||||
snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
|
||||
date_result = max_state_alt(STATE_CRITICAL, date_result);
|
||||
} else if (doc_data < srv_data - maximum_age) {
|
||||
int n = (srv_data - doc_data);
|
||||
if (n > (60 * 60 * 24 * 2)) {
|
||||
snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
|
||||
date_result = max_state_alt(STATE_CRITICAL, date_result);
|
||||
} else {
|
||||
snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
|
||||
date_result = max_state_alt(STATE_CRITICAL, date_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (server_date) free (server_date);
|
||||
if (document_date) free (document_date);
|
||||
|
||||
return date_result;
|
||||
}
|
||||
|
|
|
|||
620
plugins/picohttpparser.c
Normal file
620
plugins/picohttpparser.c
Normal file
|
|
@ -0,0 +1,620 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
|
||||
* Shigeo Mitsunari
|
||||
*
|
||||
* 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"
|
||||
|
||||
/* $Id: a707070d11d499609f99d09f97535642cec910a8 $ */
|
||||
|
||||
#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[] = "\000\040\177\177"; \
|
||||
int found2; \
|
||||
buf = findchar_fast(buf, buf_end, ranges2, sizeof(ranges2) - 1, &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 ranges1[] = "\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, sizeof(ranges1) - 1, &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_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 */
|
||||
headers[*num_headers].name = buf;
|
||||
static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */
|
||||
"\"\"" /* 0x22 */
|
||||
"()" /* 0x28,0x29 */
|
||||
",," /* 0x2c */
|
||||
"//" /* 0x2f */
|
||||
":@" /* 0x3a-0x40 */
|
||||
"[]" /* 0x5b-0x5d */
|
||||
"{\377"; /* 0x7b-0xff */
|
||||
int found;
|
||||
buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
|
||||
if (!found) {
|
||||
CHECK_EOF();
|
||||
}
|
||||
while (1) {
|
||||
if (*buf == ':') {
|
||||
break;
|
||||
} else if (!token_char_map[(unsigned char)*buf]) {
|
||||
*ret = -1;
|
||||
return NULL;
|
||||
}
|
||||
++buf;
|
||||
CHECK_EOF();
|
||||
}
|
||||
if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 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;
|
||||
}
|
||||
if ((buf = get_token_to_eol(buf, buf_end, &headers[*num_headers].value, &headers[*num_headers].value_len, ret)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
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 */
|
||||
ADVANCE_TOKEN(*method, *method_len);
|
||||
++buf;
|
||||
ADVANCE_TOKEN(*path, *path_len);
|
||||
++buf;
|
||||
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;
|
||||
|
||||
*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;
|
||||
}
|
||||
/* 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);
|
||||
|
||||
/* skip space */
|
||||
if (*buf++ != ' ') {
|
||||
*ret = -1;
|
||||
return NULL;
|
||||
}
|
||||
/* get message */
|
||||
if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
|
||||
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
|
||||
89
plugins/picohttpparser.h
Normal file
89
plugins/picohttpparser.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
|
||||
* Shigeo Mitsunari
|
||||
*
|
||||
* 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
|
||||
|
||||
/* $Id: 67fd3ee74103ada60258d8a16e868f483abcca87 $ */
|
||||
|
||||
#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 at the tail of the supplied buffer. 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
|
||||
Loading…
Reference in a new issue