• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }