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