• 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 
42 #define NETSTACK_CURL_EASY_SET_OPTION(handle, opt, data, asyncContext)                                   \
43     do {                                                                                                 \
44         CURLcode result = curl_easy_setopt(handle, opt, data);                                           \
45         if (result != CURLE_OK) {                                                                        \
46             const char *err = curl_easy_strerror(result);                                                \
47             NETSTACK_LOGE("Failed to set option: %{public}s, %{public}s %{public}d", #opt, err, result); \
48             (asyncContext)->SetErrorCode(result);                                                        \
49             return false;                                                                                \
50         }                                                                                                \
51     } while (0)
52 
53 namespace OHOS::NetStack::Http {
54 static constexpr size_t MAX_LIMIT = 5 * 1024 * 1024;
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 #ifdef HTTP_PROXY_ENABLE
60 static constexpr int32_t SYSPARA_MAX_SIZE = 128;
61 static constexpr const char *DEFAULT_HTTP_PROXY_HOST = "NONE";
62 static constexpr const char *DEFAULT_HTTP_PROXY_PORT = "0";
63 static constexpr const char *DEFAULT_HTTP_PROXY_EXCLUSION_LIST = "NONE";
64 static constexpr const char *HTTP_PROXY_HOST_KEY = "persist.netmanager_base.http_proxy.host";
65 static constexpr const char *HTTP_PROXY_PORT_KEY = "persist.netmanager_base.http_proxy.port";
66 static constexpr const char *HTTP_PROXY_EXCLUSIONS_KEY = "persist.netmanager_base.http_proxy.exclusion_list";
67 #endif
68 
CallbackTemplate(uv_work_t * work,int status)69 template <napi_value (*MakeJsValue)(napi_env, void *)> static void CallbackTemplate(uv_work_t *work, int status)
70 {
71     (void)status;
72 
73     auto workWrapper = static_cast<UvWorkWrapper *>(work->data);
74     napi_env env = workWrapper->env;
75     auto closeScope = [env](napi_handle_scope scope) { NapiUtils::CloseScope(env, scope); };
76     std::unique_ptr<napi_handle_scope__, decltype(closeScope)> scope(NapiUtils::OpenScope(env), closeScope);
77 
78     napi_value obj = MakeJsValue(env, workWrapper->data);
79 
80     std::pair<napi_value, napi_value> arg = {NapiUtils::GetUndefined(workWrapper->env), obj};
81     workWrapper->manager->Emit(workWrapper->type, arg);
82 
83     delete workWrapper;
84     delete work;
85 }
86 
AddCurlHandle(CURL * handle,RequestContext * context)87 bool HttpExec::AddCurlHandle(CURL *handle, RequestContext *context)
88 {
89     if (handle == nullptr || staticVariable_.curlMulti == nullptr) {
90         NETSTACK_LOGE("handle nullptr");
91         return false;
92     }
93 
94     std::thread emplaceInfoThread([context, handle] {
95         std::lock_guard guard(staticVariable_.curlMultiMutex);
96         staticVariable_.infoQueue.emplace(context, handle);
97         staticVariable_.conditionVariable.notify_all();
98     });
99     emplaceInfoThread.detach();
100 
101     return true;
102 }
103 
104 HttpExec::StaticVariable HttpExec::staticVariable_; /* NOLINT */
105 
RequestWithoutCache(RequestContext * context)106 bool HttpExec::RequestWithoutCache(RequestContext *context)
107 {
108     if (!staticVariable_.initialized) {
109         NETSTACK_LOGE("curl not init");
110         return false;
111     }
112 
113     auto handle = curl_easy_init();
114     if (!handle) {
115         NETSTACK_LOGE("Failed to create fetch task");
116         return false;
117     }
118 
119     std::vector<std::string> vec;
120     std::for_each(context->options.GetHeader().begin(), context->options.GetHeader().end(),
121                   [&vec](const std::pair<std::string, std::string> &p) {
122                       vec.emplace_back(p.first + HttpConstant::HTTP_HEADER_SEPARATOR + p.second);
123                   });
124     context->SetCurlHeaderList(MakeHeaders(vec));
125 
126     if (!SetOption(handle, context, context->GetCurlHeaderList())) {
127         NETSTACK_LOGE("set option failed");
128         return false;
129     }
130 
131     context->response.SetRequestTime(HttpTime::GetNowTimeGMT());
132 
133     if (!AddCurlHandle(handle, context)) {
134         NETSTACK_LOGE("add handle failed");
135         return false;
136     }
137 
138     return true;
139 }
140 
GetCurlDataFromHandle(CURL * handle,RequestContext * context,CURLMSG curlMsg,CURLcode result)141 bool HttpExec::GetCurlDataFromHandle(CURL *handle, RequestContext *context, CURLMSG curlMsg, CURLcode result)
142 {
143     if (curlMsg != CURLMSG_DONE) {
144         NETSTACK_LOGE("CURLMSG %{public}s", std::to_string(curlMsg).c_str());
145         context->SetErrorCode(NapiUtils::NETSTACK_NAPI_INTERNAL_ERROR);
146         return false;
147     }
148 
149     if (result != CURLE_OK) {
150         context->SetErrorCode(result);
151         NETSTACK_LOGE("CURLcode result %{public}s", std::to_string(result).c_str());
152         return false;
153     }
154 
155     context->response.SetResponseTime(HttpTime::GetNowTimeGMT());
156 
157     int64_t responseCode;
158     CURLcode code = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
159     if (code != CURLE_OK) {
160         context->SetErrorCode(code);
161         return false;
162     }
163     context->response.SetResponseCode(responseCode);
164     if (context->response.GetResponseCode() == static_cast<uint32_t>(ResponseCode::NOT_MODIFIED)) {
165         NETSTACK_LOGI("cache is NOT_MODIFIED, we use the cache");
166         context->SetResponseByCache();
167         return true;
168     }
169     NETSTACK_LOGD("responseCode is %{public}s", std::to_string(responseCode).c_str());
170 
171     struct curl_slist *cookies = nullptr;
172     code = curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &cookies);
173     if (code != CURLE_OK) {
174         context->SetErrorCode(code);
175         return false;
176     }
177 
178     std::unique_ptr<struct curl_slist, decltype(&curl_slist_free_all)> cookiesHandle(cookies, curl_slist_free_all);
179     while (cookies) {
180         context->response.AppendCookies(cookies->data, strlen(cookies->data));
181         if (cookies->next != nullptr) {
182             context->response.AppendCookies(HttpConstant::HTTP_LINE_SEPARATOR,
183                                             strlen(HttpConstant::HTTP_LINE_SEPARATOR));
184         }
185         cookies = cookies->next;
186     }
187     return true;
188 }
189 
190 #ifdef ENABLE_EVENT_HANDLER
HttpEventHandlerCallback(RequestContext * context)191 void HttpExec::HttpEventHandlerCallback(RequestContext *context)
192 {
193     std::mutex lock;
194     if (EventManager::IsManagerValid(context->GetManager())) {
195         if (context->IsRequestInStream()) {
196             auto manager = context->GetManager();
197             auto eventHandler = manager->GetNetstackEventHandler();
198             if (!eventHandler) {
199                 NETSTACK_LOGE("netstack eventHandler is nullptr");
200                 return;
201             }
202             eventHandler->PostSyncTask([&context, &lock]() {
203                 std::lock_guard<std::mutex> callbackLock(lock);
204                 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context,
205                                                      HttpAsyncWork::RequestInStreamCallback);
206             });
207             if (context->IsExecOK()) {
208                 eventHandler->PostSyncTask([&context, &lock]() {
209                     std::lock_guard<std::mutex> callbackLock(lock);
210                     NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataEnd);
211                 });
212             }
213         } else {
214             NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, HttpAsyncWork::RequestCallback);
215         }
216     }
217 }
218 #endif
219 
HandleCurlData(CURLMsg * msg)220 void HttpExec::HandleCurlData(CURLMsg *msg)
221 {
222     if (msg == nullptr) {
223         return;
224     }
225 
226     auto handle = msg->easy_handle;
227     if (handle == nullptr) {
228         return;
229     }
230 
231     auto it = staticVariable_.contextMap.find(handle);
232     if (it == staticVariable_.contextMap.end()) {
233         NETSTACK_LOGE("can not find context");
234         return;
235     }
236 
237     auto context = it->second;
238     staticVariable_.contextMap.erase(it);
239     if (context == nullptr) {
240         NETSTACK_LOGE("can not find context");
241         return;
242     }
243 
244     NETSTACK_LOGI("priority = %{public}d", context->options.GetPriority());
245     context->SetExecOK(GetCurlDataFromHandle(handle, context, msg->msg, msg->data.result));
246     if (context->IsExecOK()) {
247         CacheProxy proxy(context->options);
248         proxy.WriteResponseToCache(context->response);
249     }
250 
251     if (context->GetManager() == nullptr) {
252         NETSTACK_LOGE("can not find context manager");
253         return;
254     }
255 #ifdef ENABLE_EVENT_HANDLER
256     HttpEventHandlerCallback(context);
257 #endif
258 }
259 
ExecRequest(RequestContext * context)260 bool HttpExec::ExecRequest(RequestContext *context)
261 {
262     if (!CommonUtils::HasInternetPermission()) {
263         context->SetPermissionDenied(true);
264         return false;
265     }
266     if (context->GetManager()->IsEventDestroy()) {
267         return false;
268     }
269     context->options.SetRequestTime(HttpTime::GetNowTimeGMT());
270     CacheProxy proxy(context->options);
271     if (context->IsUsingCache() && proxy.ReadResponseFromCache(context)) {
272         return true;
273     }
274 
275     if (!RequestWithoutCache(context)) {
276         context->SetErrorCode(NapiUtils::NETSTACK_NAPI_INTERNAL_ERROR);
277         if (EventManager::IsManagerValid(context->GetManager())) {
278             if (context->IsRequestInStream()) {
279                 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context,
280                                                      HttpAsyncWork::RequestInStreamCallback);
281             } else {
282                 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, HttpAsyncWork::RequestCallback);
283             }
284         }
285         return false;
286     }
287 
288     return true;
289 }
290 
RequestCallback(RequestContext * context)291 napi_value HttpExec::RequestCallback(RequestContext *context)
292 {
293     napi_value object = NapiUtils::CreateObject(context->GetEnv());
294     if (NapiUtils::GetValueType(context->GetEnv(), object) != napi_object) {
295         return nullptr;
296     }
297 
298     NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESPONSE_CODE,
299                                  context->response.GetResponseCode());
300     NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_COOKIES,
301                                      context->response.GetCookies());
302 
303     napi_value header = MakeResponseHeader(context->GetEnv(), context);
304     if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) {
305         NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_HEADER, header);
306     }
307 
308     if (context->options.GetHttpDataType() != HttpDataType::NO_DATA_TYPE && ProcByExpectDataType(object, context)) {
309         return object;
310     }
311 
312     auto contentType = CommonUtils::ToLower(const_cast<std::map<std::string, std::string> &>(
313         context->response.GetHeader())[HttpConstant::HTTP_CONTENT_TYPE]);
314     if (contentType.find(HttpConstant::HTTP_CONTENT_TYPE_OCTET_STREAM) != std::string::npos ||
315         contentType.find(HttpConstant::HTTP_CONTENT_TYPE_IMAGE) != std::string::npos) {
316         void *data = nullptr;
317         auto body = context->response.GetResult();
318         napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), body.size(), &data);
319         if (data != nullptr && arrayBuffer != nullptr) {
320             if (memcpy_s(data, body.size(), body.c_str(), body.size()) != EOK) {
321                 NETSTACK_LOGE("memcpy_s failed!");
322                 return object;
323             }
324             NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, arrayBuffer);
325         }
326         NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
327                                      static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER));
328         return object;
329     }
330 
331     /* now just support utf8 */
332     NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT,
333                                      context->response.GetResult());
334     NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
335                                  static_cast<uint32_t>(HttpDataType::STRING));
336     return object;
337 }
338 
RequestInStreamCallback(OHOS::NetStack::Http::RequestContext * context)339 napi_value HttpExec::RequestInStreamCallback(OHOS::NetStack::Http::RequestContext *context)
340 {
341     napi_value number = NapiUtils::CreateUint32(context->GetEnv(), context->response.GetResponseCode());
342     if (NapiUtils::GetValueType(context->GetEnv(), number) != napi_number) {
343         return nullptr;
344     }
345     return number;
346 }
347 
MakeUrl(const std::string & url,std::string param,const std::string & extraParam)348 std::string HttpExec::MakeUrl(const std::string &url, std::string param, const std::string &extraParam)
349 {
350     if (param.empty()) {
351         param += extraParam;
352     } else {
353         param += HttpConstant::HTTP_URL_PARAM_SEPARATOR;
354         param += extraParam;
355     }
356 
357     if (param.empty()) {
358         return url;
359     }
360 
361     return url + HttpConstant::HTTP_URL_PARAM_START + param;
362 }
363 
MethodForGet(const std::string & method)364 bool HttpExec::MethodForGet(const std::string &method)
365 {
366     return (method == HttpConstant::HTTP_METHOD_HEAD || method == HttpConstant::HTTP_METHOD_OPTIONS ||
367             method == HttpConstant::HTTP_METHOD_TRACE || method == HttpConstant::HTTP_METHOD_GET ||
368             method == HttpConstant::HTTP_METHOD_CONNECT);
369 }
370 
MethodForPost(const std::string & method)371 bool HttpExec::MethodForPost(const std::string &method)
372 {
373     return (method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT ||
374             method == HttpConstant::HTTP_METHOD_DELETE);
375 }
376 
EncodeUrlParam(std::string & str)377 bool HttpExec::EncodeUrlParam(std::string &str)
378 {
379     char encoded[4];
380     std::string encodeOut;
381     size_t length = strlen(str.c_str());
382     for (size_t i = 0; i < length; ++i) {
383         auto c = static_cast<uint8_t>(str.c_str()[i]);
384         if (IsUnReserved(c)) {
385             encodeOut += static_cast<char>(c);
386         } else {
387             if (sprintf_s(encoded, sizeof(encoded), "%%%02X", c) < 0) {
388                 return false;
389             }
390             encodeOut += encoded;
391         }
392     }
393 
394     if (str == encodeOut) {
395         return false;
396     }
397     str = encodeOut;
398     return true;
399 }
400 
AddRequestInfo()401 void HttpExec::AddRequestInfo()
402 {
403     std::lock_guard guard(staticVariable_.curlMultiMutex);
404     int num = 0;
405     while (!staticVariable_.infoQueue.empty()) {
406         if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
407             break;
408         }
409 
410         auto info = staticVariable_.infoQueue.top();
411         staticVariable_.infoQueue.pop();
412         auto ret = curl_multi_add_handle(staticVariable_.curlMulti, info.handle);
413         if (ret == CURLM_OK) {
414             staticVariable_.contextMap[info.handle] = info.context;
415         }
416 
417         ++num;
418         if (num >= CURL_HANDLE_NUM) {
419             break;
420         }
421     }
422 }
423 
RunThread()424 void HttpExec::RunThread()
425 {
426     while (staticVariable_.runThread && staticVariable_.curlMulti != nullptr) {
427         AddRequestInfo();
428         SendRequest();
429         ReadResponse();
430         std::this_thread::sleep_for(std::chrono::milliseconds(CURL_TIMEOUT_MS));
431         std::unique_lock l(staticVariable_.curlMultiMutex);
432         staticVariable_.conditionVariable.wait_for(l, std::chrono::seconds(CONDITION_TIMEOUT_S), [] {
433             return !staticVariable_.infoQueue.empty() || !staticVariable_.contextMap.empty();
434         });
435     }
436 }
437 
SendRequest()438 void HttpExec::SendRequest()
439 {
440     std::lock_guard guard(staticVariable_.curlMultiMutex);
441 
442     int runningHandle = 0;
443     int num = 0;
444     do {
445         if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
446             break;
447         }
448 
449         auto ret = curl_multi_perform(staticVariable_.curlMulti, &runningHandle);
450 
451         if (runningHandle > 0) {
452             ret = curl_multi_poll(staticVariable_.curlMulti, nullptr, 0, CURL_MAX_WAIT_MSECS, nullptr);
453         }
454 
455         if (ret != CURLM_OK) {
456             return;
457         }
458 
459         ++num;
460         if (num >= CURL_HANDLE_NUM) {
461             break;
462         }
463     } while (runningHandle > 0);
464 }
465 
ReadResponse()466 void HttpExec::ReadResponse()
467 {
468     std::lock_guard guard(staticVariable_.curlMultiMutex);
469     CURLMsg *msg = nullptr; /* NOLINT */
470     do {
471         if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
472             break;
473         }
474 
475         int leftMsg;
476         msg = curl_multi_info_read(staticVariable_.curlMulti, &leftMsg);
477         if (msg) {
478             if (msg->msg == CURLMSG_DONE) {
479                 HandleCurlData(msg);
480             }
481             if (msg->easy_handle) {
482                 (void)curl_multi_remove_handle(staticVariable_.curlMulti, msg->easy_handle);
483                 (void)curl_easy_cleanup(msg->easy_handle);
484             }
485         }
486     } while (msg);
487 }
488 
GetGlobalHttpProxyInfo(std::string & host,int32_t & port,std::string & exclusions)489 void HttpExec::GetGlobalHttpProxyInfo(std::string &host, int32_t &port, std::string &exclusions)
490 {
491 #ifdef HTTP_PROXY_ENABLE
492     char httpProxyHost[SYSPARA_MAX_SIZE] = {0};
493     char httpProxyPort[SYSPARA_MAX_SIZE] = {0};
494     char httpProxyExclusions[SYSPARA_MAX_SIZE] = {0};
495     GetParameter(HTTP_PROXY_HOST_KEY, DEFAULT_HTTP_PROXY_HOST, httpProxyHost, sizeof(httpProxyHost));
496     GetParameter(HTTP_PROXY_PORT_KEY, DEFAULT_HTTP_PROXY_PORT, httpProxyPort, sizeof(httpProxyPort));
497     GetParameter(HTTP_PROXY_EXCLUSIONS_KEY, DEFAULT_HTTP_PROXY_EXCLUSION_LIST, httpProxyExclusions,
498                  sizeof(httpProxyExclusions));
499 
500     host = Base64::Decode(httpProxyHost);
501     if (host == DEFAULT_HTTP_PROXY_HOST) {
502         host = std::string();
503     }
504     exclusions = httpProxyExclusions;
505     if (exclusions == DEFAULT_HTTP_PROXY_EXCLUSION_LIST) {
506         exclusions = std::string();
507     }
508 
509     port = std::atoi(httpProxyPort);
510 #endif
511 }
512 
GetHttpProxyInfo(RequestContext * context,std::string & host,int32_t & port,std::string & exclusions)513 void HttpExec::GetHttpProxyInfo(RequestContext *context, std::string &host, int32_t &port, std::string &exclusions)
514 {
515     if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_DEFAULT) {
516 #ifdef HAS_NETMANAGER_BASE
517         using namespace NetManagerStandard;
518         HttpProxy httpProxy;
519         NetConnClient::GetInstance().GetDefaultHttpProxy(httpProxy);
520         host = httpProxy.GetHost();
521         port = httpProxy.GetPort();
522         exclusions = CommonUtils::ToString(httpProxy.GetExclusionList());
523 #else
524         GetGlobalHttpProxyInfo(host, port, exclusions);
525 #endif
526     } else if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_SPECIFIED) {
527         context->options.GetSpecifiedHttpProxy(host, port, exclusions);
528     }
529 }
530 
Initialize()531 bool HttpExec::Initialize()
532 {
533     std::lock_guard<std::mutex> lock(staticVariable_.mutexForInitialize);
534     if (staticVariable_.initialized) {
535         return true;
536     }
537     NETSTACK_LOGI("call curl_global_init");
538     if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
539         NETSTACK_LOGE("Failed to initialize 'curl'");
540         return false;
541     }
542 
543     staticVariable_.curlMulti = curl_multi_init();
544     if (staticVariable_.curlMulti == nullptr) {
545         NETSTACK_LOGE("Failed to initialize 'curl_multi'");
546         return false;
547     }
548 
549     staticVariable_.workThread = std::thread(RunThread);
550 
551     staticVariable_.initialized = true;
552     return staticVariable_.initialized;
553 }
554 
SetOtherOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)555 bool HttpExec::SetOtherOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
556 {
557     std::string host, exclusions;
558     int32_t port = 0;
559     GetHttpProxyInfo(context, host, port, exclusions);
560     if (!host.empty()) {
561         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXY, host.c_str(), context);
562         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYPORT, port, context);
563         if (!exclusions.empty()) {
564             NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROXY, exclusions.c_str(), context);
565         }
566         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP, context);
567         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPPROXYTUNNEL, 1L, context);
568     }
569 
570 #ifdef NETSTACK_PROXY_PASS
571     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYUSERPWD, NETSTACK_PROXY_PASS, context);
572 #endif // NETSTACK_PROXY_PASS
573 
574 #ifdef NO_SSL_CERTIFICATION
575     // in real life, you should buy a ssl certification and rename it to /etc/ssl/cert.pem
576     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
577     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
578 #else
579 #ifndef WINDOWS_PLATFORM
580     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, context->options.GetCaPath().c_str(), context);
581 #endif // WINDOWS_PLATFORM
582 #endif // NO_SSL_CERTIFICATION
583 
584 #ifdef HTTP_CURL_PRINT_VERBOSE
585     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_VERBOSE, 1L, context);
586 #endif
587 
588 #ifndef WINDOWS_PLATFORM
589     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_ACCEPT_ENCODING, HttpConstant::HTTP_CONTENT_ENCODING_GZIP, context);
590 #endif
591     return true;
592 }
593 
SetOption(CURL * curl,RequestContext * context,struct curl_slist * requestHeader)594 bool HttpExec::SetOption(CURL *curl, RequestContext *context, struct curl_slist *requestHeader)
595 {
596     const std::string &method = context->options.GetMethod();
597     if (!MethodForGet(method) && !MethodForPost(method)) {
598         NETSTACK_LOGE("method %{public}s not supported", method.c_str());
599         return false;
600     }
601 
602     if (context->options.GetMethod() == HttpConstant::HTTP_METHOD_HEAD) {
603         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOBODY, 1L, context);
604     }
605 
606     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, context->options.GetUrl().c_str(), context);
607     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CUSTOMREQUEST, method.c_str(), context);
608 
609     if (MethodForPost(method) && !context->options.GetBody().empty()) {
610         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POST, 1L, context);
611         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, context->options.GetBody().c_str(), context);
612         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDSIZE, context->options.GetBody().size(), context);
613     }
614 
615     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback, context);
616     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFODATA, context, context);
617     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROGRESS, 0L, context);
618 
619     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEFUNCTION, OnWritingMemoryBody, context);
620     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEDATA, context, context);
621 
622     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader, context);
623     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERDATA, context, context);
624 
625     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPHEADER, requestHeader, context);
626 
627     // Some servers don't like requests that are made without a user-agent field, so we provide one
628     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_USERAGENT, HttpConstant::HTTP_DEFAULT_USER_AGENT, context);
629 
630     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_FOLLOWLOCATION, 1L, context);
631 
632     /* first #undef CURL_DISABLE_COOKIES in curl config */
633     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_COOKIEFILE, "", context);
634 
635     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOSIGNAL, 1L, context);
636 
637     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_TIMEOUT_MS, context->options.GetReadTimeout(), context);
638     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CONNECTTIMEOUT_MS, context->options.GetConnectTimeout(), context);
639 
640     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTP_VERSION, context->options.GetHttpVersion(), context);
641     if (!SetOtherOption(curl, context)) {
642         return false;
643     }
644 
645     return true;
646 }
647 
OnWritingMemoryBody(const void * data,size_t size,size_t memBytes,void * userData)648 size_t HttpExec::OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData)
649 {
650     auto context = static_cast<RequestContext *>(userData);
651     if (context == nullptr) {
652         return 0;
653     }
654     if (context->GetManager()->IsEventDestroy()) {
655         return 0;
656     }
657     if (context->IsRequestInStream()) {
658         context->SetTempData(data, size * memBytes);
659         NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataReceive);
660         return size * memBytes;
661     }
662     if (context->response.GetResult().size() > MAX_LIMIT) {
663         NETSTACK_LOGE("response data exceeds the maximum limit");
664         return 0;
665     }
666     context->response.AppendResult(data, size * memBytes);
667     return size * memBytes;
668 }
669 
OnWritingMemoryHeader(const void * data,size_t size,size_t memBytes,void * userData)670 size_t HttpExec::OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData)
671 {
672     auto context = static_cast<RequestContext *>(userData);
673     if (context == nullptr) {
674         return 0;
675     }
676     if (context->GetManager()->IsEventDestroy()) {
677         return 0;
678     }
679     if (context->response.GetResult().size() > MAX_LIMIT) {
680         NETSTACK_LOGE("response data exceeds the maximum limit");
681         return 0;
682     }
683     context->response.AppendRawHeader(data, size * memBytes);
684     if (CommonUtils::EndsWith(context->response.GetRawHeader(), HttpConstant::HTTP_RESPONSE_HEADER_SEPARATOR)) {
685         context->response.ParseHeaders();
686         if (context->GetManager() && EventManager::IsManagerValid(context->GetManager())) {
687             context->GetManager()->EmitByUv(ON_HEADER_RECEIVE, context, CallbackTemplate<MakeResponseHeader>);
688             context->GetManager()->EmitByUv(ON_HEADERS_RECEIVE, context, CallbackTemplate<MakeResponseHeader>);
689         }
690     }
691     return size * memBytes;
692 }
693 
OnDataReceive(napi_env env,napi_status status,void * data)694 void HttpExec::OnDataReceive(napi_env env, napi_status status, void *data)
695 {
696     auto context = static_cast<RequestContext *>(data);
697     if (context == nullptr) {
698         return;
699     }
700 
701     void *buffer = nullptr;
702     auto tempData = context->GetTempData();
703     context->PopTempData();
704     if (tempData.empty()) {
705         NETSTACK_LOGI("[GetTempData] tempDate is empty!");
706         return;
707     }
708     napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), tempData.size(), &buffer);
709     if (buffer == nullptr || arrayBuffer == nullptr) {
710         return;
711     }
712     if (memcpy_s(buffer, tempData.size(), tempData.data(), tempData.size()) != EOK) {
713         NETSTACK_LOGE("[CreateArrayBuffer] memory copy failed");
714         return;
715     }
716     context->Emit(ON_DATA_RECEIVE, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), arrayBuffer));
717 }
718 
OnDataProgress(napi_env env,napi_status status,void * data)719 void HttpExec::OnDataProgress(napi_env env, napi_status status, void *data)
720 {
721     auto context = static_cast<RequestContext *>(data);
722     if (context == nullptr) {
723         return;
724     }
725     auto progress = NapiUtils::CreateObject(context->GetEnv());
726     if (NapiUtils::GetValueType(context->GetEnv(), progress) == napi_undefined) {
727         return;
728     }
729     NapiUtils::SetUint32Property(context->GetEnv(), progress, "receiveSize",
730                                  static_cast<uint32_t>(context->GetDlLen().nLen));
731     NapiUtils::SetUint32Property(context->GetEnv(), progress, "totalSize",
732                                  static_cast<uint32_t>(context->GetDlLen().tLen));
733     context->PopDlLen();
734     context->Emit(ON_DATA_RECEIVE_PROGRESS, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), progress));
735 }
736 
OnDataEnd(napi_env env,napi_status status,void * data)737 void HttpExec::OnDataEnd(napi_env env, napi_status status, void *data)
738 {
739     auto context = static_cast<RequestContext *>(data);
740     if (context == nullptr) {
741         return;
742     }
743     auto undefined = NapiUtils::GetUndefined(context->GetEnv());
744     context->Emit(ON_DATA_END, std::make_pair(undefined, undefined));
745 }
746 
ProgressCallback(void * userData,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)747 int HttpExec::ProgressCallback(void *userData, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
748                                curl_off_t ulnow)
749 {
750     (void)ultotal;
751     (void)ulnow;
752     auto context = static_cast<RequestContext *>(userData);
753     if (context == nullptr || !context->IsRequestInStream()) {
754         return 0;
755     }
756     if (context->GetManager()->IsEventDestroy()) {
757         return 0;
758     }
759     if (dltotal != 0) {
760         context->SetDlLen(dlnow, dltotal);
761         NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataProgress);
762     }
763     return 0;
764 }
765 
MakeHeaders(const std::vector<std::string> & vec)766 struct curl_slist *HttpExec::MakeHeaders(const std::vector<std::string> &vec)
767 {
768     struct curl_slist *header = nullptr;
769     std::for_each(vec.begin(), vec.end(), [&header](const std::string &s) {
770         if (!s.empty()) {
771             header = curl_slist_append(header, s.c_str());
772         }
773     });
774     return header;
775 }
776 
MakeResponseHeader(napi_env env,void * ctx)777 napi_value HttpExec::MakeResponseHeader(napi_env env, void *ctx)
778 {
779     auto context = reinterpret_cast<RequestContext *>(ctx);
780     (void)env;
781     napi_value header = NapiUtils::CreateObject(context->GetEnv());
782     if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) {
783         std::for_each(context->response.GetHeader().begin(), context->response.GetHeader().end(),
784                       [context, header](const std::pair<std::string, std::string> &p) {
785                           if (!p.first.empty() && !p.second.empty()) {
786                               NapiUtils::SetStringPropertyUtf8(context->GetEnv(), header, p.first, p.second);
787                           }
788                       });
789     }
790     return header;
791 }
792 
IsUnReserved(unsigned char in)793 bool HttpExec::IsUnReserved(unsigned char in)
794 {
795     if ((in >= '0' && in <= '9') || (in >= 'a' && in <= 'z') || (in >= 'A' && in <= 'Z')) {
796         return true;
797     }
798     switch (in) {
799         case '-':
800         case '.':
801         case '_':
802         case '~':
803             return true;
804         default:
805             break;
806     }
807     return false;
808 }
809 
ProcByExpectDataType(napi_value object,RequestContext * context)810 bool HttpExec::ProcByExpectDataType(napi_value object, RequestContext *context)
811 {
812     switch (context->options.GetHttpDataType()) {
813         case HttpDataType::STRING: {
814             NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT,
815                                              context->response.GetResult());
816             NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
817                                          static_cast<uint32_t>(HttpDataType::STRING));
818             return true;
819         }
820         case HttpDataType::OBJECT: {
821             if (context->response.GetResult().size() > HttpConstant::MAX_JSON_PARSE_SIZE) {
822                 return false;
823             }
824 
825             napi_value obj = NapiUtils::JsonParse(context->GetEnv(), context->response.GetResult());
826             if (obj) {
827                 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, obj);
828                 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
829                                              static_cast<uint32_t>(HttpDataType::OBJECT));
830                 return true;
831             }
832 
833             // parse maybe failed
834             return false;
835         }
836         case HttpDataType::ARRAY_BUFFER: {
837             void *data = nullptr;
838             auto body = context->response.GetResult();
839             napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), body.size(), &data);
840             if (data != nullptr && arrayBuffer != nullptr) {
841                 if (memcpy_s(data, body.size(), body.c_str(), body.size()) < 0) {
842                     NETSTACK_LOGE("[ProcByExpectDataType] memory copy failed");
843                     return true;
844                 }
845                 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, arrayBuffer);
846                 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
847                                              static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER));
848             }
849             return true;
850         }
851         default:
852             break;
853     }
854     return false;
855 }
856 
857 #ifndef MAC_PLATFORM
AsyncRunRequest(RequestContext * context)858 void HttpExec::AsyncRunRequest(RequestContext *context)
859 {
860     HttpAsyncWork::ExecRequest(context->GetEnv(), context);
861 }
862 #endif
863 
IsInitialized()864 bool HttpExec::IsInitialized()
865 {
866     return staticVariable_.initialized;
867 }
868 
DeInitialize()869 void HttpExec::DeInitialize()
870 {
871     std::lock_guard<std::mutex> lock(staticVariable_.curlMultiMutex);
872     staticVariable_.runThread = false;
873     staticVariable_.conditionVariable.notify_all();
874     if (staticVariable_.workThread.joinable()) {
875         staticVariable_.workThread.join();
876     }
877     if (staticVariable_.curlMulti) {
878         curl_multi_cleanup(staticVariable_.curlMulti);
879     }
880     staticVariable_.initialized = false;
881 }
882 
ExecFlush(BaseContext * context)883 bool HttpResponseCacheExec::ExecFlush(BaseContext *context)
884 {
885     (void)context;
886     CacheProxy::FlushCache();
887     return true;
888 }
889 
FlushCallback(BaseContext * context)890 napi_value HttpResponseCacheExec::FlushCallback(BaseContext *context)
891 {
892     return NapiUtils::GetUndefined(context->GetEnv());
893 }
894 
ExecDelete(BaseContext * context)895 bool HttpResponseCacheExec::ExecDelete(BaseContext *context)
896 {
897     (void)context;
898     CacheProxy::StopCacheAndDelete();
899     return true;
900 }
901 
DeleteCallback(BaseContext * context)902 napi_value HttpResponseCacheExec::DeleteCallback(BaseContext *context)
903 {
904     return NapiUtils::GetUndefined(context->GetEnv());
905 }
906 } // namespace OHOS::NetStack::Http
907