@@ -20,6 +20,94 @@ using namespace winrt::Windows::Foundation;
20
20
using namespace winrt ::Windows::Security::Cryptography;
21
21
using namespace winrt ::Windows::Security::Cryptography::Core;
22
22
23
+ CancellationDisposable::CancellationDisposable (IAsyncInfo const & async, std::function<void ()>&& onCancel) noexcept
24
+ : m_async{ async }
25
+ , m_onCancel{ std::move (onCancel) }
26
+ {
27
+ }
28
+
29
+ CancellationDisposable::CancellationDisposable (CancellationDisposable&& other) noexcept
30
+ : m_async{ std::move (other.m_async ) }
31
+ , m_onCancel{ std::move (other.m_onCancel ) }
32
+ {
33
+ }
34
+
35
+ CancellationDisposable& CancellationDisposable::operator =(CancellationDisposable&& other) noexcept
36
+ {
37
+ if (this != &other)
38
+ {
39
+ CancellationDisposable temp{ std::move (*this ) };
40
+ m_async = std::move (other.m_async );
41
+ m_onCancel = std::move (other.m_onCancel );
42
+ }
43
+ return *this ;
44
+ }
45
+
46
+ CancellationDisposable::~CancellationDisposable () noexcept
47
+ {
48
+ Cancel ();
49
+ }
50
+
51
+ void CancellationDisposable::Cancel () noexcept
52
+ {
53
+ if (m_async)
54
+ {
55
+ if (m_async.Status () == AsyncStatus::Started)
56
+ {
57
+ m_async.Cancel ();
58
+ }
59
+
60
+ if (m_onCancel)
61
+ {
62
+ m_onCancel ();
63
+ }
64
+ }
65
+ }
66
+
67
+ TaskCancellationManager::~TaskCancellationManager () noexcept
68
+ {
69
+ // Do the explicit cleaning to make sure that CancellationDisposable
70
+ // destructors run while this instance still has valid fields because
71
+ // they are used by the onCancel callback.
72
+ // We also want to clear the m_pendingTasks before running the
73
+ // CancellationDisposable destructors since they touch the m_pendingTasks.
74
+ std::map<TaskId, CancellationDisposable> pendingTasks;
75
+ {
76
+ std::scoped_lock lock{ m_mutex };
77
+ pendingTasks = std::move (m_pendingTasks);
78
+ }
79
+ }
80
+
81
+
82
+ IAsyncAction TaskCancellationManager::Add (TaskId taskId, IAsyncAction const & asyncAction) noexcept
83
+ {
84
+ std::scoped_lock lock{ m_mutex };
85
+ m_pendingTasks.try_emplace (taskId, asyncAction, [this , taskId]()
86
+ {
87
+ Cancel (taskId);
88
+ });
89
+ return asyncAction;
90
+ }
91
+
92
+ void TaskCancellationManager::Cancel (TaskId taskId) noexcept
93
+ {
94
+ // The destructor of the token does the cancellation. We must do it outside of lock.
95
+ CancellationDisposable token;
96
+
97
+ {
98
+ std::scoped_lock lock{ m_mutex };
99
+ if (!m_pendingTasks.empty ())
100
+ {
101
+ if (auto it = m_pendingTasks.find (taskId); it != m_pendingTasks.end ())
102
+ {
103
+ token = std::move (it->second );
104
+ m_pendingTasks.erase (it);
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+
23
111
24
112
void RNFetchBlob::Initialize (winrt::Microsoft::ReactNative::ReactContext const & reactContext) noexcept
25
113
{
@@ -803,7 +891,7 @@ catch (const hresult_error& ex)
803
891
winrt::fire_and_forget RNFetchBlob::lstat (
804
892
std::string path,
805
893
std::function<void (std::string, winrt::Microsoft::ReactNative::JSValueArray&)> callback) noexcept
806
- try
894
+ try
807
895
{
808
896
std::filesystem::path directory (path);
809
897
directory.make_preferred ();
@@ -902,7 +990,7 @@ catch (const hresult_error& ex)
902
990
// df - Implemented, not tested
903
991
winrt::fire_and_forget RNFetchBlob::df (
904
992
std::function<void (std::string, winrt::Microsoft::ReactNative::JSValueObject&)> callback) noexcept
905
- try
993
+ try
906
994
{
907
995
auto localFolder{ Windows::Storage::ApplicationData::Current ().LocalFolder () };
908
996
auto properties{ co_await localFolder.Properties ().RetrievePropertiesAsync ({L" System.FreeSpace" , L" System.Capacity" }) };
@@ -925,7 +1013,7 @@ winrt::fire_and_forget RNFetchBlob::slice(
925
1013
uint32_t start,
926
1014
uint32_t end,
927
1015
winrt::Microsoft::ReactNative::ReactPromise<std::string> promise) noexcept
928
- try
1016
+ try
929
1017
{
930
1018
winrt::hstring srcDirectoryPath, srcFileName, destDirectoryPath, destFileName;
931
1019
splitPath (src, srcDirectoryPath, srcFileName);
@@ -962,25 +1050,64 @@ winrt::fire_and_forget RNFetchBlob::fetchBlob(
962
1050
std::function<void (std::string, std::string, std::string)> callback) noexcept
963
1051
{
964
1052
// winrt::Windows::Web::Http::HttpMethod::Hea
965
- // Delete, Patch, Post, Put, Get, Options, Head
1053
+
966
1054
// Method
967
1055
RNFetchBlobConfig config;
968
- config.appendExt = options[" appendExt" ].AsString ();
1056
+ if (options[" appendExt" ].IsNull () == true )
1057
+ {
1058
+ config.appendExt = " " ;
1059
+ }
1060
+ else
1061
+ {
1062
+ std::filesystem::path path (options[" appendExt" ].AsString ());
1063
+ path.make_preferred ();
1064
+ config.appendExt = winrt::to_string (path.c_str ());
1065
+ }
1066
+ config.appendExt = options[" appendExt" ].IsNull () ? " " : options[" appendExt" ].AsString ();
969
1067
config.fileCache = options[" fileCache" ].AsBoolean ();
970
- config.followRedirect = options[" followRedirect" ].IsNull () ? true : options[ " followRedirect " ]. AsBoolean ();
1068
+ config.followRedirect = options[" followRedirect" ].AsBoolean ();
971
1069
config.overwrite = options[" overwrite" ].AsBoolean ();
972
- config.path = options[" path" ].AsString ();
1070
+ if (options[" path" ].IsNull () == true )
1071
+ {
1072
+ config.path = " " ;
1073
+ }
1074
+ else
1075
+ {
1076
+ std::filesystem::path path{ options[" path" ].AsString () };
1077
+ path.make_preferred ();
1078
+ config.path = winrt::to_string (path.c_str ());
1079
+ }
973
1080
config.timeout = options[" timeout" ].AsInt32 ();
974
- config.trusty = options[" trusty" ].AsBoolean ();;
1081
+ config.trusty = options[" trusty" ].AsBoolean ();
975
1082
1083
+ if (config.followRedirect == true )
1084
+ {
1085
+ // TODO: find winrt config property
1086
+ }
1087
+ if (config.fileCache == true )
1088
+ {
1089
+
1090
+ }
1091
+ if (config.timeout > 0 )
1092
+ {
1093
+ // TODO: find winrt config property
1094
+ }
1095
+ if (config.trusty == true )
1096
+ {
1097
+ // TODO: find winrt config property
1098
+ }
1099
+
1100
+
976
1101
977
1102
winrt::Windows::Web::Http::HttpMethod httpMethod{ winrt::Windows::Web::Http::HttpMethod::Post () };
1103
+
978
1104
std::string methodUpperCase{ method };
979
1105
for (auto & c : methodUpperCase)
980
1106
{
981
1107
toupper (c);
982
1108
}
983
1109
1110
+ // Delete, Patch, Post, Put, Get, Options, Head
984
1111
if (methodUpperCase.compare (" DELETE" ) == 0 )
985
1112
{
986
1113
httpMethod = winrt::Windows::Web::Http::HttpMethod::Delete ();
@@ -1029,11 +1156,11 @@ winrt::fire_and_forget RNFetchBlob::fetchBlob(
1029
1156
winrt::Windows::Web::Http::HttpBufferContent content{ CryptographicBuffer::ConvertStringToBinary (winrt::to_hstring (body), BinaryStringEncoding::Utf8) };
1030
1157
requestMessage.Content (content);
1031
1158
}
1032
-
1159
+ // requestMessage.Content(requestContent);
1033
1160
1034
- winrt::Windows::Web::Http::HttpResponseMessage response = co_await m_httpClient.SendRequestAsync (requestMessage, winrt::Windows::Web::Http::HttpCompletionOption::ResponseHeadersRead);
1161
+ // winrt::Windows::Web::Http::HttpResponseMessage response = co_await m_httpClient.SendRequestAsync(requestMessage, winrt::Windows::Web::Http::HttpCompletionOption::ResponseHeadersRead);
1035
1162
1036
- co_return ;
1163
+ co_await m_tasks. Add (taskId, ProcessRequestAsync (requestMessage, config, callback)) ;
1037
1164
}
1038
1165
1039
1166
void RNFetchBlob::fetchBlobForm (
@@ -1070,9 +1197,15 @@ void RNFetchBlob::enableUploadProgressReport(
1070
1197
// cancelRequest
1071
1198
void RNFetchBlob::cancelRequest (
1072
1199
std::string taskId,
1073
- std::function<void (std::string)> callback) noexcept
1200
+ std::function<void (std::string, std::string)> callback) noexcept
1201
+ try
1074
1202
{
1075
- return ;
1203
+ m_tasks.Cancel (taskId);
1204
+ callback (" " , taskId);
1205
+ }
1206
+ catch (const hresult_error& ex)
1207
+ {
1208
+ callback (winrt::to_string (ex.message ()), " " );
1076
1209
}
1077
1210
1078
1211
void RNFetchBlob::removeSession (
@@ -1116,7 +1249,37 @@ void RNFetchBlob::splitPath(const std::wstring& fullPath, winrt::hstring& direct
1116
1249
fileName = path.has_filename () ? winrt::to_hstring (path.filename ().c_str ()) : L" " ;
1117
1250
}
1118
1251
1252
+ winrt::Windows::Foundation::IAsyncAction RNFetchBlob::ProcessRequestAsync (
1253
+ winrt::Windows::Web::Http::HttpRequestMessage& httpRequestMessage,
1254
+ const RNFetchBlobConfig& config,
1255
+ std::function<void (std::string, std::string, std::string)>& callback)
1256
+ {
1257
+ winrt::Windows::Web::Http::HttpResponseMessage response = co_await m_httpClient.SendRequestAsync (httpRequestMessage, winrt::Windows::Web::Http::HttpCompletionOption::ResponseHeadersRead);
1258
+
1259
+ if (config.path .empty () == false )
1260
+ {
1261
+ std::filesystem::path path{ config.path };
1262
+ StorageFolder storageFolder{ co_await StorageFolder::GetFolderFromPathAsync (path.parent_path ().wstring ()) };
1263
+ StorageFile storageFile{ co_await storageFolder.CreateFileAsync (path.filename ().wstring (), CreationCollisionOption::ReplaceExisting) };
1264
+ IRandomAccessStream stream{ co_await storageFile.OpenAsync (FileAccessMode::ReadWrite) };
1265
+ IOutputStream outputStream{ stream.GetOutputStreamAt (0 ) };
1266
+ //
1267
+ auto contentStream = co_await response.Content ().ReadAsInputStreamAsync ();
1268
+ Buffer buffer{ 8 * 1024 };
1119
1269
1270
+ for (;;)
1271
+ {
1272
+ buffer.Length (0 );
1273
+ auto readBuffer = co_await contentStream.ReadAsync (buffer, buffer.Capacity (), InputStreamOptions::None);
1274
+ if (readBuffer.Length () == 0 )
1275
+ {
1276
+ break ;
1277
+ }
1278
+
1279
+ co_await outputStream.WriteAsync (readBuffer);
1280
+ }
1281
+ }
1282
+ }
1120
1283
1121
1284
RNFetchBlobStream::RNFetchBlobStream (Streams::IRandomAccessStream& _streamInstance, EncodingOptions _encoding) noexcept
1122
1285
: streamInstance{ std::move (_streamInstance) }
0 commit comments