• 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 #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 }