Line data Source code
1 : /**
2 : Copyright (c) 2016-2021 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "SPNetworkHandle.h"
25 :
26 : #include "SPString.h"
27 : #include "SPStringView.h"
28 : #include "SPLog.h"
29 : #include "SPValid.h"
30 : #include "SPCrypto.h"
31 : #include "SPFilesystem.h"
32 : #include "SPNetworkContext.h"
33 :
34 : #if MODULE_STAPPLER_BITMAP
35 : #include "SPBitmap.h"
36 : #endif
37 :
38 : #include "curl/curl.h"
39 :
40 : namespace STAPPLER_VERSIONIZED stappler::network {
41 :
42 : SPUNUSED static CURL *CurlHandle_alloc();
43 : SPUNUSED static void CurlHandle_release(CURL *curl);
44 :
45 : template <>
46 0 : bool Handle<memory::PoolInterface>::init(Method method, StringView url) {
47 0 : if (url.size() == 0 || method == Method::Unknown) {
48 0 : return false;
49 : }
50 :
51 0 : send.url = url.str<memory::PoolInterface>();
52 0 : send.method = method;
53 0 : return true;
54 : }
55 :
56 : template <>
57 3025 : bool Handle<memory::StandartInterface>::init(Method method, StringView url) {
58 3025 : if (url.size() == 0 || method == Method::Unknown) {
59 0 : return false;
60 : }
61 :
62 3025 : send.url = url.str<memory::StandartInterface>();
63 3025 : send.method = method;
64 3025 : return true;
65 : }
66 :
67 : template <>
68 0 : bool Handle<memory::PoolInterface>::perform() {
69 0 : return network::perform(*this, nullptr, nullptr);
70 : }
71 :
72 : template <>
73 3000 : bool Handle<memory::StandartInterface>::perform() {
74 3000 : return network::perform(*this, nullptr, nullptr);
75 : }
76 :
77 : template <>
78 0 : bool MultiHandle<memory::PoolInterface>::perform(const Callback<bool(Handle<memory::PoolInterface> *, RefBase<memory::PoolInterface> *)> &cb) {
79 0 : auto m = curl_multi_init();
80 0 : memory::PoolInterface::MapType<CURL *, Context<memory::PoolInterface>> handles;
81 :
82 0 : auto initPending = [&, this] {
83 0 : for (auto &it : pending) {
84 0 : auto h = CurlHandle_alloc();
85 0 : auto i = handles.emplace(h, Context<memory::PoolInterface>()).first;
86 0 : i->second.userdata = it.second;
87 0 : i->second.curl = h;
88 0 : i->second.origHandle = it.first;
89 0 : network::prepare(*it.first->getData(), &i->second, nullptr);
90 :
91 0 : curl_multi_add_handle(m, h);
92 : }
93 0 : auto s = pending.size();
94 0 : pending.clear();
95 0 : return s;
96 0 : };
97 :
98 0 : auto cancel = [&] {
99 0 : for (auto &it : handles) {
100 0 : curl_multi_remove_handle(m, it.first);
101 0 : it.second.code = CURLE_FAILED_INIT;
102 0 : network::finalize(*it.second.handle, &it.second, nullptr);
103 :
104 0 : CurlHandle_release(it.first);
105 : }
106 0 : curl_multi_cleanup(m);
107 0 : };
108 :
109 0 : handles.reserve(pending.size()); // mem_pool has non-std map::reserve
110 :
111 0 : int running = initPending();
112 : do {
113 0 : auto err = curl_multi_perform(m, &running);
114 0 : if (err != CURLM_OK) {
115 0 : log::error("CURL", "Fail to perform multi: ", err);
116 0 : return false;
117 : }
118 :
119 0 : if (running > 0) {
120 0 : err = curl_multi_poll(m, NULL, 0, 1000, nullptr);
121 0 : if (err != CURLM_OK) {
122 0 : log::error("CURL", "Fail to poll multi: ", err);
123 0 : return false;
124 : }
125 : }
126 :
127 0 : struct CURLMsg *msg = nullptr;
128 : do {
129 0 : int msgq = 0;
130 0 : msg = curl_multi_info_read(m, &msgq);
131 0 : if (msg && (msg->msg == CURLMSG_DONE)) {
132 0 : CURL *e = msg->easy_handle;
133 0 : curl_multi_remove_handle(m, e);
134 :
135 0 : auto it = handles.find(e);
136 0 : if (it != handles.end()) {
137 0 : it->second.code = msg->data.result;
138 0 : network::finalize(*it->second.handle, &it->second, nullptr);
139 0 : if (cb) {
140 0 : if (!cb(it->second.origHandle, it->second.userdata)) {
141 0 : handles.erase(it);
142 0 : cancel();
143 0 : return false;
144 : }
145 : }
146 0 : handles.erase(it);
147 : }
148 :
149 0 : CurlHandle_release(e);
150 : }
151 0 : } while (msg);
152 :
153 0 : running += initPending();
154 0 : } while (running > 0);
155 :
156 0 : curl_multi_cleanup(m);
157 0 : return true;
158 0 : }
159 :
160 : template <>
161 0 : bool MultiHandle<memory::StandartInterface>::perform(const Callback<bool(Handle<memory::StandartInterface> *, RefBase<memory::StandartInterface> *)> &cb) {
162 0 : auto m = curl_multi_init();
163 0 : memory::StandartInterface::MapType<CURL *, Context<memory::StandartInterface>> handles;
164 :
165 0 : auto initPending = [&, this] {
166 0 : for (auto &it : pending) {
167 0 : auto h = CurlHandle_alloc();
168 0 : auto i = handles.emplace(h, Context<memory::StandartInterface>()).first;
169 0 : i->second.userdata = it.second;
170 0 : i->second.curl = h;
171 0 : i->second.origHandle = it.first;
172 0 : network::prepare(*it.first->getData(), &i->second, nullptr);
173 :
174 0 : curl_multi_add_handle(m, h);
175 : }
176 0 : auto s = pending.size();
177 0 : pending.clear();
178 0 : return s;
179 0 : };
180 :
181 0 : auto cancel = [&] {
182 0 : for (auto &it : handles) {
183 0 : curl_multi_remove_handle(m, it.first);
184 0 : it.second.code = CURLE_FAILED_INIT;
185 0 : network::finalize(*it.second.handle, &it.second, nullptr);
186 :
187 0 : CurlHandle_release(it.first);
188 : }
189 0 : curl_multi_cleanup(m);
190 0 : };
191 :
192 : // handles.reserve(pending.size());
193 :
194 0 : int running = initPending();
195 : do {
196 0 : auto err = curl_multi_perform(m, &running);
197 0 : if (err != CURLM_OK) {
198 0 : log::error("CURL", string::toString<memory::StandartInterface>("Fail to perform multi: ", err));
199 0 : return false;
200 : }
201 :
202 0 : if (running > 0) {
203 0 : err = curl_multi_poll(m, NULL, 0, 1000, nullptr);
204 0 : if (err != CURLM_OK) {
205 0 : log::error("CURL", string::toString<memory::StandartInterface>("Fail to poll multi: ", err));
206 0 : return false;
207 : }
208 : }
209 :
210 0 : struct CURLMsg *msg = nullptr;
211 : do {
212 0 : int msgq = 0;
213 0 : msg = curl_multi_info_read(m, &msgq);
214 0 : if (msg && (msg->msg == CURLMSG_DONE)) {
215 0 : CURL *e = msg->easy_handle;
216 0 : curl_multi_remove_handle(m, e);
217 :
218 0 : auto it = handles.find(e);
219 0 : if (it != handles.end()) {
220 0 : it->second.code = msg->data.result;
221 0 : network::finalize(*it->second.handle, &it->second, nullptr);
222 0 : if (cb) {
223 0 : if (!cb(it->second.origHandle, it->second.userdata)) {
224 0 : handles.erase(it);
225 0 : cancel();
226 0 : return false;
227 : }
228 : }
229 0 : handles.erase(it);
230 : }
231 :
232 0 : CurlHandle_release(e);
233 : }
234 0 : } while (msg);
235 :
236 0 : running += initPending();
237 0 : } while (running > 0);
238 :
239 0 : curl_multi_cleanup(m);
240 0 : return true;
241 0 : }
242 :
243 : }
|