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