nghttp2/src/util.cc

956 lines
23 KiB
C++
Raw Normal View History

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"
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <cerrno>
#include <cassert>
2012-01-29 16:34:10 +01:00
#include <cstdio>
#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>
#include "timegm.h"
2013-07-12 17:19:03 +02:00
namespace nghttp2 {
2012-01-29 16:34:10 +01:00
const unsigned char NGHTTP2_H2_PROTO_ALIAS[] = "h2-16";
size_t NGHTTP2_H2_PROTO_ALIAS_LEN = sizeof(NGHTTP2_H2_PROTO_ALIAS) - 1;
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
}
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());
}
2014-11-27 15:39:04 +01:00
bool in_token(char c) {
static const char extra[] = {'!', '#', '$', '%', '&', '\'', '*', '+',
'-', '.', '^', '_', '`', '|', '~'};
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-11-27 15:39:04 +01:00
std::string percent_encode_token(const std::string &target) {
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)) {
dest += c;
} else {
2014-05-14 15:39:28 +02:00
dest += "%";
dest += UPPER_XDIGITS[c >> 4];
dest += UPPER_XDIGITS[(c & 0x0f)];
}
}
return dest;
}
2014-11-27 15:39:04 +01:00
std::string percentDecode(std::string::const_iterator first,
std::string::const_iterator last) {
2012-01-29 16:34:10 +01:00
std::string result;
2014-11-27 15:39:04 +01:00
for (; first != last; ++first) {
if (*first == '%') {
if (first + 1 != last && first + 2 != last && isHexDigit(*(first + 1)) &&
isHexDigit(*(first + 2))) {
std::string numstr(first + 1, first + 3);
2012-01-29 16:34:10 +01:00
result += strtol(numstr.c_str(), 0, 16);
first += 2;
} else {
result += *first;
}
} else {
result += *first;
}
}
return result;
}
2014-11-27 15:39:04 +01:00
std::string quote_string(const std::string &target) {
auto cnt = std::count(std::begin(target), std::end(target), '"');
2014-11-27 15:39:04 +01:00
if (cnt == 0) {
return target;
}
std::string res;
res.reserve(target.size() + cnt);
2014-11-27 15:39:04 +01:00
for (auto c : target) {
if (c == '"') {
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];
p = std::copy(s, s + 3, p);
*p++ = ',';
*p++ = ' ';
p = cpydig(p, tms.tm_mday, 2);
*p++ = ' ';
s = MONTH[tms.tm_mon];
p = std::copy(s, s + 3, p);
*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";
p = std::copy(s, s + 4, p);
return res;
}
2014-11-27 15:39:04 +01:00
std::string common_log_date(time_t t) {
struct tm tms;
2014-11-27 15:39:04 +01:00
if (localtime_r(&t, &tms) == nullptr) {
return "";
}
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
// 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];
p = std::copy(s, s + 3, p);
*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++ = ' ';
auto gmtoff = tms.tm_gmtoff;
2014-11-27 15:39:04 +01:00
if (gmtoff >= 0) {
*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
#else // !HAVE_STRUCT_TM_TM_GMTOFF
char buf[32];
strftime(buf, sizeof(buf), "%d/%b/%Y:%T %z", &tms);
return buf;
#endif // !HAVE_STRUCT_TM_TM_GMTOFF
}
2014-11-27 15:39:04 +01:00
std::string iso8601_date(int64_t ms) {
time_t sec = ms / 1000;
tm tms;
2014-11-27 15:39:04 +01:00
if (localtime_r(&sec, &tms) == nullptr) {
return "";
}
#ifdef HAVE_STRUCT_TM_TM_GMTOFF
// 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);
auto gmtoff = tms.tm_gmtoff;
2014-11-27 15:39:04 +01:00
if (gmtoff == 0) {
*p++ = 'Z';
} else {
2014-11-27 15:39:04 +01:00
if (gmtoff > 0) {
*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
#else // !HAVE_STRUCT_TM_TM_GMTOFF
char buf[128];
auto nwrite = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &tms);
nwrite += snprintf(&buf[nwrite], sizeof(buf) - nwrite, ".%03d",
static_cast<int>(ms % 1000));
auto nzone = strftime(&buf[nwrite], sizeof(buf) - nwrite, "%z", &tms);
// %z of strftime writes +hhmm or -hhmm not Z, +hh:mm or -hh:mm. Do
// %nothing if nzone is not 5. we don't know how to cope with this.
2014-11-27 15:39:04 +01:00
if (nzone == 5) {
if (memcmp(&buf[nwrite], "+0000", 5) == 0) {
// 0000 should be Z
memcpy(&buf[nwrite], "Z", 2);
} else {
// Move mm part to right by 1 including terminal \0
memmove(&buf[nwrite + 4], &buf[nwrite + 3], 3);
// Insert ':' between hh and mm
buf[nwrite + 3] = ':';
}
}
return buf;
#endif // !HAVE_STRUCT_TM_TM_GMTOFF
}
2014-11-27 15:39:04 +01:00
time_t parse_http_date(const std::string &s) {
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) {
return 0;
}
return timegm(&tm);
}
2014-11-27 15:39:04 +01:00
bool startsWith(const std::string &a, const std::string &b) {
return startsWith(a.begin(), a.end(), b.begin(), b.end());
}
2014-11-27 15:39:04 +01:00
bool istartsWith(const std::string &a, const std::string &b) {
return istartsWith(a.begin(), a.end(), b.begin(), b.end());
}
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)
;
}
} // namespace
2014-11-27 15:39:04 +01:00
bool istartsWith(const char *a, const char *b) {
if (!a || !b) {
return false;
}
streq_advance(&a, &b);
return !*b;
}
2014-11-27 15:39:04 +01:00
bool istartsWith(const char *a, size_t n, const char *b) {
return istartsWith(a, a + n, b, b + strlen(b));
}
2014-11-27 15:39:04 +01:00
bool endsWith(const std::string &a, const std::string &b) {
return endsWith(a.begin(), a.end(), b.begin(), b.end());
}
2014-11-27 15:39:04 +01:00
bool strieq(const std::string &a, const std::string &b) {
if (a.size() != b.size()) {
return false;
}
2014-11-27 15:39:04 +01:00
for (size_t i = 0; i < a.size(); ++i) {
if (lowcase(a[i]) != lowcase(b[i])) {
return false;
}
}
return true;
}
2014-11-27 15:39:04 +01:00
bool strieq(const char *a, const char *b) {
if (!a || !b) {
return false;
}
2014-11-27 15:39:04 +01:00
for (; *a && *b && lowcase(*a) == lowcase(*b); ++a, ++b)
;
return !*a && !*b;
}
2014-11-27 15:39:04 +01:00
bool strieq(const char *a, const uint8_t *b, size_t bn) {
if (!a || !b) {
2013-07-19 17:08:14 +02:00
return false;
}
2013-07-21 16:49:12 +02:00
const uint8_t *blast = b + bn;
2014-11-27 15:39:04 +01:00
for (; *a && b != blast && lowcase(*a) == lowcase(*b); ++a, ++b)
;
2013-07-21 16:49:12 +02:00
return !*a && b == blast;
2013-07-19 17:08:14 +02:00
}
2014-11-27 15:39:04 +01:00
bool strieq(const char *a, const char *b, size_t bn) {
return strieq(a, reinterpret_cast<const uint8_t *>(b), bn);
}
2014-11-27 15:39:04 +01:00
int strcompare(const char *a, const uint8_t *b, size_t bn) {
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) {
return -1;
2014-11-27 15:39:04 +01:00
} else if (*a > *b) {
return 1;
}
}
2014-11-27 15:39:04 +01:00
if (!*a && b == blast) {
return 0;
2014-11-27 15:39:04 +01:00
} else if (b == blast) {
return 1;
} else {
return -1;
}
}
2014-11-27 15:39:04 +01:00
bool strifind(const char *a, const char *b) {
if (!a || !b) {
return false;
}
2014-11-27 15:39:04 +01:00
for (size_t i = 0; a[i]; ++i) {
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) {
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';
} 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) {
for (auto i = std::begin(base64str); i != std::end(base64str); ++i) {
switch (*i) {
case '+':
*i = '-';
break;
case '/':
*i = '_';
break;
case '=':
base64str.erase(i, std::end(base64str));
return;
}
}
return;
}
2014-11-27 15:39:04 +01:00
void to_base64(std::string &token68str) {
for (auto i = std::begin(token68str); i != std::end(token68str); ++i) {
switch (*i) {
case '-':
*i = '+';
break;
case '_':
*i = '/';
break;
}
}
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), '=');
}
return;
}
2014-11-27 15:39:04 +01:00
void inp_strlower(std::string &s) {
for (auto i = std::begin(s); i != std::end(s); ++i) {
if ('A' <= *i && *i <= 'Z') {
2013-08-27 17:09:46 +02:00
*i = (*i) - 'A' + 'a';
}
}
}
2014-01-08 17:27:56 +01:00
namespace {
// Calculates DamerauLevenshtein 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.
2014-11-27 15:39:04 +01:00
int levenshtein(const char *a, const char *b, int swapcost, int subcost,
int addcost, int delcost) {
2014-01-08 17:27:56 +01:00
int alen = strlen(a);
int blen = strlen(b);
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;
}
int prefix_match = 0;
auto unkoptlen = strlen(unkopt);
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) {
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)) {
if (optnamelen == unkoptlen) {
// Exact match, then we don't show any condidates.
2014-11-27 15:39:04 +01:00
return;
}
++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.
int sim = levenshtein(unkopt, options[i].name, 0, 2, 1, 3);
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) {
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)) {
return true;
} else {
return false;
}
2014-11-27 15:39:04 +01:00
} else if (!has_uri_field(u2, field)) {
return false;
}
2014-11-27 15:39:04 +01:00
if (u1.field_data[field].len != u2.field_data[field].len) {
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;
}
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]) {
return true;
} else {
return false;
}
2014-11-27 15:39:04 +01:00
} else if (!t[0]) {
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)
;
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);
} 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")) {
return 443;
2014-11-27 15:39:04 +01:00
} else if (util::fieldeq(uri, u, UF_SCHEMA, "http")) {
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) {
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);
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-11-27 15:39:04 +01:00
bool numeric_host(const char *hostname) {
struct addrinfo hints;
2014-11-27 15:39:04 +01:00
struct addrinfo *res;
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)) {
return false;
}
freeaddrinfo(res);
return true;
}
2014-11-27 15:39:04 +01:00
int reopen_log_file(const char *path) {
#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) {
// 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);
} else {
fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
S_IRUSR | S_IWUSR | S_IRGRP);
}
#elif defined O_CLOEXEC
auto fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
S_IRUSR | S_IWUSR | S_IRGRP);
#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);
// We get race condition if execve is called at the same time.
2014-11-27 15:39:04 +01:00
if (fd != -1) {
make_socket_closeonexec(fd);
}
#endif // !O_CLOEXEC
2014-11-27 15:39:04 +01:00
if (fd == -1) {
return -1;
}
return fd;
}
2014-11-27 15:39:04 +01:00
std::string ascii_dump(const uint8_t *data, size_t len) {
std::string res;
2014-11-27 15:39:04 +01:00
for (size_t i = 0; i < len; ++i) {
auto c = data[i];
2014-11-27 15:39:04 +01:00
if (c >= 0x20 && c < 0x7f) {
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) {
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));
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));
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) {
// 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-11-27 15:39:04 +01:00
int64_t to_time64(const timeval &tv) {
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) {
return streq(NGHTTP2_PROTO_VERSION_ID, NGHTTP2_PROTO_VERSION_ID_LEN, proto,
len) ||
streq(NGHTTP2_H2_PROTO_ALIAS, NGHTTP2_H2_PROTO_ALIAS_LEN, proto, len);
}
namespace {
bool select_h2(const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen,
const unsigned char *target, unsigned int tlen) {
for (auto p = in, end = in + inlen; p < end;) {
auto len = *p++;
if (p + len > end) {
return false;
}
if (len != tlen) {
p += len;
continue;
}
if (memcmp(target, p, tlen) == 0) {
*out = target;
*outlen = tlen;
return true;
}
p += len;
}
return false;
}
} // namespace
bool select_h2(const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen) {
return select_h2(out, outlen, in, inlen, NGHTTP2_H2_PROTO_ALIAS,
NGHTTP2_H2_PROTO_ALIAS_LEN) ||
select_h2(
out, outlen, in, inlen,
reinterpret_cast<const unsigned char *>(NGHTTP2_PROTO_VERSION_ID),
NGHTTP2_PROTO_VERSION_ID_LEN);
2014-11-14 15:14:39 +01:00
}
2014-11-27 15:39:04 +01:00
std::vector<unsigned char> get_default_alpn() {
auto res = std::vector<unsigned char>(1 + NGHTTP2_PROTO_VERSION_ID_LEN + 1 +
NGHTTP2_H2_PROTO_ALIAS_LEN);
2014-11-14 15:14:39 +01:00
auto p = res.data();
*p++ = NGHTTP2_H2_PROTO_ALIAS_LEN;
memcpy(p, NGHTTP2_H2_PROTO_ALIAS, NGHTTP2_H2_PROTO_ALIAS_LEN);
p += NGHTTP2_H2_PROTO_ALIAS_LEN;
2014-11-14 15:14:39 +01:00
*p++ = NGHTTP2_PROTO_VERSION_ID_LEN;
memcpy(p, NGHTTP2_PROTO_VERSION_ID, NGHTTP2_PROTO_VERSION_ID_LEN);
return res;
}
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;
}
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) {
#ifdef SOCK_NONBLOCK
auto fd = socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (fd == -1) {
return -1;
}
#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
make_socket_nodelay(fd);
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;
}
bool ipv6_numeric_addr(const char *host) {
uint8_t dst[16];
return inet_pton(AF_INET6, host, dst) == 1;
}
int64_t parse_uint_with_unit(const char *s) {
int64_t n = 0;
size_t i;
auto len = strlen(s);
if (len == 0) {
return -1;
}
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) {
return -1;
}
n *= 10;
if (n > max - (s[i] - '0')) {
return -1;
}
n += s[i] - '0';
continue;
}
break;
}
if (i == len) {
return n;
}
if (i == 0 || i + 1 != len) {
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;
}
if (n > max / mul) {
return -1;
}
return n * mul;
}
2012-01-29 16:34:10 +01:00
} // namespace util
2013-07-12 17:19:03 +02:00
} // namespace nghttp2