9
9
#include < nlohmann/json.hpp>
10
10
11
11
#if defined(_WIN32)
12
+ # define WIN32_LEAN_AND_MEAN
12
13
# ifndef NOMINMAX
13
14
# define NOMINMAX
14
15
# endif
22
23
23
24
#if defined(LLAMA_USE_CURL)
24
25
# include < curl/curl.h>
26
+ #else
27
+ # include " http.h"
25
28
#endif
26
29
27
30
#include < signal.h>
@@ -397,7 +400,6 @@ class File {
397
400
# endif
398
401
};
399
402
400
- #ifdef LLAMA_USE_CURL
401
403
class HttpClient {
402
404
public:
403
405
int init (const std::string & url, const std::vector<std::string> & headers, const std::string & output_file,
@@ -428,6 +430,8 @@ class HttpClient {
428
430
return 0 ;
429
431
}
430
432
433
+ #ifdef LLAMA_USE_CURL
434
+
431
435
~HttpClient () {
432
436
if (chunk) {
433
437
curl_slist_free_all (chunk);
@@ -532,6 +536,117 @@ class HttpClient {
532
536
return curl_easy_perform (curl);
533
537
}
534
538
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
+
535
650
static std::string human_readable_time (double seconds) {
536
651
int hrs = static_cast <int >(seconds) / 3600 ;
537
652
int mins = (static_cast <int >(seconds) % 3600 ) / 60 ;
@@ -644,8 +759,8 @@ class HttpClient {
644
759
str->append (static_cast <char *>(ptr), size * nmemb);
645
760
return size * nmemb;
646
761
}
762
+
647
763
};
648
- #endif
649
764
650
765
class LlamaData {
651
766
public:
@@ -673,7 +788,6 @@ class LlamaData {
673
788
}
674
789
675
790
private:
676
- #ifdef LLAMA_USE_CURL
677
791
int download (const std::string & url, const std::string & output_file, const bool progress,
678
792
const std::vector<std::string> & headers = {}, std::string * response_str = nullptr ) {
679
793
HttpClient http;
@@ -683,14 +797,6 @@ class LlamaData {
683
797
684
798
return 0 ;
685
799
}
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
694
800
695
801
// Helper function to handle model tag extraction and URL construction
696
802
std::pair<std::string, std::string> extract_model_and_tag (std::string & model, const std::string & base_url) {
0 commit comments