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