Skip to content

Commit 6b28ce3

Browse files
committed
Implement cancelRequest and TaskManager
1 parent d7fa9d6 commit 6b28ce3

File tree

3 files changed

+222
-15
lines changed

3 files changed

+222
-15
lines changed

RNFetchBlobWin/App.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ const App: () => React$Node = () => {
358358
// this is much more performant.
359359
fileCache : true,
360360
})
361-
.fetch('GET', 'https://enag6ppx4ilaf.x.pipedream.net/', {
361+
.fetch('GET', 'https://bit.ly/2TtutbS', {
362362
'Content-Type' : 'multipart/form-data'
363363
}, "Hello World!")
364364
.then((res) => {

RNFetchBlobWin/windows/RNFetchBlobWin/RNFetchBlob.cpp

Lines changed: 176 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,94 @@ using namespace winrt::Windows::Foundation;
2020
using namespace winrt::Windows::Security::Cryptography;
2121
using namespace winrt::Windows::Security::Cryptography::Core;
2222

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+
23111

24112
void RNFetchBlob::Initialize(winrt::Microsoft::ReactNative::ReactContext const& reactContext) noexcept
25113
{
@@ -803,7 +891,7 @@ catch (const hresult_error& ex)
803891
winrt::fire_and_forget RNFetchBlob::lstat(
804892
std::string path,
805893
std::function<void(std::string, winrt::Microsoft::ReactNative::JSValueArray&)> callback) noexcept
806-
try
894+
try
807895
{
808896
std::filesystem::path directory(path);
809897
directory.make_preferred();
@@ -902,7 +990,7 @@ catch (const hresult_error& ex)
902990
// df - Implemented, not tested
903991
winrt::fire_and_forget RNFetchBlob::df(
904992
std::function<void(std::string, winrt::Microsoft::ReactNative::JSValueObject&)> callback) noexcept
905-
try
993+
try
906994
{
907995
auto localFolder{ Windows::Storage::ApplicationData::Current().LocalFolder() };
908996
auto properties{ co_await localFolder.Properties().RetrievePropertiesAsync({L"System.FreeSpace", L"System.Capacity"}) };
@@ -925,7 +1013,7 @@ winrt::fire_and_forget RNFetchBlob::slice(
9251013
uint32_t start,
9261014
uint32_t end,
9271015
winrt::Microsoft::ReactNative::ReactPromise<std::string> promise) noexcept
928-
try
1016+
try
9291017
{
9301018
winrt::hstring srcDirectoryPath, srcFileName, destDirectoryPath, destFileName;
9311019
splitPath(src, srcDirectoryPath, srcFileName);
@@ -962,25 +1050,64 @@ winrt::fire_and_forget RNFetchBlob::fetchBlob(
9621050
std::function<void(std::string, std::string, std::string)> callback) noexcept
9631051
{
9641052
//winrt::Windows::Web::Http::HttpMethod::Hea
965-
// Delete, Patch, Post, Put, Get, Options, Head
1053+
9661054
// Method
9671055
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();
9691067
config.fileCache = options["fileCache"].AsBoolean();
970-
config.followRedirect = options["followRedirect"].IsNull() ? true : options["followRedirect"].AsBoolean();
1068+
config.followRedirect = options["followRedirect"].AsBoolean();
9711069
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+
}
9731080
config.timeout = options["timeout"].AsInt32();
974-
config.trusty = options["trusty"].AsBoolean();;
1081+
config.trusty = options["trusty"].AsBoolean();
9751082

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+
9761101

9771102
winrt::Windows::Web::Http::HttpMethod httpMethod{ winrt::Windows::Web::Http::HttpMethod::Post() };
1103+
9781104
std::string methodUpperCase{ method };
9791105
for (auto& c : methodUpperCase)
9801106
{
9811107
toupper(c);
9821108
}
9831109

1110+
// Delete, Patch, Post, Put, Get, Options, Head
9841111
if (methodUpperCase.compare("DELETE") == 0)
9851112
{
9861113
httpMethod = winrt::Windows::Web::Http::HttpMethod::Delete();
@@ -1029,11 +1156,11 @@ winrt::fire_and_forget RNFetchBlob::fetchBlob(
10291156
winrt::Windows::Web::Http::HttpBufferContent content{ CryptographicBuffer::ConvertStringToBinary(winrt::to_hstring(body), BinaryStringEncoding::Utf8) };
10301157
requestMessage.Content(content);
10311158
}
1032-
1159+
//requestMessage.Content(requestContent);
10331160

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);
10351162

1036-
co_return;
1163+
co_await m_tasks.Add(taskId, ProcessRequestAsync(requestMessage, config, callback));
10371164
}
10381165

10391166
void RNFetchBlob::fetchBlobForm(
@@ -1070,9 +1197,15 @@ void RNFetchBlob::enableUploadProgressReport(
10701197
// cancelRequest
10711198
void RNFetchBlob::cancelRequest(
10721199
std::string taskId,
1073-
std::function<void(std::string)> callback) noexcept
1200+
std::function<void(std::string, std::string)> callback) noexcept
1201+
try
10741202
{
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()), "");
10761209
}
10771210

10781211
void RNFetchBlob::removeSession(
@@ -1116,7 +1249,37 @@ void RNFetchBlob::splitPath(const std::wstring& fullPath, winrt::hstring& direct
11161249
fileName = path.has_filename() ? winrt::to_hstring(path.filename().c_str()) : L"";
11171250
}
11181251

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 };
11191269

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+
}
11201283

11211284
RNFetchBlobStream::RNFetchBlobStream(Streams::IRandomAccessStream& _streamInstance, EncodingOptions _encoding) noexcept
11221285
: streamInstance{ std::move(_streamInstance) }

RNFetchBlobWin/windows/RNFetchBlobWin/RNFetchBlob.h

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,44 @@ namespace CryptographyCore = winrt::Windows::Security::Cryptography::Core;
1010

1111
enum struct EncodingOptions { UTF8, BASE64, ASCII, URI };
1212

13+
struct CancellationDisposable
14+
{
15+
CancellationDisposable() = default;
16+
CancellationDisposable(winrt::Windows::Foundation::IAsyncInfo const& async, std::function<void()>&& onCancel) noexcept;
17+
18+
CancellationDisposable(CancellationDisposable&& other) noexcept;
19+
CancellationDisposable& operator=(CancellationDisposable&& other) noexcept;
20+
21+
CancellationDisposable(CancellationDisposable const&) = delete;
22+
CancellationDisposable& operator=(CancellationDisposable const&) = delete;
23+
24+
~CancellationDisposable() noexcept;
25+
26+
void Cancel() noexcept;
27+
private:
28+
winrt::Windows::Foundation::IAsyncInfo m_async{ nullptr };
29+
std::function<void()> m_onCancel;
30+
};
31+
32+
struct TaskCancellationManager
33+
{
34+
using TaskId = std::string;
35+
36+
TaskCancellationManager() = default;
37+
~TaskCancellationManager() noexcept;
38+
39+
TaskCancellationManager(TaskCancellationManager const&) = delete;
40+
TaskCancellationManager& operator=(TaskCancellationManager const&) = delete;
41+
42+
winrt::Windows::Foundation::IAsyncAction Add(TaskId taskId, winrt::Windows::Foundation::IAsyncAction const& asyncAction) noexcept;
43+
void Cancel(TaskId taskId) noexcept;
44+
45+
private:
46+
std::mutex m_mutex; // to protect m_pendingTasks
47+
std::map<TaskId, CancellationDisposable> m_pendingTasks;
48+
};
49+
50+
1351
struct RNFetchBlobStream
1452
{
1553
public:
@@ -238,7 +276,7 @@ struct RNFetchBlob
238276
REACT_METHOD(cancelRequest);
239277
void cancelRequest(
240278
std::string taskId,
241-
std::function<void(std::string)> callback) noexcept;
279+
std::function<void(std::string, std::string)> callback) noexcept;
242280

243281
REACT_METHOD(removeSession);
244282
void removeSession(
@@ -258,6 +296,12 @@ struct RNFetchBlob
258296

259297
std::map<StreamId, RNFetchBlobStream> m_streamMap;
260298
winrt::Microsoft::ReactNative::ReactContext m_reactContext;
299+
TaskCancellationManager m_tasks;
300+
301+
winrt::Windows::Foundation::IAsyncAction ProcessRequestAsync(
302+
winrt::Windows::Web::Http::HttpRequestMessage& httpRequestMessage,
303+
const RNFetchBlobConfig& config,
304+
std::function<void(std::string, std::string, std::string)>& callback);
261305

262306
const std::map<std::string, std::function<CryptographyCore::HashAlgorithmProvider()>> availableHashes{
263307
{"md5", []() { return CryptographyCore::HashAlgorithmProvider::OpenAlgorithm(CryptographyCore::HashAlgorithmNames::Md5()); } },

0 commit comments

Comments
 (0)