Line data Source code
1 : /**
2 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "XLNetworkController.h"
24 : #include "SPNetworkContext.h"
25 : #include "XLNetworkRequest.h"
26 :
27 : namespace STAPPLER_VERSIONIZED stappler::xenolith::network {
28 :
29 0 : bool Handle::init(StringView url) {
30 0 : return NetworkHandle::init(Method::Get, url);
31 : }
32 :
33 0 : bool Handle::init(StringView url, FilePath fileName) {
34 0 : if (!init(Method::Get, url)) {
35 0 : return false;
36 : }
37 :
38 0 : if (!fileName.get().empty()) {
39 0 : setReceiveFile(fileName.get(), false);
40 : }
41 0 : return true;
42 : }
43 :
44 21 : bool Handle::init(Method method, StringView url) {
45 21 : return NetworkHandle::init(method, url);
46 : }
47 :
48 21 : bool Handle::prepare(Context *ctx) {
49 21 : if (_mtime > 0) {
50 0 : auto httpTime = Time::microseconds(_mtime).toHttp<Interface>();
51 0 : ctx->headers = curl_slist_append(ctx->headers, toString("If-Modified-Since: ", httpTime).data());
52 0 : }
53 :
54 21 : if (!_etag.empty()) {
55 0 : ctx->headers = curl_slist_append(ctx->headers, toString("If-None-Match: ", _etag).data());
56 : }
57 :
58 21 : if (!_sharegroup.empty() && ctx->share) {
59 0 : setCookieFile(filesystem::writablePath<Interface>(toString("network.", _controller->getName(), ".", _sharegroup, ".cookies")));
60 : }
61 :
62 21 : if (!_controller->getApplication()->getInfo().userAgent.empty()) {
63 21 : setUserAgent(_controller->getApplication()->getInfo().userAgent);
64 : }
65 :
66 21 : return true;
67 : }
68 :
69 21 : bool Handle::finalize(Context *ctx, bool ret) {
70 21 : _success = ctx->success;
71 :
72 21 : if (getResponseCode() < 300) {
73 21 : auto httpTime = getReceivedHeaderString("Last-Modified");
74 21 : auto eTag = getReceivedHeaderString("ETag");
75 :
76 21 : _mtime = Time::fromHttp(httpTime).toMicroseconds();
77 21 : _etag = eTag.str<Interface>();
78 : } else {
79 0 : auto &f = getReceiveDataSource();
80 0 : if (auto str = std::get_if<String>(&f)) {
81 0 : filesystem::remove(*str);
82 : }
83 : }
84 :
85 21 : return ret;
86 : }
87 :
88 42 : Request::~Request() { }
89 :
90 21 : bool Request::init(const Callback<bool(Handle &)> &setupCallback, Rc<Ref> &&ref) {
91 21 : _owner = move(ref);
92 21 : return setupCallback(_handle);
93 : }
94 :
95 0 : void Request::perform(Application *app, CompleteCallback &&cb) {
96 0 : auto c = app->getExtension<Controller>();
97 0 : if (!c) {
98 0 : return;
99 : }
100 :
101 0 : perform(c, move(cb));
102 : }
103 :
104 21 : void Request::perform(Controller *c, CompleteCallback &&cb) {
105 21 : if (cb) {
106 21 : _onComplete = move(cb);
107 : }
108 :
109 21 : _handle._request = this;
110 21 : _handle._controller = c;
111 :
112 21 : _uploadProgress = pair(0, 0);
113 21 : _downloadProgress = pair(0, 0);
114 :
115 21 : auto &source = _handle.getReceiveDataSource();
116 21 : if (std::holds_alternative<std::monostate>(source)) {
117 0 : if (!_ignoreResponseData) {
118 0 : _targetHeaderCallback = _handle.getHeaderCallback();
119 :
120 0 : _handle.setHeaderCallback([this] (StringView key, StringView value) {
121 0 : handleHeader(key, value);
122 0 : });
123 :
124 0 : _handle.setReceiveCallback([this] (char *buf, size_t size) -> size_t {
125 0 : return handleReceive(buf, size);
126 : });
127 0 : _handle.setVerifyTls(false);
128 : }
129 : }
130 21 : c->run(this);
131 21 : }
132 :
133 0 : void Request::setIgnoreResponseData(bool value) {
134 0 : if (!_running) {
135 0 : _ignoreResponseData = value;
136 : }
137 0 : }
138 :
139 0 : void Request::setUploadProgress(ProgressCallback &&cb) {
140 0 : _onUploadProgress = move(cb);
141 0 : }
142 :
143 21 : void Request::setDownloadProgress(ProgressCallback &&cb) {
144 21 : _onDownloadProgress = move(cb);
145 21 : }
146 :
147 0 : void Request::handleHeader(StringView key, StringView value) {
148 0 : if (!_ignoreResponseData) {
149 0 : if (key == "content-length") {
150 0 : auto length = value.readInteger(10).get(0);
151 0 : _data.resize(size_t(length));
152 : }
153 : }
154 0 : if (_targetHeaderCallback) {
155 0 : _targetHeaderCallback(key, value);
156 : }
157 0 : }
158 :
159 0 : size_t Request::handleReceive(char *buf, size_t nbytes) {
160 0 : if (_data.size() < (nbytes + _nbytes)) {
161 0 : _data.resize(nbytes + _nbytes);
162 : }
163 0 : memcpy(_data.data() + _nbytes, buf, nbytes);
164 0 : _nbytes += nbytes;
165 0 : return nbytes;
166 : }
167 :
168 21 : void Request::notifyOnComplete(bool success) {
169 21 : if (_onComplete) {
170 21 : _onComplete(*this, success);
171 : }
172 21 : _running = false;
173 21 : _handle._request = nullptr;
174 21 : }
175 :
176 0 : void Request::notifyOnUploadProgress(int64_t total, int64_t now) {
177 0 : _uploadProgress = pair(total, std::max(now, _uploadProgress.second)); // prevent out-of-order updates
178 0 : if (_onUploadProgress && _uploadProgress.second == now) {
179 0 : _onUploadProgress(*this, total, now);
180 : }
181 0 : }
182 :
183 21 : void Request::notifyOnDownloadProgress(int64_t total, int64_t now) {
184 21 : _downloadProgress = pair(total, std::max(now, _downloadProgress.second)); // prevent out-of-order updates
185 21 : if (_onDownloadProgress && _downloadProgress.second == now) {
186 21 : _onDownloadProgress(*this, total, now);
187 : }
188 21 : }
189 :
190 : }
|