• 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 static constexpr unsigned int SHA256_LEN = 32;
89 static constexpr int SHA256_BASE64_LEN = 44;  // 32-byte base64 -> 44 bytes
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
Sha256sum(unsigned char * buf,size_t buflen,std::string & digestStr)587 bool Sha256sum(unsigned char *buf, size_t buflen, std::string &digestStr)
588 {
589     EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
590     unsigned int digestLen = 0;
591     unsigned char digest[SHA256_LEN];
592     unsigned char out[SHA256_BASE64_LEN + 1] = {0};
593     if (!mdctx) {
594         NETSTACK_LOGE("create MD_CTX failed.");
595         return false;
596     }
597     if (!EVP_DigestInit(mdctx, EVP_sha256())) {
598         NETSTACK_LOGE("EVP_DigestInit failed.");
599         return false;
600     }
601     if (!EVP_DigestUpdate(mdctx, buf, buflen)) {
602         NETSTACK_LOGE("EVP_DigestUpdate failed.");
603         return false;
604     }
605     if (!EVP_DigestFinal_ex(mdctx, digest, &digestLen)) {
606         NETSTACK_LOGE("EVP_DigestFinal_ex failed.");
607         return false;
608     }
609     EVP_MD_CTX_free(mdctx);
610     if (digestLen != SHA256_LEN) {
611         NETSTACK_LOGE("SHA256 length invalid");
612         return false;
613     }
614     int base64Len = EVP_EncodeBlock(out, digest, SHA256_LEN);
615     if (base64Len != SHA256_BASE64_LEN) {
616         NETSTACK_LOGE("SHA256-Base64 length invalid.");
617         return false;
618     }
619     digestStr = std::string(reinterpret_cast<const char *>(out), SHA256_BASE64_LEN);
620     return true;
621 }
622 
VerifyCertPubkey(X509 * cert,const std::string & pinnedPubkey)623 static int VerifyCertPubkey(X509 *cert, const std::string &pinnedPubkey)
624 {
625     if (pinnedPubkey.empty()) {
626         // if no pinned pubkey specified, don't pin (Curl default)
627         return CURLE_OK;
628     }
629     if (cert == nullptr) {
630         NETSTACK_LOGE("no cert specified.");
631         return CURLE_BAD_FUNCTION_ARGUMENT;
632     }
633     unsigned char *certPubkey = nullptr;
634     int pubkeyLen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &certPubkey);
635     std::string certPubKeyDigest;
636     if (!Sha256sum(certPubkey, pubkeyLen, certPubKeyDigest)) {
637         return CURLE_BAD_FUNCTION_ARGUMENT;
638     }
639     NETSTACK_LOGI("pubkey sha256: %{public}s", certPubKeyDigest.c_str());
640     if (CommonUtils::IsCertPubKeyInPinned(certPubKeyDigest, pinnedPubkey)) {
641         return CURLE_OK;
642     }
643     return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
644 }
645 
VerifyCallback(int preverifyOk,X509_STORE_CTX * ctx)646 static int VerifyCallback(int preverifyOk, X509_STORE_CTX *ctx)
647 {
648     X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
649     int err = X509_STORE_CTX_get_error(ctx);
650     int depth = X509_STORE_CTX_get_error_depth(ctx);
651 
652     NETSTACK_LOGI("X509_STORE_CTX error code %{public}d, depth %{public}d", err, depth);
653 
654     SSL *ssl = static_cast<SSL *>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
655     SSL_CTX *sslctx = SSL_get_SSL_CTX(ssl);
656     RequestContext *requestContext = static_cast<RequestContext *>(SSL_CTX_get_ex_data(sslctx,
657         SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX));
658     if (requestContext->IsRootCaVerifiedOk()) {
659         // root CA hash verified, normal procedure.
660         return preverifyOk;
661     }
662 
663     int verifyResult = VerifyCertPubkey(cert, requestContext->GetPinnedPubkey());
664     if (!requestContext->IsRootCaVerified()) {
665         // not verified yet, so this is the root CA verifying.
666         NETSTACK_LOGD("Verifying Root CA.");
667         requestContext->SetRootCaVerifiedOk(verifyResult == CURLE_OK);
668         requestContext->SetRootCaVerified();
669     }
670     if (verifyResult != CURLE_OK && depth == 0) {
671         // peer site certificate, since root ca verify not ok, and peer site is also not ok
672         // return failed.
673         return 0;
674     }
675     return preverifyOk;
676 }
677 #endif  // HTTP_ONLY_VERIFY_ROOT_CA_ENABLE
678 
VerifyRootCaSslCtxFunction(CURL * curl,void * sslCtx,void * context)679 CURLcode VerifyRootCaSslCtxFunction(CURL *curl, void *sslCtx, void *context)
680 {
681 #ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE
682     SSL_CTX *ctx = static_cast<SSL_CTX *>(sslCtx);
683     SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, VerifyCallback);
684     SSL_CTX_set_ex_data(ctx, SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX, context);
685 #endif
686     return CURLE_OK;
687 }
688 
SslCtxFunction(CURL * curl,void * sslCtx,void * parm)689 CURLcode SslCtxFunction(CURL *curl, void *sslCtx, void *parm)
690 {
691     auto requestContext = static_cast<RequestContext *>(parm);
692     if (requestContext == nullptr) {
693         NETSTACK_LOGE("requestContext is null");
694         return CURLE_SSL_CERTPROBLEM;
695     }
696     CURLcode result = MultiPathSslCtxFunction(curl, sslCtx, &requestContext->GetCertsPath());
697     if (result != CURLE_OK) {
698         return result;
699     }
700     if (!requestContext->GetPinnedPubkey().empty()) {
701         return VerifyRootCaSslCtxFunction(curl, sslCtx, requestContext);
702     }
703     return CURLE_OK;
704 }
705 
SetServerSSLCertOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)706 bool NetHttpClientExec::SetServerSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
707 {
708 #ifndef NO_SSL_CERTIFICATION
709 #ifdef HAS_NETMANAGER_BASE
710     auto hostname = CommonUtils::GetHostnameFromURL(context->options.GetUrl());
711 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
712     std::vector<std::string> certs;
713     // add app cert path
714     auto ret = NetManagerStandard::NetConnClient::GetInstance().GetTrustAnchorsForHostName(hostname, certs);
715     if (ret != 0) {
716         NETSTACK_LOGE("GetTrustAnchorsForHostName error. ret [%{public}d]", ret);
717     }
718 #ifdef HTTP_MULTIPATH_CERT_ENABLE
719     // add user cert path
720     certs.emplace_back(USER_CERT_ROOT_PATH);
721     certs.emplace_back(BASE_PATH + std::to_string(getuid() / UID_TRANSFORM_DIVISOR));
722     // add system cert path
723     certs.emplace_back(HTTP_PREPARE_CA_PATH);
724     context->SetCertsPath(std::move(certs), context->options.GetCaPath());
725     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 1L, context);
726     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 2L, context);
727     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context);
728 #else
729     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context);
730     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
731     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
732 #endif // HTTP_MULTIPATH_CERT_ENABLE
733 #else
734     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
735     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
736 #endif //  !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
737     // pin trusted certifcate keys.
738     std::string pins;
739     if (NetManagerStandard::NetConnClient::GetInstance().GetPinSetForHostName(hostname, pins) != 0 || pins.empty()) {
740         NETSTACK_LOGD("Get no pinset by host name");
741     } else if (NetManagerStandard::NetConnClient::GetInstance().IsPinOpenModeVerifyRootCa(hostname)) {
742         context->SetPinnedPubkey(pins);
743     } else {
744         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PINNEDPUBLICKEY, pins.c_str(), context);
745     }
746 #if defined(HTTP_MULTIPATH_CERT_ENABLE) || defined(HTTP_ONLY_VERIFY_ROOT_CA_ENABLE)
747     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_FUNCTION, SslCtxFunction, context);
748     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_DATA, context, context);
749 #endif
750 #else
751     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context);
752     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
753     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
754 #endif // HAS_NETMANAGER_BASE
755 #else
756     // in real life, you should buy a ssl certification and rename it to /etc/ssl/cert.pem
757     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
758     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
759 #endif // NO_SSL_CERTIFICATION
760 
761     return true;
762 }
763 
SetMultiPartOption(CURL * curl,RequestContext * context)764 bool NetHttpClientExec::SetMultiPartOption(CURL *curl, RequestContext *context)
765 {
766     auto header =  context->options.GetHeader();
767     auto type = CommonUtils::ToLower(header[HTTP_CONTENT_TYPE]);
768     if (type != HTTP_CONTENT_TYPE_MULTIPART) {
769         return true;
770     }
771     auto multiPartDataList = context->options.GetMultiPartDataList();
772     if (multiPartDataList.empty()) {
773         return true;
774     }
775     curl_mime *multipart = curl_mime_init(curl);
776     if (multipart == nullptr) {
777         return false;
778     }
779     context->SetMultipart(multipart);
780     curl_mimepart *part = nullptr;
781     bool hasData = false;
782     for (auto &multiFormData : multiPartDataList) {
783         if (multiFormData.name.empty()) {
784             continue;
785         }
786         if (multiFormData.data.empty() && multiFormData.filePath.empty()) {
787             NETSTACK_LOGE("Failed to set multiFormData error no data and filepath at the same time");
788             continue;
789         }
790         part = curl_mime_addpart(multipart);
791         SetFormDataOption(multiFormData, part, curl, context);
792         hasData = true;
793     }
794     if (hasData) {
795         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_MIMEPOST, multipart, context);
796     }
797     return true;
798 }
799 
SetFormDataOption(MultiFormData & multiFormData,curl_mimepart * part,CURL * curl,RequestContext * context)800 void NetHttpClientExec::SetFormDataOption(MultiFormData &multiFormData, curl_mimepart *part,
801     CURL *curl, RequestContext *context)
802 {
803     CURLcode result = curl_mime_name(part, multiFormData.name.c_str());
804     if (result != CURLE_OK) {
805         NETSTACK_LOGE("Failed to set name error: %{public}s", curl_easy_strerror(result));
806         return;
807     }
808     if (!multiFormData.contentType.empty()) {
809         result = curl_mime_type(part, multiFormData.contentType.c_str());
810         if (result != CURLE_OK) {
811             NETSTACK_LOGE("Failed to set contentType error: %{public}s", curl_easy_strerror(result));
812         }
813     }
814     if (!multiFormData.remoteFileName.empty()) {
815         result = curl_mime_filename(part, multiFormData.remoteFileName.c_str());
816         if (result != CURLE_OK) {
817             NETSTACK_LOGE("Failed to set remoteFileName error: %{public}s", curl_easy_strerror(result));
818         }
819     }
820     if (!multiFormData.data.empty()) {
821         result = curl_mime_data(part, multiFormData.data.c_str(), CURL_ZERO_TERMINATED);
822         if (result != CURLE_OK) {
823             NETSTACK_LOGE("Failed to set data error: %{public}s", curl_easy_strerror(result));
824         }
825     } else {
826         result = curl_mime_filedata(part, multiFormData.filePath.c_str());
827         if (result != CURLE_OK) {
828             NETSTACK_LOGE("Failed to set file data error: %{public}s", curl_easy_strerror(result));
829         }
830     }
831 }
832 
SetSSLCertOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)833 bool NetHttpClientExec::SetSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
834 {
835     std::string cert;
836     std::string certType;
837     std::string key;
838     SecureChar keyPasswd;
839     context->options.GetClientCert(cert, certType, key, keyPasswd);
840     if (cert.empty()) {
841         NETSTACK_LOGI("SetSSLCertOption param is empty.");
842         return false;
843     }
844     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLCERT, cert.c_str(), context);
845     if (!key.empty()) {
846         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLKEY, key.c_str(), context);
847     }
848     if (!certType.empty()) {
849         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLCERTTYPE, certType.c_str(), context);
850     }
851     if (keyPasswd.Length() > 0) {
852         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_KEYPASSWD, keyPasswd.Data(), context);
853     }
854     return true;
855 }
856 
SetDnsOption(CURL * curl,RequestContext * context)857 bool NetHttpClientExec::SetDnsOption(CURL *curl, RequestContext *context)
858 {
859     std::vector<std::string> dnsServers = context->options.GetDnsServers();
860     if (dnsServers.empty()) {
861         return true;
862     }
863     std::string serverList;
864     for (auto &server : dnsServers) {
865         serverList += server + ",";
866         NETSTACK_LOGI("SetDns server: %{public}s", CommonUtils::AnonymizeIp(server).c_str());
867     }
868     serverList.pop_back();
869     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_DNS_SERVERS, serverList.c_str(), context);
870     return true;
871 }
872 
SetRequestOption(CURL * curl,RequestContext * context)873 bool NetHttpClientExec::SetRequestOption(CURL *curl, RequestContext *context)
874 {
875     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTP_VERSION, context->options.GetHttpVersion(), context);
876     const std::string range = context->options.GetRangeString();
877     if (range.empty()) {
878         // Some servers don't like requests that are made without a user-agent field, so we provide one
879         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_USERAGENT, HTTP_DEFAULT_USER_AGENT, context);
880     } else {
881         // https://curl.se/libcurl/c/CURLOPT_RANGE.html
882         if (context->options.GetMethod() == HTTP_METHOD_PUT) {
883             context->SetErrorCode(CURLE_RANGE_ERROR);
884             NETSTACK_LOGE("For HTTP PUT uploads this option should not be used, since it may conflict with \
885                           other options.");
886             return false;
887         }
888         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_RANGE, range.c_str(), context);
889     }
890     if (!context->options.GetDohUrl().empty()) {
891         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_DOH_URL, context->options.GetDohUrl().c_str(), context);
892     }
893     SetDnsOption(curl, context);
894     SetSSLCertOption(curl, context);
895     SetMultiPartOption(curl, context);
896     return true;
897 }
898 
SetOption(CURL * curl,RequestContext * context,struct curl_slist * requestHeader)899 bool NetHttpClientExec::SetOption(CURL *curl, RequestContext *context, struct curl_slist *requestHeader)
900 {
901     const std::string &method = context->options.GetMethod();
902     if (!MethodForGet(method) && !MethodForPost(method)) {
903         NETSTACK_LOGE("method %{public}s not supported", method.c_str());
904         return false;
905     }
906 
907     if (context->options.GetMethod() == HTTP_METHOD_HEAD) {
908         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOBODY, 1L, context);
909     }
910 
911     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, context->options.GetUrl().c_str(), context);
912     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CUSTOMREQUEST, method.c_str(), context);
913 
914     if (MethodForPost(method) && !context->options.GetBody().empty()) {
915         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POST, 1L, context);
916         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, context->options.GetBody().c_str(), context);
917         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDSIZE, context->options.GetBody().size(), context);
918     }
919 
920     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback, context);
921     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFODATA, context, context);
922     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROGRESS, 0L, context);
923 
924     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEFUNCTION, OnWritingMemoryBody, context);
925     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEDATA, context, context);
926 
927     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader, context);
928     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERDATA, context, context);
929     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPHEADER, requestHeader, context);
930     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_FOLLOWLOCATION, 1L, context);
931 
932     /* first #undef CURL_DISABLE_COOKIES in curl config */
933     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_COOKIEFILE, "", context);
934     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOSIGNAL, 1L, context);
935     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_TIMEOUT_MS, context->options.GetReadTimeout(), context);
936     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CONNECTTIMEOUT_MS, context->options.GetConnectTimeout(), context);
937 
938     SetRequestOption(curl, context);
939 
940     if (!SetOtherOption(curl, context)) {
941         return false;
942     }
943     return true;
944 }
945 
OnWritingMemoryBody(const void * data,size_t size,size_t memBytes,void * userData)946 size_t NetHttpClientExec::OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData)
947 {
948     auto context = static_cast<RequestContext *>(userData);
949     if (context == nullptr) {
950         return 0;
951     }
952     if (context->IsDestroyed()) {
953         context->StopAndCachePerformanceTiming(RESPONSE_BODY_TIMING);
954         return 0;
955     }
956     size_t callbackSize = 0;
957     if (context->streamingCallback != nullptr) {
958         callbackSize = context->streamingCallback->dataReceive.size();
959     }
960     if (context->IsRequestInStream() && callbackSize > 0) {
961         context->SetTempData(data, size * memBytes);
962         // call OnDataReceive
963         auto tmp = context->GetTempData();
964         context->PopTempData();
965         for (size_t i = 0; i < callbackSize; i++) {
966             CArrUI8 body;
967             body.head = reinterpret_cast<uint8_t*>(MallocCString(tmp));
968             body.size = static_cast<int64_t>(tmp.size());
969             context->streamingCallback->dataReceive[i](body);
970         }
971         context->StopAndCachePerformanceTiming(RESPONSE_BODY_TIMING);
972         return size * memBytes;
973     }
974     if (context->response.GetResult().size() > context->options.GetMaxLimit()) {
975         NETSTACK_LOGE("response data exceeds the maximum limit");
976         context->StopAndCachePerformanceTiming(RESPONSE_BODY_TIMING);
977         return 0;
978     }
979     context->response.AppendResult(data, size * memBytes);
980     context->StopAndCachePerformanceTiming(RESPONSE_BODY_TIMING);
981     return size * memBytes;
982 }
983 
MakeHeaderWithSetCookie(RequestContext * context)984 static std::map<std::string, std::string> MakeHeaderWithSetCookie(RequestContext *context)
985 {
986     std::map<std::string, std::string> tempMap = context->response.GetHeader();
987     std::string setCookies;
988     size_t loop = 0;
989     for (const auto &setCookie : context->response.GetsetCookie()) {
990         setCookies += setCookie;
991         if (loop + 1 < context->response.GetsetCookie().size()) {
992             setCookies += HTTP_LINE_SEPARATOR;
993         }
994         ++loop;
995     }
996     tempMap[RESPONSE_KEY_SET_COOKIE] = setCookies;
997     return tempMap;
998 }
999 
OnWritingMemoryHeader(const void * data,size_t size,size_t memBytes,void * userData)1000 size_t NetHttpClientExec::OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData)
1001 {
1002     auto context = static_cast<RequestContext *>(userData);
1003     if (context == nullptr) {
1004         return 0;
1005     }
1006     if (context->IsDestroyed()) {
1007         context->StopAndCachePerformanceTiming(RESPONSE_HEADER_TIMING);
1008         return 0;
1009     }
1010     if (context->response.GetResult().size() > context->options.GetMaxLimit()) {
1011         NETSTACK_LOGE("response data exceeds the maximum limit");
1012         context->StopAndCachePerformanceTiming(RESPONSE_HEADER_TIMING);
1013         return 0;
1014     }
1015     context->response.AppendRawHeader(data, size * memBytes);
1016     if (CommonUtils::EndsWith(context->response.GetRawHeader(), HTTP_RESPONSE_HEADER_SEPARATOR)) {
1017         context->response.ParseHeaders();
1018         int callbackSize = 0;
1019         int callOnceSize = 0;
1020         if (context->streamingCallback) {
1021             callbackSize = static_cast<int>(context->streamingCallback->headersReceive.size());
1022             callOnceSize = static_cast<int>(context->streamingCallback->headersReceiveOnce.size());
1023         }
1024 
1025         // call onHeadersReceive
1026         if (!context->IsDestroyed() && (callbackSize > 0 || callOnceSize > 0)) {
1027             auto headersMap = MakeHeaderWithSetCookie(context);
1028             for (int i = 0; i < callbackSize; i++) {
1029                 auto ret = g_map2CArrString(headersMap);
1030                 context->streamingCallback->headersReceive[i](ret);
1031             }
1032             for (int i = 0; i < callOnceSize; i++) {
1033                 auto ret = g_map2CArrString(headersMap);
1034                 context->streamingCallback->headersReceiveOnce[i](ret);
1035             }
1036             context->streamingCallback->headersReceiveOnce.clear();
1037         }
1038     }
1039     context->StopAndCachePerformanceTiming(RESPONSE_HEADER_TIMING);
1040     return size * memBytes;
1041 }
1042 
MakeHeaders(const std::vector<std::string> & vec)1043 struct curl_slist *NetHttpClientExec::MakeHeaders(const std::vector<std::string> &vec)
1044 {
1045     struct curl_slist *header = nullptr;
1046     std::for_each(vec.begin(), vec.end(), [&header](const std::string &s) {
1047         if (!s.empty()) {
1048             header = curl_slist_append(header, s.c_str());
1049         }
1050     });
1051     return header;
1052 }
1053 
ProgressCallback(void * userData,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)1054 int NetHttpClientExec::ProgressCallback(void *userData, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
1055     curl_off_t ulnow)
1056 {
1057     auto context = static_cast<RequestContext*>(userData);
1058     if (context == nullptr) {
1059         return 0;
1060     }
1061     if (ultotal != 0 && ultotal >= ulnow && !context->CompareWithLastElement(ulnow, ultotal)) {
1062         context->SetUlLen(ulnow, ultotal);
1063         size_t callbackSize = 0;
1064         if (context->streamingCallback != nullptr) {
1065             callbackSize = context->streamingCallback->dataSendProgress.size();
1066         }
1067         // call OnDataUploadProgress
1068         if (!IsContextDeleted(context) && callbackSize > 0) {
1069             auto ulLen = context->GetUlLen();
1070             for (size_t i = 0; i < callbackSize; i++) {
1071                 CDataSendProgressInfo info = {.sendSize = ulLen.nLen, .totalSize = ulLen.tLen};
1072                 context->streamingCallback->dataSendProgress[i](info);
1073             }
1074         }
1075     }
1076     if (!context->IsRequestInStream()) {
1077         return 0;
1078     }
1079     if (dltotal != 0) {
1080         context->SetDlLen(dlnow, dltotal);
1081         int callbackSize = 0;
1082         if (context->streamingCallback != nullptr) {
1083             callbackSize = static_cast<int>(context->streamingCallback->dataReceiveProgress.size());
1084         }
1085 
1086         // call OnDataProgress
1087         if (!IsContextDeleted(context) && callbackSize > 0 && dlnow != 0) {
1088             auto dlLen = context->GetDlLen();
1089             for (int i = 0; i < callbackSize; i++) {
1090                 CDataReceiveProgressInfo info = {.receiveSize = dlLen.nLen, .totalSize = dlLen.tLen};
1091                 context->streamingCallback->dataReceiveProgress[i](info);
1092             }
1093         }
1094     }
1095     return 0;
1096 }
1097 
IsUnReserved(unsigned char in)1098 bool NetHttpClientExec::IsUnReserved(unsigned char in)
1099 {
1100     if ((in >= '0' && in <= '9') || (in >= 'a' && in <= 'z') || (in >= 'A' && in <= 'Z')) {
1101         return true;
1102     }
1103     switch (in) {
1104         case '-':
1105         case '.':
1106         case '_':
1107         case '~':
1108             return true;
1109         default:
1110             break;
1111     }
1112     return false;
1113 }
1114 
IsInitialized()1115 bool NetHttpClientExec::IsInitialized()
1116 {
1117     return staticVariable_.initialized;
1118 }
1119 
DeInitialize()1120 void NetHttpClientExec::DeInitialize()
1121 {
1122     std::lock_guard<std::mutex> lock(staticVariable_.curlMultiMutex);
1123     staticVariable_.runThread = false;
1124     staticVariable_.conditionVariable.notify_all();
1125     if (staticVariable_.workThread.joinable()) {
1126         staticVariable_.workThread.join();
1127     }
1128     if (staticVariable_.curlMulti) {
1129         curl_multi_cleanup(staticVariable_.curlMulti);
1130     }
1131     staticVariable_.initialized = false;
1132 }
1133 }