Brunobkr/llama.cpp_AlgMor24_github
ΩFFFΣLLIa • llama.cpp • AlgMor24 ██████╗ ███████╗███████╗███████╗██╗ ██╗ ██╗ █████╗ ██╔═══██╗██╔════╝██╔════╝██╔════╝██║ ██║ ██║██╔══██╗ ██║ ██║█████╗ █████╗ █████╗ ██║ ██║ ██║███████║ ██║ ██║██╔══╝ ██╔══╝ ██╔══╝ ██║ ██║ ██║██╔══██║ ╚██████╔╝██║ ██║ ███████╗███████╗███████╗██║██║ ██║ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝ High-Performance LLM / VLM Inference & Autonomous Agentic Ecosystem… See the full description on the dataset page: https://huggingface.co/datasets/Brunobkr/llama.cpp_AlgMor24_github.
03k
1#include "httplib.h"2namespace httplib {3 4/*5 * Implementation that will be part of the .cc file if split into .h + .cc.6 */7 8namespace stream {9 10// stream::Result implementations11Result::Result() : chunk_size_(8192) {}12 13Result::Result(ClientImpl::StreamHandle &&handle, size_t chunk_size)14 : handle_(std::move(handle)), chunk_size_(chunk_size) {}15 16Result::Result(Result &&other) noexcept17 : handle_(std::move(other.handle_)), buffer_(std::move(other.buffer_)),18 current_size_(other.current_size_), chunk_size_(other.chunk_size_),19 finished_(other.finished_) {20 other.current_size_ = 0;21 other.finished_ = true;22}23 24Result &Result::operator=(Result &&other) noexcept {25 if (this != &other) {26 handle_ = std::move(other.handle_);27 buffer_ = std::move(other.buffer_);28 current_size_ = other.current_size_;29 chunk_size_ = other.chunk_size_;30 finished_ = other.finished_;31 other.current_size_ = 0;32 other.finished_ = true;33 }34 return *this;35}36 37bool Result::is_valid() const { return handle_.is_valid(); }38Result::operator bool() const { return is_valid(); }39 40int Result::status() const {41 return handle_.response ? handle_.response->status : -1;42}43 44const Headers &Result::headers() const {45 static const Headers empty_headers;46 return handle_.response ? handle_.response->headers : empty_headers;47}48 49std::string Result::get_header_value(const std::string &key,50 const char *def) const {51 return handle_.response ? handle_.response->get_header_value(key, def) : def;52}53 54bool Result::has_header(const std::string &key) const {55 return handle_.response ? handle_.response->has_header(key) : false;56}57 58Error Result::error() const { return handle_.error; }59Error Result::read_error() const { return handle_.get_read_error(); }60bool Result::has_read_error() const { return handle_.has_read_error(); }61 62bool Result::next() {63 if (!handle_.is_valid() || finished_) { return false; }64 65 if (buffer_.size() < chunk_size_) { buffer_.resize(chunk_size_); }66 67 ssize_t n = handle_.read(&buffer_[0], chunk_size_);68 if (n > 0) {69 current_size_ = static_cast<size_t>(n);70 return true;71 }72 73 current_size_ = 0;74 finished_ = true;75 return false;76}77 78const char *Result::data() const { return buffer_.data(); }79size_t Result::size() const { return current_size_; }80 81std::string Result::read_all() {82 std::string result;83 while (next()) {84 result.append(data(), size());85 }86 return result;87}88 89} // namespace stream90 91namespace sse {92 93// SSEMessage implementations94SSEMessage::SSEMessage() : event("message") {}95 96void SSEMessage::clear() {97 event = "message";98 data.clear();99 id.clear();100}101 102// SSEClient implementations103SSEClient::SSEClient(Client &client, const std::string &path)104 : client_(client), path_(path) {}105 106SSEClient::SSEClient(Client &client, const std::string &path,107 const Headers &headers)108 : client_(client), path_(path), headers_(headers) {}109 110SSEClient::~SSEClient() { stop(); }111 112SSEClient &SSEClient::on_message(MessageHandler handler) {113 on_message_ = std::move(handler);114 return *this;115}116 117SSEClient &SSEClient::on_event(const std::string &type,118 MessageHandler handler) {119 event_handlers_[type] = std::move(handler);120 return *this;121}122 123SSEClient &SSEClient::on_open(OpenHandler handler) {124 on_open_ = std::move(handler);125 return *this;126}127 128SSEClient &SSEClient::on_error(ErrorHandler handler) {129 on_error_ = std::move(handler);130 return *this;131}132 133SSEClient &SSEClient::set_reconnect_interval(int ms) {134 reconnect_interval_ms_ = ms;135 return *this;136}137 138SSEClient &SSEClient::set_max_reconnect_attempts(int n) {139 max_reconnect_attempts_ = n;140 return *this;141}142 143SSEClient &SSEClient::set_headers(const Headers &headers) {144 std::lock_guard<std::mutex> lock(headers_mutex_);145 headers_ = headers;146 return *this;147}148 149bool SSEClient::is_connected() const { return connected_.load(); }150 151const std::string &SSEClient::last_event_id() const {152 return last_event_id_;153}154 155void SSEClient::start() {156 running_.store(true);157 run_event_loop();158}159 160void SSEClient::start_async() {161 running_.store(true);162 async_thread_ = std::thread([this]() { run_event_loop(); });163}164 165void SSEClient::stop() {166 running_.store(false);167 client_.stop(); // Cancel any pending operations168 if (async_thread_.joinable()) { async_thread_.join(); }169}170 171bool SSEClient::parse_sse_line(const std::string &line, SSEMessage &msg,172 int &retry_ms) {173 // Blank line signals end of event174 if (line.empty() || line == "\r") { return true; }175 176 // Lines starting with ':' are comments (ignored)177 if (!line.empty() && line[0] == ':') { return false; }178 179 // Find the colon separator180 auto colon_pos = line.find(':');181 if (colon_pos == std::string::npos) {182 // Line with no colon is treated as field name with empty value183 return false;184 }185 186 auto field = line.substr(0, colon_pos);187 std::string value;188 189 // Value starts after colon, skip optional single space190 if (colon_pos + 1 < line.size()) {191 auto value_start = colon_pos + 1;192 if (line[value_start] == ' ') { value_start++; }193 value = line.substr(value_start);194 // Remove trailing \r if present195 if (!value.empty() && value.back() == '\r') { value.pop_back(); }196 }197 198 // Handle known fields199 if (field == "event") {200 msg.event = value;201 } else if (field == "data") {202 // Multiple data lines are concatenated with newlines203 if (!msg.data.empty()) { msg.data += "\n"; }204 msg.data += value;205 } else if (field == "id") {206 // Empty id is valid (clears the last event ID)207 msg.id = value;208 } else if (field == "retry") {209 // Parse retry interval in milliseconds210 {211 int v = 0;212 auto res =213 detail::from_chars(value.data(), value.data() + value.size(), v);214 if (res.ec == std::errc{}) { retry_ms = v; }215 }216 }217 // Unknown fields are ignored per SSE spec218 219 return false;220}221 222void SSEClient::run_event_loop() {223 auto reconnect_count = 0;224 225 while (running_.load()) {226 // Build headers, including Last-Event-ID if we have one227 Headers request_headers;228 {229 std::lock_guard<std::mutex> lock(headers_mutex_);230 request_headers = headers_;231 }232 if (!last_event_id_.empty()) {233 request_headers.emplace("Last-Event-ID", last_event_id_);234 }235 236 // Open streaming connection237 auto result = stream::Get(client_, path_, request_headers);238 239 // Connection error handling240 if (!result) {241 connected_.store(false);242 if (on_error_) { on_error_(result.error()); }243 244 if (!should_reconnect(reconnect_count)) { break; }245 wait_for_reconnect();246 reconnect_count++;247 continue;248 }249 250 if (result.status() != StatusCode::OK_200) {251 connected_.store(false);252 if (on_error_) { on_error_(Error::Connection); }253 254 // For certain errors, don't reconnect.255 // Note: 401 is intentionally absent so that handlers can refresh256 // credentials via set_headers() and let the client reconnect.257 if (result.status() == StatusCode::NoContent_204 ||258 result.status() == StatusCode::NotFound_404 ||259 result.status() == StatusCode::Forbidden_403) {260 break;261 }262 263 if (!should_reconnect(reconnect_count)) { break; }264 wait_for_reconnect();265 reconnect_count++;266 continue;267 }268 269 // Connection successful270 connected_.store(true);271 reconnect_count = 0;272 if (on_open_) { on_open_(); }273 274 // Event receiving loop275 std::string buffer;276 SSEMessage current_msg;277 278 while (running_.load() && result.next()) {279 buffer.append(result.data(), result.size());280 281 // Process complete lines in the buffer282 size_t line_start = 0;283 size_t newline_pos;284 285 while ((newline_pos = buffer.find('\n', line_start)) !=286 std::string::npos) {287 auto line = buffer.substr(line_start, newline_pos - line_start);288 line_start = newline_pos + 1;289 290 // Parse the line and check if event is complete291 auto event_complete =292 parse_sse_line(line, current_msg, reconnect_interval_ms_);293 294 if (event_complete && !current_msg.data.empty()) {295 // Update last_event_id for reconnection296 if (!current_msg.id.empty()) { last_event_id_ = current_msg.id; }297 298 // Dispatch event to appropriate handler299 dispatch_event(current_msg);300 301 current_msg.clear();302 }303 }304 305 // Keep unprocessed data in buffer306 buffer.erase(0, line_start);307 }308 309 // Connection ended310 connected_.store(false);311 312 if (!running_.load()) { break; }313 314 // Check for read errors315 if (result.has_read_error()) {316 if (on_error_) { on_error_(result.read_error()); }317 }318 319 if (!should_reconnect(reconnect_count)) { break; }320 wait_for_reconnect();321 reconnect_count++;322 }323 324 connected_.store(false);325}326 327void SSEClient::dispatch_event(const SSEMessage &msg) {328 // Check for specific event type handler first329 auto it = event_handlers_.find(msg.event);330 if (it != event_handlers_.end()) {331 it->second(msg);332 return;333 }334 335 // Fall back to generic message handler336 if (on_message_) { on_message_(msg); }337}338 339bool SSEClient::should_reconnect(int count) const {340 if (!running_.load()) { return false; }341 if (max_reconnect_attempts_ == 0) { return true; } // unlimited342 return count < max_reconnect_attempts_;343}344 345void SSEClient::wait_for_reconnect() {346 // Use small increments to check running_ flag frequently347 auto waited = 0;348 while (running_.load() && waited < reconnect_interval_ms_) {349 std::this_thread::sleep_for(std::chrono::milliseconds(100));350 waited += 100;351 }352}353 354} // namespace sse355 356#ifdef CPPHTTPLIB_SSL_ENABLED357/*358 * TLS abstraction layer - internal function declarations359 * These are implementation details and not part of the public API.360 */361namespace tls {362 363// Client context364ctx_t create_client_context();365void free_context(ctx_t ctx);366bool set_min_version(ctx_t ctx, Version version);367bool load_ca_pem(ctx_t ctx, const char *pem, size_t len);368bool load_ca_file(ctx_t ctx, const char *file_path);369bool load_ca_dir(ctx_t ctx, const char *dir_path);370bool load_system_certs(ctx_t ctx);371bool set_client_cert_pem(ctx_t ctx, const char *cert, const char *key,372 const char *password);373bool set_client_cert_file(ctx_t ctx, const char *cert_path,374 const char *key_path, const char *password);375 376// Server context377ctx_t create_server_context();378bool set_server_cert_pem(ctx_t ctx, const char *cert, const char *key,379 const char *password);380bool set_server_cert_file(ctx_t ctx, const char *cert_path,381 const char *key_path, const char *password);382bool set_client_ca_file(ctx_t ctx, const char *ca_file, const char *ca_dir);383void set_verify_client(ctx_t ctx, bool require);384 385// Session management386session_t create_session(ctx_t ctx, socket_t sock);387void free_session(session_t session);388bool set_sni(session_t session, const char *hostname, bool verify_hostname);389 390// Handshake (non-blocking capable)391TlsError connect(session_t session);392TlsError accept(session_t session);393 394// Handshake with timeout (blocking until timeout)395bool connect_nonblocking(session_t session, socket_t sock, time_t timeout_sec,396 time_t timeout_usec, TlsError *err);397bool accept_nonblocking(session_t session, socket_t sock, time_t timeout_sec,398 time_t timeout_usec, TlsError *err);399 400// I/O (non-blocking capable)401ssize_t read(session_t session, void *buf, size_t len, TlsError &err);402ssize_t write(session_t session, const void *buf, size_t len, TlsError &err);403int pending(const_session_t session);404void shutdown(session_t session, bool graceful);405 406// Connection state407bool is_peer_closed(session_t session, socket_t sock);408 409// Certificate verification410cert_t get_peer_cert(const_session_t session);411void free_cert(cert_t cert);412bool verify_hostname(cert_t cert, const char *hostname);413uint64_t hostname_mismatch_code();414long get_verify_result(const_session_t session);415 416// Certificate introspection417std::string get_cert_subject_cn(cert_t cert);418std::string get_cert_issuer_name(cert_t cert);419bool get_cert_sans(cert_t cert, std::vector<SanEntry> &sans);420bool get_cert_validity(cert_t cert, time_t ¬_before, time_t ¬_after);421std::string get_cert_serial(cert_t cert);422bool get_cert_der(cert_t cert, std::vector<unsigned char> &der);423const char *get_sni(const_session_t session);424 425// CA store management426ca_store_t create_ca_store(const char *pem, size_t len);427void free_ca_store(ca_store_t store);428bool set_ca_store(ctx_t ctx, ca_store_t store);429size_t get_ca_certs(ctx_t ctx, std::vector<cert_t> &certs);430std::vector<std::string> get_ca_names(ctx_t ctx);431 432// Dynamic certificate update (for servers)433bool update_server_cert(ctx_t ctx, const char *cert_pem, const char *key_pem,434 const char *password);435bool update_server_client_ca(ctx_t ctx, const char *ca_pem);436 437// Certificate verification callback438bool set_verify_callback(ctx_t ctx, VerifyCallback callback);439long get_verify_error(const_session_t session);440std::string verify_error_string(long error_code);441 442// TlsError information443uint64_t peek_error();444uint64_t get_error();445std::string error_string(uint64_t code);446 447} // namespace tls448#endif // CPPHTTPLIB_SSL_ENABLED449 450/*451 * Group 1: detail namespace - Non-SSL utilities452 */453 454namespace detail {455 456bool set_socket_opt_impl(socket_t sock, int level, int optname,457 const void *optval, socklen_t optlen) {458 return setsockopt(sock, level, optname,459#ifdef _WIN32460 reinterpret_cast<const char *>(optval),461#else462 optval,463#endif464 optlen) == 0;465}466 467bool set_socket_opt_time(socket_t sock, int level, int optname,468 time_t sec, time_t usec) {469#ifdef _WIN32470 auto timeout = static_cast<uint32_t>(sec * 1000 + usec / 1000);471#else472 timeval timeout;473 timeout.tv_sec = static_cast<long>(sec);474 timeout.tv_usec = static_cast<decltype(timeout.tv_usec)>(usec);475#endif476 return set_socket_opt_impl(sock, level, optname, &timeout, sizeof(timeout));477}478 479bool is_hex(char c, int &v) {480 if (is_ascii_digit(c)) {481 v = c - '0';482 return true;483 } else if ('A' <= c && c <= 'F') {484 v = c - 'A' + 10;485 return true;486 } else if ('a' <= c && c <= 'f') {487 v = c - 'a' + 10;488 return true;489 }490 return false;491}492 493bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,494 int &val) {495 if (i >= s.size()) { return false; }496 497 val = 0;498 for (; cnt; i++, cnt--) {499 if (!s[i]) { return false; }500 auto v = 0;501 if (is_hex(s[i], v)) {502 val = val * 16 + v;503 } else {504 return false;505 }506 }507 return true;508}509 510std::string from_i_to_hex(size_t n) {511 static const auto charset = "0123456789abcdef";512 std::string ret;513 do {514 ret = charset[n & 15] + ret;515 n >>= 4;516 } while (n > 0);517 return ret;518}519 520std::string compute_etag(const FileStat &fs) {521 if (!fs.is_file()) { return std::string(); }522 523 // If mtime cannot be determined (negative value indicates an error524 // or sentinel), do not generate an ETag. Returning a neutral / fixed525 // value like 0 could collide with a real file that legitimately has526 // mtime == 0 (epoch) and lead to misleading validators.527 auto mtime_raw = fs.mtime();528 if (mtime_raw < 0) { return std::string(); }529 530 auto mtime = static_cast<size_t>(mtime_raw);531 auto size = fs.size();532 533 return std::string("W/\"") + from_i_to_hex(mtime) + "-" +534 from_i_to_hex(size) + "\"";535}536 537// Format time_t as HTTP-date (RFC 9110 Section 5.6.7): "Sun, 06 Nov 1994538// 08:49:37 GMT" This implementation is defensive: it validates `mtime`, checks539// return values from `gmtime_r`/`gmtime_s`, and ensures `strftime` succeeds.540std::string file_mtime_to_http_date(time_t mtime) {541 if (mtime < 0) { return std::string(); }542 543 struct tm tm_buf;544#ifdef _WIN32545 if (gmtime_s(&tm_buf, &mtime) != 0) { return std::string(); }546#else547 if (gmtime_r(&mtime, &tm_buf) == nullptr) { return std::string(); }548#endif549 char buf[64];550 if (strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", &tm_buf) == 0) {551 return std::string();552 }553 554 return std::string(buf);555}556 557// Parse HTTP-date (RFC 9110 Section 5.6.7) to time_t. Returns -1 on failure.558time_t parse_http_date(const std::string &date_str) {559 struct tm tm_buf;560 561 // Create a classic locale object once for all parsing attempts562 const std::locale classic_locale = std::locale::classic();563 564 // Try to parse using std::get_time (C++11, cross-platform)565 auto try_parse = [&](const char *fmt) -> bool {566 std::istringstream ss(date_str);567 ss.imbue(classic_locale);568 569 memset(&tm_buf, 0, sizeof(tm_buf));570 ss >> std::get_time(&tm_buf, fmt);571 572 return !ss.fail();573 };574 575 // RFC 9110 preferred format (HTTP-date): "Sun, 06 Nov 1994 08:49:37 GMT"576 if (!try_parse("%a, %d %b %Y %H:%M:%S")) {577 // RFC 850 format: "Sunday, 06-Nov-94 08:49:37 GMT"578 if (!try_parse("%A, %d-%b-%y %H:%M:%S")) {579 // asctime format: "Sun Nov 6 08:49:37 1994"580 if (!try_parse("%a %b %d %H:%M:%S %Y")) {581 return static_cast<time_t>(-1);582 }583 }584 }585 586#ifdef _WIN32587 return _mkgmtime(&tm_buf);588#elif defined _AIX589 return mktime(&tm_buf);590#else591 return timegm(&tm_buf);592#endif593}594 595bool is_weak_etag(const std::string &s) {596 // Check if the string is a weak ETag (starts with 'W/"')597 return s.size() > 3 && s[0] == 'W' && s[1] == '/' && s[2] == '"';598}599 600bool is_strong_etag(const std::string &s) {601 // Check if the string is a strong ETag (starts and ends with '"', at least 2602 // chars)603 return s.size() >= 2 && s[0] == '"' && s.back() == '"';604}605 606size_t to_utf8(int code, char *buff) {607 if (code < 0x0080) {608 buff[0] = static_cast<char>(code & 0x7F);609 return 1;610 } else if (code < 0x0800) {611 buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));612 buff[1] = static_cast<char>(0x80 | (code & 0x3F));613 return 2;614 } else if (code < 0xD800) {615 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));616 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));617 buff[2] = static_cast<char>(0x80 | (code & 0x3F));618 return 3;619 } else if (code < 0xE000) { // D800 - DFFF is invalid...620 return 0;621 } else if (code < 0x10000) {622 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));623 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));624 buff[2] = static_cast<char>(0x80 | (code & 0x3F));625 return 3;626 } else if (code < 0x110000) {627 buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));628 buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));629 buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));630 buff[3] = static_cast<char>(0x80 | (code & 0x3F));631 return 4;632 }633 634 // NOTREACHED635 return 0;636}637 638} // namespace detail639 640namespace ws {641namespace impl {642 643bool is_valid_utf8(const std::string &s) {644 size_t i = 0;645 auto n = s.size();646 while (i < n) {647 auto c = static_cast<unsigned char>(s[i]);648 size_t len;649 uint32_t cp;650 if (c < 0x80) {651 i++;652 continue;653 } else if ((c & 0xE0) == 0xC0) {654 len = 2;655 cp = c & 0x1F;656 } else if ((c & 0xF0) == 0xE0) {657 len = 3;658 cp = c & 0x0F;659 } else if ((c & 0xF8) == 0xF0) {660 len = 4;661 cp = c & 0x07;662 } else {663 return false;664 }665 if (i + len > n) { return false; }666 for (size_t j = 1; j < len; j++) {667 auto b = static_cast<unsigned char>(s[i + j]);668 if ((b & 0xC0) != 0x80) { return false; }669 cp = (cp << 6) | (b & 0x3F);670 }671 // Overlong encoding check672 if (len == 2 && cp < 0x80) { return false; }673 if (len == 3 && cp < 0x800) { return false; }674 if (len == 4 && cp < 0x10000) { return false; }675 // Surrogate halves (U+D800..U+DFFF) and beyond U+10FFFF are invalid676 if (cp >= 0xD800 && cp <= 0xDFFF) { return false; }677 if (cp > 0x10FFFF) { return false; }678 i += len;679 }680 return true;681}682 683} // namespace impl684} // namespace ws685 686namespace detail {687 688// NOTE: This code came up with the following stackoverflow post:689// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c690std::string base64_encode(const std::string &in) {691 static const auto lookup =692 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";693 694 std::string out;695 out.reserve(in.size());696 697 // Unsigned: the accumulator is never masked, so with a signed int the698 // `val << 8` below overflows once enough bytes are folded in (undefined699 // behaviour before C++20). Only the low bits are ever emitted, so the700 // wrap-around of an unsigned accumulator does not affect the output.701 uint32_t val = 0;702 auto valb = -6;703 704 for (auto c : in) {705 val = (val << 8) + static_cast<uint8_t>(c);706 valb += 8;707 while (valb >= 0) {708 out.push_back(lookup[(val >> valb) & 0x3F]);709 valb -= 6;710 }711 }712 713 if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }714 715 while (out.size() % 4) {716 out.push_back('=');717 }718 719 return out;720}721 722std::string sha1(const std::string &input) {723 // RFC 3174 SHA-1 implementation724 auto left_rotate = [](uint32_t x, uint32_t n) -> uint32_t {725 return (x << n) | (x >> (32 - n));726 };727 728 uint32_t h0 = 0x67452301;729 uint32_t h1 = 0xEFCDAB89;730 uint32_t h2 = 0x98BADCFE;731 uint32_t h3 = 0x10325476;732 uint32_t h4 = 0xC3D2E1F0;733 734 // Pre-processing: adding padding bits735 std::string msg = input;736 uint64_t original_bit_len = static_cast<uint64_t>(msg.size()) * 8;737 msg.push_back(static_cast<char>(0x80u));738 while (msg.size() % 64 != 56) {739 msg.push_back(0);740 }741 742 // Append original length in bits as 64-bit big-endian743 for (int i = 56; i >= 0; i -= 8) {744 msg.push_back(static_cast<char>((original_bit_len >> i) & 0xFF));745 }746 747 // Process each 512-bit chunk748 for (size_t offset = 0; offset < msg.size(); offset += 64) {749 uint32_t w[80];750 751 for (size_t i = 0; i < 16; i++) {752 w[i] =753 (static_cast<uint32_t>(static_cast<uint8_t>(msg[offset + i * 4]))754 << 24) |755 (static_cast<uint32_t>(static_cast<uint8_t>(msg[offset + i * 4 + 1]))756 << 16) |757 (static_cast<uint32_t>(static_cast<uint8_t>(msg[offset + i * 4 + 2]))758 << 8) |759 (static_cast<uint32_t>(760 static_cast<uint8_t>(msg[offset + i * 4 + 3])));761 }762 763 for (int i = 16; i < 80; i++) {764 w[i] = left_rotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);765 }766 767 uint32_t a = h0, b = h1, c = h2, d = h3, e = h4;768 769 for (int i = 0; i < 80; i++) {770 uint32_t f, k;771 if (i < 20) {772 f = (b & c) | ((~b) & d);773 k = 0x5A827999;774 } else if (i < 40) {775 f = b ^ c ^ d;776 k = 0x6ED9EBA1;777 } else if (i < 60) {778 f = (b & c) | (b & d) | (c & d);779 k = 0x8F1BBCDC;780 } else {781 f = b ^ c ^ d;782 k = 0xCA62C1D6;783 }784 785 uint32_t temp = left_rotate(a, 5) + f + e + k + w[i];786 e = d;787 d = c;788 c = left_rotate(b, 30);789 b = a;790 a = temp;791 }792 793 h0 += a;794 h1 += b;795 h2 += c;796 h3 += d;797 h4 += e;798 }799 800 // Produce the final hash as a 20-byte binary string801 std::string hash(20, '\0');802 for (size_t i = 0; i < 4; i++) {803 hash[i] = static_cast<char>((h0 >> (24 - i * 8)) & 0xFF);804 hash[4 + i] = static_cast<char>((h1 >> (24 - i * 8)) & 0xFF);805 hash[8 + i] = static_cast<char>((h2 >> (24 - i * 8)) & 0xFF);806 hash[12 + i] = static_cast<char>((h3 >> (24 - i * 8)) & 0xFF);807 hash[16 + i] = static_cast<char>((h4 >> (24 - i * 8)) & 0xFF);808 }809 return hash;810}811 812std::string websocket_accept_key(const std::string &client_key) {813 const std::string magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";814 return base64_encode(sha1(client_key + magic));815}816 817bool is_websocket_upgrade(const Request &req) {818 if (req.method != "GET") { return false; }819 820 // Check Upgrade: websocket (case-insensitive)821 auto upgrade_it = req.headers.find("Upgrade");822 if (upgrade_it == req.headers.end()) { return false; }823 auto upgrade_val = case_ignore::to_lower(upgrade_it->second);824 if (upgrade_val != "websocket") { return false; }825 826 // Check Connection header contains "Upgrade"827 auto connection_it = req.headers.find("Connection");828 if (connection_it == req.headers.end()) { return false; }829 auto connection_val = case_ignore::to_lower(connection_it->second);830 if (connection_val.find("upgrade") == std::string::npos) { return false; }831 832 // Check Sec-WebSocket-Key is a valid base64-encoded 16-byte value (24 chars)833 // RFC 6455 Section 4.2.1834 auto ws_key = req.get_header_value("Sec-WebSocket-Key");835 if (ws_key.size() != 24 || ws_key[22] != '=' || ws_key[23] != '=') {836 return false;837 }838 static const std::string b64chars =839 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";840 for (size_t i = 0; i < 22; i++) {841 if (b64chars.find(ws_key[i]) == std::string::npos) { return false; }842 }843 844 // Check Sec-WebSocket-Version: 13845 auto version = req.get_header_value("Sec-WebSocket-Version");846 if (version != "13") { return false; }847 848 return true;849}850 851bool write_websocket_frame(Stream &strm, ws::Opcode opcode,852 const char *data, size_t len, bool fin,853 bool mask) {854 // First byte: FIN + opcode855 uint8_t header[2];856 header[0] = static_cast<uint8_t>((fin ? 0x80 : 0x00) |857 (static_cast<uint8_t>(opcode) & 0x0F));858 859 // Second byte: MASK + payload length860 if (len < 126) {861 header[1] = static_cast<uint8_t>(len);862 if (mask) { header[1] |= 0x80; }863 if (strm.write(reinterpret_cast<char *>(header), 2) < 0) { return false; }864 } else if (len <= 0xFFFF) {865 header[1] = 126;866 if (mask) { header[1] |= 0x80; }867 if (strm.write(reinterpret_cast<char *>(header), 2) < 0) { return false; }868 uint8_t ext[2];869 ext[0] = static_cast<uint8_t>((len >> 8) & 0xFF);870 ext[1] = static_cast<uint8_t>(len & 0xFF);871 if (strm.write(reinterpret_cast<char *>(ext), 2) < 0) { return false; }872 } else {873 header[1] = 127;874 if (mask) { header[1] |= 0x80; }875 if (strm.write(reinterpret_cast<char *>(header), 2) < 0) { return false; }876 uint8_t ext[8];877 for (int i = 7; i >= 0; i--) {878 ext[7 - i] =879 static_cast<uint8_t>((static_cast<uint64_t>(len) >> (i * 8)) & 0xFF);880 }881 if (strm.write(reinterpret_cast<char *>(ext), 8) < 0) { return false; }882 }883 884 if (mask) {885 // Generate random mask key886 thread_local std::mt19937 rng(std::random_device{}());887 uint8_t mask_key[4];888 auto r = rng();889 std::memcpy(mask_key, &r, 4);890 if (strm.write(reinterpret_cast<char *>(mask_key), 4) < 0) { return false; }891 892 // Write masked payload in chunks893 const size_t chunk_size = 4096;894 std::vector<char> buf((std::min)(len, chunk_size));895 for (size_t offset = 0; offset < len; offset += chunk_size) {896 size_t n = (std::min)(chunk_size, len - offset);897 for (size_t i = 0; i < n; i++) {898 buf[i] =899 data[offset + i] ^ static_cast<char>(mask_key[(offset + i) % 4]);900 }901 if (strm.write(buf.data(), n) < 0) { return false; }902 }903 } else {904 if (len > 0) {905 if (strm.write(data, len) < 0) { return false; }906 }907 }908 909 return true;910}911 912} // namespace detail913 914namespace ws {915namespace impl {916 917bool read_websocket_frame(Stream &strm, Opcode &opcode,918 std::string &payload, bool &fin,919 bool expect_masked, size_t max_len) {920 // Read first 2 bytes921 uint8_t header[2];922 if (strm.read(reinterpret_cast<char *>(header), 2) != 2) { return false; }923 924 fin = (header[0] & 0x80) != 0;925 926 // RSV1, RSV2, RSV3 must be 0 when no extension is negotiated927 if (header[0] & 0x70) { return false; }928 929 opcode = static_cast<Opcode>(header[0] & 0x0F);930 bool masked = (header[1] & 0x80) != 0;931 uint64_t payload_len = header[1] & 0x7F;932 933 // RFC 6455 Section 5.5: control frames MUST NOT be fragmented and934 // MUST have a payload length of 125 bytes or less935 bool is_control = (static_cast<uint8_t>(opcode) & 0x08) != 0;936 if (is_control) {937 if (!fin) { return false; }938 if (payload_len > 125) { return false; }939 }940 941 if (masked != expect_masked) { return false; }942 943 // Extended payload length944 if (payload_len == 126) {945 uint8_t ext[2];946 if (strm.read(reinterpret_cast<char *>(ext), 2) != 2) { return false; }947 payload_len = (static_cast<uint64_t>(ext[0]) << 8) | ext[1];948 } else if (payload_len == 127) {949 uint8_t ext[8];950 if (strm.read(reinterpret_cast<char *>(ext), 8) != 8) { return false; }951 // RFC 6455 Section 5.2: the most significant bit MUST be 0952 if (ext[0] & 0x80) { return false; }953 payload_len = 0;954 for (int i = 0; i < 8; i++) {955 payload_len = (payload_len << 8) | ext[i];956 }957 }958 959 if (payload_len > max_len) { return false; }960 961 // Read mask key if present962 uint8_t mask_key[4] = {0};963 if (masked) {964 if (strm.read(reinterpret_cast<char *>(mask_key), 4) != 4) { return false; }965 }966 967 // Read payload968 payload.resize(static_cast<size_t>(payload_len));969 if (payload_len > 0) {970 size_t total_read = 0;971 while (total_read < payload_len) {972 auto n = strm.read(&payload[total_read],973 static_cast<size_t>(payload_len - total_read));974 if (n <= 0) { return false; }975 total_read += static_cast<size_t>(n);976 }977 }978 979 // Unmask if needed980 if (masked) {981 for (size_t i = 0; i < payload.size(); i++) {982 payload[i] ^= static_cast<char>(mask_key[i % 4]);983 }984 }985 986 return true;987}988 989} // namespace impl990} // namespace ws991 992namespace detail {993 994bool is_valid_path(const std::string &path) {995 size_t level = 0;996 size_t i = 0;997 998 // Skip slash999 while (i < path.size() && path[i] == '/') {1000 i++;1001 }1002 1003 while (i < path.size()) {1004 // Read component1005 auto beg = i;1006 while (i < path.size() && path[i] != '/') {1007 if (path[i] == '\0') {1008 return false;1009 } else if (path[i] == '\\') {1010 return false;1011 }1012 i++;1013 }1014 1015 auto len = i - beg;1016 assert(len > 0);1017 1018 if (!path.compare(beg, len, ".")) {1019 ;1020 } else if (!path.compare(beg, len, "..")) {1021 if (level == 0) { return false; }1022 level--;1023 } else {1024 level++;1025 }1026 1027 // Skip slash1028 while (i < path.size() && path[i] == '/') {1029 i++;1030 }1031 }1032 1033 return true;1034}1035 1036bool canonicalize_path(const char *path, std::string &resolved) {1037#if defined(_WIN32)1038 char buf[_MAX_PATH];1039 if (_fullpath(buf, path, _MAX_PATH) == nullptr) { return false; }1040 resolved = buf;1041#elif defined(PATH_MAX)1042 char buf[PATH_MAX];1043 if (realpath(path, buf) == nullptr) { return false; }1044 resolved = buf;1045#else1046 auto buf = realpath(path, nullptr);1047 auto guard = scope_exit([&]() { std::free(buf); });1048 if (buf == nullptr) { return false; }1049 resolved = buf;1050#endif1051 return true;1052}1053 1054bool is_path_within_base(const std::string &resolved_path,1055 const std::string &resolved_base) {1056#if defined(_WIN32)1057 return _strnicmp(resolved_path.c_str(), resolved_base.c_str(),1058 resolved_base.size()) == 0;1059#else1060 return strncmp(resolved_path.c_str(), resolved_base.c_str(),1061 resolved_base.size()) == 0;1062#endif1063}1064 1065FileStat::FileStat(const std::string &path) {1066#if defined(_WIN32)1067 auto wpath = u8string_to_wstring(path.c_str());1068 ret_ = _wstat(wpath.c_str(), &st_);1069#else1070 ret_ = stat(path.c_str(), &st_);1071#endif1072}1073bool FileStat::is_file() const {1074 return ret_ >= 0 && S_ISREG(st_.st_mode);1075}1076bool FileStat::is_dir() const {1077 return ret_ >= 0 && S_ISDIR(st_.st_mode);1078}1079 1080time_t FileStat::mtime() const {1081 return ret_ >= 0 ? static_cast<time_t>(st_.st_mtime)1082 : static_cast<time_t>(-1);1083}1084 1085size_t FileStat::size() const {1086 return ret_ >= 0 ? static_cast<size_t>(st_.st_size) : 0;1087}1088 1089std::string encode_path(const std::string &s) {1090 std::string result;1091 result.reserve(s.size());1092 1093 for (size_t i = 0; s[i]; i++) {1094 switch (s[i]) {1095 case ' ': result += "%20"; break;1096 case '+': result += "%2B"; break;1097 case '\r': result += "%0D"; break;1098 case '\n': result += "%0A"; break;1099 case '\'': result += "%27"; break;1100 case ',': result += "%2C"; break;1101 // case ':': result += "%3A"; break; // ok? probably...1102 case ';': result += "%3B"; break;1103 default:1104 auto c = static_cast<uint8_t>(s[i]);1105 if (c >= 0x80) {1106 result += '%';1107 char hex[4];1108 auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);1109 assert(len == 2);1110 result.append(hex, static_cast<size_t>(len));1111 } else {1112 result += s[i];1113 }1114 break;1115 }1116 }1117 1118 return result;1119}1120 1121std::string file_extension(const std::string &path) {1122 std::smatch m;1123 thread_local auto re = std::regex("\\.([a-zA-Z0-9]+)$");1124 if (std::regex_search(path, m, re)) { return m[1].str(); }1125 return std::string();1126}1127 1128bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }1129 1130template <typename T>1131bool parse_header(const char *beg, const char *end, T fn);1132 1133template <typename T>1134bool parse_header(const char *beg, const char *end, T fn) {1135 // Skip trailing spaces and tabs.1136 while (beg < end && is_space_or_tab(end[-1])) {1137 end--;1138 }1139 1140 auto p = beg;1141 while (p < end && *p != ':') {1142 p++;1143 }1144 1145 auto name = std::string(beg, p);1146 if (!detail::fields::is_field_name(name)) { return false; }1147 1148 if (p == end) { return false; }1149 1150 auto key_end = p;1151 1152 if (*p++ != ':') { return false; }1153 1154 while (p < end && is_space_or_tab(*p)) {1155 p++;1156 }1157 1158 if (p <= end) {1159 auto key_len = key_end - beg;1160 if (!key_len) { return false; }1161 1162 auto key = std::string(beg, key_end);1163 auto val = std::string(p, end);1164 1165 if (!detail::fields::is_field_value(val)) { return false; }1166 1167 // RFC 9110 §5.5: header field values are opaque octets and MUST NOT be1168 // percent-decoded by the recipient. Applications that need to interpret a1169 // value as a URI component should call httplib::decode_uri_component()1170 // (or decode_path_component()) explicitly.1171 fn(key, val);1172 1173 return true;1174 }1175 1176 return false;1177}1178 1179bool parse_trailers(stream_line_reader &line_reader, Headers &dest,1180 const Headers &src_headers) {1181 // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentions "The chunked1182 // transfer coding is complete when a chunk with a chunk-size of zero is1183 // received, possibly followed by a trailer section, and finally terminated by1184 // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.11185 //1186 // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section1187 // doesn't care for the existence of the final CRLF. In other words, it seems1188 // to be ok whether the final CRLF exists or not in the chunked data.1189 // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.31190 //1191 // According to the reference code in RFC 9112, cpp-httplib now allows1192 // chunked transfer coding data without the final CRLF.1193 1194 // RFC 7230 Section 4.1.2 - Headers prohibited in trailers1195 thread_local case_ignore::unordered_set<std::string> prohibited_trailers = {1196 "transfer-encoding",1197 "content-length",1198 "host",1199 "authorization",1200 "www-authenticate",