• 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::unique_lock l(staticVariable_.curlMultiMutex);
384         staticVariable_.conditionVariable.wait_for(l, std::chrono::seconds(CONDITION_TIMEOUT_S), [] {
385             return !staticVariable_.infoQueue.empty() || !staticVariable_.contextMap.empty();
386         });
387     }
388 }
389 
SendRequest()390 void HttpExec::SendRequest()
391 {
392     std::lock_guard guard(staticVariable_.curlMultiMutex);
393 
394     int runningHandle = 0;
395     int num = 0;
396     do {
397         if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
398             break;
399         }
400 
401         auto ret = curl_multi_perform(staticVariable_.curlMulti, &runningHandle);
402 
403         if (runningHandle > 0) {
404             ret = curl_multi_poll(staticVariable_.curlMulti, nullptr, 0, CURL_MAX_WAIT_MSECS, nullptr);
405         }
406 
407         if (ret != CURLM_OK) {
408             return;
409         }
410 
411         ++num;
412         if (num >= CURL_HANDLE_NUM) {
413             break;
414         }
415     } while (runningHandle > 0);
416 }
417 
ReadResponse()418 void HttpExec::ReadResponse()
419 {
420     std::lock_guard guard(staticVariable_.curlMultiMutex);
421     CURLMsg *msg = nullptr; /* NOLINT */
422     do {
423         if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
424             break;
425         }
426 
427         int leftMsg;
428         msg = curl_multi_info_read(staticVariable_.curlMulti, &leftMsg);
429         if (msg) {
430             if (msg->msg == CURLMSG_DONE) {
431                 HandleCurlData(msg);
432             }
433             if (msg->easy_handle) {
434                 (void)curl_multi_remove_handle(staticVariable_.curlMulti, msg->easy_handle);
435                 (void)curl_easy_cleanup(msg->easy_handle);
436             }
437         }
438     } while (msg);
439 }
440 
GetGlobalHttpProxyInfo(std::string & host,int32_t & port,std::string & exclusions)441 void HttpExec::GetGlobalHttpProxyInfo(std::string &host, int32_t &port, std::string &exclusions)
442 {
443 #ifdef HTTP_PROXY_ENABLE
444     char httpProxyHost[SYSPARA_MAX_SIZE] = {0};
445     char httpProxyPort[SYSPARA_MAX_SIZE] = {0};
446     char httpProxyExclusions[SYSPARA_MAX_SIZE] = {0};
447     GetParameter(HTTP_PROXY_HOST_KEY, DEFAULT_HTTP_PROXY_HOST, httpProxyHost, sizeof(httpProxyHost));
448     GetParameter(HTTP_PROXY_PORT_KEY, DEFAULT_HTTP_PROXY_PORT, httpProxyPort, sizeof(httpProxyPort));
449     GetParameter(HTTP_PROXY_EXCLUSIONS_KEY, DEFAULT_HTTP_PROXY_EXCLUSION_LIST, httpProxyExclusions,
450                  sizeof(httpProxyExclusions));
451 
452     host = Base64::Decode(httpProxyHost);
453     if (host == DEFAULT_HTTP_PROXY_HOST) {
454         host = std::string();
455     }
456     exclusions = httpProxyExclusions;
457     if (exclusions == DEFAULT_HTTP_PROXY_EXCLUSION_LIST) {
458         exclusions = std::string();
459     }
460 
461     port = std::atoi(httpProxyPort);
462 #endif
463 }
464 
Initialize()465 bool HttpExec::Initialize()
466 {
467     std::lock_guard<std::mutex> lock(staticVariable_.curlMultiMutex);
468     if (staticVariable_.initialized) {
469         return true;
470     }
471     NETSTACK_LOGI("call curl_global_init");
472     if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
473         NETSTACK_LOGE("Failed to initialize 'curl'");
474         return false;
475     }
476 
477     staticVariable_.curlMulti = curl_multi_init();
478     if (staticVariable_.curlMulti == nullptr) {
479         NETSTACK_LOGE("Failed to initialize 'curl_multi'");
480         return false;
481     }
482 
483     staticVariable_.workThread = std::thread(RunThread);
484 
485     staticVariable_.initialized = true;
486     return staticVariable_.initialized;
487 }
488 
SetOption(CURL * curl,RequestContext * context,struct curl_slist * requestHeader)489 bool HttpExec::SetOption(CURL *curl, RequestContext *context, struct curl_slist *requestHeader)
490 {
491     const std::string &method = context->options.GetMethod();
492     if (!MethodForGet(method) && !MethodForPost(method)) {
493         NETSTACK_LOGE("method %{public}s not supported", method.c_str());
494         return false;
495     }
496 
497     if (context->options.GetMethod() == HttpConstant::HTTP_METHOD_HEAD) {
498         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOBODY, 1L, context);
499     }
500 
501     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, context->options.GetUrl().c_str(), context);
502     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CUSTOMREQUEST, method.c_str(), context);
503 
504     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_FORBID_REUSE, 1L, context);
505 
506     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback, context);
507     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFODATA, context, context);
508     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROGRESS, 0L, context);
509 
510     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEFUNCTION, OnWritingMemoryBody, context);
511     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEDATA, context, context);
512 
513     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader, context);
514     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERDATA, context, context);
515 
516     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPHEADER, requestHeader, context);
517 
518     // Some servers don't like requests that are made without a user-agent field, so we provide one
519     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_USERAGENT, HttpConstant::HTTP_DEFAULT_USER_AGENT, context);
520 
521     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_FOLLOWLOCATION, 1L, context);
522 
523     /* first #undef CURL_DISABLE_COOKIES in curl config */
524     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_COOKIEFILE, "", context);
525 
526     std::string host, exclusions;
527     int32_t port = 0;
528     if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_DEFAULT) {
529         GetGlobalHttpProxyInfo(host, port, exclusions);
530     } else if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_SPECIFIED) {
531         context->options.GetSpecifiedHttpProxy(host, port, exclusions);
532     }
533     if (!host.empty()) {
534         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXY, host.c_str(), context);
535         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYPORT, port, context);
536         if (!exclusions.empty()) {
537             NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROXY, exclusions.c_str(), context);
538         }
539         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP, context);
540         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPPROXYTUNNEL, 1L, context);
541     }
542 #ifdef NETSTACK_PROXY_PASS
543     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYUSERPWD, NETSTACK_PROXY_PASS, context);
544 #endif // NETSTACK_PROXY_PASS
545 
546 #if NO_SSL_CERTIFICATION
547     // in real life, you should buy a ssl certification and rename it to /etc/ssl/cert.pem
548     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
549     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
550 #else
551 #ifndef WINDOWS_PLATFORM
552     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, HttpConstant::HTTP_DEFAULT_CA_PATH, context);
553 #endif // WINDOWS_PLATFORM
554 #endif // NO_SSL_CERTIFICATION
555 
556     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOSIGNAL, 1L, context);
557 #if HTTP_CURL_PRINT_VERBOSE
558     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_VERBOSE, 1L, context);
559 #endif
560     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_TIMEOUT_MS, context->options.GetReadTimeout(), context);
561     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CONNECTTIMEOUT_MS, context->options.GetConnectTimeout(), context);
562 #ifndef WINDOWS_PLATFORM
563     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_ACCEPT_ENCODING, HttpConstant::HTTP_CONTENT_ENCODING_GZIP, context);
564 #endif
565 
566     if (MethodForPost(method) && !context->options.GetBody().empty()) {
567         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POST, 1L, context);
568         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, context->options.GetBody().c_str(), context);
569         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDSIZE, context->options.GetBody().size(), context);
570     }
571 
572     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTP_VERSION, context->options.GetHttpVersion(), context);
573 
574     return true;
575 }
576 
OnWritingMemoryBody(const void * data,size_t size,size_t memBytes,void * userData)577 size_t HttpExec::OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData)
578 {
579     auto context = static_cast<RequestContext *>(userData);
580     if (context == nullptr) {
581         return 0;
582     }
583     if (context->IsRequest2()) {
584         context->SetTempData(data, size * memBytes);
585         NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataReceive);
586         return size * memBytes;
587     }
588     if (context->response.GetResult().size() > MAX_LIMIT) {
589         return 0;
590     }
591     context->response.AppendResult(data, size * memBytes);
592     return size * memBytes;
593 }
594 
OnWritingMemoryHeader(const void * data,size_t size,size_t memBytes,void * userData)595 size_t HttpExec::OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData)
596 {
597     auto context = static_cast<RequestContext *>(userData);
598     if (context == nullptr) {
599         return 0;
600     }
601     if (context->response.GetResult().size() > MAX_LIMIT) {
602         return 0;
603     }
604     context->response.AppendRawHeader(data, size * memBytes);
605     return size * memBytes;
606 }
607 
OnDataReceive(napi_env env,napi_status status,void * data)608 void HttpExec::OnDataReceive(napi_env env, napi_status status, void *data)
609 {
610     auto context = static_cast<RequestContext *>(data);
611     if (context == nullptr) {
612         return;
613     }
614 
615     void *buffer = nullptr;
616     auto tempData = context->GetTempData();
617     context->PopTempData();
618     if (tempData.empty()) {
619         NETSTACK_LOGI("[GetTempData] tempDate is empty!");
620         return;
621     }
622     napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), tempData.size(), &buffer);
623     if (buffer == nullptr || arrayBuffer == nullptr) {
624         return;
625     }
626     if (memcpy_s(buffer, tempData.size(), tempData.data(), tempData.size()) != EOK) {
627         NETSTACK_LOGE("[CreateArrayBuffer] memory copy failed");
628         return;
629     }
630     context->Emit(ON_DATA_RECEIVE, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), arrayBuffer));
631 }
632 
OnDataProgress(napi_env env,napi_status status,void * data)633 void HttpExec::OnDataProgress(napi_env env, napi_status status, void *data)
634 {
635     auto context = static_cast<RequestContext *>(data);
636     if (context == nullptr) {
637         return;
638     }
639     auto progress = NapiUtils::CreateObject(context->GetEnv());
640     if (NapiUtils::GetValueType(context->GetEnv(), progress) == napi_undefined) {
641         return;
642     }
643     NapiUtils::SetUint32Property(context->GetEnv(), progress, "receiveSize",
644                                  static_cast<uint32_t>(context->GetNowLen()));
645     context->PopNowLen();
646     NapiUtils::SetUint32Property(context->GetEnv(), progress, "totalSize",
647                                  static_cast<uint32_t>(context->GetTotalLen()));
648     context->PopTotalLen();
649     context->Emit(ON_DATA_PROGRESS, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), progress));
650 }
651 
OnDataEnd(napi_env env,napi_status status,void * data)652 void HttpExec::OnDataEnd(napi_env env, napi_status status, void *data)
653 {
654     auto context = static_cast<RequestContext *>(data);
655     if (context == nullptr) {
656         return;
657     }
658     auto undefined = NapiUtils::GetUndefined(context->GetEnv());
659     context->Emit(ON_DATA_END, std::make_pair(undefined, undefined));
660 }
661 
ProgressCallback(void * userData,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)662 int HttpExec::ProgressCallback(void *userData, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
663                                curl_off_t ulnow)
664 {
665     (void)ultotal;
666     (void)ulnow;
667     auto context = static_cast<RequestContext *>(userData);
668     if (context == nullptr || !context->IsRequest2()) {
669         return 0;
670     }
671     context->SetTotalLen(dltotal);
672     context->SetNowLen(dlnow);
673     if (dlnow != 0) {
674         NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataProgress);
675     }
676     return 0;
677 }
678 
MakeHeaders(const std::vector<std::string> & vec)679 struct curl_slist *HttpExec::MakeHeaders(const std::vector<std::string> &vec)
680 {
681     struct curl_slist *header = nullptr;
682     std::for_each(vec.begin(), vec.end(), [&header](const std::string &s) {
683         if (!s.empty()) {
684             header = curl_slist_append(header, s.c_str());
685         }
686     });
687     return header;
688 }
689 
MakeResponseHeader(RequestContext * context)690 napi_value HttpExec::MakeResponseHeader(RequestContext *context)
691 {
692     napi_value header = NapiUtils::CreateObject(context->GetEnv());
693     if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) {
694         std::for_each(context->response.GetHeader().begin(), context->response.GetHeader().end(),
695                       [context, header](const std::pair<std::string, std::string> &p) {
696                           if (!p.first.empty() && !p.second.empty()) {
697                               NapiUtils::SetStringPropertyUtf8(context->GetEnv(), header, p.first, p.second);
698                           }
699                       });
700     }
701     return header;
702 }
703 
OnHeaderReceive(RequestContext * context,napi_value header)704 void HttpExec::OnHeaderReceive(RequestContext *context, napi_value header)
705 {
706     napi_value undefined = NapiUtils::GetUndefined(context->GetEnv());
707     context->Emit(ON_HEADER_RECEIVE, std::make_pair(undefined, header));
708     context->Emit(ON_HEADERS_RECEIVE, std::make_pair(undefined, header));
709 }
710 
IsUnReserved(unsigned char in)711 bool HttpExec::IsUnReserved(unsigned char in)
712 {
713     if ((in >= '0' && in <= '9') || (in >= 'a' && in <= 'z') || (in >= 'A' && in <= 'Z')) {
714         return true;
715     }
716     switch (in) {
717         case '-':
718         case '.':
719         case '_':
720         case '~':
721             return true;
722         default:
723             break;
724     }
725     return false;
726 }
727 
ProcByExpectDataType(napi_value object,RequestContext * context)728 bool HttpExec::ProcByExpectDataType(napi_value object, RequestContext *context)
729 {
730     switch (context->options.GetHttpDataType()) {
731         case HttpDataType::STRING: {
732             NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT,
733                                              context->response.GetResult());
734             NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
735                                          static_cast<uint32_t>(HttpDataType::STRING));
736             return true;
737         }
738         case HttpDataType::OBJECT: {
739             if (context->response.GetResult().size() > HttpConstant::MAX_JSON_PARSE_SIZE) {
740                 return false;
741             }
742 
743             napi_value obj = NapiUtils::JsonParse(context->GetEnv(), context->response.GetResult());
744             if (obj) {
745                 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, obj);
746                 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
747                                              static_cast<uint32_t>(HttpDataType::OBJECT));
748                 return true;
749             }
750 
751             // parse maybe failed
752             return false;
753         }
754         case HttpDataType::ARRAY_BUFFER: {
755             void *data = nullptr;
756             auto body = context->response.GetResult();
757             napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), body.size(), &data);
758             if (data != nullptr && arrayBuffer != nullptr) {
759                 if (memcpy_s(data, body.size(), body.c_str(), body.size()) < 0) {
760                     NETSTACK_LOGE("[ProcByExpectDataType] memory copy failed");
761                     return true;
762                 }
763                 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, arrayBuffer);
764                 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
765                                              static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER));
766             }
767             return true;
768         }
769         default:
770             break;
771     }
772     return false;
773 }
774 
775 #ifndef MAC_PLATFORM
AsyncRunRequest(RequestContext * context)776 void HttpExec::AsyncRunRequest(RequestContext *context)
777 {
778     HttpAsyncWork::ExecRequest(context->GetEnv(), context);
779 }
780 #endif
781 
IsInitialized()782 bool HttpExec::IsInitialized()
783 {
784     return staticVariable_.initialized;
785 }
786 
DeInitialize()787 void HttpExec::DeInitialize()
788 {
789     std::lock_guard<std::mutex> lock(staticVariable_.curlMultiMutex);
790     staticVariable_.runThread = false;
791     staticVariable_.conditionVariable.notify_all();
792     if (staticVariable_.workThread.joinable()) {
793         staticVariable_.workThread.join();
794     }
795     if (staticVariable_.curlMulti) {
796         curl_multi_cleanup(staticVariable_.curlMulti);
797     }
798     staticVariable_.initialized = false;
799 }
800 
ExecFlush(BaseContext * context)801 bool HttpResponseCacheExec::ExecFlush(BaseContext *context)
802 {
803     (void)context;
804     CacheProxy::FlushCache();
805     return true;
806 }
807 
FlushCallback(BaseContext * context)808 napi_value HttpResponseCacheExec::FlushCallback(BaseContext *context)
809 {
810     return NapiUtils::GetUndefined(context->GetEnv());
811 }
812 
ExecDelete(BaseContext * context)813 bool HttpResponseCacheExec::ExecDelete(BaseContext *context)
814 {
815     (void)context;
816     CacheProxy::StopCacheAndDelete();
817     return true;
818 }
819 
DeleteCallback(BaseContext * context)820 napi_value HttpResponseCacheExec::DeleteCallback(BaseContext *context)
821 {
822     return NapiUtils::GetUndefined(context->GetEnv());
823 }
824 } // namespace OHOS::NetStack
825