Buckets:
| struct common_http_url { | |
| std::string scheme; | |
| std::string user; | |
| std::string password; | |
| std::string host; | |
| int port; | |
| std::string path; | |
| }; | |
| // bracket an IPv6 literal host for a URL authority (RFC 3986) | |
| static std::string common_http_format_host(const std::string & host) { | |
| return host.find(':') != std::string::npos ? "[" + host + "]" : host; | |
| } | |
| static common_http_url common_http_parse_url(const std::string & url) { | |
| common_http_url parts; | |
| auto scheme_end = url.find("://"); | |
| if (scheme_end == std::string::npos) { | |
| throw std::runtime_error("invalid URL: no scheme"); | |
| } | |
| parts.scheme = url.substr(0, scheme_end); | |
| if (parts.scheme != "http" && parts.scheme != "https") { | |
| throw std::runtime_error("unsupported URL scheme: " + parts.scheme); | |
| } | |
| auto rest = url.substr(scheme_end + 3); | |
| auto at_pos = rest.find('@'); | |
| if (at_pos != std::string::npos) { | |
| auto auth = rest.substr(0, at_pos); | |
| auto colon_pos = auth.find(':'); | |
| if (colon_pos != std::string::npos) { | |
| parts.user = auth.substr(0, colon_pos); | |
| parts.password = auth.substr(colon_pos + 1); | |
| } else { | |
| parts.user = auth; | |
| } | |
| rest = rest.substr(at_pos + 1); | |
| } | |
| auto slash_pos = rest.find('/'); | |
| if (slash_pos != std::string::npos) { | |
| parts.host = rest.substr(0, slash_pos); | |
| parts.path = rest.substr(slash_pos); | |
| } else { | |
| parts.host = rest; | |
| parts.path = "/"; | |
| } | |
| // split the authority into host and optional port, a bracketed IPv6 literal keeps its inner colons (RFC 3986) | |
| std::string port_str; | |
| if (!parts.host.empty() && parts.host.front() == '[') { | |
| auto close = parts.host.find(']'); | |
| if (close == std::string::npos) { | |
| throw std::runtime_error("invalid IPv6 URL authority: " + parts.host); | |
| } | |
| auto after = parts.host.substr(close + 1); | |
| if (!after.empty() && after.front() == ':') { | |
| port_str = after.substr(1); | |
| } | |
| parts.host = parts.host.substr(1, close - 1); | |
| } else { | |
| auto colon_pos = parts.host.find(':'); | |
| if (colon_pos != std::string::npos) { | |
| port_str = parts.host.substr(colon_pos + 1); | |
| parts.host = parts.host.substr(0, colon_pos); | |
| } | |
| } | |
| if (!port_str.empty()) { | |
| parts.port = std::stoi(port_str); | |
| } else if (parts.scheme == "http") { | |
| parts.port = 80; | |
| } else if (parts.scheme == "https") { | |
| parts.port = 443; | |
| } else { | |
| throw std::runtime_error("unsupported URL scheme: " + parts.scheme); | |
| } | |
| return parts; | |
| } | |
| // match a loopback destination: a remote proxy can never reach the client machine's own | |
| // loopback, so these hosts are always contacted directly, before NO_PROXY is consulted | |
| // (similar to Go's net/http) | |
| static bool common_http_is_loopback(const std::string & host) { | |
| auto lower = [](std::string s) { | |
| for (auto & c : s) { | |
| if (c >= 'A' && c <= 'Z') { | |
| c += 'a' - 'A'; | |
| } | |
| } | |
| return s; | |
| }; | |
| const std::string h = lower(host); | |
| if (h == "localhost" || h == "::1" || h == "0.0.0.0") { | |
| return true; | |
| } | |
| if (h.size() > 10 && h.compare(h.size() - 10, 10, ".localhost") == 0) { | |
| return true; | |
| } | |
| // IPv4 literal: exactly four all-digit octets with values 0-255, loopback is 127.0.0.0/8 | |
| int octets[4] = { 0, 0, 0, 0 }; | |
| size_t pos = 0; | |
| int n = 0; | |
| bool complete = false; | |
| while (n < 4 && pos <= h.size()) { | |
| size_t dot = h.find('.', pos); | |
| if (dot == std::string::npos) { | |
| dot = h.size(); | |
| } | |
| if (dot == pos || dot - pos > 3) { | |
| return false; | |
| } | |
| int val = 0; | |
| for (size_t i = pos; i < dot; i++) { | |
| if (h[i] < '0' || h[i] > '9') { | |
| return false; | |
| } | |
| val = val * 10 + (h[i] - '0'); | |
| } | |
| if (val > 255) { | |
| return false; | |
| } | |
| octets[n++] = val; | |
| if (dot == h.size()) { | |
| complete = (n == 4); | |
| break; | |
| } | |
| pos = dot + 1; | |
| } | |
| return complete && octets[0] == 127; | |
| } | |
| // match a host against a NO_PROXY list: comma separated entries, "*" bypasses the proxy for every host, | |
| // an entry matches the host itself or any of its subdomains, a leading dot is optional, ports are ignored | |
| static bool common_http_proxy_bypass(const std::string & no_proxy, const std::string & host) { | |
| auto lower = [](std::string s) { | |
| for (auto & c : s) { | |
| if (c >= 'A' && c <= 'Z') { | |
| c += 'a' - 'A'; | |
| } | |
| } | |
| return s; | |
| }; | |
| const std::string h = lower(host); | |
| for (size_t pos = 0; pos < no_proxy.size(); ) { | |
| size_t end = no_proxy.find(',', pos); | |
| if (end == std::string::npos) { | |
| end = no_proxy.size(); | |
| } | |
| std::string entry = lower(no_proxy.substr(pos, end - pos)); | |
| pos = end + 1; | |
| entry.erase(0, entry.find_first_not_of(" \t")); | |
| auto last = entry.find_last_not_of(" \t"); | |
| if (last == std::string::npos) { | |
| continue; | |
| } | |
| entry.erase(last + 1); | |
| if (entry == "*") { | |
| return true; | |
| } | |
| if (entry.front() == '.') { | |
| entry.erase(0, 1); | |
| } | |
| if (entry.empty()) { | |
| continue; | |
| } | |
| if (h == entry) { | |
| return true; | |
| } | |
| if (h.size() > entry.size() && h[h.size() - entry.size() - 1] == '.' && | |
| h.compare(h.size() - entry.size(), entry.size(), entry) == 0) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| static std::pair<httplib::Client, common_http_url> common_http_client(const std::string & url) { | |
| common_http_url parts = common_http_parse_url(url); | |
| if (parts.host.empty()) { | |
| throw std::runtime_error("error: invalid URL format"); | |
| } | |
| if (parts.scheme == "https") { | |
| throw std::runtime_error( | |
| "HTTPS is not supported. Please rebuild with one of:\n" | |
| " -DLLAMA_BUILD_BORINGSSL=ON\n" | |
| " -DLLAMA_BUILD_LIBRESSL=ON\n" | |
| " -DLLAMA_OPENSSL=ON (default, requires OpenSSL dev files installed)" | |
| ); | |
| } | |
| httplib::Client cli(parts.scheme + "://" + common_http_format_host(parts.host) + ":" + std::to_string(parts.port)); | |
| if (!parts.user.empty()) { | |
| cli.set_basic_auth(parts.user, parts.password); | |
| } | |
| cli.set_follow_location(true); | |
| // Honor HTTP_PROXY / HTTPS_PROXY environment variables (both lower- and upper-case). | |
| auto getenv_s = [](const char * upper, const char * lower) -> std::string { | |
| const char * val = std::getenv(upper); | |
| if (!val) { | |
| val = std::getenv(lower); | |
| } | |
| return val ? val : ""; | |
| }; | |
| const std::string proxy_url = (parts.scheme == "https") | |
| ? getenv_s("HTTPS_PROXY", "https_proxy") | |
| : getenv_s("HTTP_PROXY", "http_proxy"); | |
| if (!proxy_url.empty() && !common_http_is_loopback(parts.host) && | |
| !common_http_proxy_bypass(getenv_s("NO_PROXY", "no_proxy"), parts.host)) { | |
| try { | |
| common_http_url proxy = common_http_parse_url(proxy_url); | |
| cli.set_proxy(proxy.host, proxy.port); | |
| if (!proxy.user.empty()) { | |
| cli.set_proxy_basic_auth(proxy.user, proxy.password); | |
| } | |
| } catch (const std::exception & e) { | |
| // fall back to a direct connection, the URL itself is not logged as it can carry credentials | |
| LOG_WRN("%s: ignoring malformed proxy URL from the environment: %s\n", __func__, e.what()); | |
| } | |
| } | |
| return { std::move(cli), std::move(parts) }; | |
| } | |
| static std::string common_http_show_masked_url(const common_http_url & parts) { | |
| return parts.scheme + "://" + (parts.user.empty() ? "" : "****:****@") + common_http_format_host(parts.host) + parts.path; | |
| } | |
| static int common_http_get_free_port() { | |
| WSADATA wsaData; | |
| if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { | |
| return -1; | |
| } | |
| typedef SOCKET native_socket_t; | |
| typedef int native_socket_t; | |
| native_socket_t sock = socket(AF_INET, SOCK_STREAM, 0); | |
| if (sock == INVALID_SOCKET_VAL) { | |
| WSACleanup(); | |
| return -1; | |
| } | |
| struct sockaddr_in serv_addr; | |
| std::memset(&serv_addr, 0, sizeof(serv_addr)); | |
| serv_addr.sin_family = AF_INET; | |
| serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); | |
| serv_addr.sin_port = htons(0); | |
| if (bind(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) { | |
| CLOSE_SOCKET(sock); | |
| WSACleanup(); | |
| return -1; | |
| } | |
| int namelen = sizeof(serv_addr); | |
| socklen_t namelen = sizeof(serv_addr); | |
| if (getsockname(sock, (struct sockaddr*)&serv_addr, &namelen) != 0) { | |
| CLOSE_SOCKET(sock); | |
| WSACleanup(); | |
| return -1; | |
| } | |
| int port = ntohs(serv_addr.sin_port); | |
| CLOSE_SOCKET(sock); | |
| WSACleanup(); | |
| return port; | |
| } | |
Xet Storage Details
- Size:
- 9.62 kB
- Xet hash:
- f5fb61b7b6e76c5ff9123a06b7bf7a892194d7dc7239893def3bd3b62f06a336
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.