1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "net_http_client_exec.h"
17
18 #include <cstddef>
19 #include <cstring>
20 #include <memory>
21 #include <thread>
22 #include <unistd.h>
23 #include <pthread.h>
24 #ifdef HTTP_MULTIPATH_CERT_ENABLE
25 #include <openssl/ssl.h>
26 #endif
27 #ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE
28 #ifndef HTTP_MULTIPATH_CERT_ENABLE
29 #include <openssl/ssl.h>
30 #endif
31 #include <openssl/pem.h>
32 #include <openssl/sha.h>
33 #include <openssl/x509.h>
34 #endif
35 #if HAS_NETMANAGER_BASE
36 #include <netdb.h>
37 #endif
38
39 #ifdef HTTP_PROXY_ENABLE
40 #include "parameter.h"
41 #endif
42 #ifdef HAS_NETMANAGER_BASE
43 #include "http_proxy.h"
44 #include "net_conn_client.h"
45 #endif
46
47 #include "net_http_utils.h"
48 #include "net_http_cache_proxy.h"
49 #include "constant.h"
50 #include "netstack_common_utils.h"
51 #include "netstack_log.h"
52 #include "securec.h"
53
54 #define NETSTACK_CURL_EASY_SET_OPTION(handle, opt, data, asyncContext) \
55 do { \
56 CURLcode result = curl_easy_setopt(handle, opt, data); \
57 if (result != CURLE_OK) { \
58 const char *err = curl_easy_strerror(result); \
59 NETSTACK_LOGE("Failed to set option: %{public}s, %{public}s %{public}d", #opt, err, result); \
60 (asyncContext)->SetErrorCode(result); \
61 return false; \
62 } \
63 } while (0)
64
65 namespace OHOS::NetStack::Http {
66 static constexpr int CURL_TIMEOUT_MS = 50;
67 static constexpr int CONDITION_TIMEOUT_S = 3600;
68 static constexpr int CURL_MAX_WAIT_MSECS = 10;
69 static constexpr int CURL_HANDLE_NUM = 10;
70 static constexpr const char *TLS12_SECURITY_CIPHER_SUITE = R"(DEFAULT:!eNULL:!EXPORT)";
71 static constexpr int NETSTACK_NAPI_INTERNAL_ERROR = 2300002;
72
73 #ifdef HTTP_MULTIPATH_CERT_ENABLE
74 static constexpr const int32_t UID_TRANSFORM_DIVISOR = 200000;
75 static constexpr const char *BASE_PATH = "/data/certificates/user_cacerts/";
76 static constexpr const char *USER_CERT_ROOT_PATH = "/data/certificates/user_cacerts/0/";
77 static constexpr int32_t SYSPARA_MAX_SIZE = 128;
78 static constexpr const char *DEFAULT_HTTP_PROXY_HOST = "NONE";
79 static constexpr const char *DEFAULT_HTTP_PROXY_PORT = "0";
80 static constexpr const char *DEFAULT_HTTP_PROXY_EXCLUSION_LIST = "NONE";
81 static constexpr const char *HTTP_PROXY_HOST_KEY = "persist.netmanager_base.http_proxy.host";
82 static constexpr const char *HTTP_PROXY_PORT_KEY = "persist.netmanager_base.http_proxy.port";
83 static constexpr const char *HTTP_PROXY_EXCLUSIONS_KEY = "persist.netmanager_base.http_proxy.exclusion_list";
84 #endif
85
86 #ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE
87 static constexpr const int SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX = 1;
88 #endif
89
AddCurlHandle(CURL * handle,RequestContext * context)90 bool NetHttpClientExec::AddCurlHandle(CURL *handle, RequestContext *context)
91 {
92 if (handle == nullptr || staticVariable_.curlMulti == nullptr) {
93 NETSTACK_LOGE("handle nullptr");
94 return false;
95 }
96
97 std::thread([context, handle] {
98 std::lock_guard guard(staticVariable_.curlMultiMutex);
99 // Do SetServerSSLCertOption here to avoid blocking the main thread.
100 SetServerSSLCertOption(handle, context);
101 staticVariable_.infoQueue.emplace(context, handle);
102 staticVariable_.conditionVariable.notify_all();
103 {
104 std::lock_guard lockGuard(staticContextSet_.mutexForContextVec);
105 NetHttpClientExec::staticContextSet_.contextSet.emplace(context);
106 }
107 }).detach();
108
109 return true;
110 }
111
112 NetHttpClientExec::StaticVariable NetHttpClientExec::staticVariable_; /* NOLINT */
113 NetHttpClientExec::StaticContextVec NetHttpClientExec::staticContextSet_;
114
ExecRequest(RequestContext * context)115 void NetHttpClientExec::ExecRequest(RequestContext *context)
116 {
117 if (!CommonUtils::HasInternetPermission()) {
118 context->SetPermissionDenied(true);
119 return;
120 }
121 context->options.SetRequestTime(GetNowTimeGMT());
122 CacheProxy proxy(context->options);
123 if (context->IsUsingCache() && proxy.ReadResponseFromCache(context)) {
124 return;
125 }
126 if (!RequestWithoutCache(context)) {
127 context->SetErrorCode(NETSTACK_NAPI_INTERNAL_ERROR);
128 context->SendResponse();
129 delete context;
130 context = nullptr;
131 }
132 }
133
RequestWithoutCache(RequestContext * context)134 bool NetHttpClientExec::RequestWithoutCache(RequestContext *context)
135 {
136 if (!staticVariable_.initialized) {
137 NETSTACK_LOGE("curl not init");
138 return false;
139 }
140
141 auto handle = curl_easy_init();
142 if (!handle) {
143 NETSTACK_LOGE("Failed to create fetch task");
144 return false;
145 }
146
147 std::vector<std::string> vec;
148 std::for_each(context->options.GetHeader().begin(), context->options.GetHeader().end(),
149 [&vec](const std::pair<std::string, std::string> &p) {
150 if (!p.second.empty()) {
151 vec.emplace_back(p.first + HTTP_HEADER_SEPARATOR + p.second);
152 } else {
153 vec.emplace_back(p.first + HTTP_HEADER_BLANK_SEPARATOR);
154 }
155 });
156 context->SetCurlHeaderList(MakeHeaders(vec));
157
158 if (!SetOption(handle, context, context->GetCurlHeaderList())) {
159 NETSTACK_LOGE("set option failed");
160 return false;
161 }
162
163 context->response.SetRequestTime(GetNowTimeGMT());
164
165 if (!AddCurlHandle(handle, context)) {
166 NETSTACK_LOGE("add handle failed");
167 return false;
168 }
169
170 return true;
171 }
172
GetCurlDataFromHandle(CURL * handle,RequestContext * context,CURLMSG curlMsg,CURLcode result)173 bool NetHttpClientExec::GetCurlDataFromHandle(CURL *handle, RequestContext *context, CURLMSG curlMsg, CURLcode result)
174 {
175 if (curlMsg != CURLMSG_DONE) {
176 NETSTACK_LOGE("CURLMSG %{public}s", std::to_string(curlMsg).c_str());
177 context->SetErrorCode(NETSTACK_NAPI_INTERNAL_ERROR);
178 return false;
179 }
180
181 if (result != CURLE_OK) {
182 context->SetErrorCode(result);
183 NETSTACK_LOGE("CURLcode result %{public}s", std::to_string(result).c_str());
184 return false;
185 }
186
187 context->response.SetResponseTime(GetNowTimeGMT());
188
189 int64_t responseCode;
190 CURLcode code = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
191 if (code != CURLE_OK) {
192 context->SetErrorCode(code);
193 return false;
194 }
195 context->response.SetResponseCode(responseCode);
196 if (context->response.GetResponseCode() == static_cast<uint32_t>(ResponseCode::NOT_MODIFIED)) {
197 NETSTACK_LOGI("cache is NOT_MODIFIED, we use the cache");
198 context->SetResponseByCache();
199 return true;
200 }
201 NETSTACK_LOGI("responseCode is %{public}s", std::to_string(responseCode).c_str());
202
203 struct curl_slist *cookies = nullptr;
204 code = curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &cookies);
205 if (code != CURLE_OK) {
206 context->SetErrorCode(code);
207 return false;
208 }
209
210 std::unique_ptr<struct curl_slist, decltype(&curl_slist_free_all)> cookiesHandle(cookies, curl_slist_free_all);
211 while (cookies) {
212 context->response.AppendCookies(cookies->data, strlen(cookies->data));
213 if (cookies->next != nullptr) {
214 context->response.AppendCookies(HTTP_LINE_SEPARATOR,
215 strlen(HTTP_LINE_SEPARATOR));
216 }
217 cookies = cookies->next;
218 }
219 return true;
220 }
221
GetTimingFromCurl(CURL * handle,CURLINFO info)222 double NetHttpClientExec::GetTimingFromCurl(CURL *handle, CURLINFO info)
223 {
224 time_t timing;
225 CURLcode result = curl_easy_getinfo(handle, info, &timing);
226 if (result != CURLE_OK) {
227 NETSTACK_LOGE("Failed to get timing: %{public}d, %{public}s", info, curl_easy_strerror(result));
228 return 0;
229 }
230 return TimeUtils::Microseconds2Milliseconds(timing);
231 }
232
CacheCurlPerformanceTiming(CURL * handle,RequestContext * context)233 void NetHttpClientExec::CacheCurlPerformanceTiming(CURL* handle, RequestContext* context)
234 {
235 context->CachePerformanceTimingItem(
236 RESPONSE_DNS_TIMING, NetHttpClientExec::GetTimingFromCurl(handle, CURLINFO_NAMELOOKUP_TIME_T));
237 context->CachePerformanceTimingItem(
238 RESPONSE_TCP_TIMING, NetHttpClientExec::GetTimingFromCurl(handle, CURLINFO_CONNECT_TIME_T));
239 context->CachePerformanceTimingItem(
240 RESPONSE_TLS_TIMING, NetHttpClientExec::GetTimingFromCurl(handle, CURLINFO_APPCONNECT_TIME_T));
241 context->CachePerformanceTimingItem(
242 RESPONSE_FIRST_SEND_TIMING, NetHttpClientExec::GetTimingFromCurl(handle, CURLINFO_PRETRANSFER_TIME_T));
243 context->CachePerformanceTimingItem(RESPONSE_FIRST_RECEIVE_TIMING,
244 NetHttpClientExec::GetTimingFromCurl(handle, CURLINFO_STARTTRANSFER_TIME_T));
245 context->CachePerformanceTimingItem(
246 RESPONSE_TOTAL_FINISH_TIMING, NetHttpClientExec::GetTimingFromCurl(handle, CURLINFO_TOTAL_TIME_T));
247 context->CachePerformanceTimingItem(
248 RESPONSE_REDIRECT_TIMING, NetHttpClientExec::GetTimingFromCurl(handle, CURLINFO_REDIRECT_TIME_T));
249 }
250
HandleCurlData(CURLMsg * msg)251 void NetHttpClientExec::HandleCurlData(CURLMsg *msg)
252 {
253 if (msg == nullptr) {
254 return;
255 }
256
257 auto handle = msg->easy_handle;
258 if (handle == nullptr) {
259 return;
260 }
261
262 auto it = staticVariable_.contextMap.find(handle);
263 if (it == staticVariable_.contextMap.end()) {
264 NETSTACK_LOGE("can not find context");
265 return;
266 }
267
268 auto context = it->second;
269 staticVariable_.contextMap.erase(it);
270 if (context == nullptr) {
271 NETSTACK_LOGE("can not find context");
272 return;
273 }
274 NETSTACK_LOGI("priority = %{public}d", context->options.GetPriority());
275 context->SetExecOK(GetCurlDataFromHandle(handle, context, msg->msg, msg->data.result));
276 CacheCurlPerformanceTiming(handle, context);
277 if (context->IsExecOK()) {
278 CacheProxy proxy(context->options);
279 proxy.WriteResponseToCache(context->response);
280 }
281 size_t callbackSize = 0;
282 if (context->IsRequestInStream() && context->streamingCallback != nullptr) {
283 callbackSize = context->streamingCallback->dataEnd.size();
284 }
285 // call onDataEnd
286 if (callbackSize > 0) {
287 for (size_t i = 0; i < callbackSize; i++) {
288 context->streamingCallback->dataEnd[i]();
289 }
290 }
291 context->SendResponse();
292 delete context;
293 context = nullptr;
294 }
295
MakeUrl(const std::string & url,std::string param,const std::string & extraParam)296 std::string NetHttpClientExec::MakeUrl(const std::string &url, std::string param, const std::string &extraParam)
297 {
298 if (param.empty()) {
299 param += extraParam;
300 } else {
301 param += HTTP_URL_PARAM_SEPARATOR;
302 param += extraParam;
303 }
304
305 if (param.empty()) {
306 return url;
307 }
308
309 return url + HTTP_URL_PARAM_START + param;
310 }
311
312
MethodForGet(const std::string & method)313 bool NetHttpClientExec::MethodForGet(const std::string &method)
314 {
315 return (method == HTTP_METHOD_HEAD || method == HTTP_METHOD_OPTIONS ||
316 method == HTTP_METHOD_TRACE || method == HTTP_METHOD_GET ||
317 method == HTTP_METHOD_CONNECT);
318 }
319
MethodForPost(const std::string & method)320 bool NetHttpClientExec::MethodForPost(const std::string &method)
321 {
322 return (method == HTTP_METHOD_POST || method == HTTP_METHOD_PUT ||
323 method == HTTP_METHOD_DELETE);
324 }
325
EncodeUrlParam(std::string & str)326 bool NetHttpClientExec::EncodeUrlParam(std::string &str)
327 {
328 char encoded[4];
329 std::string encodeOut;
330 size_t length = strlen(str.c_str());
331 for (size_t i = 0; i < length; ++i) {
332 auto c = static_cast<uint8_t>(str.c_str()[i]);
333 if (IsUnReserved(c)) {
334 encodeOut += static_cast<char>(c);
335 } else {
336 if (sprintf_s(encoded, sizeof(encoded), "%%%02X", c) < 0) {
337 return false;
338 }
339 encodeOut += encoded;
340 }
341 }
342
343 if (str == encodeOut) {
344 return false;
345 }
346 str = encodeOut;
347 return true;
348 }
349
AddRequestInfo()350 void NetHttpClientExec::AddRequestInfo()
351 {
352 std::lock_guard guard(staticVariable_.curlMultiMutex);
353 int num = 0;
354 while (!staticVariable_.infoQueue.empty()) {
355 if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
356 break;
357 }
358
359 auto info = staticVariable_.infoQueue.top();
360 staticVariable_.infoQueue.pop();
361 auto ret = curl_multi_add_handle(staticVariable_.curlMulti, info.handle);
362 if (ret == CURLM_OK) {
363 staticVariable_.contextMap[info.handle] = info.context;
364 }
365
366 ++num;
367 if (num >= CURL_HANDLE_NUM) {
368 break;
369 }
370 }
371 }
372
IsContextDeleted(RequestContext * context)373 bool NetHttpClientExec::IsContextDeleted(RequestContext *context)
374 {
375 if (context == nullptr) {
376 return true;
377 }
378 {
379 std::lock_guard<std::mutex> lockGuard(NetHttpClientExec::staticContextSet_.mutexForContextVec);
380 auto it = std::find(NetHttpClientExec::staticContextSet_.contextSet.begin(),
381 NetHttpClientExec::staticContextSet_.contextSet.end(), context);
382 if (it == NetHttpClientExec::staticContextSet_.contextSet.end()) {
383 NETSTACK_LOGI("context has been deleted in libuv thread");
384 return true;
385 }
386 }
387 return false;
388 }
389
RunThread()390 void NetHttpClientExec::RunThread()
391 {
392 while (staticVariable_.runThread && staticVariable_.curlMulti != nullptr) {
393 AddRequestInfo();
394 SendRequest();
395 ReadResponse();
396 std::this_thread::sleep_for(std::chrono::milliseconds(CURL_TIMEOUT_MS));
397 std::unique_lock l(staticVariable_.curlMultiMutex);
398 staticVariable_.conditionVariable.wait_for(l, std::chrono::seconds(CONDITION_TIMEOUT_S), [] {
399 return !staticVariable_.infoQueue.empty() || !staticVariable_.contextMap.empty();
400 });
401 }
402 }
403
SendRequest()404 void NetHttpClientExec::SendRequest()
405 {
406 std::lock_guard guard(staticVariable_.curlMultiMutex);
407
408 int runningHandle = 0;
409 int num = 0;
410 do {
411 if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
412 break;
413 }
414
415 auto ret = curl_multi_perform(staticVariable_.curlMulti, &runningHandle);
416
417 if (runningHandle > 0) {
418 ret = curl_multi_poll(staticVariable_.curlMulti, nullptr, 0, CURL_MAX_WAIT_MSECS, nullptr);
419 }
420
421 if (ret != CURLM_OK) {
422 return;
423 }
424
425 ++num;
426 if (num >= CURL_HANDLE_NUM) {
427 break;
428 }
429 } while (runningHandle > 0);
430 }
431
ReadResponse()432 void NetHttpClientExec::ReadResponse()
433 {
434 std::lock_guard guard(staticVariable_.curlMultiMutex);
435 CURLMsg *msg = nullptr; /* NOLINT */
436 do {
437 if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
438 break;
439 }
440
441 int leftMsg;
442 msg = curl_multi_info_read(staticVariable_.curlMulti, &leftMsg);
443 if (msg) {
444 if (msg->msg == CURLMSG_DONE) {
445 HandleCurlData(msg);
446 }
447 if (msg->easy_handle) {
448 (void)curl_multi_remove_handle(staticVariable_.curlMulti, msg->easy_handle);
449 (void)curl_easy_cleanup(msg->easy_handle);
450 }
451 }
452 } while (msg);
453 }
454
GetGlobalHttpProxyInfo(std::string & host,int32_t & port,std::string & exclusions)455 void NetHttpClientExec::GetGlobalHttpProxyInfo(std::string &host, int32_t &port, std::string &exclusions)
456 {
457 #ifdef HTTP_PROXY_ENABLE
458 char httpProxyHost[SYSPARA_MAX_SIZE] = {0};
459 char httpProxyPort[SYSPARA_MAX_SIZE] = {0};
460 char httpProxyExclusions[SYSPARA_MAX_SIZE] = {0};
461 GetParameter(HTTP_PROXY_HOST_KEY, DEFAULT_HTTP_PROXY_HOST, httpProxyHost, sizeof(httpProxyHost));
462 GetParameter(HTTP_PROXY_PORT_KEY, DEFAULT_HTTP_PROXY_PORT, httpProxyPort, sizeof(httpProxyPort));
463 GetParameter(HTTP_PROXY_EXCLUSIONS_KEY, DEFAULT_HTTP_PROXY_EXCLUSION_LIST, httpProxyExclusions,
464 sizeof(httpProxyExclusions));
465
466 host = Decode(httpProxyHost);
467 if (host == DEFAULT_HTTP_PROXY_HOST) {
468 host = std::string();
469 }
470 exclusions = httpProxyExclusions;
471 if (exclusions == DEFAULT_HTTP_PROXY_EXCLUSION_LIST) {
472 exclusions = std::string();
473 }
474
475 port = std::atoi(httpProxyPort);
476 #endif
477 }
478
GetHttpProxyInfo(RequestContext * context,std::string & host,int32_t & port,std::string & exclusions)479 void NetHttpClientExec::GetHttpProxyInfo(RequestContext *context, std::string &host,
480 int32_t &port, std::string &exclusions)
481 {
482 if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_DEFAULT) {
483 #ifdef HAS_NETMANAGER_BASE
484 using namespace NetManagerStandard;
485 HttpProxy httpProxy;
486 NetConnClient::GetInstance().GetDefaultHttpProxy(httpProxy);
487 host = httpProxy.GetHost();
488 port = httpProxy.GetPort();
489 exclusions = CommonUtils::ToString(httpProxy.GetExclusionList());
490 #else
491 GetGlobalHttpProxyInfo(host, port, exclusions);
492 #endif
493 } else if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_SPECIFIED) {
494 context->options.GetSpecifiedHttpProxy(host, port, exclusions);
495 }
496 }
497
Initialize()498 bool NetHttpClientExec::Initialize()
499 {
500 std::lock_guard<std::mutex> lock(staticVariable_.mutexForInitialize);
501 if (staticVariable_.initialized) {
502 return true;
503 }
504 NETSTACK_LOGI("call curl_global_init");
505 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
506 NETSTACK_LOGE("Failed to initialize 'curl'");
507 return false;
508 }
509
510 staticVariable_.curlMulti = curl_multi_init();
511 if (staticVariable_.curlMulti == nullptr) {
512 NETSTACK_LOGE("Failed to initialize 'curl_multi'");
513 return false;
514 }
515
516 staticVariable_.workThread = std::thread(RunThread);
517
518 staticVariable_.initialized = true;
519 return staticVariable_.initialized;
520 }
521
SetOtherOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)522 bool NetHttpClientExec::SetOtherOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
523 {
524 std::string url = context->options.GetUrl();
525 std::string host;
526 std::string exclusions;
527 int32_t port = 0;
528 GetHttpProxyInfo(context, host, port, exclusions);
529 if (!host.empty() && !CommonUtils::IsHostNameExcluded(url, exclusions, ",")) {
530 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXY, host.c_str(), context);
531 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYPORT, port, context);
532 auto curlTunnelValue = (url.find("https://") != std::string::npos) ? 1L : 0L;
533 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPPROXYTUNNEL, curlTunnelValue, context);
534 auto proxyType = (host.find("https://") != std::string::npos) ? CURLPROXY_HTTPS : CURLPROXY_HTTP;
535 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYTYPE, proxyType, context);
536 }
537 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2, context);
538 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CIPHER_LIST, TLS12_SECURITY_CIPHER_SUITE, context);
539 #ifdef NETSTACK_PROXY_PASS
540 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYUSERPWD, NETSTACK_PROXY_PASS, context);
541 #endif // NETSTACK_PROXY_PASS
542
543 #ifdef HTTP_CURL_PRINT_VERBOSE
544 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_VERBOSE, 1L, context);
545 #endif
546
547 #ifndef WINDOWS_PLATFORM
548 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_ACCEPT_ENCODING, "", context);
549 #endif
550 return true;
551 }
552
MultiPathSslCtxFunction(CURL * curl,void * sslCtx,const CertsPath * certsPath)553 CURLcode MultiPathSslCtxFunction(CURL *curl, void *sslCtx, const CertsPath *certsPath)
554 {
555 #ifdef HTTP_MULTIPATH_CERT_ENABLE
556 if (certsPath == nullptr) {
557 NETSTACK_LOGE("certsPath is null");
558 return CURLE_SSL_CERTPROBLEM;
559 }
560 if (sslCtx == nullptr) {
561 NETSTACK_LOGE("ssl_ctx is null");
562 return CURLE_SSL_CERTPROBLEM;
563 }
564
565 for (const auto &path : certsPath->certPathList) {
566 if (path.empty() || access(path.c_str(), F_OK) != 0) {
567 NETSTACK_LOGD("certificate directory path is not exist");
568 continue;
569 }
570 if (!SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(sslCtx), nullptr, path.c_str())) {
571 NETSTACK_LOGE("loading certificates from directory error.");
572 continue;
573 }
574 }
575 if (access(certsPath->certFile.c_str(), F_OK) != 0) {
576 NETSTACK_LOGD("certificate directory path is not exist");
577 } else if (!SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(sslCtx), certsPath->certFile.c_str(), nullptr)) {
578 NETSTACK_LOGE("loading certificates from context cert error.");
579 }
580 #endif // HTTP_MULTIPATH_CERT_ENABLE
581 return CURLE_OK;
582 }
583
584 #ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE
VerifyCertPubkey(X509 * cert,const std::string & pinnedPubkey)585 static int VerifyCertPubkey(X509 *cert, const std::string &pinnedPubkey)
586 {
587 if (pinnedPubkey.empty()) {
588 // if no pinned pubkey specified, don't pin (Curl default)
589 return CURLE_OK;
590 }
591 if (cert == nullptr) {
592 NETSTACK_LOGE("no cert specified.");
593 return CURLE_BAD_FUNCTION_ARGUMENT;
594 }
595 unsigned char *certPubkey = nullptr;
596 int pubkeyLen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &certPubkey);
597 std::string certPubKeyDigest;
598 if (!CommonUtils::Sha256sum(certPubkey, pubkeyLen, certPubKeyDigest)) {
599 return CURLE_BAD_FUNCTION_ARGUMENT;
600 }
601 NETSTACK_LOGI("pubkey sha256: %{public}s", certPubKeyDigest.c_str());
602 if (CommonUtils::IsCertPubKeyInPinned(certPubKeyDigest, pinnedPubkey)) {
603 return CURLE_OK;
604 }
605 return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
606 }
607
VerifyCallback(int preverifyOk,X509_STORE_CTX * ctx)608 static int VerifyCallback(int preverifyOk, X509_STORE_CTX *ctx)
609 {
610 X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
611 int err = X509_STORE_CTX_get_error(ctx);
612 int depth = X509_STORE_CTX_get_error_depth(ctx);
613
614 NETSTACK_LOGI("X509_STORE_CTX error code %{public}d, depth %{public}d", err, depth);
615
616 SSL *ssl = static_cast<SSL *>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
617 SSL_CTX *sslctx = SSL_get_SSL_CTX(ssl);
618 RequestContext *requestContext = static_cast<RequestContext *>(SSL_CTX_get_ex_data(sslctx,
619 SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX));
620 if (requestContext->IsRootCaVerifiedOk()) {
621 // root CA hash verified, normal procedure.
622 return preverifyOk;
623 }
624
625 int verifyResult = VerifyCertPubkey(cert, requestContext->GetPinnedPubkey());
626 if (!requestContext->IsRootCaVerified()) {
627 // not verified yet, so this is the root CA verifying.
628 NETSTACK_LOGD("Verifying Root CA.");
629 requestContext->SetRootCaVerifiedOk(verifyResult == CURLE_OK);
630 requestContext->SetRootCaVerified();
631 }
632 if (verifyResult != CURLE_OK && depth == 0) {
633 // peer site certificate, since root ca verify not ok, and peer site is also not ok
634 // return failed.
635 return 0;
636 }
637 return preverifyOk;
638 }
639 #endif // HTTP_ONLY_VERIFY_ROOT_CA_ENABLE
640
VerifyRootCaSslCtxFunction(CURL * curl,void * sslCtx,void * context)641 CURLcode VerifyRootCaSslCtxFunction(CURL *curl, void *sslCtx, void *context)
642 {
643 #ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE
644 SSL_CTX *ctx = static_cast<SSL_CTX *>(sslCtx);
645 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, VerifyCallback);
646 SSL_CTX_set_ex_data(ctx, SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX, context);
647 #endif
648 return CURLE_OK;
649 }
650
SslCtxFunction(CURL * curl,void * sslCtx,void * parm)651 CURLcode SslCtxFunction(CURL *curl, void *sslCtx, void *parm)
652 {
653 auto requestContext = static_cast<RequestContext *>(parm);
654 if (requestContext == nullptr) {
655 NETSTACK_LOGE("requestContext is null");
656 return CURLE_SSL_CERTPROBLEM;
657 }
658 CURLcode result = MultiPathSslCtxFunction(curl, sslCtx, &requestContext->GetCertsPath());
659 if (result != CURLE_OK) {
660 return result;
661 }
662 if (!requestContext->GetPinnedPubkey().empty()) {
663 return VerifyRootCaSslCtxFunction(curl, sslCtx, requestContext);
664 }
665 return CURLE_OK;
666 }
667
SetServerSSLCertOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)668 bool NetHttpClientExec::SetServerSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
669 {
670 #ifndef NO_SSL_CERTIFICATION
671 #ifdef HAS_NETMANAGER_BASE
672 auto hostname = CommonUtils::GetHostnameFromURL(context->options.GetUrl());
673 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
674 std::vector<std::string> certs;
675 // add app cert path
676 auto ret = NetManagerStandard::NetConnClient::GetInstance().GetTrustAnchorsForHostName(hostname, certs);
677 if (ret != 0) {
678 NETSTACK_LOGE("GetTrustAnchorsForHostName error. ret [%{public}d]", ret);
679 }
680 #ifdef HTTP_MULTIPATH_CERT_ENABLE
681 // add user cert path
682 certs.emplace_back(USER_CERT_ROOT_PATH);
683 certs.emplace_back(BASE_PATH + std::to_string(getuid() / UID_TRANSFORM_DIVISOR));
684 // add system cert path
685 certs.emplace_back(HTTP_PREPARE_CA_PATH);
686 context->SetCertsPath(std::move(certs), context->options.GetCaPath());
687 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 1L, context);
688 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 2L, context);
689 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context);
690 #else
691 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context);
692 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
693 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
694 #endif // HTTP_MULTIPATH_CERT_ENABLE
695 #else
696 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
697 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
698 #endif // !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
699 // pin trusted certifcate keys.
700 std::string pins;
701 if (NetManagerStandard::NetConnClient::GetInstance().GetPinSetForHostName(hostname, pins) != 0 || pins.empty()) {
702 NETSTACK_LOGD("Get no pinset by host name");
703 } else if (NetManagerStandard::NetConnClient::GetInstance().IsPinOpenModeVerifyRootCa(hostname)) {
704 context->SetPinnedPubkey(pins);
705 } else {
706 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PINNEDPUBLICKEY, pins.c_str(), context);
707 }
708 #if defined(HTTP_MULTIPATH_CERT_ENABLE) || defined(HTTP_ONLY_VERIFY_ROOT_CA_ENABLE)
709 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_FUNCTION, SslCtxFunction, context);
710 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_DATA, context, context);
711 #endif
712 #else
713 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context);
714 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
715 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
716 #endif // HAS_NETMANAGER_BASE
717 #else
718 // in real life, you should buy a ssl certification and rename it to /etc/ssl/cert.pem
719 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
720 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
721 #endif // NO_SSL_CERTIFICATION
722
723 return true;
724 }
725
SetMultiPartOption(CURL * curl,RequestContext * context)726 bool NetHttpClientExec::SetMultiPartOption(CURL *curl, RequestContext *context)
727 {
728 auto header = context->options.GetHeader();
729 auto type = CommonUtils::ToLower(header[HTTP_CONTENT_TYPE]);
730 if (type != HTTP_CONTENT_TYPE_MULTIPART) {
731 return true;
732 }
733 auto multiPartDataList = context->options.GetMultiPartDataList();
734 if (multiPartDataList.empty()) {
735 return true;
736 }
737 curl_mime *multipart = curl_mime_init(curl);
738 if (multipart == nullptr) {
739 return false;
740 }
741 context->SetMultipart(multipart);
742 curl_mimepart *part = nullptr;
743 bool hasData = false;
744 for (auto &multiFormData : multiPartDataList) {
745 if (multiFormData.name.empty()) {
746 continue;
747 }
748 if (multiFormData.data.empty() && multiFormData.filePath.empty()) {
749 NETSTACK_LOGE("Failed to set name error no data and filepath at the same time");
750 continue;
751 }
752 part = curl_mime_addpart(multipart);
753 SetFormDataOption(multiFormData, part, curl, context);
754 hasData = true;
755 }
756 if (hasData) {
757 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_MIMEPOST, multipart, context);
758 }
759 return true;
760 }
761
SetFormDataOption(MultiFormData & multiFormData,curl_mimepart * part,CURL * curl,RequestContext * context)762 void NetHttpClientExec::SetFormDataOption(MultiFormData &multiFormData, curl_mimepart *part,
763 CURL *curl, RequestContext *context)
764 {
765 CURLcode result = curl_mime_name(part, multiFormData.name.c_str());
766 if (result != CURLE_OK) {
767 NETSTACK_LOGE("Failed to set name error: %{public}s", curl_easy_strerror(result));
768 return;
769 }
770 if (!multiFormData.contentType.empty()) {
771 result = curl_mime_type(part, multiFormData.contentType.c_str());
772 if (result != CURLE_OK) {
773 NETSTACK_LOGE("Failed to set contentType error: %{public}s", curl_easy_strerror(result));
774 }
775 }
776 if (!multiFormData.remoteFileName.empty()) {
777 result = curl_mime_filename(part, multiFormData.remoteFileName.c_str());
778 if (result != CURLE_OK) {
779 NETSTACK_LOGE("Failed to set remoteFileName error: %{public}s", curl_easy_strerror(result));
780 }
781 }
782 if (!multiFormData.data.empty()) {
783 result = curl_mime_data(part, multiFormData.data.c_str(), CURL_ZERO_TERMINATED);
784 if (result != CURLE_OK) {
785 NETSTACK_LOGE("Failed to set data error: %{public}s", curl_easy_strerror(result));
786 }
787 } else {
788 result = curl_mime_filedata(part, multiFormData.filePath.c_str());
789 if (result != CURLE_OK) {
790 NETSTACK_LOGE("Failed to set file data error: %{public}s", curl_easy_strerror(result));
791 }
792 }
793 }
794
SetSSLCertOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)795 bool NetHttpClientExec::SetSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
796 {
797 std::string cert;
798 std::string certType;
799 std::string key;
800 SecureChar keyPasswd;
801 context->options.GetClientCert(cert, certType, key, keyPasswd);
802 if (cert.empty()) {
803 NETSTACK_LOGI("SetSSLCertOption param is empty.");
804 return false;
805 }
806 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLCERT, cert.c_str(), context);
807 if (!key.empty()) {
808 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLKEY, key.c_str(), context);
809 }
810 if (!certType.empty()) {
811 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLCERTTYPE, certType.c_str(), context);
812 }
813 if (keyPasswd.Length() > 0) {
814 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_KEYPASSWD, keyPasswd.Data(), context);
815 }
816 return true;
817 }
818
SetDnsOption(CURL * curl,RequestContext * context)819 bool NetHttpClientExec::SetDnsOption(CURL *curl, RequestContext *context)
820 {
821 std::vector<std::string> dnsServers = context->options.GetDnsServers();
822 if (dnsServers.empty()) {
823 return true;
824 }
825 std::string serverList;
826 for (auto &server : dnsServers) {
827 serverList += server + ",";
828 NETSTACK_LOGI("SetDns server: %{public}s", CommonUtils::AnonymizeIp(server).c_str());
829 }
830 serverList.pop_back();
831 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_DNS_SERVERS, serverList.c_str(), context);
832 return true;
833 }
834
SetRequestOption(CURL * curl,RequestContext * context)835 bool NetHttpClientExec::SetRequestOption(CURL *curl, RequestContext *context)
836 {
837 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTP_VERSION, context->options.GetHttpVersion(), context);
838 const std::string range = context->options.GetRangeString();
839 if (range.empty()) {
840 // Some servers don't like requests that are made without a user-agent field, so we provide one
841 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_USERAGENT, HTTP_DEFAULT_USER_AGENT, context);
842 } else {
843 // https://curl.se/libcurl/c/CURLOPT_RANGE.html
844 if (context->options.GetMethod() == HTTP_METHOD_PUT) {
845 context->SetErrorCode(CURLE_RANGE_ERROR);
846 NETSTACK_LOGE("For HTTP PUT uploads this option should not be used, since it may conflict with \
847 other options.");
848 return false;
849 }
850 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_RANGE, range.c_str(), context);
851 }
852 if (!context->options.GetDohUrl().empty()) {
853 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_DOH_URL, context->options.GetDohUrl().c_str(), context);
854 }
855 SetDnsOption(curl, context);
856 SetSSLCertOption(curl, context);
857 SetMultiPartOption(curl, context);
858 return true;
859 }
860
SetOption(CURL * curl,RequestContext * context,struct curl_slist * requestHeader)861 bool NetHttpClientExec::SetOption(CURL *curl, RequestContext *context, struct curl_slist *requestHeader)
862 {
863 const std::string &method = context->options.GetMethod();
864 if (!MethodForGet(method) && !MethodForPost(method)) {
865 NETSTACK_LOGE("method %{public}s not supported", method.c_str());
866 return false;
867 }
868
869 if (context->options.GetMethod() == HTTP_METHOD_HEAD) {
870 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOBODY, 1L, context);
871 }
872
873 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, context->options.GetUrl().c_str(), context);
874 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CUSTOMREQUEST, method.c_str(), context);
875
876 if (MethodForPost(method) && !context->options.GetBody().empty()) {
877 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POST, 1L, context);
878 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, context->options.GetBody().c_str(), context);
879 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDSIZE, context->options.GetBody().size(), context);
880 }
881
882 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback, context);
883 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFODATA, context, context);
884 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROGRESS, 0L, context);
885
886 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEFUNCTION, OnWritingMemoryBody, context);
887 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEDATA, context, context);
888
889 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader, context);
890 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERDATA, context, context);
891 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPHEADER, requestHeader, context);
892 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_FOLLOWLOCATION, 1L, context);
893
894 /* first #undef CURL_DISABLE_COOKIES in curl config */
895 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_COOKIEFILE, "", context);
896 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOSIGNAL, 1L, context);
897 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_TIMEOUT_MS, context->options.GetReadTimeout(), context);
898 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CONNECTTIMEOUT_MS, context->options.GetConnectTimeout(), context);
899
900 SetRequestOption(curl, context);
901
902 if (!SetOtherOption(curl, context)) {
903 return false;
904 }
905 return true;
906 }
907
OnWritingMemoryBody(const void * data,size_t size,size_t memBytes,void * userData)908 size_t NetHttpClientExec::OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData)
909 {
910 auto context = static_cast<RequestContext *>(userData);
911 if (context == nullptr) {
912 return 0;
913 }
914 if (context->IsDestroyed()) {
915 context->StopAndCachePerformanceTiming(RESPONSE_BODY_TIMING);
916 return 0;
917 }
918 size_t callbackSize = 0;
919 if (context->streamingCallback != nullptr) {
920 callbackSize = context->streamingCallback->dataReceive.size();
921 }
922 if (context->IsRequestInStream() && callbackSize > 0) {
923 context->SetTempData(data, size * memBytes);
924 // call OnDataReceive
925 auto tmp = context->GetTempData();
926 context->PopTempData();
927 for (size_t i = 0; i < callbackSize; i++) {
928 CArrUI8 body;
929 body.head = reinterpret_cast<uint8_t*>(MallocCString(tmp));
930 body.size = static_cast<int64_t>(tmp.size());
931 context->streamingCallback->dataReceive[i](body);
932 }
933 context->StopAndCachePerformanceTiming(RESPONSE_BODY_TIMING);
934 return size * memBytes;
935 }
936 if (context->response.GetResult().size() > context->options.GetMaxLimit()) {
937 NETSTACK_LOGE("response data exceeds the maximum limit");
938 context->StopAndCachePerformanceTiming(RESPONSE_BODY_TIMING);
939 return 0;
940 }
941 context->response.AppendResult(data, size * memBytes);
942 context->StopAndCachePerformanceTiming(RESPONSE_BODY_TIMING);
943 return size * memBytes;
944 }
945
MakeHeaderWithSetCookie(RequestContext * context)946 static std::map<std::string, std::string> MakeHeaderWithSetCookie(RequestContext *context)
947 {
948 std::map<std::string, std::string> tempMap = context->response.GetHeader();
949 std::string setCookies;
950 size_t loop = 0;
951 for (const auto &setCookie : context->response.GetsetCookie()) {
952 setCookies += setCookie;
953 if (loop + 1 < context->response.GetsetCookie().size()) {
954 setCookies += HTTP_LINE_SEPARATOR;
955 }
956 ++loop;
957 }
958 tempMap[RESPONSE_KEY_SET_COOKIE] = setCookies;
959 return tempMap;
960 }
961
OnWritingMemoryHeader(const void * data,size_t size,size_t memBytes,void * userData)962 size_t NetHttpClientExec::OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData)
963 {
964 auto context = static_cast<RequestContext *>(userData);
965 if (context == nullptr) {
966 return 0;
967 }
968 if (context->IsDestroyed()) {
969 context->StopAndCachePerformanceTiming(RESPONSE_HEADER_TIMING);
970 return 0;
971 }
972 if (context->response.GetResult().size() > context->options.GetMaxLimit()) {
973 NETSTACK_LOGE("response data exceeds the maximum limit");
974 context->StopAndCachePerformanceTiming(RESPONSE_HEADER_TIMING);
975 return 0;
976 }
977 context->response.AppendRawHeader(data, size * memBytes);
978 if (CommonUtils::EndsWith(context->response.GetRawHeader(), HTTP_RESPONSE_HEADER_SEPARATOR)) {
979 context->response.ParseHeaders();
980 int callbackSize = 0;
981 int callOnceSize = 0;
982 if (context->streamingCallback) {
983 callbackSize = static_cast<int>(context->streamingCallback->headersReceive.size());
984 callOnceSize = static_cast<int>(context->streamingCallback->headersReceiveOnce.size());
985 }
986
987 // call onHeadersReceive
988 if (!context->IsDestroyed() && (callbackSize > 0 || callOnceSize > 0)) {
989 auto headersMap = MakeHeaderWithSetCookie(context);
990 for (int i = 0; i < callbackSize; i++) {
991 auto ret = g_map2CArrString(headersMap);
992 context->streamingCallback->headersReceive[i](ret);
993 }
994 for (int i = 0; i < callOnceSize; i++) {
995 auto ret = g_map2CArrString(headersMap);
996 context->streamingCallback->headersReceiveOnce[i](ret);
997 }
998 context->streamingCallback->headersReceiveOnce.clear();
999 }
1000 }
1001 context->StopAndCachePerformanceTiming(RESPONSE_HEADER_TIMING);
1002 return size * memBytes;
1003 }
1004
MakeHeaders(const std::vector<std::string> & vec)1005 struct curl_slist *NetHttpClientExec::MakeHeaders(const std::vector<std::string> &vec)
1006 {
1007 struct curl_slist *header = nullptr;
1008 std::for_each(vec.begin(), vec.end(), [&header](const std::string &s) {
1009 if (!s.empty()) {
1010 header = curl_slist_append(header, s.c_str());
1011 }
1012 });
1013 return header;
1014 }
1015
ProgressCallback(void * userData,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)1016 int NetHttpClientExec::ProgressCallback(void *userData, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
1017 curl_off_t ulnow)
1018 {
1019 auto context = static_cast<RequestContext*>(userData);
1020 if (context == nullptr) {
1021 return 0;
1022 }
1023 if (ultotal != 0 && ultotal >= ulnow && !context->CompareWithLastElement(ulnow, ultotal)) {
1024 context->SetUlLen(ulnow, ultotal);
1025 size_t callbackSize = 0;
1026 if (context->streamingCallback != nullptr) {
1027 callbackSize = context->streamingCallback->dataSendProgress.size();
1028 }
1029 // call OnDataUploadProgress
1030 if (!IsContextDeleted(context) && callbackSize > 0) {
1031 auto ulLen = context->GetUlLen();
1032 for (size_t i = 0; i < callbackSize; i++) {
1033 CDataSendProgressInfo info = {.sendSize = ulLen.nLen, .totalSize = ulLen.tLen};
1034 context->streamingCallback->dataSendProgress[i](info);
1035 }
1036 }
1037 }
1038 if (!context->IsRequestInStream()) {
1039 return 0;
1040 }
1041 if (dltotal != 0) {
1042 context->SetDlLen(dlnow, dltotal);
1043 int callbackSize = 0;
1044 if (context->streamingCallback != nullptr) {
1045 callbackSize = static_cast<int>(context->streamingCallback->dataReceiveProgress.size());
1046 }
1047
1048 // call OnDataProgress
1049 if (!IsContextDeleted(context) && callbackSize > 0 && dlnow != 0) {
1050 auto dlLen = context->GetDlLen();
1051 for (int i = 0; i < callbackSize; i++) {
1052 CDataReceiveProgressInfo info = {.receiveSize = dlLen.nLen, .totalSize = dlLen.tLen};
1053 context->streamingCallback->dataReceiveProgress[i](info);
1054 }
1055 }
1056 }
1057 return 0;
1058 }
1059
IsUnReserved(unsigned char in)1060 bool NetHttpClientExec::IsUnReserved(unsigned char in)
1061 {
1062 if ((in >= '0' && in <= '9') || (in >= 'a' && in <= 'z') || (in >= 'A' && in <= 'Z')) {
1063 return true;
1064 }
1065 switch (in) {
1066 case '-':
1067 case '.':
1068 case '_':
1069 case '~':
1070 return true;
1071 default:
1072 break;
1073 }
1074 return false;
1075 }
1076
IsInitialized()1077 bool NetHttpClientExec::IsInitialized()
1078 {
1079 return staticVariable_.initialized;
1080 }
1081
DeInitialize()1082 void NetHttpClientExec::DeInitialize()
1083 {
1084 std::lock_guard<std::mutex> lock(staticVariable_.curlMultiMutex);
1085 staticVariable_.runThread = false;
1086 staticVariable_.conditionVariable.notify_all();
1087 if (staticVariable_.workThread.joinable()) {
1088 staticVariable_.workThread.join();
1089 }
1090 if (staticVariable_.curlMulti) {
1091 curl_multi_cleanup(staticVariable_.curlMulti);
1092 }
1093 staticVariable_.initialized = false;
1094 }
1095 }