2012-01-29 16:34:10 +01:00
|
|
|
|
/*
|
2014-03-30 12:09:21 +02:00
|
|
|
|
* nghttp2 - HTTP/2 C Library
|
2012-01-29 16:34:10 +01:00
|
|
|
|
*
|
|
|
|
|
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
|
|
|
|
*
|
|
|
|
|
* 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 "util.h"
|
|
|
|
|
|
2015-05-13 15:30:35 +02:00
|
|
|
|
#ifdef HAVE_TIME_H
|
2012-02-07 17:50:58 +01:00
|
|
|
|
#include <time.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
|
#endif // HAVE_TIME_H
|
2014-03-15 07:32:38 +01:00
|
|
|
|
#include <sys/types.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
|
#ifdef HAVE_SYS_SOCKET_H
|
2014-03-15 07:32:38 +01:00
|
|
|
|
#include <sys/socket.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
|
#endif // HAVE_SYS_SOCKET_H
|
|
|
|
|
#ifdef HAVE_NETDB_H
|
2014-03-15 07:32:38 +01:00
|
|
|
|
#include <netdb.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
|
#endif // HAVE_NETDB_H
|
2014-07-05 11:22:40 +02:00
|
|
|
|
#include <sys/stat.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
|
#ifdef HAVE_FCNTL_H
|
2014-07-05 11:22:40 +02:00
|
|
|
|
#include <fcntl.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
|
#endif // HAVE_FCNTL_H
|
|
|
|
|
#ifdef HAVE_NETINET_IN_H
|
2014-12-27 18:59:06 +01:00
|
|
|
|
#include <netinet/in.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
|
#endif // HAVE_NETINET_IN_H
|
2014-12-27 18:59:06 +01:00
|
|
|
|
#include <netinet/tcp.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
|
#ifdef HAVE_ARPA_INET_H
|
2015-01-10 13:33:53 +01:00
|
|
|
|
#include <arpa/inet.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
|
#endif // HAVE_ARPA_INET_H
|
2012-02-07 17:50:58 +01:00
|
|
|
|
|
2015-01-31 10:13:07 +01:00
|
|
|
|
#include <cmath>
|
2015-01-10 16:28:00 +01:00
|
|
|
|
#include <cerrno>
|
2013-08-10 17:08:44 +02:00
|
|
|
|
#include <cassert>
|
2012-01-29 16:34:10 +01:00
|
|
|
|
#include <cstdio>
|
2012-02-07 17:50:58 +01:00
|
|
|
|
#include <cstring>
|
2014-01-08 17:27:56 +01:00
|
|
|
|
#include <iostream>
|
2012-01-29 16:34:10 +01:00
|
|
|
|
|
2014-11-14 15:14:39 +01:00
|
|
|
|
#include <nghttp2/nghttp2.h>
|
|
|
|
|
|
2013-03-07 13:17:55 +01:00
|
|
|
|
#include "timegm.h"
|
2015-02-20 15:50:17 +01:00
|
|
|
|
#include "template.h"
|
2013-03-07 13:17:55 +01:00
|
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
|
namespace nghttp2 {
|
2012-01-29 16:34:10 +01:00
|
|
|
|
|
|
|
|
|
namespace util {
|
|
|
|
|
|
2012-11-23 13:14:39 +01:00
|
|
|
|
const char DEFAULT_STRIP_CHARSET[] = "\r\n\t ";
|
2012-01-29 16:34:10 +01:00
|
|
|
|
|
2014-05-14 15:39:28 +02:00
|
|
|
|
const char UPPER_XDIGITS[] = "0123456789ABCDEF";
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool isAlpha(const char c) {
|
2012-01-29 16:34:10 +01:00
|
|
|
|
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool isDigit(const char c) { return '0' <= c && c <= '9'; }
|
2012-01-29 16:34:10 +01:00
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool isHexDigit(const char c) {
|
2012-01-29 16:34:10 +01:00
|
|
|
|
return isDigit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool inRFC3986UnreservedChars(const char c) {
|
|
|
|
|
static const char unreserved[] = {'-', '.', '_', '~'};
|
2012-01-29 16:34:10 +01:00
|
|
|
|
return isAlpha(c) || isDigit(c) ||
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::find(&unreserved[0], &unreserved[4], c) != &unreserved[4];
|
2012-01-29 16:34:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 15:22:21 +01:00
|
|
|
|
bool in_rfc3986_sub_delims(const char c) {
|
|
|
|
|
static const char sub_delims[] = {'!', '$', '&', '\'', '(', ')',
|
|
|
|
|
'*', '+', ',', ';', '='};
|
|
|
|
|
return std::find(std::begin(sub_delims), std::end(sub_delims), c) !=
|
|
|
|
|
std::end(sub_delims);
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::string percentEncode(const unsigned char *target, size_t len) {
|
2012-01-29 16:34:10 +01:00
|
|
|
|
std::string dest;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
2014-05-14 15:39:28 +02:00
|
|
|
|
unsigned char c = target[i];
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (inRFC3986UnreservedChars(c)) {
|
2014-05-14 15:39:28 +02:00
|
|
|
|
dest += c;
|
2012-01-29 16:34:10 +01:00
|
|
|
|
} else {
|
2014-05-14 15:39:28 +02:00
|
|
|
|
dest += "%";
|
|
|
|
|
dest += UPPER_XDIGITS[c >> 4];
|
|
|
|
|
dest += UPPER_XDIGITS[(c & 0x0f)];
|
2012-01-29 16:34:10 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::string percentEncode(const std::string &target) {
|
|
|
|
|
return percentEncode(reinterpret_cast<const unsigned char *>(target.c_str()),
|
2012-01-29 16:34:10 +01:00
|
|
|
|
target.size());
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 15:22:21 +01:00
|
|
|
|
std::string percent_encode_path(const std::string &s) {
|
|
|
|
|
std::string dest;
|
|
|
|
|
for (auto c : s) {
|
|
|
|
|
if (inRFC3986UnreservedChars(c) || in_rfc3986_sub_delims(c) || c == '/') {
|
|
|
|
|
dest += c;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dest += "%";
|
|
|
|
|
dest += UPPER_XDIGITS[(c >> 4) & 0x0f];
|
|
|
|
|
dest += UPPER_XDIGITS[(c & 0x0f)];
|
|
|
|
|
}
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool in_token(char c) {
|
|
|
|
|
static const char extra[] = {'!', '#', '$', '%', '&', '\'', '*', '+',
|
|
|
|
|
'-', '.', '^', '_', '`', '|', '~'};
|
2014-04-03 06:20:50 +02:00
|
|
|
|
|
|
|
|
|
return isAlpha(c) || isDigit(c) ||
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::find(&extra[0], &extra[sizeof(extra)], c) !=
|
|
|
|
|
&extra[sizeof(extra)];
|
2014-04-03 06:20:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2015-02-07 08:09:49 +01:00
|
|
|
|
bool in_attr_char(char c) {
|
|
|
|
|
static const char bad[] = {'*', '\'', '%'};
|
|
|
|
|
return util::in_token(c) &&
|
|
|
|
|
std::find(std::begin(bad), std::end(bad) - 1, c) == std::end(bad) - 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::string percent_encode_token(const std::string &target) {
|
2014-04-03 06:20:50 +02:00
|
|
|
|
auto len = target.size();
|
|
|
|
|
std::string dest;
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
2014-05-14 15:39:28 +02:00
|
|
|
|
unsigned char c = target[i];
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (c != '%' && in_token(c)) {
|
2014-04-03 06:20:50 +02:00
|
|
|
|
dest += c;
|
|
|
|
|
} else {
|
2014-05-14 15:39:28 +02:00
|
|
|
|
dest += "%";
|
|
|
|
|
dest += UPPER_XDIGITS[c >> 4];
|
|
|
|
|
dest += UPPER_XDIGITS[(c & 0x0f)];
|
2014-04-03 06:20:50 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-10 14:35:10 +01:00
|
|
|
|
uint32_t hex_to_uint(char c) {
|
|
|
|
|
if (c <= '9') {
|
|
|
|
|
return c - '0';
|
|
|
|
|
}
|
|
|
|
|
if (c <= 'Z') {
|
|
|
|
|
return c - 'A' + 10;
|
|
|
|
|
}
|
|
|
|
|
if (c <= 'z') {
|
|
|
|
|
return c - 'a' + 10;
|
|
|
|
|
}
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::string quote_string(const std::string &target) {
|
2014-10-27 16:17:32 +01:00
|
|
|
|
auto cnt = std::count(std::begin(target), std::end(target), '"');
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (cnt == 0) {
|
2014-10-27 16:17:32 +01:00
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string res;
|
|
|
|
|
res.reserve(target.size() + cnt);
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (auto c : target) {
|
|
|
|
|
if (c == '"') {
|
2014-10-27 16:17:32 +01:00
|
|
|
|
res += "\\\"";
|
|
|
|
|
} else {
|
|
|
|
|
res += c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 13:26:48 +01:00
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
|
template <typename Iterator>
|
|
|
|
|
Iterator cpydig(Iterator d, uint32_t n, size_t len) {
|
2014-11-10 13:26:48 +01:00
|
|
|
|
auto p = d + len - 1;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
*p-- = (n % 10) + '0';
|
|
|
|
|
n /= 10;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
} while (p >= d);
|
2014-11-10 13:26:48 +01:00
|
|
|
|
|
|
|
|
|
return d + len;
|
|
|
|
|
}
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
|
const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
|
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
|
|
|
const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
2014-11-10 13:26:48 +01:00
|
|
|
|
} // namespace
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::string http_date(time_t t) {
|
2014-11-10 13:26:48 +01:00
|
|
|
|
struct tm tms;
|
|
|
|
|
std::string res;
|
2014-05-14 14:23:21 +02:00
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (gmtime_r(&t, &tms) == nullptr) {
|
2014-11-10 13:26:48 +01:00
|
|
|
|
return res;
|
2014-05-14 14:23:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 13:26:48 +01:00
|
|
|
|
/* Sat, 27 Sep 2014 06:31:15 GMT */
|
|
|
|
|
res.resize(29);
|
|
|
|
|
|
|
|
|
|
auto p = std::begin(res);
|
|
|
|
|
|
|
|
|
|
auto s = DAY_OF_WEEK[tms.tm_wday];
|
2015-02-06 13:35:03 +01:00
|
|
|
|
p = std::copy_n(s, 3, p);
|
2014-11-10 13:26:48 +01:00
|
|
|
|
*p++ = ',';
|
|
|
|
|
*p++ = ' ';
|
|
|
|
|
p = cpydig(p, tms.tm_mday, 2);
|
|
|
|
|
*p++ = ' ';
|
|
|
|
|
s = MONTH[tms.tm_mon];
|
2015-02-06 13:35:03 +01:00
|
|
|
|
p = std::copy_n(s, 3, p);
|
2014-11-10 13:26:48 +01:00
|
|
|
|
*p++ = ' ';
|
|
|
|
|
p = cpydig(p, tms.tm_year + 1900, 4);
|
|
|
|
|
*p++ = ' ';
|
|
|
|
|
p = cpydig(p, tms.tm_hour, 2);
|
|
|
|
|
*p++ = ':';
|
|
|
|
|
p = cpydig(p, tms.tm_min, 2);
|
|
|
|
|
*p++ = ':';
|
|
|
|
|
p = cpydig(p, tms.tm_sec, 2);
|
|
|
|
|
s = " GMT";
|
2015-02-06 13:35:03 +01:00
|
|
|
|
p = std::copy_n(s, 4, p);
|
2014-11-10 13:26:48 +01:00
|
|
|
|
|
|
|
|
|
return res;
|
2012-02-07 17:50:58 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::string common_log_date(time_t t) {
|
2014-11-23 08:40:38 +01:00
|
|
|
|
struct tm tms;
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (localtime_r(&t, &tms) == nullptr) {
|
2014-11-23 08:40:38 +01:00
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Format data like this:
|
|
|
|
|
// 03/Jul/2014:00:19:38 +0900
|
|
|
|
|
std::string res;
|
|
|
|
|
res.resize(26);
|
|
|
|
|
|
|
|
|
|
auto p = std::begin(res);
|
|
|
|
|
|
|
|
|
|
p = cpydig(p, tms.tm_mday, 2);
|
|
|
|
|
*p++ = '/';
|
|
|
|
|
auto s = MONTH[tms.tm_mon];
|
2015-02-06 13:35:03 +01:00
|
|
|
|
p = std::copy_n(s, 3, p);
|
2014-11-23 08:40:38 +01:00
|
|
|
|
*p++ = '/';
|
|
|
|
|
p = cpydig(p, tms.tm_year + 1900, 4);
|
|
|
|
|
*p++ = ':';
|
|
|
|
|
p = cpydig(p, tms.tm_hour, 2);
|
|
|
|
|
*p++ = ':';
|
|
|
|
|
p = cpydig(p, tms.tm_min, 2);
|
|
|
|
|
*p++ = ':';
|
|
|
|
|
p = cpydig(p, tms.tm_sec, 2);
|
|
|
|
|
*p++ = ' ';
|
|
|
|
|
|
2015-06-29 16:14:54 +02:00
|
|
|
|
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
|
2014-11-23 08:40:38 +01:00
|
|
|
|
auto gmtoff = tms.tm_gmtoff;
|
2015-06-29 16:14:54 +02:00
|
|
|
|
#else // !HAVE_STRUCT_TM_TM_GMTOFF
|
2015-07-15 16:44:24 +02:00
|
|
|
|
auto gmtoff = nghttp2_timegm(&tms) - t;
|
2015-06-29 16:14:54 +02:00
|
|
|
|
#endif // !HAVE_STRUCT_TM_TM_GMTOFF
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (gmtoff >= 0) {
|
2014-11-23 08:40:38 +01:00
|
|
|
|
*p++ = '+';
|
|
|
|
|
} else {
|
|
|
|
|
*p++ = '-';
|
|
|
|
|
gmtoff = -gmtoff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p = cpydig(p, gmtoff / 3600, 2);
|
|
|
|
|
p = cpydig(p, (gmtoff % 3600) / 60, 2);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::string iso8601_date(int64_t ms) {
|
2014-11-23 08:40:38 +01:00
|
|
|
|
time_t sec = ms / 1000;
|
|
|
|
|
|
|
|
|
|
tm tms;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (localtime_r(&sec, &tms) == nullptr) {
|
2014-11-23 08:40:38 +01:00
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Format data like this:
|
|
|
|
|
// 2014-11-15T12:58:24.741Z
|
|
|
|
|
// 2014-11-15T12:58:24.741+09:00
|
|
|
|
|
std::string res;
|
|
|
|
|
res.resize(29);
|
|
|
|
|
|
|
|
|
|
auto p = std::begin(res);
|
|
|
|
|
|
|
|
|
|
p = cpydig(p, tms.tm_year + 1900, 4);
|
|
|
|
|
*p++ = '-';
|
|
|
|
|
p = cpydig(p, tms.tm_mon + 1, 2);
|
|
|
|
|
*p++ = '-';
|
|
|
|
|
p = cpydig(p, tms.tm_mday, 2);
|
|
|
|
|
*p++ = 'T';
|
|
|
|
|
p = cpydig(p, tms.tm_hour, 2);
|
|
|
|
|
*p++ = ':';
|
|
|
|
|
p = cpydig(p, tms.tm_min, 2);
|
|
|
|
|
*p++ = ':';
|
|
|
|
|
p = cpydig(p, tms.tm_sec, 2);
|
|
|
|
|
*p++ = '.';
|
|
|
|
|
p = cpydig(p, ms % 1000, 3);
|
|
|
|
|
|
2015-06-29 16:14:54 +02:00
|
|
|
|
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
|
2014-11-23 08:40:38 +01:00
|
|
|
|
auto gmtoff = tms.tm_gmtoff;
|
2015-06-29 16:14:54 +02:00
|
|
|
|
#else // !HAVE_STRUCT_TM_TM_GMTOFF
|
2015-07-15 16:44:24 +02:00
|
|
|
|
auto gmtoff = nghttp2_timegm(&tms) - sec;
|
2015-06-29 16:14:54 +02:00
|
|
|
|
#endif // !HAVE_STRUCT_TM_TM_GMTOFF
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (gmtoff == 0) {
|
2014-11-23 08:40:38 +01:00
|
|
|
|
*p++ = 'Z';
|
|
|
|
|
} else {
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (gmtoff > 0) {
|
2014-11-23 08:40:38 +01:00
|
|
|
|
*p++ = '+';
|
|
|
|
|
} else {
|
|
|
|
|
*p++ = '-';
|
|
|
|
|
gmtoff = -gmtoff;
|
|
|
|
|
}
|
|
|
|
|
p = cpydig(p, gmtoff / 3600, 2);
|
|
|
|
|
*p++ = ':';
|
|
|
|
|
p = cpydig(p, (gmtoff % 3600) / 60, 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.resize(p - std::begin(res));
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
time_t parse_http_date(const std::string &s) {
|
2012-02-07 17:50:58 +01:00
|
|
|
|
tm tm;
|
|
|
|
|
memset(&tm, 0, sizeof(tm));
|
2014-11-27 15:39:04 +01:00
|
|
|
|
char *r = strptime(s.c_str(), "%a, %d %b %Y %H:%M:%S GMT", &tm);
|
|
|
|
|
if (r == 0) {
|
2012-02-07 17:50:58 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2015-07-15 16:44:24 +02:00
|
|
|
|
return nghttp2_timegm(&tm);
|
2012-02-07 17:50:58 +01:00
|
|
|
|
}
|
|
|
|
|
|
2012-05-19 10:05:12 +02:00
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
|
void streq_advance(const char **ap, const char **bp) {
|
|
|
|
|
for (; **ap && **bp && lowcase(**ap) == lowcase(**bp); ++*ap, ++*bp)
|
|
|
|
|
;
|
2012-05-19 10:05:12 +02:00
|
|
|
|
}
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool istartsWith(const char *a, const char *b) {
|
|
|
|
|
if (!a || !b) {
|
2012-05-19 10:05:12 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
streq_advance(&a, &b);
|
|
|
|
|
return !*b;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool strieq(const char *a, const char *b) {
|
|
|
|
|
if (!a || !b) {
|
2012-05-19 10:05:12 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (; *a && *b && lowcase(*a) == lowcase(*b); ++a, ++b)
|
|
|
|
|
;
|
2012-05-19 10:05:12 +02:00
|
|
|
|
return !*a && !*b;
|
|
|
|
|
}
|
2012-02-07 17:50:58 +01:00
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
int strcompare(const char *a, const uint8_t *b, size_t bn) {
|
2013-08-10 17:08:44 +02:00
|
|
|
|
assert(a && b);
|
|
|
|
|
const uint8_t *blast = b + bn;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (; *a && b != blast; ++a, ++b) {
|
|
|
|
|
if (*a < *b) {
|
2013-08-10 17:08:44 +02:00
|
|
|
|
return -1;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
} else if (*a > *b) {
|
2013-08-10 17:08:44 +02:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (!*a && b == blast) {
|
2013-08-10 17:08:44 +02:00
|
|
|
|
return 0;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
} else if (b == blast) {
|
2013-08-10 17:08:44 +02:00
|
|
|
|
return 1;
|
|
|
|
|
} else {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool strifind(const char *a, const char *b) {
|
|
|
|
|
if (!a || !b) {
|
2012-06-04 16:48:31 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (size_t i = 0; a[i]; ++i) {
|
2012-06-04 16:48:31 +02:00
|
|
|
|
const char *ap = &a[i], *bp = b;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (; *ap && *bp && lowcase(*ap) == lowcase(*bp); ++ap, ++bp)
|
|
|
|
|
;
|
|
|
|
|
if (!*bp) {
|
2012-06-04 16:48:31 +02:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
char upcase(char c) {
|
|
|
|
|
if ('a' <= c && c <= 'z') {
|
|
|
|
|
return c - 'a' + 'A';
|
2012-11-18 13:23:13 +01:00
|
|
|
|
} else {
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-14 16:09:33 +02:00
|
|
|
|
namespace {
|
|
|
|
|
const char LOWER_XDIGITS[] = "0123456789abcdef";
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::string format_hex(const unsigned char *s, size_t len) {
|
2013-07-15 17:15:04 +02:00
|
|
|
|
std::string res;
|
2014-05-14 16:09:33 +02:00
|
|
|
|
res.resize(len * 2);
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
2014-05-14 16:09:33 +02:00
|
|
|
|
unsigned char c = s[i];
|
|
|
|
|
|
|
|
|
|
res[i * 2] = LOWER_XDIGITS[c >> 4];
|
|
|
|
|
res[i * 2 + 1] = LOWER_XDIGITS[c & 0x0f];
|
2013-07-15 17:15:04 +02:00
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
void to_token68(std::string &base64str) {
|
2015-02-10 14:35:10 +01:00
|
|
|
|
std::transform(std::begin(base64str), std::end(base64str),
|
|
|
|
|
std::begin(base64str), [](char c) {
|
|
|
|
|
switch (c) {
|
2013-08-03 11:51:01 +02:00
|
|
|
|
case '+':
|
2015-02-10 14:35:10 +01:00
|
|
|
|
return '-';
|
2013-08-03 11:51:01 +02:00
|
|
|
|
case '/':
|
2015-02-10 14:35:10 +01:00
|
|
|
|
return '_';
|
|
|
|
|
default:
|
|
|
|
|
return c;
|
2013-08-03 11:51:01 +02:00
|
|
|
|
}
|
2015-02-10 14:35:10 +01:00
|
|
|
|
});
|
2015-02-10 17:05:22 +01:00
|
|
|
|
base64str.erase(std::find(std::begin(base64str), std::end(base64str), '='),
|
|
|
|
|
std::end(base64str));
|
2013-08-03 11:51:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
void to_base64(std::string &token68str) {
|
2015-02-10 14:35:10 +01:00
|
|
|
|
std::transform(std::begin(token68str), std::end(token68str),
|
|
|
|
|
std::begin(token68str), [](char c) {
|
|
|
|
|
switch (c) {
|
2013-08-03 11:51:01 +02:00
|
|
|
|
case '-':
|
2015-02-10 14:35:10 +01:00
|
|
|
|
return '+';
|
2013-08-03 11:51:01 +02:00
|
|
|
|
case '_':
|
2015-02-10 14:35:10 +01:00
|
|
|
|
return '/';
|
|
|
|
|
default:
|
|
|
|
|
return c;
|
2013-08-03 11:51:01 +02:00
|
|
|
|
}
|
2015-02-10 14:35:10 +01:00
|
|
|
|
});
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (token68str.size() & 0x3) {
|
2013-11-09 08:18:01 +01:00
|
|
|
|
token68str.append(4 - (token68str.size() & 0x3), '=');
|
|
|
|
|
}
|
2013-08-03 11:51:01 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-08 17:27:56 +01:00
|
|
|
|
namespace {
|
|
|
|
|
// Calculates Damerau–Levenshtein distance between c-string a and b
|
|
|
|
|
// with given costs. swapcost, subcost, addcost and delcost are cost
|
|
|
|
|
// to swap 2 adjacent characters, substitute characters, add character
|
|
|
|
|
// and delete character respectively.
|
2015-01-27 16:58:42 +01:00
|
|
|
|
int levenshtein(const char *a, int alen, const char *b, int blen, int swapcost,
|
|
|
|
|
int subcost, int addcost, int delcost) {
|
2014-11-27 15:39:04 +01:00
|
|
|
|
auto dp = std::vector<std::vector<int>>(3, std::vector<int>(blen + 1));
|
|
|
|
|
for (int i = 0; i <= blen; ++i) {
|
2014-01-08 17:27:56 +01:00
|
|
|
|
dp[1][i] = i;
|
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (int i = 1; i <= alen; ++i) {
|
2014-01-08 17:27:56 +01:00
|
|
|
|
dp[0][0] = i;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (int j = 1; j <= blen; ++j) {
|
|
|
|
|
dp[0][j] = dp[1][j - 1] + (a[i - 1] == b[j - 1] ? 0 : subcost);
|
|
|
|
|
if (i >= 2 && j >= 2 && a[i - 1] != b[j - 1] && a[i - 2] == b[j - 1] &&
|
|
|
|
|
a[i - 1] == b[j - 2]) {
|
|
|
|
|
dp[0][j] = std::min(dp[0][j], dp[2][j - 2] + swapcost);
|
2014-01-08 17:27:56 +01:00
|
|
|
|
}
|
|
|
|
|
dp[0][j] = std::min(dp[0][j],
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::min(dp[1][j] + delcost, dp[0][j - 1] + addcost));
|
2014-01-08 17:27:56 +01:00
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::rotate(std::begin(dp), std::begin(dp) + 2, std::end(dp));
|
2014-01-08 17:27:56 +01:00
|
|
|
|
}
|
|
|
|
|
return dp[1][blen];
|
|
|
|
|
}
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
void show_candidates(const char *unkopt, option *options) {
|
|
|
|
|
for (; *unkopt == '-'; ++unkopt)
|
|
|
|
|
;
|
|
|
|
|
if (*unkopt == '\0') {
|
2014-01-08 17:27:56 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
2015-01-27 16:58:42 +01:00
|
|
|
|
auto unkoptend = unkopt;
|
|
|
|
|
for (; *unkoptend && *unkoptend != '='; ++unkoptend)
|
|
|
|
|
;
|
|
|
|
|
auto unkoptlen = unkoptend - unkopt;
|
|
|
|
|
if (unkoptlen == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-01-13 15:51:10 +01:00
|
|
|
|
int prefix_match = 0;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
auto cands = std::vector<std::pair<int, const char *>>();
|
|
|
|
|
for (size_t i = 0; options[i].name != nullptr; ++i) {
|
2014-01-13 15:51:10 +01:00
|
|
|
|
auto optnamelen = strlen(options[i].name);
|
|
|
|
|
// Use cost 0 for prefix match
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (istartsWith(options[i].name, options[i].name + optnamelen, unkopt,
|
|
|
|
|
unkopt + unkoptlen)) {
|
2015-02-02 14:40:41 +01:00
|
|
|
|
if (optnamelen == static_cast<size_t>(unkoptlen)) {
|
2014-01-13 15:51:10 +01:00
|
|
|
|
// Exact match, then we don't show any condidates.
|
2014-11-27 15:39:04 +01:00
|
|
|
|
return;
|
2014-01-13 15:51:10 +01:00
|
|
|
|
}
|
|
|
|
|
++prefix_match;
|
|
|
|
|
cands.emplace_back(0, options[i].name);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// Use cost 0 for suffix match, but match at least 3 characters
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (unkoptlen >= 3 &&
|
|
|
|
|
iendsWith(options[i].name, options[i].name + optnamelen, unkopt,
|
|
|
|
|
unkopt + unkoptlen)) {
|
2014-01-08 17:27:56 +01:00
|
|
|
|
cands.emplace_back(0, options[i].name);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// cost values are borrowed from git, help.c.
|
2015-01-27 16:58:42 +01:00
|
|
|
|
int sim =
|
|
|
|
|
levenshtein(unkopt, unkoptlen, options[i].name, optnamelen, 0, 2, 1, 3);
|
2014-01-08 17:27:56 +01:00
|
|
|
|
cands.emplace_back(sim, options[i].name);
|
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (prefix_match == 1 || cands.empty()) {
|
2014-01-08 17:27:56 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
std::sort(std::begin(cands), std::end(cands));
|
|
|
|
|
int threshold = cands[0].first;
|
|
|
|
|
// threshold value is a magic value.
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (threshold > 6) {
|
2014-01-08 17:27:56 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
std::cerr << "\nDid you mean:\n";
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (auto &item : cands) {
|
|
|
|
|
if (item.first > threshold) {
|
2014-01-08 17:27:56 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
std::cerr << "\t--" << item.second << "\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool has_uri_field(const http_parser_url &u, http_parser_url_fields field) {
|
2014-02-27 13:47:04 +01:00
|
|
|
|
return u.field_set & (1 << field);
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool fieldeq(const char *uri1, const http_parser_url &u1, const char *uri2,
|
|
|
|
|
const http_parser_url &u2, http_parser_url_fields field) {
|
|
|
|
|
if (!has_uri_field(u1, field)) {
|
|
|
|
|
if (!has_uri_field(u2, field)) {
|
2014-02-27 13:47:04 +01:00
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
} else if (!has_uri_field(u2, field)) {
|
2014-02-27 13:47:04 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (u1.field_data[field].len != u2.field_data[field].len) {
|
2014-02-27 13:47:04 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
return memcmp(uri1 + u1.field_data[field].off,
|
|
|
|
|
uri2 + u2.field_data[field].off, u1.field_data[field].len) == 0;
|
2014-02-27 13:47:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool fieldeq(const char *uri, const http_parser_url &u,
|
2014-11-27 15:39:04 +01:00
|
|
|
|
http_parser_url_fields field, const char *t) {
|
|
|
|
|
if (!has_uri_field(u, field)) {
|
|
|
|
|
if (!t[0]) {
|
2014-02-27 13:47:04 +01:00
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
} else if (!t[0]) {
|
2014-02-27 13:47:04 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int i, len = u.field_data[field].len;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
const char *p = uri + u.field_data[field].off;
|
|
|
|
|
for (i = 0; i < len && t[i] && p[i] == t[i]; ++i)
|
|
|
|
|
;
|
2014-02-27 13:47:04 +01:00
|
|
|
|
return i == len && !t[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string get_uri_field(const char *uri, const http_parser_url &u,
|
2014-11-27 15:39:04 +01:00
|
|
|
|
http_parser_url_fields field) {
|
|
|
|
|
if (util::has_uri_field(u, field)) {
|
|
|
|
|
return std::string(uri + u.field_data[field].off, u.field_data[field].len);
|
2014-02-27 13:47:04 +01:00
|
|
|
|
} else {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
uint16_t get_default_port(const char *uri, const http_parser_url &u) {
|
|
|
|
|
if (util::fieldeq(uri, u, UF_SCHEMA, "https")) {
|
2014-02-27 13:47:04 +01:00
|
|
|
|
return 443;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
} else if (util::fieldeq(uri, u, UF_SCHEMA, "http")) {
|
2014-02-27 13:47:04 +01:00
|
|
|
|
return 80;
|
|
|
|
|
} else {
|
|
|
|
|
return 443;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool porteq(const char *uri1, const http_parser_url &u1, const char *uri2,
|
|
|
|
|
const http_parser_url &u2) {
|
2014-02-27 13:47:04 +01:00
|
|
|
|
uint16_t port1, port2;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
port1 =
|
|
|
|
|
util::has_uri_field(u1, UF_PORT) ? u1.port : get_default_port(uri1, u1);
|
|
|
|
|
port2 =
|
|
|
|
|
util::has_uri_field(u2, UF_PORT) ? u2.port : get_default_port(uri2, u2);
|
2014-02-27 13:47:04 +01:00
|
|
|
|
return port1 == port2;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u,
|
|
|
|
|
http_parser_url_fields field) {
|
|
|
|
|
if (util::has_uri_field(u, field)) {
|
|
|
|
|
o.write(uri + u.field_data[field].off, u.field_data[field].len);
|
2014-02-27 13:47:04 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool numeric_host(const char *hostname) {
|
2014-03-15 07:32:38 +01:00
|
|
|
|
struct addrinfo hints;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
struct addrinfo *res;
|
2014-03-15 07:32:38 +01:00
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
|
hints.ai_flags = AI_NUMERICHOST;
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (getaddrinfo(hostname, nullptr, &hints, &res)) {
|
2014-03-15 07:32:38 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
freeaddrinfo(res);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-26 14:59:25 +01:00
|
|
|
|
std::string numeric_name(const struct sockaddr *sa, socklen_t salen) {
|
2015-02-26 15:59:53 +01:00
|
|
|
|
std::array<char, NI_MAXHOST> host;
|
|
|
|
|
auto rv = getnameinfo(sa, salen, host.data(), host.size(), nullptr, 0,
|
|
|
|
|
NI_NUMERICHOST);
|
2015-02-24 15:50:18 +01:00
|
|
|
|
if (rv != 0) {
|
|
|
|
|
return "unknown";
|
|
|
|
|
}
|
2015-02-26 15:59:53 +01:00
|
|
|
|
return host.data();
|
2015-02-24 15:50:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
int reopen_log_file(const char *path) {
|
2014-08-17 12:01:51 +02:00
|
|
|
|
#if defined(__ANDROID__) || defined(ANDROID)
|
|
|
|
|
int fd;
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (strcmp("/proc/self/fd/1", path) == 0 ||
|
|
|
|
|
strcmp("/proc/self/fd/2", path) == 0) {
|
2014-08-17 12:01:51 +02:00
|
|
|
|
|
|
|
|
|
// We will get permission denied error when O_APPEND is used for
|
|
|
|
|
// these paths.
|
2014-11-27 15:39:04 +01:00
|
|
|
|
fd =
|
|
|
|
|
open(path, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP);
|
2014-08-17 12:01:51 +02:00
|
|
|
|
} else {
|
|
|
|
|
fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
|
|
|
|
|
S_IRUSR | S_IWUSR | S_IRGRP);
|
|
|
|
|
}
|
2014-10-08 16:44:38 +02:00
|
|
|
|
#elif defined O_CLOEXEC
|
|
|
|
|
|
2014-08-12 15:22:02 +02:00
|
|
|
|
auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
|
2014-07-05 15:41:53 +02:00
|
|
|
|
S_IRUSR | S_IWUSR | S_IRGRP);
|
2014-10-08 16:44:38 +02:00
|
|
|
|
#else // !O_CLOEXEC
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
auto fd =
|
|
|
|
|
open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
|
2014-10-08 16:44:38 +02:00
|
|
|
|
|
|
|
|
|
// We get race condition if execve is called at the same time.
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (fd != -1) {
|
2015-01-06 14:03:44 +01:00
|
|
|
|
make_socket_closeonexec(fd);
|
2014-10-08 16:44:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // !O_CLOEXEC
|
2014-07-05 11:22:40 +02:00
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (fd == -1) {
|
2014-07-05 11:22:40 +02:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::string ascii_dump(const uint8_t *data, size_t len) {
|
2014-07-12 16:30:13 +02:00
|
|
|
|
std::string res;
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
2014-07-12 16:30:13 +02:00
|
|
|
|
auto c = data[i];
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (c >= 0x20 && c < 0x7f) {
|
2014-07-12 16:30:13 +02:00
|
|
|
|
res += c;
|
|
|
|
|
} else {
|
|
|
|
|
res += ".";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
char *get_exec_path(int argc, char **const argv, const char *cwd) {
|
|
|
|
|
if (argc == 0 || cwd == nullptr) {
|
2014-08-12 15:22:02 +02:00
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto argv0 = argv[0];
|
|
|
|
|
auto len = strlen(argv0);
|
|
|
|
|
|
|
|
|
|
char *path;
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
if (argv0[0] == '/') {
|
|
|
|
|
path = static_cast<char *>(malloc(len + 1));
|
2015-04-15 14:18:39 +02:00
|
|
|
|
if (path == nullptr) {
|
2015-04-23 17:16:14 +02:00
|
|
|
|
return nullptr;
|
2015-04-15 02:20:45 +02:00
|
|
|
|
}
|
2014-08-12 15:22:02 +02:00
|
|
|
|
memcpy(path, argv0, len + 1);
|
|
|
|
|
} else {
|
|
|
|
|
auto cwdlen = strlen(cwd);
|
2014-11-27 15:39:04 +01:00
|
|
|
|
path = static_cast<char *>(malloc(len + 1 + cwdlen + 1));
|
2015-04-15 14:18:39 +02:00
|
|
|
|
if (path == nullptr) {
|
2015-04-23 17:16:14 +02:00
|
|
|
|
return nullptr;
|
2015-04-15 02:20:45 +02:00
|
|
|
|
}
|
2014-08-12 15:22:02 +02:00
|
|
|
|
memcpy(path, cwd, cwdlen);
|
|
|
|
|
path[cwdlen] = '/';
|
|
|
|
|
memcpy(path + cwdlen + 1, argv0, len + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool check_path(const std::string &path) {
|
2014-09-24 16:05:13 +02:00
|
|
|
|
// We don't like '\' in path.
|
|
|
|
|
return !path.empty() && path[0] == '/' &&
|
2014-11-27 15:39:04 +01:00
|
|
|
|
path.find('\\') == std::string::npos &&
|
|
|
|
|
path.find("/../") == std::string::npos &&
|
|
|
|
|
path.find("/./") == std::string::npos &&
|
|
|
|
|
!util::endsWith(path, "/..") && !util::endsWith(path, "/.");
|
2014-09-24 16:05:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
int64_t to_time64(const timeval &tv) {
|
2014-11-05 16:56:07 +01:00
|
|
|
|
return tv.tv_sec * 1000000 + tv.tv_usec;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
bool check_h2_is_selected(const unsigned char *proto, size_t len) {
|
2015-04-05 16:00:59 +02:00
|
|
|
|
return streq_l(NGHTTP2_PROTO_VERSION_ID, proto, len) ||
|
2015-02-20 15:50:17 +01:00
|
|
|
|
streq_l(NGHTTP2_H2_16, proto, len) ||
|
2015-04-05 16:00:59 +02:00
|
|
|
|
streq_l(NGHTTP2_H2_14, proto, len);
|
2014-12-15 14:51:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
bool select_h2(const unsigned char **out, unsigned char *outlen,
|
2015-02-06 13:45:14 +01:00
|
|
|
|
const unsigned char *in, unsigned int inlen, const char *key,
|
|
|
|
|
unsigned int keylen) {
|
|
|
|
|
for (auto p = in, end = in + inlen; p + keylen <= end; p += *p + 1) {
|
2015-02-10 16:20:33 +01:00
|
|
|
|
if (std::equal(key, key + keylen, p)) {
|
2015-02-06 13:45:14 +01:00
|
|
|
|
*out = p + 1;
|
|
|
|
|
*outlen = *p;
|
2014-12-15 14:51:34 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
bool select_h2(const unsigned char **out, unsigned char *outlen,
|
|
|
|
|
const unsigned char *in, unsigned int inlen) {
|
2015-04-05 16:00:59 +02:00
|
|
|
|
return select_h2(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN,
|
|
|
|
|
str_size(NGHTTP2_PROTO_ALPN)) ||
|
2015-02-20 15:50:17 +01:00
|
|
|
|
select_h2(out, outlen, in, inlen, NGHTTP2_H2_16_ALPN,
|
|
|
|
|
str_size(NGHTTP2_H2_16_ALPN)) ||
|
2015-04-05 16:00:59 +02:00
|
|
|
|
select_h2(out, outlen, in, inlen, NGHTTP2_H2_14_ALPN,
|
|
|
|
|
str_size(NGHTTP2_H2_14_ALPN));
|
2014-11-14 15:14:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
|
std::vector<unsigned char> get_default_alpn() {
|
2015-02-20 15:50:17 +01:00
|
|
|
|
auto res = std::vector<unsigned char>(str_size(NGHTTP2_PROTO_ALPN) +
|
|
|
|
|
str_size(NGHTTP2_H2_16_ALPN) +
|
2015-04-05 16:00:59 +02:00
|
|
|
|
str_size(NGHTTP2_H2_14_ALPN));
|
2015-02-06 13:45:14 +01:00
|
|
|
|
auto p = std::begin(res);
|
|
|
|
|
|
2015-02-20 15:50:17 +01:00
|
|
|
|
p = std::copy_n(NGHTTP2_PROTO_ALPN, str_size(NGHTTP2_PROTO_ALPN), p);
|
2015-04-05 16:00:59 +02:00
|
|
|
|
p = std::copy_n(NGHTTP2_H2_16_ALPN, str_size(NGHTTP2_H2_16_ALPN), p);
|
|
|
|
|
p = std::copy_n(NGHTTP2_H2_14_ALPN, str_size(NGHTTP2_H2_14_ALPN), p);
|
2014-11-14 15:14:39 +01:00
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
|
int make_socket_closeonexec(int fd) {
|
|
|
|
|
int flags;
|
|
|
|
|
int rv;
|
|
|
|
|
while ((flags = fcntl(fd, F_GETFD)) == -1 && errno == EINTR)
|
|
|
|
|
;
|
|
|
|
|
while ((rv = fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1 && errno == EINTR)
|
|
|
|
|
;
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-05 07:59:51 +01:00
|
|
|
|
int make_socket_nonblocking(int fd) {
|
|
|
|
|
int flags;
|
|
|
|
|
int rv;
|
|
|
|
|
while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
|
|
|
|
|
;
|
|
|
|
|
while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
|
|
|
|
|
;
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
|
int make_socket_nodelay(int fd) {
|
|
|
|
|
int val = 1;
|
|
|
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&val),
|
|
|
|
|
sizeof(val)) == -1) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int create_nonblock_socket(int family) {
|
2015-01-10 16:28:00 +01:00
|
|
|
|
#ifdef SOCK_NONBLOCK
|
2014-12-27 18:59:06 +01:00
|
|
|
|
auto fd = socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
|
|
|
|
|
|
|
|
|
if (fd == -1) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2015-01-10 16:28:00 +01:00
|
|
|
|
#else // !SOCK_NONBLOCK
|
|
|
|
|
auto fd = socket(family, SOCK_STREAM, 0);
|
|
|
|
|
|
|
|
|
|
if (fd == -1) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
make_socket_nonblocking(fd);
|
|
|
|
|
make_socket_closeonexec(fd);
|
|
|
|
|
#endif // !SOCK_NONBLOCK
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
2015-02-22 04:27:51 +01:00
|
|
|
|
if (family == AF_INET || family == AF_INET6) {
|
|
|
|
|
make_socket_nodelay(fd);
|
|
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool check_socket_connected(int fd) {
|
|
|
|
|
int error;
|
|
|
|
|
socklen_t len = sizeof(error);
|
|
|
|
|
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) {
|
|
|
|
|
if (error != 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-10 13:33:53 +01:00
|
|
|
|
bool ipv6_numeric_addr(const char *host) {
|
|
|
|
|
uint8_t dst[16];
|
|
|
|
|
return inet_pton(AF_INET6, host, dst) == 1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-27 16:36:44 +01:00
|
|
|
|
namespace {
|
|
|
|
|
std::pair<int64_t, size_t> parse_uint_digits(const void *ss, size_t len) {
|
|
|
|
|
const uint8_t *s = static_cast<const uint8_t *>(ss);
|
2015-01-13 13:54:53 +01:00
|
|
|
|
int64_t n = 0;
|
|
|
|
|
size_t i;
|
|
|
|
|
if (len == 0) {
|
2015-01-27 16:36:44 +01:00
|
|
|
|
return {-1, 0};
|
2015-01-13 13:54:53 +01:00
|
|
|
|
}
|
|
|
|
|
constexpr int64_t max = std::numeric_limits<int64_t>::max();
|
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
|
|
|
if ('0' <= s[i] && s[i] <= '9') {
|
|
|
|
|
if (n > max / 10) {
|
2015-01-27 16:36:44 +01:00
|
|
|
|
return {-1, 0};
|
2015-01-13 13:54:53 +01:00
|
|
|
|
}
|
|
|
|
|
n *= 10;
|
|
|
|
|
if (n > max - (s[i] - '0')) {
|
2015-01-27 16:36:44 +01:00
|
|
|
|
return {-1, 0};
|
2015-01-13 13:54:53 +01:00
|
|
|
|
}
|
|
|
|
|
n += s[i] - '0';
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-01-27 16:36:44 +01:00
|
|
|
|
if (i == 0) {
|
|
|
|
|
return {-1, 0};
|
|
|
|
|
}
|
|
|
|
|
return {n, i};
|
|
|
|
|
}
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
int64_t parse_uint_with_unit(const char *s) {
|
|
|
|
|
int64_t n;
|
|
|
|
|
size_t i;
|
|
|
|
|
auto len = strlen(s);
|
|
|
|
|
std::tie(n, i) = parse_uint_digits(s, len);
|
|
|
|
|
if (n == -1) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2015-01-13 13:54:53 +01:00
|
|
|
|
if (i == len) {
|
|
|
|
|
return n;
|
|
|
|
|
}
|
2015-01-27 16:36:44 +01:00
|
|
|
|
if (i + 1 != len) {
|
2015-01-13 13:54:53 +01:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
int mul = 1;
|
|
|
|
|
switch (s[i]) {
|
|
|
|
|
case 'K':
|
|
|
|
|
case 'k':
|
|
|
|
|
mul = 1 << 10;
|
|
|
|
|
break;
|
|
|
|
|
case 'M':
|
|
|
|
|
case 'm':
|
|
|
|
|
mul = 1 << 20;
|
|
|
|
|
break;
|
|
|
|
|
case 'G':
|
|
|
|
|
case 'g':
|
|
|
|
|
mul = 1 << 30;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2015-01-27 16:36:44 +01:00
|
|
|
|
constexpr int64_t max = std::numeric_limits<int64_t>::max();
|
2015-01-13 13:54:53 +01:00
|
|
|
|
if (n > max / mul) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return n * mul;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-15 15:07:25 +01:00
|
|
|
|
int64_t parse_uint(const char *s) {
|
|
|
|
|
return parse_uint(reinterpret_cast<const uint8_t *>(s), strlen(s));
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-17 11:33:30 +01:00
|
|
|
|
int64_t parse_uint(const std::string &s) {
|
|
|
|
|
return parse_uint(reinterpret_cast<const uint8_t *>(s.c_str()), s.size());
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-15 15:07:25 +01:00
|
|
|
|
int64_t parse_uint(const uint8_t *s, size_t len) {
|
2015-01-27 16:36:44 +01:00
|
|
|
|
int64_t n;
|
|
|
|
|
size_t i;
|
|
|
|
|
std::tie(n, i) = parse_uint_digits(s, len);
|
|
|
|
|
if (n == -1 || i != len) {
|
2015-01-15 15:07:25 +01:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
2015-01-27 16:36:44 +01:00
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 15:28:47 +01:00
|
|
|
|
double parse_duration_with_unit(const char *s) {
|
2015-03-30 16:20:40 +02:00
|
|
|
|
constexpr auto max = std::numeric_limits<int64_t>::max();
|
2015-01-27 16:36:44 +01:00
|
|
|
|
int64_t n;
|
|
|
|
|
size_t i;
|
|
|
|
|
auto len = strlen(s);
|
|
|
|
|
std::tie(n, i) = parse_uint_digits(s, len);
|
|
|
|
|
if (n == -1) {
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
if (i == len) {
|
|
|
|
|
return static_cast<double>(n);
|
|
|
|
|
}
|
|
|
|
|
switch (s[i]) {
|
|
|
|
|
case 'S':
|
|
|
|
|
case 's':
|
2015-03-30 16:20:40 +02:00
|
|
|
|
// seconds
|
2015-01-27 16:36:44 +01:00
|
|
|
|
if (i + 1 != len) {
|
|
|
|
|
goto fail;
|
2015-01-15 15:07:25 +01:00
|
|
|
|
}
|
2015-01-27 16:36:44 +01:00
|
|
|
|
return static_cast<double>(n);
|
|
|
|
|
case 'M':
|
|
|
|
|
case 'm':
|
2015-03-30 16:20:40 +02:00
|
|
|
|
if (i + 1 == len) {
|
|
|
|
|
// minutes
|
|
|
|
|
if (n > max / 60) {
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
return static_cast<double>(n) * 60;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-27 16:36:44 +01:00
|
|
|
|
if (i + 2 != len || (s[i + 1] != 's' && s[i + 1] != 'S')) {
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
2015-03-30 16:20:40 +02:00
|
|
|
|
// milliseconds
|
2015-01-27 16:36:44 +01:00
|
|
|
|
return static_cast<double>(n) / 1000.;
|
2015-03-30 16:20:40 +02:00
|
|
|
|
case 'H':
|
|
|
|
|
case 'h':
|
|
|
|
|
// hours
|
|
|
|
|
if (i + 1 != len) {
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
if (n > max / 3600) {
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
return static_cast<double>(n) * 3600;
|
2015-01-15 15:07:25 +01:00
|
|
|
|
}
|
2015-01-27 16:36:44 +01:00
|
|
|
|
fail:
|
|
|
|
|
return std::numeric_limits<double>::infinity();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string duration_str(double t) {
|
2015-01-29 15:28:47 +01:00
|
|
|
|
if (t == 0.) {
|
|
|
|
|
return "0";
|
|
|
|
|
}
|
2015-01-27 16:36:44 +01:00
|
|
|
|
auto frac = static_cast<int64_t>(t * 1000) % 1000;
|
|
|
|
|
if (frac > 0) {
|
|
|
|
|
return utos(static_cast<int64_t>(t * 1000)) + "ms";
|
|
|
|
|
}
|
2015-03-30 16:20:40 +02:00
|
|
|
|
auto v = static_cast<int64_t>(t);
|
|
|
|
|
if (v % 60) {
|
|
|
|
|
return utos(v) + "s";
|
|
|
|
|
}
|
|
|
|
|
v /= 60;
|
|
|
|
|
if (v % 60) {
|
|
|
|
|
return utos(v) + "m";
|
|
|
|
|
}
|
|
|
|
|
v /= 60;
|
|
|
|
|
return utos(v) + "h";
|
2015-01-15 15:07:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-01-31 10:13:07 +01:00
|
|
|
|
std::string format_duration(const std::chrono::microseconds &u) {
|
|
|
|
|
const char *unit = "us";
|
|
|
|
|
int d = 0;
|
|
|
|
|
auto t = u.count();
|
|
|
|
|
if (t >= 1000000) {
|
|
|
|
|
d = 1000000;
|
|
|
|
|
unit = "s";
|
|
|
|
|
} else if (t >= 1000) {
|
|
|
|
|
d = 1000;
|
|
|
|
|
unit = "ms";
|
|
|
|
|
} else {
|
|
|
|
|
return utos(t) + unit;
|
|
|
|
|
}
|
|
|
|
|
return dtos(static_cast<double>(t) / d) + unit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string dtos(double n) {
|
|
|
|
|
auto f = utos(static_cast<int64_t>(round(100. * n)) % 100);
|
|
|
|
|
return utos(static_cast<int64_t>(n)) + "." + (f.size() == 1 ? "0" : "") + f;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-22 04:27:51 +01:00
|
|
|
|
std::string make_hostport(const char *host, uint16_t port) {
|
|
|
|
|
auto ipv6 = ipv6_numeric_addr(host);
|
|
|
|
|
std::string hostport;
|
|
|
|
|
|
|
|
|
|
if (ipv6) {
|
|
|
|
|
hostport += "[";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hostport += host;
|
|
|
|
|
|
|
|
|
|
if (ipv6) {
|
|
|
|
|
hostport += "]";
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-13 15:01:55 +01:00
|
|
|
|
if (port != 80 && port != 443) {
|
|
|
|
|
hostport += ":";
|
|
|
|
|
hostport += utos(port);
|
|
|
|
|
}
|
2015-02-22 04:27:51 +01:00
|
|
|
|
|
|
|
|
|
return hostport;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-23 18:30:51 +01:00
|
|
|
|
namespace {
|
|
|
|
|
void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) {
|
|
|
|
|
auto stop = std::min(first + 8, last);
|
|
|
|
|
for (auto k = first; k != stop; ++k) {
|
|
|
|
|
fprintf(out, "%02x ", *k);
|
|
|
|
|
}
|
2015-03-24 13:43:28 +01:00
|
|
|
|
// each byte needs 3 spaces (2 hex value and space)
|
|
|
|
|
for (; stop != first + 8; ++stop) {
|
|
|
|
|
fputs(" ", out);
|
|
|
|
|
}
|
|
|
|
|
// we have extra space after 8 bytes
|
|
|
|
|
fputc(' ', out);
|
2015-03-23 18:30:51 +01:00
|
|
|
|
}
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
void hexdump(FILE *out, const uint8_t *src, size_t len) {
|
|
|
|
|
if (len == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
size_t buflen = 0;
|
|
|
|
|
auto repeated = false;
|
|
|
|
|
std::array<uint8_t, 16> buf{};
|
|
|
|
|
auto end = src + len;
|
|
|
|
|
auto i = src;
|
|
|
|
|
for (;;) {
|
2015-03-24 17:27:18 +01:00
|
|
|
|
auto nextlen =
|
|
|
|
|
std::min(static_cast<size_t>(16), static_cast<size_t>(end - i));
|
2015-03-23 18:30:51 +01:00
|
|
|
|
if (nextlen == buflen &&
|
|
|
|
|
std::equal(std::begin(buf), std::begin(buf) + buflen, i)) {
|
2015-03-24 13:43:28 +01:00
|
|
|
|
// as long as adjacent 16 bytes block are the same, we just
|
|
|
|
|
// print single '*'.
|
2015-03-23 18:30:51 +01:00
|
|
|
|
if (!repeated) {
|
|
|
|
|
repeated = true;
|
2015-03-24 13:43:28 +01:00
|
|
|
|
fputs("*\n", out);
|
2015-03-23 18:30:51 +01:00
|
|
|
|
}
|
|
|
|
|
i += nextlen;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
repeated = false;
|
|
|
|
|
fprintf(out, "%08lx", static_cast<unsigned long>(i - src));
|
|
|
|
|
if (i == end) {
|
2015-03-24 13:43:28 +01:00
|
|
|
|
fputc('\n', out);
|
2015-03-23 18:30:51 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
2015-03-24 13:43:28 +01:00
|
|
|
|
fputs(" ", out);
|
2015-03-23 18:30:51 +01:00
|
|
|
|
hexdump8(out, i, end);
|
|
|
|
|
hexdump8(out, i + 8, std::max(i + 8, end));
|
2015-03-24 13:43:28 +01:00
|
|
|
|
fputc('|', out);
|
2015-03-23 18:30:51 +01:00
|
|
|
|
auto stop = std::min(i + 16, end);
|
|
|
|
|
buflen = stop - i;
|
|
|
|
|
auto p = buf.data();
|
|
|
|
|
for (; i != stop; ++i) {
|
|
|
|
|
*p++ = *i;
|
|
|
|
|
if (0x20 <= *i && *i <= 0x7e) {
|
2015-03-24 13:43:28 +01:00
|
|
|
|
fputc(*i, out);
|
2015-03-23 18:30:51 +01:00
|
|
|
|
} else {
|
2015-03-24 13:43:28 +01:00
|
|
|
|
fputc('.', out);
|
2015-03-23 18:30:51 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-03-24 13:43:28 +01:00
|
|
|
|
fputs("|\n", out);
|
2015-03-23 18:30:51 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-29 16:34:10 +01:00
|
|
|
|
} // namespace util
|
|
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
|
} // namespace nghttp2
|