Skip to content

Commit 71bb17f

Browse files
committed
Fix problems with CPPHTTPLIB_NO_EXCEPTIONS
1 parent 094bf11 commit 71bb17f

2 files changed

Lines changed: 97 additions & 40 deletions

File tree

httplib.h

Lines changed: 93 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ using socket_t = int;
305305
#include <chrono>
306306
#include <climits>
307307
#include <condition_variable>
308+
#include <cstdlib>
308309
#include <cstring>
309310
#include <errno.h>
310311
#include <exception>
@@ -322,6 +323,7 @@ using socket_t = int;
322323
#include <sstream>
323324
#include <string>
324325
#include <sys/stat.h>
326+
#include <system_error>
325327
#include <thread>
326328
#include <unordered_map>
327329
#include <unordered_set>
@@ -511,6 +513,69 @@ struct scope_exit {
511513
bool execute_on_destruction;
512514
};
513515

516+
// Simple from_chars implementation for integer and double types (C++17
517+
// substitute)
518+
template <typename T> struct from_chars_result {
519+
const char *ptr;
520+
std::errc ec;
521+
};
522+
523+
template <typename T>
524+
inline from_chars_result<T> from_chars(const char *first, const char *last,
525+
T &value, int base = 10) {
526+
value = 0;
527+
const char *p = first;
528+
bool negative = false;
529+
530+
if (p != last && *p == '-') {
531+
negative = true;
532+
++p;
533+
}
534+
if (p == last) { return {first, std::errc::invalid_argument}; }
535+
536+
T result = 0;
537+
for (; p != last; ++p) {
538+
char c = *p;
539+
int digit = -1;
540+
if ('0' <= c && c <= '9') {
541+
digit = c - '0';
542+
} else if ('a' <= c && c <= 'z') {
543+
digit = c - 'a' + 10;
544+
} else if ('A' <= c && c <= 'Z') {
545+
digit = c - 'A' + 10;
546+
} else {
547+
break;
548+
}
549+
550+
if (digit < 0 || digit >= base) { break; }
551+
if (result > ((std::numeric_limits<T>::max)() - digit) / base) {
552+
return {p, std::errc::result_out_of_range};
553+
}
554+
result = result * base + digit;
555+
}
556+
557+
if (p == first || (negative && p == first + 1)) {
558+
return {first, std::errc::invalid_argument};
559+
}
560+
561+
value = negative ? -result : result;
562+
return {p, std::errc{}};
563+
}
564+
565+
// from_chars for double (simple wrapper for strtod)
566+
inline from_chars_result<double> from_chars(const char *first, const char *last,
567+
double &value) {
568+
std::string s(first, last);
569+
char *endptr = nullptr;
570+
errno = 0;
571+
value = std::strtod(s.c_str(), &endptr);
572+
if (endptr == s.c_str()) { return {first, std::errc::invalid_argument}; }
573+
if (errno == ERANGE) {
574+
return {first + (endptr - s.c_str()), std::errc::result_out_of_range};
575+
}
576+
return {first + (endptr - s.c_str()), std::errc{}};
577+
}
578+
514579
} // namespace detail
515580

516581
enum SSLVerifierResponse {
@@ -3261,18 +3326,12 @@ class SSEClient {
32613326
msg.id = value;
32623327
} else if (field == "retry") {
32633328
// Parse retry interval in milliseconds
3264-
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
32653329
{
3266-
std::istringstream iss(value);
3267-
iss >> retry_ms;
3268-
}
3269-
#else
3270-
try {
3271-
retry_ms = std::stoi(value);
3272-
} catch (...) {
3273-
// Invalid retry value, ignore
3330+
int v = 0;
3331+
auto res =
3332+
detail::from_chars(value.data(), value.data() + value.size(), v);
3333+
if (res.ec == std::errc{}) { retry_ms = v; }
32743334
}
3275-
#endif
32763335
}
32773336
// Unknown fields are ignored per SSE spec
32783337

@@ -6335,10 +6394,20 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
63356394
return;
63366395
}
63376396

6338-
const auto first =
6339-
static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));
6340-
const auto last =
6341-
static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));
6397+
ssize_t first = -1;
6398+
if (!lhs.empty()) {
6399+
ssize_t v;
6400+
auto res = detail::from_chars(lhs.data(), lhs.data() + lhs.size(), v);
6401+
if (res.ec == std::errc{}) { first = v; }
6402+
}
6403+
6404+
ssize_t last = -1;
6405+
if (!rhs.empty()) {
6406+
ssize_t v;
6407+
auto res = detail::from_chars(rhs.data(), rhs.data() + rhs.size(), v);
6408+
if (res.ec == std::errc{}) { last = v; }
6409+
}
6410+
63426411
if ((first == -1 && last == -1) ||
63436412
(first != -1 && last != -1 && first > last)) {
63446413
all_valid_ranges = false;
@@ -6413,25 +6482,17 @@ inline bool parse_accept_header(const std::string &s,
64136482
return;
64146483
}
64156484

6416-
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
64176485
{
6418-
std::istringstream iss(quality_str);
6419-
iss >> accept_entry.quality;
6420-
6421-
// Check if conversion was successful and entire string was consumed
6422-
if (iss.fail() || !iss.eof()) {
6486+
double v = 0.0;
6487+
auto res = detail::from_chars(
6488+
quality_str.data(), quality_str.data() + quality_str.size(), v);
6489+
if (res.ec == std::errc{}) {
6490+
accept_entry.quality = v;
6491+
} else {
64236492
has_invalid_entry = true;
64246493
return;
64256494
}
64266495
}
6427-
#else
6428-
try {
6429-
accept_entry.quality = std::stod(quality_str);
6430-
} catch (...) {
6431-
has_invalid_entry = true;
6432-
return;
6433-
}
6434-
#endif
64356496
// Check if quality is in valid range [0.0, 1.0]
64366497
if (accept_entry.quality < 0.0 || accept_entry.quality > 1.0) {
64376498
has_invalid_entry = true;
@@ -13376,8 +13437,10 @@ inline bool SSLClient::check_host_name(const char *pattern,
1337613437
partial_match = true;
1337713438
} else if (h.size() >= prefix_length) {
1337813439
partial_match =
13379-
std::equal(p.begin(), p.begin() + prefix_length, h.begin(),
13380-
[](const char ca, const char cb) {
13440+
std::equal(p.begin(),
13441+
p.begin() + static_cast<std::string::difference_type>(
13442+
prefix_length),
13443+
h.begin(), [](const char ca, const char cb) {
1338113444
return httplib::detail::case_ignore::to_lower(ca) ==
1338213445
httplib::detail::case_ignore::to_lower(cb);
1338313446
});

test/test.cc

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13565,18 +13565,12 @@ class SSEParsingTest : public ::testing::Test {
1356513565
msg.id = value;
1356613566
} else if (field == "retry") {
1356713567
// Parse retry interval in milliseconds
13568-
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
1356913568
{
13570-
std::istringstream iss(value);
13571-
iss >> retry_ms;
13569+
int v = 0;
13570+
auto res =
13571+
detail::from_chars(value.data(), value.data() + value.size(), v);
13572+
if (res.ec == std::errc{}) { retry_ms = v; }
1357213573
}
13573-
#else
13574-
try {
13575-
retry_ms = std::stoi(value);
13576-
} catch (...) {
13577-
// Invalid retry value, ignore
13578-
}
13579-
#endif
1358013574
}
1358113575
// Unknown fields are ignored per SSE spec
1358213576

0 commit comments

Comments
 (0)