@@ -499,37 +499,39 @@ std::string network::post(std::string url, std::string data) {
499499 return response;
500500}
501501
502- std::string network::get (std::string url) { return post (url, " " ); }
503-
504- void network::get_async (std::string url,
505- std::function<void (std::string)> callback,
506- std::function<void(std::string)> error_callback) {
502+ std::string network::get (std::string url) { return post (url, " " ); }
503+
504+ void network::get_async (std::string url,
505+ std::function<void (std::string)> callback,
506+ std::function<void(std::string)> error_callback) {
507507 std::thread ([url, callback, error_callback,
508508 &ctx = *qjs::Context::current]() {
509- try {
510- auto res = get (url);
511- ctx.enqueueJob ([=]() { callback (res); });
512- } catch (std::exception &e) {
513- spdlog::error (" Error in network::get_async: {}" , e.what ());
514- error_callback (e.what ());
515- }
516- }).detach ();
517- }
518-
519- void network::post_async (std::string url, std::string data,
509+ try {
510+ auto res = get (url);
511+ ctx.enqueueJob ([=]() { callback (res); });
512+ } catch (std::exception &e) {
513+ spdlog::error (" Error in network::get_async: {}" , e.what ());
514+ auto error = std::string (e.what ());
515+ ctx.enqueueJob ([=]() { error_callback (error); });
516+ }
517+ }).detach ();
518+ }
519+
520+ void network::post_async (std::string url, std::string data,
520521 std::function<void (std::string)> callback,
521522 std::function<void(std::string)> error_callback) {
522523 std::thread ([url, data, callback, error_callback,
523524 &ctx = *qjs::Context::current]() {
524- try {
525- auto res = post (url, data);
526- ctx.enqueueJob ([=]() { callback (res); });
527- } catch (std::exception &e) {
528- spdlog::error (" Error in network::post_async: {}" , e.what ());
529- error_callback (e.what ());
530- }
531- }).detach ();
532- }
525+ try {
526+ auto res = post (url, data);
527+ ctx.enqueueJob ([=]() { callback (res); });
528+ } catch (std::exception &e) {
529+ spdlog::error (" Error in network::post_async: {}" , e.what ());
530+ auto error = std::string (e.what ());
531+ ctx.enqueueJob ([=]() { error_callback (error); });
532+ }
533+ }).detach ();
534+ }
533535subproc_result_data subproc::run (std::string cmd) {
534536 subproc_result_data result;
535537 SECURITY_ATTRIBUTES sa;
@@ -672,24 +674,172 @@ std::vector<std::string> fs::readdir(std::string path) {
672674 std::back_inserter (result));
673675 return result;
674676}
675- bool breeze::is_light_theme () { return is_light_mode (); }
676- void network::download_async (std::string url, std::string path,
677- std::function<void ()> callback,
678- std::function<void(std::string)> error_callback) {
679-
680- std::thread ([url, path, callback, error_callback,
681- &ctx = *qjs::Context::current]() {
682- try {
683- auto data = get (url);
684- fs::write_binary (path,
685- std::vector<uint8_t >(data.begin (), data.end ()));
686- ctx.enqueueJob ([=]() { callback (); });
687- callback ();
688- } catch (std::exception &e) {
689- error_callback (e.what ());
690- }
691- }).detach ();
692- }
677+ bool breeze::is_light_theme () { return is_light_mode (); }
678+ void network::download_async (std::string url, std::string path,
679+ std::function<void ()> callback,
680+ std::function<void(std::string)> error_callback) {
681+ std::thread ([url, path, callback, error_callback,
682+ &ctx = *qjs::Context::current]() {
683+ try {
684+ auto data = get (url);
685+ fs::write_binary (path,
686+ std::vector<uint8_t >(data.begin (), data.end ()));
687+ ctx.enqueueJob ([=]() { callback (); });
688+ } catch (std::exception &e) {
689+ auto error = std::string (e.what ());
690+ spdlog::error (" Error in network::download_async: {}" , error);
691+ ctx.enqueueJob ([=]() { error_callback (error); });
692+ }
693+ }).detach ();
694+ }
695+
696+ void network::download_with_progress_async (
697+ std::string url, std::string path, std::function<void ()> callback,
698+ std::function<void(std::string)> error_callback,
699+ std::function<void(size_t , size_t )> progress_callback) {
700+ std::thread ([url, path, callback, error_callback,
701+ progress_callback, &ctx = *qjs::Context::current]() {
702+ HINTERNET hSession = nullptr ;
703+ HINTERNET hConnect = nullptr ;
704+ HINTERNET hRequest = nullptr ;
705+ auto close_handles = [&]() {
706+ if (hRequest) {
707+ WinHttpCloseHandle (hRequest);
708+ hRequest = nullptr ;
709+ }
710+ if (hConnect) {
711+ WinHttpCloseHandle (hConnect);
712+ hConnect = nullptr ;
713+ }
714+ if (hSession) {
715+ WinHttpCloseHandle (hSession);
716+ hSession = nullptr ;
717+ }
718+ };
719+
720+ try {
721+ hSession =
722+ WinHttpOpen (L" BreezeShell" , WINHTTP_ACCESS_TYPE_DEFAULT_PROXY ,
723+ WINHTTP_NO_PROXY_NAME , WINHTTP_NO_PROXY_BYPASS , 0 );
724+ if (!hSession) {
725+ throw std::runtime_error (" Failed to initialize WinHTTP" );
726+ }
727+
728+ URL_COMPONENTS urlComp = {sizeof (URL_COMPONENTS )};
729+ wchar_t hostName[256 ] = {0 };
730+ wchar_t urlPath[1024 ] = {0 };
731+ urlComp.lpszHostName = hostName;
732+ urlComp.dwHostNameLength = sizeof (hostName) / sizeof (wchar_t );
733+ urlComp.lpszUrlPath = urlPath;
734+ urlComp.dwUrlPathLength = sizeof (urlPath) / sizeof (wchar_t );
735+
736+ std::wstring wideUrl = utf8_to_wstring (url);
737+
738+ if (!WinHttpCrackUrl (wideUrl.c_str (), wideUrl.length (), 0 ,
739+ &urlComp)) {
740+ throw std::runtime_error (" Invalid URL format" );
741+ }
742+
743+ hConnect =
744+ WinHttpConnect (hSession, hostName,
745+ urlComp.nScheme == INTERNET_SCHEME_HTTPS
746+ ? INTERNET_DEFAULT_HTTPS_PORT
747+ : INTERNET_DEFAULT_HTTP_PORT ,
748+ 0 );
749+ if (!hConnect) {
750+ throw std::runtime_error (" Failed to connect to server" );
751+ }
752+
753+ DWORD flags = WINHTTP_FLAG_REFRESH ;
754+ if (urlComp.nScheme == INTERNET_SCHEME_HTTPS ) {
755+ flags |= WINHTTP_FLAG_SECURE ;
756+ }
757+
758+ hRequest = WinHttpOpenRequest (
759+ hConnect, L" GET" , urlPath, nullptr , WINHTTP_NO_REFERER ,
760+ WINHTTP_DEFAULT_ACCEPT_TYPES , flags);
761+ if (!hRequest) {
762+ throw std::runtime_error (" Failed to create request" );
763+ }
764+
765+ if (!WinHttpSendRequest (hRequest, WINHTTP_NO_ADDITIONAL_HEADERS , 0 ,
766+ WINHTTP_NO_REQUEST_DATA , 0 , 0 , 0 ) ||
767+ !WinHttpReceiveResponse (hRequest, nullptr )) {
768+ throw std::runtime_error (" Failed to send/receive request" );
769+ }
770+
771+ DWORD statusCode = 0 ;
772+ DWORD statusCodeSize = sizeof (statusCode);
773+ WinHttpQueryHeaders (
774+ hRequest,
775+ WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER ,
776+ WINHTTP_HEADER_NAME_BY_INDEX , &statusCode, &statusCodeSize,
777+ WINHTTP_NO_HEADER_INDEX );
778+
779+ if (statusCode >= 400 ) {
780+ throw std::runtime_error (" Server returned error: " +
781+ std::to_string (statusCode));
782+ }
783+
784+ DWORD contentLength = 0 ;
785+ DWORD contentLengthSize = sizeof (contentLength);
786+ size_t totalBytes = 0 ;
787+ if (WinHttpQueryHeaders (
788+ hRequest,
789+ WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER ,
790+ WINHTTP_HEADER_NAME_BY_INDEX , &contentLength,
791+ &contentLengthSize, WINHTTP_NO_HEADER_INDEX )) {
792+ totalBytes = contentLength;
793+ }
794+
795+ std::ofstream file (path, std::ios::binary | std::ios::trunc);
796+ if (!file.is_open ()) {
797+ throw std::runtime_error (" Failed to open file for writing" );
798+ }
799+
800+ size_t downloadedBytes = 0 ;
801+ DWORD bytesAvailable = 0 ;
802+ do {
803+ bytesAvailable = 0 ;
804+ if (!WinHttpQueryDataAvailable (hRequest, &bytesAvailable)) {
805+ throw std::runtime_error (" Failed to query download data" );
806+ }
807+ if (!bytesAvailable)
808+ break ;
809+
810+ std::vector<char > buffer (bytesAvailable);
811+ DWORD bytesRead = 0 ;
812+ if (!WinHttpReadData (hRequest, buffer.data (), bytesAvailable,
813+ &bytesRead)) {
814+ throw std::runtime_error (" Failed to read download data" );
815+ }
816+ if (!bytesRead)
817+ continue ;
818+
819+ file.write (buffer.data (), bytesRead);
820+ if (!file.good ()) {
821+ throw std::runtime_error (" Failed to write download file" );
822+ }
823+
824+ downloadedBytes += bytesRead;
825+ ctx.enqueueJob ([=]() {
826+ progress_callback (downloadedBytes, totalBytes);
827+ });
828+ } while (bytesAvailable > 0 );
829+
830+ file.close ();
831+ close_handles ();
832+ ctx.enqueueJob ([=]() { callback (); });
833+ } catch (std::exception &e) {
834+ close_handles ();
835+ std::filesystem::remove (path);
836+ auto error = std::string (e.what ());
837+ spdlog::error (" Error in network::download_with_progress_async: {}" ,
838+ error);
839+ ctx.enqueueJob ([=]() { error_callback (error); });
840+ }
841+ }).detach ();
842+ }
693843std::string win32::resid_from_string (std::string str) {
694844 return res_string_loader::string_to_id_string (utf8_to_wstring (str));
695845}
0 commit comments