@@ -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
516581enum 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 });
0 commit comments