Compare commits

...

7 Commits

Author SHA1 Message Date
yhirose d45250fd88 Appled HANDLE_EINTR to send and select system calls 2020-05-01 21:38:23 -04:00
yhirose 528cacdc0d Changed CPPHTTPLIB_THREAD_POOL_COUNT back to 8. (#454) 2020-05-01 21:23:02 -04:00
Matthew DeVore ed1b6afa10 Fix crash caused by header field regex complexity (#457) 2020-05-01 12:44:13 -04:00
yhirose 08fc7085e5 Fixed #456 2020-04-30 19:40:23 -04:00
yhirose 8333340e2c Chagned to use inline function instead of macro 2020-04-27 12:36:39 -04:00
yhirose 98a0887571 Merge branch 'je-ik-sketch-eintr-handling' 2020-04-27 12:33:21 -04:00
Jan Lukavsky b0a189e50e Sketch handling EINTR errors 2020-04-27 17:36:44 +02:00
3 changed files with 64 additions and 27 deletions
+45 -21
View File
@@ -50,7 +50,7 @@
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
#define CPPHTTPLIB_THREAD_POOL_COUNT \
((std::max)(1u, std::thread::hardware_concurrency() - 1))
((std::max)(8u, std::thread::hardware_concurrency() - 1))
#endif
/*
@@ -1198,7 +1198,22 @@ inline int close_socket(socket_t sock) {
#endif
}
inline int select_read(socket_t sock, time_t sec, time_t usec) {
template <typename T>
inline ssize_t handle_EINTR(T fn) {
ssize_t res = false;
while (true) {
res = fn();
if (res < 0 && errno == EINTR) {
continue;
}
break;
}
return res;
}
#define HANDLE_EINTR(method, ...) (handle_EINTR([&]() { return method(__VA_ARGS__); }))
inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct pollfd pfd_read;
pfd_read.fd = sock;
@@ -1206,7 +1221,7 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
return poll(&pfd_read, 1, timeout);
return HANDLE_EINTR(poll, &pfd_read, 1, timeout);
#else
fd_set fds;
FD_ZERO(&fds);
@@ -1216,11 +1231,11 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
tv.tv_sec = static_cast<long>(sec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
return HANDLE_EINTR(select, static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
#endif
}
inline int select_write(socket_t sock, time_t sec, time_t usec) {
inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct pollfd pfd_read;
pfd_read.fd = sock;
@@ -1228,7 +1243,7 @@ inline int select_write(socket_t sock, time_t sec, time_t usec) {
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
return poll(&pfd_read, 1, timeout);
return HANDLE_EINTR(poll, &pfd_read, 1, timeout);
#else
fd_set fds;
FD_ZERO(&fds);
@@ -1238,7 +1253,7 @@ inline int select_write(socket_t sock, time_t sec, time_t usec) {
tv.tv_sec = static_cast<long>(sec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
return HANDLE_EINTR(select, static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
#endif
}
@@ -1250,13 +1265,13 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
if (poll(&pfd_read, 1, timeout) > 0 &&
pfd_read.revents & (POLLIN | POLLOUT)) {
auto poll_res = HANDLE_EINTR(poll, &pfd_read, 1, timeout);
if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
int error = 0;
socklen_t len = sizeof(error);
return getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *>(&error), &len) >= 0 &&
!error;
auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *>(&error), &len);
return res >= 0 && !error;
}
return false;
#else
@@ -1271,7 +1286,7 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
tv.tv_sec = static_cast<long>(sec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
if (select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv) > 0 &&
if (HANDLE_EINTR(select, static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv) > 0 &&
(FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
int error = 0;
socklen_t len = sizeof(error);
@@ -1515,8 +1530,8 @@ inline bool bind_ip_address(socket_t sock, const char *host) {
return ret;
}
inline std::string if2ip(const std::string &ifn) {
#ifndef _WIN32
inline std::string if2ip(const std::string &ifn) {
struct ifaddrs *ifap;
getifaddrs(&ifap);
for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
@@ -1532,9 +1547,9 @@ inline std::string if2ip(const std::string &ifn) {
}
}
freeifaddrs(ifap);
#endif
return std::string();
}
#endif
inline socket_t create_client_socket(const char *host, int port,
time_t timeout_sec,
@@ -1542,9 +1557,11 @@ inline socket_t create_client_socket(const char *host, int port,
return create_socket(
host, port, [&](socket_t sock, struct addrinfo &ai) -> bool {
if (!intf.empty()) {
#ifndef _WIN32
auto ip = if2ip(intf);
if (ip.empty()) { ip = intf; }
if (!bind_ip_address(sock, ip.c_str())) { return false; }
#endif
}
set_nonblocking(sock, true);
@@ -1845,7 +1862,7 @@ inline bool read_headers(Stream &strm, Headers &headers) {
// the left or right side of the header value:
// - https://stackoverflow.com/questions/50179659/
// - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
static const std::regex re(R"(([^:]+):[\t ]*(.+))");
static const std::regex re(R"(([^:]+):[\t ]*([^\t ].*))");
std::cmatch m;
if (std::regex_match(line_reader.ptr(), end, m, re)) {
@@ -2834,11 +2851,11 @@ inline void Response::set_header(const char *key, const std::string &val) {
}
}
inline void Response::set_redirect(const char *url, int status) {
inline void Response::set_redirect(const char *url, int stat) {
if (!detail::has_crlf(url)) {
set_header("Location", url);
if (300 <= status && status < 400) {
this->status = status;
if (300 <= stat && stat < 400) {
this->status = stat;
} else {
this->status = 302;
}
@@ -2947,7 +2964,7 @@ inline ssize_t SocketStream::read(char *ptr, size_t size) {
}
return recv(sock_, ptr, static_cast<int>(size), 0);
#else
return recv(sock_, ptr, size, 0);
return HANDLE_EINTR(recv, sock_, ptr, size, 0);
#endif
}
@@ -2960,7 +2977,7 @@ inline ssize_t SocketStream::write(const char *ptr, size_t size) {
}
return send(sock_, ptr, static_cast<int>(size), 0);
#else
return send(sock_, ptr, size, 0);
return HANDLE_EINTR(send, sock_, ptr, size, 0);
#endif
}
@@ -5094,8 +5111,15 @@ inline std::shared_ptr<Response> Get(const char *url) {
} // namespace url
namespace detail {
#undef HANDLE_EINTR
} // namespace detail
// ----------------------------------------------------------------------------
} // namespace httplib
#endif // CPPHTTPLIB_HTTPLIB_H
+13
View File
@@ -2333,6 +2333,19 @@ TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity2) {
"&&&%%%");
}
TEST(ServerRequestParsingTest, ExcessiveWhitespaceInUnparseableHeaderLine) {
// Make sure this doesn't crash the server.
// In a previous version of the header line regex, the "\r" rendered the line
// unparseable and the regex engine repeatedly backtracked, trying to look for
// a new position where the leading white space ended and the field value
// began.
// The crash occurs with libc++ but not libstdc++.
test_raw_request("GET /hi HTTP/1.1\r\n"
"a:" + std::string(2000, ' ') + '\r' + std::string(20, 'z') +
"\r\n"
"\r\n");
}
TEST(ServerRequestParsingTest, InvalidFirstChunkLengthInRequest) {
std::string out;
+6 -6
View File
@@ -91,7 +91,7 @@
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
@@ -108,7 +108,7 @@
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
@@ -118,12 +118,12 @@
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>Ws2_32.lib;AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
@@ -144,7 +144,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
@@ -171,4 +171,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>