• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 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 "http_exec.h"
17 
18 #include <cstddef>
19 #include <cstring>
20 #include <memory>
21 #include <thread>
22 #include <unistd.h>
23 
24 #ifdef HTTP_PROXY_ENABLE
25 #include "parameter.h"
26 #endif
27 #ifdef HAS_NETMANAGER_BASE
28 #include "http_proxy.h"
29 #include "net_conn_client.h"
30 #endif
31 #include "base64_utils.h"
32 #include "cache_proxy.h"
33 #include "constant.h"
34 #include "event_list.h"
35 #include "http_async_work.h"
36 #include "http_time.h"
37 #include "napi_utils.h"
38 #include "netstack_common_utils.h"
39 #include "netstack_log.h"
40 #include "securec.h"
41 #include "secure_char.h"
42 
43 #define NETSTACK_CURL_EASY_SET_OPTION(handle, opt, data, asyncContext)                                   \
44     do {                                                                                                 \
45         CURLcode result = curl_easy_setopt(handle, opt, data);                                           \
46         if (result != CURLE_OK) {                                                                        \
47             const char *err = curl_easy_strerror(result);                                                \
48             NETSTACK_LOGE("Failed to set option: %{public}s, %{public}s %{public}d", #opt, err, result); \
49             (asyncContext)->SetErrorCode(result);                                                        \
50             return false;                                                                                \
51         }                                                                                                \
52     } while (0)
53 
54 namespace OHOS::NetStack::Http {
55 static constexpr int CURL_TIMEOUT_MS = 50;
56 static constexpr int CONDITION_TIMEOUT_S = 3600;
57 static constexpr int CURL_MAX_WAIT_MSECS = 10;
58 static constexpr int CURL_HANDLE_NUM = 10;
59 static constexpr const uint32_t EVENT_PARAM_ZERO = 0;
60 static constexpr const uint32_t EVENT_PARAM_ONE = 1;
61 static constexpr const uint32_t EVENT_PARAM_TWO = 2;
62 static constexpr const char *TLS12_SECURITY_CIPHER_SUITE = R"(DEFAULT:!eNULL:!EXPORT)";
63 
64 #ifdef HTTP_PROXY_ENABLE
65 static constexpr int32_t SYSPARA_MAX_SIZE = 128;
66 static constexpr const char *DEFAULT_HTTP_PROXY_HOST = "NONE";
67 static constexpr const char *DEFAULT_HTTP_PROXY_PORT = "0";
68 static constexpr const char *DEFAULT_HTTP_PROXY_EXCLUSION_LIST = "NONE";
69 static constexpr const char *HTTP_PROXY_HOST_KEY = "persist.netmanager_base.http_proxy.host";
70 static constexpr const char *HTTP_PROXY_PORT_KEY = "persist.netmanager_base.http_proxy.port";
71 static constexpr const char *HTTP_PROXY_EXCLUSIONS_KEY = "persist.netmanager_base.http_proxy.exclusion_list";
72 #endif
73 
RequestContextDeleter(RequestContext * context)74 static void RequestContextDeleter(RequestContext *context)
75 {
76     std::lock_guard lockGuard(HttpExec::staticContextSet_.mutexForContextVec);
77     auto it = std::find(HttpExec::staticContextSet_.contextSet.begin(), HttpExec::staticContextSet_.contextSet.end(),
78                         context);
79     if (it == HttpExec::staticContextSet_.contextSet.end()) {
80         NETSTACK_LOGE("can't find request context in set");
81     } else {
82         HttpExec::staticContextSet_.contextSet.erase(it);
83     }
84 
85     context->DeleteReference();
86     delete context;
87     context = nullptr;
88 }
89 
AsyncWorkRequestInStreamCallback(napi_env env,napi_status status,void * data)90 static void AsyncWorkRequestInStreamCallback(napi_env env, napi_status status, void *data)
91 {
92     if (status != napi_ok) {
93         return;
94     }
95     std::unique_ptr<RequestContext, decltype(&RequestContextDeleter)> context(static_cast<RequestContext *>(data),
96                                                                               RequestContextDeleter);
97     napi_value argv[EVENT_PARAM_TWO] = {nullptr};
98     if (context->IsParseOK() && context->IsExecOK()) {
99         argv[EVENT_PARAM_ZERO] = NapiUtils::GetUndefined(env);
100         argv[EVENT_PARAM_ONE] = HttpExec::RequestInStreamCallback(context.get());
101         if (argv[EVENT_PARAM_ONE] == nullptr) {
102             return;
103         }
104     } else {
105         argv[EVENT_PARAM_ZERO] =
106             NapiUtils::CreateErrorMessage(env, context->GetErrorCode(), context->GetErrorMessage());
107         if (argv[EVENT_PARAM_ZERO] == nullptr) {
108             return;
109         }
110 
111         argv[EVENT_PARAM_ONE] = NapiUtils::GetUndefined(env);
112     }
113 
114     napi_value undefined = NapiUtils::GetUndefined(env);
115     if (context->GetDeferred() != nullptr) {
116         if (context->IsExecOK()) {
117             napi_resolve_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ONE]);
118             context->Emit(ON_DATA_END, std::make_pair(undefined, undefined));
119         } else {
120             napi_reject_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ZERO]);
121         }
122         return;
123     }
124     napi_value func = context->GetCallback();
125     if (NapiUtils::GetValueType(env, func) == napi_function) {
126         (void)NapiUtils::CallFunction(env, undefined, func, EVENT_PARAM_TWO, argv);
127     }
128     if (context->IsExecOK()) {
129         context->Emit(ON_DATA_END, std::make_pair(undefined, undefined));
130     }
131 }
132 
AsyncWorkRequestCallback(napi_env env,napi_status status,void * data)133 static void AsyncWorkRequestCallback(napi_env env, napi_status status, void *data)
134 {
135     if (status != napi_ok) {
136         return;
137     }
138     std::unique_ptr<RequestContext, decltype(&RequestContextDeleter)> context(static_cast<RequestContext *>(data),
139                                                                               RequestContextDeleter);
140     napi_value argv[EVENT_PARAM_TWO] = {nullptr};
141     if (context->IsParseOK() && context->IsExecOK()) {
142         argv[EVENT_PARAM_ZERO] = NapiUtils::GetUndefined(env);
143         argv[EVENT_PARAM_ONE] = HttpExec::RequestCallback(context.get());
144         if (argv[EVENT_PARAM_ONE] == nullptr) {
145             return;
146         }
147     } else {
148         argv[EVENT_PARAM_ZERO] =
149             NapiUtils::CreateErrorMessage(env, context->GetErrorCode(), context->GetErrorMessage());
150         if (argv[EVENT_PARAM_ZERO] == nullptr) {
151             return;
152         }
153 
154         argv[EVENT_PARAM_ONE] = NapiUtils::GetUndefined(env);
155     }
156     napi_value undefined = NapiUtils::GetUndefined(env);
157     if (context->GetDeferred() != nullptr) {
158         if (context->IsExecOK()) {
159             napi_resolve_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ONE]);
160         } else {
161             napi_reject_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ZERO]);
162         }
163         return;
164     }
165     napi_value func = context->GetCallback();
166     if (NapiUtils::GetValueType(env, func) == napi_function) {
167         (void)NapiUtils::CallFunction(env, undefined, func, EVENT_PARAM_TWO, argv);
168     }
169 }
170 
AddCurlHandle(CURL * handle,RequestContext * context)171 bool HttpExec::AddCurlHandle(CURL *handle, RequestContext *context)
172 {
173     if (handle == nullptr || staticVariable_.curlMulti == nullptr) {
174         NETSTACK_LOGE("handle nullptr");
175         return false;
176     }
177 
178     std::thread([context, handle] {
179         std::lock_guard guard(staticVariable_.curlMultiMutex);
180         //Do SetServerSSLCertOption here to avoid blocking the main thread.
181         SetServerSSLCertOption(handle, context);
182         staticVariable_.infoQueue.emplace(context, handle);
183         staticVariable_.conditionVariable.notify_all();
184         {
185             std::lock_guard lockGuard(staticContextSet_.mutexForContextVec);
186             HttpExec::staticContextSet_.contextSet.emplace(context);
187         }
188     }).detach();
189 
190     return true;
191 }
192 
193 HttpExec::StaticVariable HttpExec::staticVariable_; /* NOLINT */
194 HttpExec::StaticContextVec HttpExec::staticContextSet_;
195 
RequestWithoutCache(RequestContext * context)196 bool HttpExec::RequestWithoutCache(RequestContext *context)
197 {
198     if (!staticVariable_.initialized) {
199         NETSTACK_LOGE("curl not init");
200         return false;
201     }
202 
203     auto handle = curl_easy_init();
204     if (!handle) {
205         NETSTACK_LOGE("Failed to create fetch task");
206         return false;
207     }
208 
209     std::vector<std::string> vec;
210     std::for_each(context->options.GetHeader().begin(), context->options.GetHeader().end(),
211                   [&vec](const std::pair<std::string, std::string> &p) {
212                       if (!p.second.empty()) {
213                           vec.emplace_back(p.first + HttpConstant::HTTP_HEADER_SEPARATOR + p.second);
214                       } else {
215                           vec.emplace_back(p.first + HttpConstant::HTTP_HEADER_BLANK_SEPARATOR);
216                       }
217                   });
218     context->SetCurlHeaderList(MakeHeaders(vec));
219 
220     if (!SetOption(handle, context, context->GetCurlHeaderList())) {
221         NETSTACK_LOGE("set option failed");
222         return false;
223     }
224 
225     context->response.SetRequestTime(HttpTime::GetNowTimeGMT());
226 
227     if (!AddCurlHandle(handle, context)) {
228         NETSTACK_LOGE("add handle failed");
229         return false;
230     }
231 
232     return true;
233 }
234 
GetCurlDataFromHandle(CURL * handle,RequestContext * context,CURLMSG curlMsg,CURLcode result)235 bool HttpExec::GetCurlDataFromHandle(CURL *handle, RequestContext *context, CURLMSG curlMsg, CURLcode result)
236 {
237     if (curlMsg != CURLMSG_DONE) {
238         NETSTACK_LOGE("CURLMSG %{public}s", std::to_string(curlMsg).c_str());
239         context->SetErrorCode(NapiUtils::NETSTACK_NAPI_INTERNAL_ERROR);
240         return false;
241     }
242 
243     if (result != CURLE_OK) {
244         context->SetErrorCode(result);
245         NETSTACK_LOGE("CURLcode result %{public}s", std::to_string(result).c_str());
246         return false;
247     }
248 
249     context->response.SetResponseTime(HttpTime::GetNowTimeGMT());
250 
251     int64_t responseCode;
252     CURLcode code = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
253     if (code != CURLE_OK) {
254         context->SetErrorCode(code);
255         return false;
256     }
257     context->response.SetResponseCode(responseCode);
258     if (context->response.GetResponseCode() == static_cast<uint32_t>(ResponseCode::NOT_MODIFIED)) {
259         NETSTACK_LOGI("cache is NOT_MODIFIED, we use the cache");
260         context->SetResponseByCache();
261         return true;
262     }
263     NETSTACK_LOGD("responseCode is %{public}s", std::to_string(responseCode).c_str());
264 
265     struct curl_slist *cookies = nullptr;
266     code = curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &cookies);
267     if (code != CURLE_OK) {
268         context->SetErrorCode(code);
269         return false;
270     }
271 
272     std::unique_ptr<struct curl_slist, decltype(&curl_slist_free_all)> cookiesHandle(cookies, curl_slist_free_all);
273     while (cookies) {
274         context->response.AppendCookies(cookies->data, strlen(cookies->data));
275         if (cookies->next != nullptr) {
276             context->response.AppendCookies(HttpConstant::HTTP_LINE_SEPARATOR,
277                                             strlen(HttpConstant::HTTP_LINE_SEPARATOR));
278         }
279         cookies = cookies->next;
280     }
281     return true;
282 }
283 
GetTimingFromCurl(CURL * handle,CURLINFO info)284 double HttpExec::GetTimingFromCurl(CURL *handle, CURLINFO info)
285 {
286     time_t timing;
287     CURLcode result = curl_easy_getinfo(handle, info, &timing);
288     if (result != CURLE_OK) {
289         NETSTACK_LOGE("Failed to get timing: %{public}d, %{public}s", info, curl_easy_strerror(result));
290         return 0;
291     }
292     return Timing::TimeUtils::Microseconds2Milliseconds(timing);
293 }
294 
CacheCurlPerformanceTiming(CURL * handle,RequestContext * context)295 void HttpExec::CacheCurlPerformanceTiming(CURL *handle, RequestContext *context)
296 {
297     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_DNS_TIMING,
298                                         HttpExec::GetTimingFromCurl(handle, CURLINFO_NAMELOOKUP_TIME_T));
299     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_TCP_TIMING,
300                                         HttpExec::GetTimingFromCurl(handle, CURLINFO_CONNECT_TIME_T));
301     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_TLS_TIMING,
302                                         HttpExec::GetTimingFromCurl(handle, CURLINFO_APPCONNECT_TIME_T));
303     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_FIRST_SEND_TIMING,
304                                         HttpExec::GetTimingFromCurl(handle, CURLINFO_PRETRANSFER_TIME_T));
305     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_FIRST_RECEIVE_TIMING,
306                                         HttpExec::GetTimingFromCurl(handle, CURLINFO_STARTTRANSFER_TIME_T));
307     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_TOTAL_FINISH_TIMING,
308                                         HttpExec::GetTimingFromCurl(handle, CURLINFO_TOTAL_TIME_T));
309     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_REDIRECT_TIMING,
310                                         HttpExec::GetTimingFromCurl(handle, CURLINFO_REDIRECT_TIME_T));
311 }
312 
HandleCurlData(CURLMsg * msg)313 void HttpExec::HandleCurlData(CURLMsg *msg)
314 {
315     if (msg == nullptr) {
316         return;
317     }
318 
319     auto handle = msg->easy_handle;
320     if (handle == nullptr) {
321         return;
322     }
323 
324     auto it = staticVariable_.contextMap.find(handle);
325     if (it == staticVariable_.contextMap.end()) {
326         NETSTACK_LOGE("can not find context");
327         return;
328     }
329 
330     auto context = it->second;
331     staticVariable_.contextMap.erase(it);
332     if (context == nullptr) {
333         NETSTACK_LOGE("can not find context");
334         return;
335     }
336     NETSTACK_LOGD("priority = %{public}d", context->options.GetPriority());
337     context->SetExecOK(GetCurlDataFromHandle(handle, context, msg->msg, msg->data.result));
338     CacheCurlPerformanceTiming(handle, context);
339     if (context->IsExecOK()) {
340         CacheProxy proxy(context->options);
341         proxy.WriteResponseToCache(context->response);
342     }
343     if (context->GetManager() == nullptr) {
344         NETSTACK_LOGE("can not find context manager");
345         return;
346     }
347 
348     if (context->IsRequestInStream()) {
349         std::this_thread::sleep_for(std::chrono::milliseconds(CURL_TIMEOUT_MS));
350         NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, AsyncWorkRequestInStreamCallback);
351     } else {
352         NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, AsyncWorkRequestCallback);
353     }
354 }
355 
ExecRequest(RequestContext * context)356 bool HttpExec::ExecRequest(RequestContext *context)
357 {
358     if (!CommonUtils::HasInternetPermission()) {
359         context->SetPermissionDenied(true);
360         return false;
361     }
362     if (context->GetManager()->IsEventDestroy()) {
363         return false;
364     }
365     context->options.SetRequestTime(HttpTime::GetNowTimeGMT());
366     CacheProxy proxy(context->options);
367     if (context->IsUsingCache() && proxy.ReadResponseFromCache(context)) {
368         return true;
369     }
370 
371     if (!RequestWithoutCache(context)) {
372         context->SetErrorCode(NapiUtils::NETSTACK_NAPI_INTERNAL_ERROR);
373         if (EventManager::IsManagerValid(context->GetManager())) {
374             if (context->IsRequestInStream()) {
375                 std::this_thread::sleep_for(std::chrono::milliseconds(CURL_TIMEOUT_MS));
376                 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, AsyncWorkRequestInStreamCallback);
377             } else {
378                 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, AsyncWorkRequestCallback);
379             }
380         }
381         return false;
382     }
383 
384     return true;
385 }
386 
BuildRequestCallback(RequestContext * context)387 napi_value HttpExec::BuildRequestCallback(RequestContext *context)
388 {
389     napi_value object = NapiUtils::CreateObject(context->GetEnv());
390     if (NapiUtils::GetValueType(context->GetEnv(), object) != napi_object) {
391         return nullptr;
392     }
393 
394     NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESPONSE_CODE,
395                                  context->response.GetResponseCode());
396     NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_COOKIES,
397                                      context->response.GetCookies());
398 
399     napi_value header = MakeResponseHeader(context->GetEnv(), context);
400     if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) {
401         NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_HEADER, header);
402     }
403 
404     if (context->options.GetHttpDataType() != HttpDataType::NO_DATA_TYPE && ProcByExpectDataType(object, context)) {
405         return object;
406     }
407 
408     auto contentType = CommonUtils::ToLower(const_cast<std::map<std::string, std::string> &>(
409         context->response.GetHeader())[HttpConstant::HTTP_CONTENT_TYPE]);
410     if (contentType.find(HttpConstant::HTTP_CONTENT_TYPE_OCTET_STREAM) != std::string::npos ||
411         contentType.find(HttpConstant::HTTP_CONTENT_TYPE_IMAGE) != std::string::npos) {
412         void *data = nullptr;
413         auto body = context->response.GetResult();
414         napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), body.size(), &data);
415         if (data != nullptr && arrayBuffer != nullptr) {
416             if (memcpy_s(data, body.size(), body.c_str(), body.size()) != EOK) {
417                 NETSTACK_LOGE("memcpy_s failed!");
418                 return object;
419             }
420             NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, arrayBuffer);
421         }
422         NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
423                                      static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER));
424         return object;
425     }
426 
427     /* now just support utf8 */
428     NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT,
429                                      context->response.GetResult());
430     NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
431                                  static_cast<uint32_t>(HttpDataType::STRING));
432     return object;
433 }
434 
RequestCallback(RequestContext * context)435 napi_value HttpExec::RequestCallback(RequestContext *context)
436 {
437     napi_value result = HttpExec::BuildRequestCallback(context);
438     context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_TOTAL_TIMING);
439     context->SetPerformanceTimingToResult(result);
440     return result;
441 }
442 
RequestInStreamCallback(OHOS::NetStack::Http::RequestContext * context)443 napi_value HttpExec::RequestInStreamCallback(OHOS::NetStack::Http::RequestContext *context)
444 {
445     napi_value number = NapiUtils::CreateUint32(context->GetEnv(), context->response.GetResponseCode());
446     if (NapiUtils::GetValueType(context->GetEnv(), number) != napi_number) {
447         return nullptr;
448     }
449     return number;
450 }
451 
MakeUrl(const std::string & url,std::string param,const std::string & extraParam)452 std::string HttpExec::MakeUrl(const std::string &url, std::string param, const std::string &extraParam)
453 {
454     if (param.empty()) {
455         param += extraParam;
456     } else {
457         param += HttpConstant::HTTP_URL_PARAM_SEPARATOR;
458         param += extraParam;
459     }
460 
461     if (param.empty()) {
462         return url;
463     }
464 
465     return url + HttpConstant::HTTP_URL_PARAM_START + param;
466 }
467 
MethodForGet(const std::string & method)468 bool HttpExec::MethodForGet(const std::string &method)
469 {
470     return (method == HttpConstant::HTTP_METHOD_HEAD || method == HttpConstant::HTTP_METHOD_OPTIONS ||
471             method == HttpConstant::HTTP_METHOD_TRACE || method == HttpConstant::HTTP_METHOD_GET ||
472             method == HttpConstant::HTTP_METHOD_CONNECT);
473 }
474 
MethodForPost(const std::string & method)475 bool HttpExec::MethodForPost(const std::string &method)
476 {
477     return (method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT ||
478             method == HttpConstant::HTTP_METHOD_DELETE);
479 }
480 
EncodeUrlParam(std::string & str)481 bool HttpExec::EncodeUrlParam(std::string &str)
482 {
483     char encoded[4];
484     std::string encodeOut;
485     size_t length = strlen(str.c_str());
486     for (size_t i = 0; i < length; ++i) {
487         auto c = static_cast<uint8_t>(str.c_str()[i]);
488         if (IsUnReserved(c)) {
489             encodeOut += static_cast<char>(c);
490         } else {
491             if (sprintf_s(encoded, sizeof(encoded), "%%%02X", c) < 0) {
492                 return false;
493             }
494             encodeOut += encoded;
495         }
496     }
497 
498     if (str == encodeOut) {
499         return false;
500     }
501     str = encodeOut;
502     return true;
503 }
504 
AddRequestInfo()505 void HttpExec::AddRequestInfo()
506 {
507     std::lock_guard guard(staticVariable_.curlMultiMutex);
508     int num = 0;
509     while (!staticVariable_.infoQueue.empty()) {
510         if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
511             break;
512         }
513 
514         auto info = staticVariable_.infoQueue.top();
515         staticVariable_.infoQueue.pop();
516         auto ret = curl_multi_add_handle(staticVariable_.curlMulti, info.handle);
517         if (ret == CURLM_OK) {
518             staticVariable_.contextMap[info.handle] = info.context;
519         }
520 
521         ++num;
522         if (num >= CURL_HANDLE_NUM) {
523             break;
524         }
525     }
526 }
527 
IsContextDeleted(RequestContext * context)528 bool HttpExec::IsContextDeleted(RequestContext *context)
529 {
530     if (context == nullptr) {
531         return true;
532     }
533     {
534         std::lock_guard<std::mutex> lockGuard(HttpExec::staticContextSet_.mutexForContextVec);
535         auto it = std::find(HttpExec::staticContextSet_.contextSet.begin(),
536                             HttpExec::staticContextSet_.contextSet.end(), context);
537         if (it == HttpExec::staticContextSet_.contextSet.end()) {
538             NETSTACK_LOGI("context has been deleted in libuv thread");
539             return true;
540         }
541     }
542     return false;
543 }
544 
RunThread()545 void HttpExec::RunThread()
546 {
547     while (staticVariable_.runThread && staticVariable_.curlMulti != nullptr) {
548         AddRequestInfo();
549         SendRequest();
550         ReadResponse();
551         std::this_thread::sleep_for(std::chrono::milliseconds(CURL_TIMEOUT_MS));
552         std::unique_lock l(staticVariable_.curlMultiMutex);
553         staticVariable_.conditionVariable.wait_for(l, std::chrono::seconds(CONDITION_TIMEOUT_S), [] {
554             return !staticVariable_.infoQueue.empty() || !staticVariable_.contextMap.empty();
555         });
556     }
557 }
558 
SendRequest()559 void HttpExec::SendRequest()
560 {
561     std::lock_guard guard(staticVariable_.curlMultiMutex);
562 
563     int runningHandle = 0;
564     int num = 0;
565     do {
566         if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
567             break;
568         }
569 
570         auto ret = curl_multi_perform(staticVariable_.curlMulti, &runningHandle);
571 
572         if (runningHandle > 0) {
573             ret = curl_multi_poll(staticVariable_.curlMulti, nullptr, 0, CURL_MAX_WAIT_MSECS, nullptr);
574         }
575 
576         if (ret != CURLM_OK) {
577             return;
578         }
579 
580         ++num;
581         if (num >= CURL_HANDLE_NUM) {
582             break;
583         }
584     } while (runningHandle > 0);
585 }
586 
ReadResponse()587 void HttpExec::ReadResponse()
588 {
589     std::lock_guard guard(staticVariable_.curlMultiMutex);
590     CURLMsg *msg = nullptr; /* NOLINT */
591     do {
592         if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
593             break;
594         }
595 
596         int leftMsg;
597         msg = curl_multi_info_read(staticVariable_.curlMulti, &leftMsg);
598         if (msg) {
599             if (msg->msg == CURLMSG_DONE) {
600                 HandleCurlData(msg);
601             }
602             if (msg->easy_handle) {
603                 (void)curl_multi_remove_handle(staticVariable_.curlMulti, msg->easy_handle);
604                 (void)curl_easy_cleanup(msg->easy_handle);
605             }
606         }
607     } while (msg);
608 }
609 
GetGlobalHttpProxyInfo(std::string & host,int32_t & port,std::string & exclusions)610 void HttpExec::GetGlobalHttpProxyInfo(std::string &host, int32_t &port, std::string &exclusions)
611 {
612 #ifdef HTTP_PROXY_ENABLE
613     char httpProxyHost[SYSPARA_MAX_SIZE] = {0};
614     char httpProxyPort[SYSPARA_MAX_SIZE] = {0};
615     char httpProxyExclusions[SYSPARA_MAX_SIZE] = {0};
616     GetParameter(HTTP_PROXY_HOST_KEY, DEFAULT_HTTP_PROXY_HOST, httpProxyHost, sizeof(httpProxyHost));
617     GetParameter(HTTP_PROXY_PORT_KEY, DEFAULT_HTTP_PROXY_PORT, httpProxyPort, sizeof(httpProxyPort));
618     GetParameter(HTTP_PROXY_EXCLUSIONS_KEY, DEFAULT_HTTP_PROXY_EXCLUSION_LIST, httpProxyExclusions,
619                  sizeof(httpProxyExclusions));
620 
621     host = Base64::Decode(httpProxyHost);
622     if (host == DEFAULT_HTTP_PROXY_HOST) {
623         host = std::string();
624     }
625     exclusions = httpProxyExclusions;
626     if (exclusions == DEFAULT_HTTP_PROXY_EXCLUSION_LIST) {
627         exclusions = std::string();
628     }
629 
630     port = std::atoi(httpProxyPort);
631 #endif
632 }
633 
GetHttpProxyInfo(RequestContext * context,std::string & host,int32_t & port,std::string & exclusions)634 void HttpExec::GetHttpProxyInfo(RequestContext *context, std::string &host, int32_t &port, std::string &exclusions)
635 {
636     if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_DEFAULT) {
637 #ifdef HAS_NETMANAGER_BASE
638         using namespace NetManagerStandard;
639         HttpProxy httpProxy;
640         NetConnClient::GetInstance().GetDefaultHttpProxy(httpProxy);
641         host = httpProxy.GetHost();
642         port = httpProxy.GetPort();
643         exclusions = CommonUtils::ToString(httpProxy.GetExclusionList());
644 #else
645         GetGlobalHttpProxyInfo(host, port, exclusions);
646 #endif
647     } else if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_SPECIFIED) {
648         context->options.GetSpecifiedHttpProxy(host, port, exclusions);
649     }
650 }
651 
Initialize()652 bool HttpExec::Initialize()
653 {
654     std::lock_guard<std::mutex> lock(staticVariable_.mutexForInitialize);
655     if (staticVariable_.initialized) {
656         return true;
657     }
658     NETSTACK_LOGD("call curl_global_init");
659     if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
660         NETSTACK_LOGE("Failed to initialize 'curl'");
661         return false;
662     }
663 
664     staticVariable_.curlMulti = curl_multi_init();
665     if (staticVariable_.curlMulti == nullptr) {
666         NETSTACK_LOGE("Failed to initialize 'curl_multi'");
667         return false;
668     }
669 
670     staticVariable_.workThread = std::thread(RunThread);
671 
672     staticVariable_.initialized = true;
673     return staticVariable_.initialized;
674 }
675 
SetOtherOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)676 bool HttpExec::SetOtherOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
677 {
678     std::string url = context->options.GetUrl();
679     std::string host, exclusions;
680     int32_t port = 0;
681     GetHttpProxyInfo(context, host, port, exclusions);
682     if (!host.empty() && !CommonUtils::IsHostNameExcluded(url, exclusions, ",")) {
683         NETSTACK_LOGD("Set CURLOPT_PROXY: %{public}s:%{public}d, %{public}s", host.c_str(), port, exclusions.c_str());
684         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXY, host.c_str(), context);
685         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYPORT, port, context);
686         auto curlTunnelValue = (url.find("https://") != std::string::npos) ? 1L : 0L;
687         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPPROXYTUNNEL, curlTunnelValue, context);
688         auto proxyType = (host.find("https://") != std::string::npos) ? CURLPROXY_HTTPS : CURLPROXY_HTTP;
689         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYTYPE, proxyType, context);
690     }
691     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2, context);
692     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CIPHER_LIST, TLS12_SECURITY_CIPHER_SUITE, context);
693 
694 #ifdef NETSTACK_PROXY_PASS
695     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYUSERPWD, NETSTACK_PROXY_PASS, context);
696 #endif // NETSTACK_PROXY_PASS
697 
698 #ifdef HTTP_CURL_PRINT_VERBOSE
699     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_VERBOSE, 1L, context);
700 #endif
701 
702 #ifndef WINDOWS_PLATFORM
703     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_ACCEPT_ENCODING, "", context);
704 #endif
705     return true;
706 }
707 
SetSSLCertOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)708 bool HttpExec::SetSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
709 {
710     std::string cert;
711     std::string certType;
712     std::string key;
713     Secure::SecureChar keyPasswd;
714     context->options.GetClientCert(cert, certType, key, keyPasswd);
715     if (cert.empty()) {
716         NETSTACK_LOGD("SetSSLCertOption param is empty.");
717         return false;
718     }
719     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLCERT, cert.c_str(), context);
720     if (!key.empty()) {
721         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLKEY, key.c_str(), context);
722     }
723     if (!certType.empty()) {
724         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLCERTTYPE, certType.c_str(), context);
725     }
726     if (keyPasswd.Length() > 0) {
727         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_KEYPASSWD, keyPasswd.Data(), context);
728     }
729     return true;
730 }
731 
SetServerSSLCertOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)732 bool HttpExec::SetServerSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
733 {
734 #ifndef NO_SSL_CERTIFICATION
735 #ifdef HAS_NETMANAGER_BASE
736     auto hostname = CommonUtils::GetHostnameFromURL(context->options.GetUrl());
737 #ifndef WINDOWS_PLATFORM
738     // customize trusted CAs.
739     std::vector<std::string> certs;
740     auto ret = NetManagerStandard::NetConnClient::GetInstance().GetTrustAnchorsForHostName(hostname, certs);
741     if (ret != 0) {
742         NETSTACK_LOGD("Get no trust anchor by host name[%{public}s], ret[%{public}d]", hostname.c_str(), ret);
743     } else {
744         std::string *pCert = nullptr;
745         for (auto &cert : certs) {
746             if (!cert.empty()) {
747                 pCert = &cert;
748                 break;
749             }
750         }
751         if (pCert != nullptr) {
752             NETSTACK_LOGD("curl set option capath: capath=%{public}s.", pCert->c_str());
753             NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context);
754             NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAPATH, pCert->c_str(), context);
755         } else {
756             NETSTACK_LOGD("Get no trust anchor by host name[%{public}s]", hostname.c_str());
757             NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, context->options.GetCaPath().c_str(), context);
758             NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAPATH, HttpConstant::HTTP_PREPARE_CA_PATH, context);
759         }
760     }
761 #endif // WINDOWS_PLATFORM
762     // pin trusted certifcate keys.
763     std::string pins;
764     auto ret1 = NetManagerStandard::NetConnClient::GetInstance().GetPinSetForHostName(hostname, pins);
765     if (ret1 != 0 || pins.empty()) {
766         NETSTACK_LOGD("Get no pinset by host name[%{public}s]", hostname.c_str());
767     } else {
768         NETSTACK_LOGD("curl set pin =[%{public}s]", pins.c_str());
769         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PINNEDPUBLICKEY, pins.c_str(), context);
770     }
771 #endif // HAS_NETMANAGER_BASE
772 #else
773     // in real life, you should buy a ssl certification and rename it to /etc/ssl/cert.pem
774     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
775     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
776 #endif // NO_SSL_CERTIFICATION
777 
778     return true;
779 }
780 
SetDnsOption(CURL * curl,RequestContext * context)781 bool HttpExec::SetDnsOption(CURL *curl, RequestContext *context)
782 {
783     std::vector<std::string> dnsServers = context->options.GetDnsServers();
784     if (dnsServers.empty()) {
785         return true;
786     }
787     std::string serverList;
788     for (auto &server : dnsServers) {
789         serverList += server + ",";
790         NETSTACK_LOGD("SetDns server: %{public}s", CommonUtils::AnonymizeIp(server).c_str());
791     }
792     serverList.pop_back();
793     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_DNS_SERVERS, serverList.c_str(), context);
794     return true;
795 }
796 
SetRequestOption(CURL * curl,RequestContext * context)797 bool HttpExec::SetRequestOption(CURL *curl, RequestContext *context)
798 {
799     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTP_VERSION, context->options.GetHttpVersion(), context);
800     const std::string range = context->options.GetRangeString();
801     if (range.empty()) {
802         // Some servers don't like requests that are made without a user-agent field, so we provide one
803         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_USERAGENT, HttpConstant::HTTP_DEFAULT_USER_AGENT, context);
804     } else {
805         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_RANGE, range.c_str(), context);
806     }
807     if (!context->options.GetDohUrl().empty()) {
808         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_DOH_URL, context->options.GetDohUrl().c_str(), context);
809     }
810     SetDnsOption(curl, context);
811     SetSSLCertOption(curl, context);
812     SetMultiPartOption(curl, context);
813     return true;
814 }
815 
SetOption(CURL * curl,RequestContext * context,struct curl_slist * requestHeader)816 bool HttpExec::SetOption(CURL *curl, RequestContext *context, struct curl_slist *requestHeader)
817 {
818     const std::string &method = context->options.GetMethod();
819     if (!MethodForGet(method) && !MethodForPost(method)) {
820         NETSTACK_LOGE("method %{public}s not supported", method.c_str());
821         return false;
822     }
823 
824     if (context->options.GetMethod() == HttpConstant::HTTP_METHOD_HEAD) {
825         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOBODY, 1L, context);
826     }
827 
828     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, context->options.GetUrl().c_str(), context);
829     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CUSTOMREQUEST, method.c_str(), context);
830 
831     if (MethodForPost(method) && !context->options.GetBody().empty()) {
832         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POST, 1L, context);
833         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, context->options.GetBody().c_str(), context);
834         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDSIZE, context->options.GetBody().size(), context);
835     }
836 
837     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback, context);
838     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFODATA, context, context);
839     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROGRESS, 0L, context);
840 
841     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEFUNCTION, OnWritingMemoryBody, context);
842     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEDATA, context, context);
843 
844     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader, context);
845     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERDATA, context, context);
846     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPHEADER, requestHeader, context);
847     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_FOLLOWLOCATION, 1L, context);
848 
849     /* first #undef CURL_DISABLE_COOKIES in curl config */
850     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_COOKIEFILE, "", context);
851     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOSIGNAL, 1L, context);
852     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_TIMEOUT_MS, context->options.GetReadTimeout(), context);
853     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CONNECTTIMEOUT_MS, context->options.GetConnectTimeout(), context);
854 
855     SetRequestOption(curl, context);
856 
857     if (!SetOtherOption(curl, context)) {
858         return false;
859     }
860     return true;
861 }
862 
OnWritingMemoryBody(const void * data,size_t size,size_t memBytes,void * userData)863 size_t HttpExec::OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData)
864 {
865     auto context = static_cast<RequestContext *>(userData);
866     if (context == nullptr) {
867         return 0;
868     }
869     if (context->GetManager()->IsEventDestroy()) {
870         context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
871         return 0;
872     }
873     if (context->IsRequestInStream()) {
874         context->SetTempData(data, size * memBytes);
875         NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataReceive);
876         context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
877         return size * memBytes;
878     }
879     if (context->response.GetResult().size() > context->options.GetMaxLimit()) {
880         NETSTACK_LOGE("response data exceeds the maximum limit");
881         context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
882         return 0;
883     }
884     context->response.AppendResult(data, size * memBytes);
885     context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
886     return size * memBytes;
887 }
888 
MakeSetCookieArray(napi_env env,napi_value header,const std::pair<const std::basic_string<char>,std::basic_string<char>> & headerElement)889 static void MakeSetCookieArray(napi_env env, napi_value header,
890                                const std::pair<const std::basic_string<char>, std::basic_string<char>> &headerElement)
891 {
892     std::vector<std::string> cookieVec =
893         CommonUtils::Split(headerElement.second, HttpConstant::RESPONSE_KEY_SET_COOKIE_SEPARATOR);
894     uint32_t index = 0;
895     auto len = cookieVec.size();
896     auto array = NapiUtils::CreateArray(env, len);
897     for (const auto &setCookie : cookieVec) {
898         auto str = NapiUtils::CreateStringUtf8(env, setCookie);
899         NapiUtils::SetArrayElement(env, array, index, str);
900         ++index;
901     }
902     NapiUtils::SetArrayProperty(env, header, HttpConstant::RESPONSE_KEY_SET_COOKIE, array);
903 }
904 
MakeHeaderWithSetCookieArray(napi_env env,napi_value header,std::map<std::string,std::string> * headerMap)905 static void MakeHeaderWithSetCookieArray(napi_env env, napi_value header, std::map<std::string, std::string> *headerMap)
906 {
907     for (const auto &it : *headerMap) {
908         if (!it.first.empty() && !it.second.empty()) {
909             if (it.first == HttpConstant::RESPONSE_KEY_SET_COOKIE) {
910                 MakeSetCookieArray(env, header, it);
911                 continue;
912             }
913             NapiUtils::SetStringPropertyUtf8(env, header, it.first, it.second);
914         }
915     }
916 }
917 
ResponseHeaderCallback(uv_work_t * work,int status)918 static void ResponseHeaderCallback(uv_work_t *work, int status)
919 {
920     (void)status;
921 
922     auto workWrapper = static_cast<UvWorkWrapper *>(work->data);
923     napi_env env = workWrapper->env;
924     auto headerMap = static_cast<std::map<std::string, std::string> *>(workWrapper->data);
925     auto closeScope = [env](napi_handle_scope scope) { NapiUtils::CloseScope(env, scope); };
926     std::unique_ptr<napi_handle_scope__, decltype(closeScope)> scope(NapiUtils::OpenScope(env), closeScope);
927     napi_value header = NapiUtils::CreateObject(env);
928     if (NapiUtils::GetValueType(env, header) == napi_object) {
929         MakeHeaderWithSetCookieArray(env, header, headerMap);
930     }
931     std::pair<napi_value, napi_value> arg = {NapiUtils::GetUndefined(env), header};
932     workWrapper->manager->Emit(workWrapper->type, arg);
933     delete headerMap;
934     headerMap = nullptr;
935     delete workWrapper;
936     workWrapper = nullptr;
937     delete work;
938     work = nullptr;
939 }
940 
MakeHeaderWithSetCookie(RequestContext * context)941 static std::map<std::string, std::string> MakeHeaderWithSetCookie(RequestContext * context)
942 {
943     std::map<std::string, std::string> tempMap = context->response.GetHeader();
944     std::string setCookies;
945     size_t loop = 0;
946     for (const auto &setCookie : context->response.GetsetCookie()) {
947         setCookies += setCookie;
948         if (loop + 1 < context->response.GetsetCookie().size()) {
949             setCookies += HttpConstant::RESPONSE_KEY_SET_COOKIE_SEPARATOR;
950         }
951         ++loop;
952     }
953     tempMap[HttpConstant::RESPONSE_KEY_SET_COOKIE] = setCookies;
954     return tempMap;
955 }
956 
OnWritingMemoryHeader(const void * data,size_t size,size_t memBytes,void * userData)957 size_t HttpExec::OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData)
958 {
959     auto context = static_cast<RequestContext *>(userData);
960     if (context == nullptr) {
961         return 0;
962     }
963     if (context->GetManager()->IsEventDestroy()) {
964         context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_HEADER_TIMING);
965         return 0;
966     }
967     if (context->response.GetResult().size() > context->options.GetMaxLimit()) {
968         NETSTACK_LOGE("response data exceeds the maximum limit");
969         context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_HEADER_TIMING);
970         return 0;
971     }
972     context->response.AppendRawHeader(data, size * memBytes);
973     if (CommonUtils::EndsWith(context->response.GetRawHeader(), HttpConstant::HTTP_RESPONSE_HEADER_SEPARATOR)) {
974         context->response.ParseHeaders();
975         if (context->GetManager() && EventManager::IsManagerValid(context->GetManager())) {
976             auto headerMap = new std::map<std::string, std::string>(MakeHeaderWithSetCookie(context));
977             context->GetManager()->EmitByUv(ON_HEADER_RECEIVE, headerMap, ResponseHeaderCallback);
978             auto headersMap = new std::map<std::string, std::string>(MakeHeaderWithSetCookie(context));
979             context->GetManager()->EmitByUv(ON_HEADERS_RECEIVE, headersMap, ResponseHeaderCallback);
980         }
981     }
982     context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_HEADER_TIMING);
983     return size * memBytes;
984 }
985 
OnDataReceive(napi_env env,napi_status status,void * data)986 void HttpExec::OnDataReceive(napi_env env, napi_status status, void *data)
987 {
988     auto context = static_cast<RequestContext *>(data);
989     if (context == nullptr) {
990         NETSTACK_LOGE("context is nullptr");
991         return;
992     }
993 
994     void *buffer = nullptr;
995     auto tempData = context->GetTempData();
996     context->PopTempData();
997     if (tempData.empty()) {
998         NETSTACK_LOGI("[GetTempData] tempDate is empty!");
999         return;
1000     }
1001     napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), tempData.size(), &buffer);
1002     if (buffer == nullptr || arrayBuffer == nullptr) {
1003         return;
1004     }
1005     if (memcpy_s(buffer, tempData.size(), tempData.data(), tempData.size()) != EOK) {
1006         NETSTACK_LOGE("[CreateArrayBuffer] memory copy failed");
1007         return;
1008     }
1009     context->Emit(ON_DATA_RECEIVE, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), arrayBuffer));
1010 }
1011 
OnDataProgress(napi_env env,napi_status status,void * data)1012 void HttpExec::OnDataProgress(napi_env env, napi_status status, void *data)
1013 {
1014     auto context = static_cast<RequestContext *>(data);
1015     if (IsContextDeleted(context)) {
1016         return;
1017     }
1018     auto progress = NapiUtils::CreateObject(context->GetEnv());
1019     if (NapiUtils::GetValueType(context->GetEnv(), progress) == napi_undefined) {
1020         return;
1021     }
1022     auto dlLen = context->GetDlLen();
1023     if (dlLen.tLen && dlLen.nLen) {
1024         NapiUtils::SetUint32Property(context->GetEnv(), progress, "receiveSize", static_cast<uint32_t>(dlLen.nLen));
1025         NapiUtils::SetUint32Property(context->GetEnv(), progress, "totalSize", static_cast<uint32_t>(dlLen.tLen));
1026 
1027         context->Emit(ON_DATA_RECEIVE_PROGRESS, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), progress));
1028     }
1029 }
1030 
OnDataUploadProgress(napi_env env,napi_status status,void * data)1031 void HttpExec::OnDataUploadProgress(napi_env env, napi_status status, void *data)
1032 {
1033     auto context = static_cast<RequestContext *>(data);
1034     if (IsContextDeleted(context)) {
1035         NETSTACK_LOGD("[OnDataUploadProgress] context is null.");
1036         return;
1037     }
1038     auto progress = NapiUtils::CreateObject(context->GetEnv());
1039     if (NapiUtils::GetValueType(context->GetEnv(), progress) == napi_undefined) {
1040         NETSTACK_LOGD("[OnDataUploadProgress] napi_undefined.");
1041         return;
1042     }
1043     NapiUtils::SetUint32Property(context->GetEnv(), progress, "sendSize",
1044                                  static_cast<uint32_t>(context->GetUlLen().nLen));
1045     NapiUtils::SetUint32Property(context->GetEnv(), progress, "totalSize",
1046                                  static_cast<uint32_t>(context->GetUlLen().tLen));
1047     context->Emit(ON_DATA_SEND_PROGRESS, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), progress));
1048 }
1049 
ProgressCallback(void * userData,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)1050 int HttpExec::ProgressCallback(void *userData, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
1051                                curl_off_t ulnow)
1052 {
1053     auto context = static_cast<RequestContext *>(userData);
1054     if (context == nullptr) {
1055         return 0;
1056     }
1057     if (ultotal != 0 && ultotal >= ulnow && !context->CompareWithLastElement(ulnow, ultotal)) {
1058         context->SetUlLen(ulnow, ultotal);
1059         NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataUploadProgress);
1060     }
1061     if (!context->IsRequestInStream()) {
1062         return 0;
1063     }
1064     if (context->GetManager()->IsEventDestroy()) {
1065         return 0;
1066     }
1067     if (dltotal != 0) {
1068         context->SetDlLen(dlnow, dltotal);
1069         NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataProgress);
1070     }
1071     return 0;
1072 }
1073 
MakeHeaders(const std::vector<std::string> & vec)1074 struct curl_slist *HttpExec::MakeHeaders(const std::vector<std::string> &vec)
1075 {
1076     struct curl_slist *header = nullptr;
1077     std::for_each(vec.begin(), vec.end(), [&header](const std::string &s) {
1078         if (!s.empty()) {
1079             header = curl_slist_append(header, s.c_str());
1080         }
1081     });
1082     return header;
1083 }
1084 
MakeResponseHeader(napi_env env,void * ctx)1085 napi_value HttpExec::MakeResponseHeader(napi_env env, void *ctx)
1086 {
1087     auto context = reinterpret_cast<RequestContext *>(ctx);
1088     (void)env;
1089     napi_value header = NapiUtils::CreateObject(context->GetEnv());
1090     if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) {
1091         for (const auto &it : context->response.GetHeader()) {
1092             if (!it.first.empty() && !it.second.empty()) {
1093                 NapiUtils::SetStringPropertyUtf8(context->GetEnv(), header, it.first, it.second);
1094             }
1095         }
1096         if (!context->response.GetsetCookie().empty()) {
1097             uint32_t index = 0;
1098             auto len = context->response.GetsetCookie().size();
1099             auto array = NapiUtils::CreateArray(context->GetEnv(), len);
1100             for (const auto &setCookie : context->response.GetsetCookie()) {
1101                 auto str = NapiUtils::CreateStringUtf8(context->GetEnv(), setCookie);
1102                 NapiUtils::SetArrayElement(context->GetEnv(), array, index, str);
1103                 ++index;
1104             }
1105             NapiUtils::SetArrayProperty(context->GetEnv(), header, HttpConstant::RESPONSE_KEY_SET_COOKIE, array);
1106         }
1107     }
1108     return header;
1109 }
1110 
IsUnReserved(unsigned char in)1111 bool HttpExec::IsUnReserved(unsigned char in)
1112 {
1113     if ((in >= '0' && in <= '9') || (in >= 'a' && in <= 'z') || (in >= 'A' && in <= 'Z')) {
1114         return true;
1115     }
1116     switch (in) {
1117         case '-':
1118         case '.':
1119         case '_':
1120         case '~':
1121             return true;
1122         default:
1123             break;
1124     }
1125     return false;
1126 }
1127 
ProcByExpectDataType(napi_value object,RequestContext * context)1128 bool HttpExec::ProcByExpectDataType(napi_value object, RequestContext *context)
1129 {
1130     switch (context->options.GetHttpDataType()) {
1131         case HttpDataType::STRING: {
1132             NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT,
1133                                              context->response.GetResult());
1134             NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
1135                                          static_cast<uint32_t>(HttpDataType::STRING));
1136             return true;
1137         }
1138         case HttpDataType::OBJECT: {
1139             if (context->response.GetResult().size() > HttpConstant::MAX_JSON_PARSE_SIZE) {
1140                 return false;
1141             }
1142 
1143             napi_value obj = NapiUtils::JsonParse(context->GetEnv(), context->response.GetResult());
1144             if (obj) {
1145                 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, obj);
1146                 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
1147                                              static_cast<uint32_t>(HttpDataType::OBJECT));
1148                 return true;
1149             }
1150 
1151             // parse maybe failed
1152             return false;
1153         }
1154         case HttpDataType::ARRAY_BUFFER: {
1155             void *data = nullptr;
1156             auto body = context->response.GetResult();
1157             napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), body.size(), &data);
1158             if (data != nullptr && arrayBuffer != nullptr) {
1159                 if (memcpy_s(data, body.size(), body.c_str(), body.size()) < 0) {
1160                     NETSTACK_LOGE("[ProcByExpectDataType] memory copy failed");
1161                     return true;
1162                 }
1163                 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, arrayBuffer);
1164                 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
1165                                              static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER));
1166             }
1167             return true;
1168         }
1169         default:
1170             break;
1171     }
1172     return false;
1173 }
1174 
1175 #ifndef MAC_PLATFORM
AsyncRunRequest(RequestContext * context)1176 void HttpExec::AsyncRunRequest(RequestContext *context)
1177 {
1178     HttpAsyncWork::ExecRequest(context->GetEnv(), context);
1179 }
1180 #endif
1181 
IsInitialized()1182 bool HttpExec::IsInitialized()
1183 {
1184     return staticVariable_.initialized;
1185 }
1186 
DeInitialize()1187 void HttpExec::DeInitialize()
1188 {
1189     std::lock_guard<std::mutex> lock(staticVariable_.curlMultiMutex);
1190     staticVariable_.runThread = false;
1191     staticVariable_.conditionVariable.notify_all();
1192     if (staticVariable_.workThread.joinable()) {
1193         staticVariable_.workThread.join();
1194     }
1195     if (staticVariable_.curlMulti) {
1196         curl_multi_cleanup(staticVariable_.curlMulti);
1197     }
1198     staticVariable_.initialized = false;
1199 }
1200 
ExecFlush(BaseContext * context)1201 bool HttpResponseCacheExec::ExecFlush(BaseContext *context)
1202 {
1203     (void)context;
1204     CacheProxy::FlushCache();
1205     return true;
1206 }
1207 
FlushCallback(BaseContext * context)1208 napi_value HttpResponseCacheExec::FlushCallback(BaseContext *context)
1209 {
1210     return NapiUtils::GetUndefined(context->GetEnv());
1211 }
1212 
ExecDelete(BaseContext * context)1213 bool HttpResponseCacheExec::ExecDelete(BaseContext *context)
1214 {
1215     (void)context;
1216     CacheProxy::StopCacheAndDelete();
1217     return true;
1218 }
1219 
DeleteCallback(BaseContext * context)1220 napi_value HttpResponseCacheExec::DeleteCallback(BaseContext *context)
1221 {
1222     return NapiUtils::GetUndefined(context->GetEnv());
1223 }
1224 
SetMultiPartOption(CURL * curl,RequestContext * context)1225 bool HttpExec::SetMultiPartOption(CURL *curl, RequestContext *context)
1226 {
1227     auto header = context->options.GetHeader();
1228     auto type = CommonUtils::ToLower(header[HttpConstant::HTTP_CONTENT_TYPE]);
1229     if (type != HttpConstant::HTTP_CONTENT_TYPE_MULTIPART) {
1230         return true;
1231     }
1232     auto multiPartDataList = context->options.GetMultiPartDataList();
1233     if (multiPartDataList.empty()) {
1234         return true;
1235     }
1236     curl_mime *multipart = curl_mime_init(curl);
1237     if (multipart == nullptr) {
1238         return false;
1239     }
1240     context->SetMultipart(multipart);
1241     curl_mimepart *part = nullptr;
1242     bool hasData = false;
1243     for (auto &multiFormData : multiPartDataList) {
1244         if (multiFormData.name.empty()) {
1245             continue;
1246         }
1247         if (multiFormData.data.empty() && multiFormData.filePath.empty()) {
1248             NETSTACK_LOGE("Failed to set name %{public}s, error no data and filepath at the same time",
1249                           multiFormData.name.c_str());
1250             continue;
1251         }
1252         part = curl_mime_addpart(multipart);
1253         SetFormDataOption(multiFormData, part, curl, context);
1254         hasData = true;
1255     }
1256     if (hasData) {
1257         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_MIMEPOST, multipart, context);
1258     }
1259     return true;
1260 }
1261 
SetFormDataOption(MultiFormData & multiFormData,curl_mimepart * part,CURL * curl,RequestContext * context)1262 void HttpExec::SetFormDataOption(MultiFormData &multiFormData, curl_mimepart *part, CURL *curl,
1263                                  RequestContext *context)
1264 {
1265     CURLcode result = curl_mime_name(part, multiFormData.name.c_str());
1266     if (result != CURLE_OK) {
1267         NETSTACK_LOGE("Failed to set name %{public}s, error: %{public}s", multiFormData.name.c_str(),
1268                       curl_easy_strerror(result));
1269         return;
1270     }
1271     if (!multiFormData.contentType.empty()) {
1272         result = curl_mime_type(part, multiFormData.contentType.c_str());
1273         if (result != CURLE_OK) {
1274             NETSTACK_LOGE("Failed to set contentType: %{public}s, error: %{public}s", multiFormData.name.c_str(),
1275                           curl_easy_strerror(result));
1276         }
1277     }
1278     if (!multiFormData.remoteFileName.empty()) {
1279         result = curl_mime_filename(part, multiFormData.remoteFileName.c_str());
1280         if (result != CURLE_OK) {
1281             NETSTACK_LOGE("Failed to set remoteFileName: %{public}s, error: %{public}s", multiFormData.name.c_str(),
1282                           curl_easy_strerror(result));
1283         }
1284     }
1285     if (!multiFormData.data.empty()) {
1286         result = curl_mime_data(part, multiFormData.data.c_str(), CURL_ZERO_TERMINATED);
1287         if (result != CURLE_OK) {
1288             NETSTACK_LOGE("Failed to set data: %{public}s, error: %{public}s", multiFormData.name.c_str(),
1289                           curl_easy_strerror(result));
1290         }
1291     } else {
1292         result = curl_mime_filedata(part, multiFormData.filePath.c_str());
1293         if (result != CURLE_OK) {
1294             NETSTACK_LOGE("Failed to set file data: %{public}s, error: %{public}s", multiFormData.name.c_str(),
1295                           curl_easy_strerror(result));
1296         }
1297     }
1298 }
1299 } // namespace OHOS::NetStack::Http
1300