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