99#include < nlohmann/json.hpp>
1010
1111#if defined(_WIN32)
12+ # define WIN32_LEAN_AND_MEAN
1213# ifndef NOMINMAX
1314# define NOMINMAX
1415# endif
2223
2324#if defined(LLAMA_USE_CURL)
2425# include < curl/curl.h>
26+ #else
27+ # include " http.h"
2528#endif
2629
2730#include < signal.h>
@@ -397,7 +400,6 @@ class File {
397400# endif
398401};
399402
400- #ifdef LLAMA_USE_CURL
401403class HttpClient {
402404 public:
403405 int init (const std::string & url, const std::vector<std::string> & headers, const std::string & output_file,
@@ -428,6 +430,8 @@ class HttpClient {
428430 return 0 ;
429431 }
430432
433+ #ifdef LLAMA_USE_CURL
434+
431435 ~HttpClient () {
432436 if (chunk) {
433437 curl_slist_free_all (chunk);
@@ -532,6 +536,117 @@ class HttpClient {
532536 return curl_easy_perform (curl);
533537 }
534538
539+ #else // LLAMA_USE_CURL is not defined
540+
541+ #define curl_off_t long long // temporary hack
542+
543+ private:
544+ // this is a direct translation of the cURL download() above
545+ int download (const std::string & url, const std::vector<std::string> & headers_vec, const std::string & output_file,
546+ const bool progress, std::string * response_str = nullptr ) {
547+ try {
548+ auto [cli, url_parts] = common_http_client (url);
549+
550+ httplib::Headers headers;
551+ for (const auto & h : headers_vec) {
552+ size_t pos = h.find (' :' );
553+ if (pos != std::string::npos) {
554+ headers.emplace (h.substr (0 , pos), h.substr (pos + 2 ));
555+ }
556+ }
557+
558+ File out;
559+ if (!output_file.empty ()) {
560+ if (!out.open (output_file, " ab" )) {
561+ printe (" Failed to open file for writing\n " );
562+ return 1 ;
563+ }
564+ if (out.lock ()) {
565+ printe (" Failed to exclusively lock file\n " );
566+ return 1 ;
567+ }
568+ }
569+
570+ size_t resume_offset = 0 ;
571+ if (!output_file.empty () && std::filesystem::exists (output_file)) {
572+ resume_offset = std::filesystem::file_size (output_file);
573+ if (resume_offset > 0 ) {
574+ headers.emplace (" Range" , " bytes=" + std::to_string (resume_offset) + " -" );
575+ }
576+ }
577+
578+ progress_data data;
579+ data.file_size = resume_offset;
580+
581+ long long total_size = 0 ;
582+ long long received_this_session = 0 ;
583+
584+ auto response_handler =
585+ [&](const httplib::Response & response) {
586+ if (resume_offset > 0 && response.status != 206 ) {
587+ printe (" \n Server does not support resuming. Restarting download.\n " );
588+ out.file = freopen (output_file.c_str (), " wb" , out.file );
589+ if (!out.file ) {
590+ return false ;
591+ }
592+ data.file_size = 0 ;
593+ }
594+ if (progress) {
595+ if (response.has_header (" Content-Length" )) {
596+ total_size = std::stoll (response.get_header_value (" Content-Length" ));
597+ } else if (response.has_header (" Content-Range" )) {
598+ auto range = response.get_header_value (" Content-Range" );
599+ auto slash = range.find (' /' );
600+ if (slash != std::string::npos) {
601+ total_size = std::stoll (range.substr (slash + 1 ));
602+ }
603+ }
604+ }
605+ return true ;
606+ };
607+
608+ auto content_receiver =
609+ [&](const char * chunk, size_t length) {
610+ if (out.file && fwrite (chunk, 1 , length, out.file ) != length) {
611+ return false ;
612+ }
613+ if (response_str) {
614+ response_str->append (chunk, length);
615+ }
616+ received_this_session += length;
617+
618+ if (progress && total_size > 0 ) {
619+ update_progress (&data, total_size, received_this_session, 0 , 0 );
620+ }
621+ return true ;
622+ };
623+
624+ auto res = cli.Get (url_parts.path , headers, response_handler, content_receiver);
625+
626+ if (data.printed ) {
627+ printe (" \n " );
628+ }
629+
630+ if (!res) {
631+ auto err = res.error ();
632+ printe (" Fetching resource '%s' failed: %s\n " , url.c_str (), httplib::to_string (err).c_str ());
633+ return 1 ;
634+ }
635+
636+ if (res->status >= 400 ) {
637+ printe (" Fetching resource '%s' failed with status code: %d\n " , url.c_str (), res->status );
638+ return 1 ;
639+ }
640+
641+ } catch (const std::exception & e) {
642+ printe (" HTTP request failed: %s\n " , e.what ());
643+ return 1 ;
644+ }
645+ return 0 ;
646+ }
647+
648+ #endif // LLAMA_USE_CURL
649+
535650 static std::string human_readable_time (double seconds) {
536651 int hrs = static_cast <int >(seconds) / 3600 ;
537652 int mins = (static_cast <int >(seconds) % 3600 ) / 60 ;
@@ -644,8 +759,8 @@ class HttpClient {
644759 str->append (static_cast <char *>(ptr), size * nmemb);
645760 return size * nmemb;
646761 }
762+
647763};
648- #endif
649764
650765class LlamaData {
651766 public:
@@ -673,7 +788,6 @@ class LlamaData {
673788 }
674789
675790 private:
676- #ifdef LLAMA_USE_CURL
677791 int download (const std::string & url, const std::string & output_file, const bool progress,
678792 const std::vector<std::string> & headers = {}, std::string * response_str = nullptr ) {
679793 HttpClient http;
@@ -683,14 +797,6 @@ class LlamaData {
683797
684798 return 0 ;
685799 }
686- #else
687- int download (const std::string &, const std::string &, const bool , const std::vector<std::string> & = {},
688- std::string * = nullptr ) {
689- printe (" %s: llama.cpp built without libcurl, downloading from an url not supported.\n " , __func__);
690-
691- return 1 ;
692- }
693- #endif
694800
695801 // Helper function to handle model tag extraction and URL construction
696802 std::pair<std::string, std::string> extract_model_and_tag (std::string & model, const std::string & base_url) {
0 commit comments