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 }