1 /*
2 * Copyright (c) 2021-2024 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 "request_context.h"
17
18 #include <algorithm>
19 #include <atomic>
20 #include <limits>
21 #include <utility>
22 #include <sstream>
23
24 #include "constant.h"
25 #include "http_exec.h"
26 #include "napi_utils.h"
27 #include "netstack_common_utils.h"
28 #include "netstack_log.h"
29 #include "secure_char.h"
30 #include "timing.h"
31 #if HAS_NETMANAGER_BASE
32 #include "http_network_message.h"
33 #endif
34
35 static constexpr const int PARAM_JUST_URL = 1;
36
37 static constexpr const int PARAM_JUST_URL_OR_CALLBACK = 1;
38
39 static constexpr const int PARAM_URL_AND_OPTIONS_OR_CALLBACK = 2;
40
41 static constexpr const int PARAM_URL_AND_OPTIONS_AND_CALLBACK = 3;
42
43 static constexpr const uint32_t DNS_SERVER_SIZE = 3;
44 namespace OHOS::NetStack::Http {
45 static const std::map<int32_t, const char *> HTTP_ERR_MAP = {
46 {HTTP_UNSUPPORTED_PROTOCOL, "Unsupported protocol"},
47 {HTTP_URL_MALFORMAT, "URL using bad/illegal format or missing URL"},
48 {HTTP_COULDNT_RESOLVE_PROXY, "Couldn't resolve proxy name"},
49 {HTTP_COULDNT_RESOLVE_HOST, "Couldn't resolve host name"},
50 {HTTP_COULDNT_CONNECT, "Couldn't connect to server"},
51 {HTTP_WEIRD_SERVER_REPLY, "Weird server reply"},
52 {HTTP_REMOTE_ACCESS_DENIED, "Access denied to remote resource"},
53 {HTTP_HTTP2_ERROR, "Error in the HTTP2 framing layer"},
54 {HTTP_PARTIAL_FILE, "Transferred a partial file"},
55 {HTTP_WRITE_ERROR, "Failed writing received data to disk/application"},
56 {HTTP_UPLOAD_FAILED, "Upload failed"},
57 {HTTP_READ_ERROR, "Failed to open/read local data from file/application"},
58 {HTTP_OUT_OF_MEMORY, "Out of memory"},
59 {HTTP_OPERATION_TIMEDOUT, "Timeout was reached"},
60 {HTTP_TOO_MANY_REDIRECTS, "Number of redirects hit maximum amount"},
61 {HTTP_GOT_NOTHING, "Server returned nothing (no headers, no data)"},
62 {HTTP_SEND_ERROR, "Failed sending data to the peer"},
63 {HTTP_RECV_ERROR, "Failure when receiving data from the peer"},
64 {HTTP_SSL_CERTPROBLEM, "Problem with the local SSL certificate"},
65 {HTTP_SSL_CIPHER, "Couldn't use specified SSL cipher"},
66 {HTTP_PEER_FAILED_VERIFICATION, "SSL peer certificate or SSH remote key was not OK"},
67 {HTTP_BAD_CONTENT_ENCODING, "Unrecognized or bad HTTP Content or Transfer-Encoding"},
68 {HTTP_FILESIZE_EXCEEDED, "Maximum file size exceeded"},
69 {HTTP_REMOTE_DISK_FULL, "Disk full or allocation exceeded"},
70 {HTTP_REMOTE_FILE_EXISTS, "Remote file already exists"},
71 {HTTP_SSL_CACERT_BADFILE, "Problem with the SSL CA cert (path? access rights?)"},
72 {HTTP_REMOTE_FILE_NOT_FOUND, "Remote file not found"},
73 {HTTP_AUTH_ERROR, "An authentication function returned an error"},
74 {HTTP_SSL_PINNEDPUBKEYNOTMATCH, "Specified pinned public key did not match"},
75 {HTTP_NOT_ALLOWED_HOST, "It is not allowed to access this domain"},
76 {HTTP_UNKNOWN_OTHER_ERROR, "Unknown Other Error"},
77 };
78 static std::atomic<int32_t> g_currentTaskId = std::numeric_limits<int32_t>::min();
RequestContext(napi_env env,EventManager * manager)79 RequestContext::RequestContext(napi_env env, EventManager *manager)
80 : BaseContext(env, manager),
81 usingCache_(true),
82 requestInStream_(false),
83 curlHeaderList_(nullptr),
84 multipart_(nullptr),
85 curlHostList_(nullptr)
86 {
87 taskId_ = g_currentTaskId++;
88 isAtomicService_ = false;
89 bundleName_ = "";
90 StartTiming();
91 #if HAS_NETMANAGER_BASE
92 networkProfilerUtils_ = std::make_unique<NetworkProfilerUtils>();
93 #endif
94 }
95
StartTiming()96 void RequestContext::StartTiming()
97 {
98 time_t startTime = Timing::TimeUtils::GetNowTimeMicroseconds();
99 timerMap_.RecieveTimer(HttpConstant::RESPONSE_HEADER_TIMING).Start(startTime);
100 timerMap_.RecieveTimer(HttpConstant::RESPONSE_BODY_TIMING).Start(startTime);
101 timerMap_.RecieveTimer(HttpConstant::RESPONSE_TOTAL_TIMING).Start(startTime);
102
103 // init RESPONSE_HEADER_TIMING and RESPONSE_BODY_TIMING
104 performanceTimingMap_[HttpConstant::RESPONSE_HEADER_TIMING] = 0.0;
105 performanceTimingMap_[HttpConstant::RESPONSE_BODY_TIMING] = 0.0;
106 }
107
ParseParams(napi_value * params,size_t paramsCount)108 void RequestContext::ParseParams(napi_value *params, size_t paramsCount)
109 {
110 bool valid = CheckParamsType(params, paramsCount);
111 if (!valid) {
112 if (paramsCount == PARAM_JUST_URL_OR_CALLBACK) {
113 if (NapiUtils::GetValueType(GetEnv(), params[0]) == napi_function) {
114 SetCallback(params[0]);
115 }
116 return;
117 }
118 if (paramsCount == PARAM_URL_AND_OPTIONS_OR_CALLBACK) {
119 if (NapiUtils::GetValueType(GetEnv(), params[1]) == napi_function) {
120 SetCallback(params[1]);
121 }
122 return;
123 }
124 if (paramsCount == PARAM_URL_AND_OPTIONS_AND_CALLBACK) {
125 if (NapiUtils::GetValueType(GetEnv(), params[PARAM_URL_AND_OPTIONS_AND_CALLBACK - 1]) == napi_function) {
126 SetCallback(params[PARAM_URL_AND_OPTIONS_AND_CALLBACK - 1]);
127 }
128 return;
129 }
130 return;
131 }
132
133 if (paramsCount == PARAM_JUST_URL) {
134 options.SetUrl(NapiUtils::GetStringFromValueUtf8(GetEnv(), params[0]));
135 SetParseOK(true);
136 return;
137 }
138
139 if (paramsCount == PARAM_URL_AND_OPTIONS_OR_CALLBACK) {
140 napi_valuetype type = NapiUtils::GetValueType(GetEnv(), params[1]);
141 if (type == napi_function) {
142 options.SetUrl(NapiUtils::GetStringFromValueUtf8(GetEnv(), params[0]));
143 SetParseOK(SetCallback(params[1]) == napi_ok);
144 return;
145 }
146 if (type == napi_object) {
147 UrlAndOptions(params[0], params[1]);
148 return;
149 }
150 return;
151 }
152
153 if (paramsCount == PARAM_URL_AND_OPTIONS_AND_CALLBACK) {
154 if (SetCallback(params[PARAM_URL_AND_OPTIONS_AND_CALLBACK - 1]) != napi_ok) {
155 return;
156 }
157 UrlAndOptions(params[0], params[1]);
158 }
159 }
160
CheckParamsType(napi_value * params,size_t paramsCount)161 bool RequestContext::CheckParamsType(napi_value *params, size_t paramsCount)
162 {
163 if (paramsCount == PARAM_JUST_URL) {
164 // just url
165 return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string;
166 }
167 if (paramsCount == PARAM_URL_AND_OPTIONS_OR_CALLBACK) {
168 // should be url, callback or url, options
169 napi_valuetype type = NapiUtils::GetValueType(GetEnv(), params[1]);
170 return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string &&
171 (type == napi_function || type == napi_object);
172 }
173 if (paramsCount == PARAM_URL_AND_OPTIONS_AND_CALLBACK) {
174 // should be url options and callback
175 return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string &&
176 NapiUtils::GetValueType(GetEnv(), params[1]) == napi_object &&
177 NapiUtils::GetValueType(GetEnv(), params[PARAM_URL_AND_OPTIONS_AND_CALLBACK - 1]) == napi_function;
178 }
179 return false;
180 }
181
ParseNumberOptions(napi_value optionsValue)182 void RequestContext::ParseNumberOptions(napi_value optionsValue)
183 {
184 if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_READ_TIMEOUT)) {
185 options.SetReadTimeout(
186 NapiUtils::GetUint32Property(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_READ_TIMEOUT));
187 }
188
189 if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_MAX_LIMIT)) {
190 options.SetMaxLimit(NapiUtils::GetUint32Property(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_MAX_LIMIT));
191 }
192
193 if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_CONNECT_TIMEOUT)) {
194 options.SetConnectTimeout(
195 NapiUtils::GetUint32Property(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_CONNECT_TIMEOUT));
196 }
197
198 if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_CACHE)) {
199 napi_value value = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_CACHE);
200 if (NapiUtils::GetValueType(GetEnv(), value) == napi_boolean) {
201 usingCache_ = NapiUtils::GetBooleanFromValue(GetEnv(), value);
202 }
203 }
204
205 if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_PROTOCOL)) {
206 napi_value value = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_PROTOCOL);
207 if (NapiUtils::GetValueType(GetEnv(), value) == napi_number) {
208 uint32_t number = NapiUtils::GetUint32FromValue(GetEnv(), value);
209 if (number == static_cast<uint32_t>(HttpProtocol::HTTP1_1) ||
210 number == static_cast<uint32_t>(HttpProtocol::HTTP2) ||
211 number == static_cast<uint32_t>(HttpProtocol::HTTP3)) {
212 options.SetUsingProtocol(static_cast<HttpProtocol>(number));
213 }
214 }
215 }
216 if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_EXPECT_DATA_TYPE)) {
217 napi_value value =
218 NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_EXPECT_DATA_TYPE);
219 if (NapiUtils::GetValueType(GetEnv(), value) == napi_number) {
220 uint32_t type = NapiUtils::GetUint32FromValue(GetEnv(), value);
221 options.SetHttpDataType(static_cast<HttpDataType>(type));
222 }
223 }
224
225 if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_PRIORITY)) {
226 napi_value value = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_PRIORITY);
227 if (NapiUtils::GetValueType(GetEnv(), value) == napi_number) {
228 uint32_t priority = NapiUtils::GetUint32FromValue(GetEnv(), value);
229 options.SetPriority(priority);
230 }
231 }
232 }
233
ParseHeader(napi_value optionsValue)234 void RequestContext::ParseHeader(napi_value optionsValue)
235 {
236 if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_HEADER)) {
237 return;
238 }
239 napi_value header = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_HEADER);
240 if (NapiUtils::GetValueType(GetEnv(), header) != napi_object) {
241 return;
242 }
243 if (HttpExec::MethodForPost(options.GetMethod())) {
244 options.SetHeader(CommonUtils::ToLower(HttpConstant::HTTP_CONTENT_TYPE),
245 HttpConstant::HTTP_CONTENT_TYPE_JSON); // default
246 }
247 auto names = NapiUtils::GetPropertyNames(GetEnv(), header);
248 std::for_each(names.begin(), names.end(), [header, this](const std::string &name) {
249 napi_value value = NapiUtils::GetNamedProperty(GetEnv(), header, name);
250 std::string valueStr = NapiUtils::NapiValueToString(GetEnv(), value);
251 options.SetHeader(CommonUtils::ToLower(name), valueStr);
252 });
253 }
254
HandleMethodForGet(napi_value extraData)255 bool RequestContext::HandleMethodForGet(napi_value extraData)
256 {
257 std::string url = options.GetUrl();
258 std::string param;
259 auto index = url.find(HttpConstant::HTTP_URL_PARAM_START);
260 if (index != std::string::npos) {
261 param = url.substr(index + 1);
262 url.resize(index);
263 }
264
265 napi_valuetype type = NapiUtils::GetValueType(GetEnv(), extraData);
266 if (type == napi_string) {
267 std::string extraParam = NapiUtils::GetStringFromValueUtf8(GetEnv(), extraData);
268
269 options.SetUrl(HttpExec::MakeUrl(url, param, extraParam));
270 return true;
271 }
272 if (type != napi_object) {
273 return true;
274 }
275
276 std::string extraParam;
277 auto names = NapiUtils::GetPropertyNames(GetEnv(), extraData);
278 std::for_each(names.begin(), names.end(), [this, extraData, &extraParam](std::string name) {
279 auto value = NapiUtils::GetStringPropertyUtf8(GetEnv(), extraData, name);
280 if (!name.empty() && !value.empty()) {
281 bool encodeName = HttpExec::EncodeUrlParam(name);
282 bool encodeValue = HttpExec::EncodeUrlParam(value);
283 if (encodeName || encodeValue) {
284 options.SetHeader(CommonUtils::ToLower(HttpConstant::HTTP_CONTENT_TYPE),
285 HttpConstant::HTTP_CONTENT_TYPE_URL_ENCODE);
286 }
287 extraParam +=
288 name + HttpConstant::HTTP_URL_NAME_VALUE_SEPARATOR + value + HttpConstant::HTTP_URL_PARAM_SEPARATOR;
289 }
290 });
291 if (!extraParam.empty()) {
292 extraParam.pop_back(); // remove the last &
293 }
294
295 options.SetUrl(HttpExec::MakeUrl(url, param, extraParam));
296 return true;
297 }
298
ParseExtraData(napi_value optionsValue)299 bool RequestContext::ParseExtraData(napi_value optionsValue)
300 {
301 if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_EXTRA_DATA)) {
302 NETSTACK_LOGD("no extraData");
303 return true;
304 }
305
306 napi_value extraData = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_EXTRA_DATA);
307 if (NapiUtils::GetValueType(GetEnv(), extraData) == napi_undefined ||
308 NapiUtils::GetValueType(GetEnv(), extraData) == napi_null) {
309 NETSTACK_LOGD("extraData is undefined or null");
310 return true;
311 }
312
313 if (HttpExec::MethodForGet(options.GetMethod())) {
314 return HandleMethodForGet(extraData);
315 }
316
317 if (HttpExec::MethodForPost(options.GetMethod())) {
318 return GetRequestBody(extraData);
319 }
320 return false;
321 }
322
ParseUsingHttpProxy(napi_value optionsValue)323 void RequestContext::ParseUsingHttpProxy(napi_value optionsValue)
324 {
325 if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_HTTP_PROXY)) {
326 NETSTACK_LOGD("Use default proxy");
327 return;
328 }
329 napi_value httpProxyValue =
330 NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_HTTP_PROXY);
331 napi_valuetype type = NapiUtils::GetValueType(GetEnv(), httpProxyValue);
332 if (type == napi_boolean) {
333 bool usingProxy = NapiUtils::GetBooleanFromValue(GetEnv(), httpProxyValue);
334 UsingHttpProxyType usingType = usingProxy ? UsingHttpProxyType::USE_DEFAULT : UsingHttpProxyType::NOT_USE;
335 options.SetUsingHttpProxyType(usingType);
336 return;
337 }
338 if (type != napi_object) {
339 return;
340 }
341 std::string host = NapiUtils::GetStringPropertyUtf8(GetEnv(), httpProxyValue, HttpConstant::HTTP_PROXY_KEY_HOST);
342 int32_t port = NapiUtils::GetInt32Property(GetEnv(), httpProxyValue, HttpConstant::HTTP_PROXY_KEY_PORT);
343 std::string exclusionList;
344 if (NapiUtils::HasNamedProperty(GetEnv(), httpProxyValue, HttpConstant::HTTP_PROXY_KEY_EXCLUSION_LIST)) {
345 napi_value exclusionListValue =
346 NapiUtils::GetNamedProperty(GetEnv(), httpProxyValue, HttpConstant::HTTP_PROXY_KEY_EXCLUSION_LIST);
347 uint32_t listLength = NapiUtils::GetArrayLength(GetEnv(), exclusionListValue);
348 for (uint32_t index = 0; index < listLength; ++index) {
349 napi_value exclusionValue = NapiUtils::GetArrayElement(GetEnv(), exclusionListValue, index);
350 std::string exclusion = NapiUtils::GetStringFromValueUtf8(GetEnv(), exclusionValue);
351 if (index != 0) {
352 exclusionList = exclusionList + HttpConstant::HTTP_PROXY_EXCLUSIONS_SEPARATOR;
353 }
354 exclusionList += exclusion;
355 }
356 }
357 options.SetSpecifiedHttpProxy(host, port, exclusionList);
358 options.SetUsingHttpProxyType(UsingHttpProxyType::USE_SPECIFIED);
359 }
360
GetRequestBody(napi_value extraData)361 bool RequestContext::GetRequestBody(napi_value extraData)
362 {
363 /* if body is empty, return false, or curl will wait for body */
364
365 napi_valuetype type = NapiUtils::GetValueType(GetEnv(), extraData);
366 if (type == napi_string) {
367 auto body = NapiUtils::GetStringFromValueUtf8(GetEnv(), extraData);
368 if (body.empty()) {
369 return false;
370 }
371 options.SetBody(body.c_str(), body.size());
372 return true;
373 }
374
375 if (NapiUtils::ValueIsArrayBuffer(GetEnv(), extraData)) {
376 size_t length = 0;
377 void *data = NapiUtils::GetInfoFromArrayBufferValue(GetEnv(), extraData, &length);
378 if (data == nullptr) {
379 return false;
380 }
381 options.SetBody(data, length);
382 return true;
383 }
384
385 if (type == napi_object) {
386 std::string body = NapiUtils::GetStringFromValueUtf8(GetEnv(), NapiUtils::JsonStringify(GetEnv(), extraData));
387 if (body.empty()) {
388 return false;
389 }
390 options.SetBody(body.c_str(), body.length());
391 return true;
392 }
393
394 NETSTACK_LOGE("only support string arraybuffer and object");
395 return false;
396 }
397
ParseCaPath(napi_value optionsValue)398 void RequestContext::ParseCaPath(napi_value optionsValue)
399 {
400 std::string caPath = NapiUtils::GetStringPropertyUtf8(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_CA_PATH);
401 if (!caPath.empty()) {
402 options.SetCaPath(caPath);
403 }
404 }
405
ParseDohUrl(napi_value optionsValue)406 void RequestContext::ParseDohUrl(napi_value optionsValue)
407 {
408 std::string dohUrl = NapiUtils::GetStringPropertyUtf8(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_DOH_URL);
409 if (!dohUrl.empty()) {
410 options.SetDohUrl(dohUrl);
411 }
412 }
413
ParseResumeFromToNumber(napi_value optionsValue)414 void RequestContext::ParseResumeFromToNumber(napi_value optionsValue)
415 {
416 napi_env env = GetEnv();
417 int64_t from = NapiUtils::GetInt64Property(env, optionsValue, HttpConstant::PARAM_KEY_RESUME_FROM);
418 int64_t to = NapiUtils::GetInt64Property(env, optionsValue, HttpConstant::PARAM_KEY_RESUME_TO);
419 options.SetRangeNumber(from, to);
420 }
421
UrlAndOptions(napi_value urlValue,napi_value optionsValue)422 void RequestContext::UrlAndOptions(napi_value urlValue, napi_value optionsValue)
423 {
424 options.SetUrl(NapiUtils::GetStringFromValueUtf8(GetEnv(), urlValue));
425
426 if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_METHOD)) {
427 napi_value requestMethod = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_METHOD);
428 if (NapiUtils::GetValueType(GetEnv(), requestMethod) == napi_string) {
429 options.SetMethod(NapiUtils::GetStringPropertyUtf8(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_METHOD));
430 }
431 }
432
433 ParseNumberOptions(optionsValue);
434 ParseUsingHttpProxy(optionsValue);
435 ParseClientCert(optionsValue);
436
437 /* parse extra data here to recover header */
438 if (!ParseExtraData(optionsValue)) {
439 return;
440 }
441
442 ParseHeader(optionsValue);
443 ParseCaPath(optionsValue);
444 ParseDohUrl(optionsValue);
445 ParseResumeFromToNumber(optionsValue);
446 ParseDnsServers(optionsValue);
447 ParseMultiFormData(optionsValue);
448 ParseCertificatePinning(optionsValue);
449 SetParseOK(true);
450 ParseAddressFamily(optionsValue);
451 }
452
IsUsingCache() const453 bool RequestContext::IsUsingCache() const
454 {
455 return usingCache_;
456 }
457
SetCurlHeaderList(curl_slist * curlHeaderList)458 void RequestContext::SetCurlHeaderList(curl_slist *curlHeaderList)
459 {
460 curlHeaderList_ = curlHeaderList;
461 }
462
GetCurlHeaderList()463 curl_slist *RequestContext::GetCurlHeaderList()
464 {
465 return curlHeaderList_;
466 }
467
SetCurlHostList(curl_slist * curlHostList)468 void RequestContext::SetCurlHostList(curl_slist *curlHostList)
469 {
470 curlHostList_ = curlHostList;
471 }
472
GetCurlHostList()473 curl_slist *RequestContext::GetCurlHostList()
474 {
475 return curlHostList_;
476 }
477
~RequestContext()478 RequestContext::~RequestContext()
479 {
480 if (curlHeaderList_ != nullptr) {
481 curl_slist_free_all(curlHeaderList_);
482 }
483 if (curlHostList_ != nullptr) {
484 curl_slist_free_all(curlHostList_);
485 }
486 if (multipart_ != nullptr) {
487 curl_mime_free(multipart_);
488 multipart_ = nullptr;
489 }
490 NETSTACK_LOGD("the destructor of request context is invoked");
491 }
492
SetCacheResponse(const HttpResponse & cacheResponse)493 void RequestContext::SetCacheResponse(const HttpResponse &cacheResponse)
494 {
495 cacheResponse_ = cacheResponse;
496 }
SetResponseByCache()497 void RequestContext::SetResponseByCache()
498 {
499 response = cacheResponse_;
500 }
501
GetErrorCode() const502 int32_t RequestContext::GetErrorCode() const
503 {
504 auto err = BaseContext::GetErrorCode();
505 if (err == PARSE_ERROR_CODE) {
506 return PARSE_ERROR_CODE;
507 }
508
509 if (BaseContext::IsPermissionDenied()) {
510 return PERMISSION_DENIED_CODE;
511 }
512
513 if (BaseContext::IsNoAllowedHost()) {
514 return HTTP_NOT_ALLOWED_HOST;
515 }
516
517 if (HTTP_ERR_MAP.find(err + HTTP_ERROR_CODE_BASE) != HTTP_ERR_MAP.end()) {
518 return err + HTTP_ERROR_CODE_BASE;
519 }
520 return HTTP_UNKNOWN_OTHER_ERROR;
521 }
522
GetErrorMessage() const523 std::string RequestContext::GetErrorMessage() const
524 {
525 auto err = BaseContext::GetErrorCode();
526 if (err == PARSE_ERROR_CODE) {
527 return PARSE_ERROR_MSG;
528 }
529
530 if (BaseContext::IsPermissionDenied()) {
531 return PERMISSION_DENIED_MSG;
532 }
533
534 if (BaseContext::IsNoAllowedHost()) {
535 return HTTP_ERR_MAP.at(HTTP_NOT_ALLOWED_HOST);
536 }
537
538 auto pos = HTTP_ERR_MAP.find(err + HTTP_ERROR_CODE_BASE);
539 if (pos != HTTP_ERR_MAP.end()) {
540 return pos->second;
541 }
542 return HTTP_ERR_MAP.at(HTTP_UNKNOWN_OTHER_ERROR);
543 }
544
EnableRequestInStream()545 void RequestContext::EnableRequestInStream()
546 {
547 requestInStream_ = true;
548 }
549
IsRequestInStream() const550 bool RequestContext::IsRequestInStream() const
551 {
552 return requestInStream_;
553 }
554
SetDlLen(curl_off_t nowLen,curl_off_t totalLen)555 void RequestContext::SetDlLen(curl_off_t nowLen, curl_off_t totalLen)
556 {
557 std::lock_guard<std::mutex> lock(dlLenLock_);
558 LoadBytes dlBytes{nowLen, totalLen};
559 dlBytes_.push(dlBytes);
560 }
561
SetCertsPath(std::vector<std::string> && certPathList,const std::string & certFile)562 void RequestContext::SetCertsPath(std::vector<std::string> &&certPathList, const std::string &certFile)
563 {
564 certsPath_.certPathList = std::move(certPathList);
565 certsPath_.certFile = certFile;
566 }
567
GetCertsPath()568 const CertsPath &RequestContext::GetCertsPath()
569 {
570 return certsPath_;
571 }
572
GetDlLen()573 LoadBytes RequestContext::GetDlLen()
574 {
575 std::lock_guard<std::mutex> lock(dlLenLock_);
576 LoadBytes dlBytes;
577 if (!dlBytes_.empty()) {
578 dlBytes.nLen = dlBytes_.front().nLen;
579 dlBytes.tLen = dlBytes_.front().tLen;
580 dlBytes_.pop();
581 }
582 return dlBytes;
583 }
584
SetUlLen(curl_off_t nowLen,curl_off_t totalLen)585 void RequestContext::SetUlLen(curl_off_t nowLen, curl_off_t totalLen)
586 {
587 std::lock_guard<std::mutex> lock(ulLenLock_);
588 if (!ulBytes_.empty()) {
589 ulBytes_.pop();
590 }
591 LoadBytes ulBytes{nowLen, totalLen};
592 ulBytes_.push(ulBytes);
593 }
594
GetUlLen()595 LoadBytes RequestContext::GetUlLen()
596 {
597 std::lock_guard<std::mutex> lock(ulLenLock_);
598 LoadBytes ulBytes;
599 if (!ulBytes_.empty()) {
600 ulBytes.nLen = ulBytes_.back().nLen;
601 ulBytes.tLen = ulBytes_.back().tLen;
602 }
603 return ulBytes;
604 }
605
CompareWithLastElement(curl_off_t nowLen,curl_off_t totalLen)606 bool RequestContext::CompareWithLastElement(curl_off_t nowLen, curl_off_t totalLen)
607 {
608 std::lock_guard<std::mutex> lock(ulLenLock_);
609 if (ulBytes_.empty()) {
610 return false;
611 }
612 const LoadBytes &lastElement = ulBytes_.back();
613 return nowLen == lastElement.nLen && totalLen == lastElement.tLen;
614 }
615
SetTempData(const void * data,size_t size)616 void RequestContext::SetTempData(const void *data, size_t size)
617 {
618 std::lock_guard<std::mutex> lock(tempDataLock_);
619 std::string tempString;
620 tempString.append(reinterpret_cast<const char *>(data), size);
621 tempData_.push(tempString);
622 }
623
GetTempData()624 std::string RequestContext::GetTempData()
625 {
626 std::lock_guard<std::mutex> lock(tempDataLock_);
627 if (!tempData_.empty()) {
628 return tempData_.front();
629 }
630 return {};
631 }
632
PopTempData()633 void RequestContext::PopTempData()
634 {
635 std::lock_guard<std::mutex> lock(tempDataLock_);
636 if (!tempData_.empty()) {
637 tempData_.pop();
638 }
639 }
640
ParseDnsServers(napi_value optionsValue)641 void RequestContext::ParseDnsServers(napi_value optionsValue)
642 {
643 napi_env env = GetEnv();
644 if (!NapiUtils::HasNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_DNS_SERVERS)) {
645 NETSTACK_LOGD("ParseDnsServers no data");
646 return;
647 }
648 napi_value dnsServerValue = NapiUtils::GetNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_DNS_SERVERS);
649 if (NapiUtils::GetValueType(env, dnsServerValue) != napi_object) {
650 return;
651 }
652 uint32_t dnsLength = NapiUtils::GetArrayLength(env, dnsServerValue);
653 if (dnsLength == 0) {
654 return;
655 }
656 std::vector<std::string> dnsServers;
657 uint32_t dnsSize = 0;
658 for (uint32_t i = 0; i < dnsLength && dnsSize < DNS_SERVER_SIZE; i++) {
659 napi_value element = NapiUtils::GetArrayElement(env, dnsServerValue, i);
660 std::string dnsServer = NapiUtils::GetStringFromValueUtf8(env, element);
661 if (dnsServer.length() == 0) {
662 continue;
663 }
664 if (!CommonUtils::IsValidIPV4(dnsServer) && !CommonUtils::IsValidIPV6(dnsServer)) {
665 continue;
666 }
667 dnsServers.push_back(dnsServer);
668 dnsSize++;
669 }
670 if (dnsSize == 0 || dnsServers.data() == nullptr || dnsServers.empty()) {
671 NETSTACK_LOGD("dnsServersArray is empty.");
672 return;
673 }
674 options.SetDnsServers(dnsServers);
675 NETSTACK_LOGD("SetDnsServers success");
676 }
677
CachePerformanceTimingItem(const std::string & key,double value)678 void RequestContext::CachePerformanceTimingItem(const std::string &key, double value)
679 {
680 performanceTimingMap_[key] = value;
681 }
682
StopAndCacheNapiPerformanceTiming(const char * key)683 void RequestContext::StopAndCacheNapiPerformanceTiming(const char *key)
684 {
685 Timing::Timer &timer = timerMap_.RecieveTimer(key);
686 timer.Stop();
687 CachePerformanceTimingItem(key, timer.Elapsed());
688 }
689
SetPerformanceTimingToResult(napi_value result)690 void RequestContext::SetPerformanceTimingToResult(napi_value result)
691 {
692 if (performanceTimingMap_.empty()) {
693 NETSTACK_LOGD("Get performanceTiming data is empty.");
694 return;
695 }
696 napi_value performanceTimingValue;
697 napi_env env = GetEnv();
698 napi_create_object(env, &performanceTimingValue);
699 for (const auto &pair : performanceTimingMap_) {
700 NapiUtils::SetDoubleProperty(env, performanceTimingValue, pair.first, pair.second);
701 }
702 NapiUtils::SetNamedProperty(env, result, HttpConstant::RESPONSE_PERFORMANCE_TIMING, performanceTimingValue);
703 }
704
ParseClientCert(napi_value optionsValue)705 void RequestContext::ParseClientCert(napi_value optionsValue)
706 {
707 if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_CLIENT_CERT)) {
708 return;
709 }
710 napi_value clientCertValue =
711 NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_CLIENT_CERT);
712 napi_valuetype type = NapiUtils::GetValueType(GetEnv(), clientCertValue);
713 if (type != napi_object) {
714 return;
715 }
716 std::string cert = NapiUtils::GetStringPropertyUtf8(GetEnv(), clientCertValue, HttpConstant::HTTP_CLIENT_CERT);
717 std::string certType =
718 NapiUtils::GetStringPropertyUtf8(GetEnv(), clientCertValue, HttpConstant::HTTP_CLIENT_CERT_TYPE);
719 std::string key = NapiUtils::GetStringPropertyUtf8(GetEnv(), clientCertValue, HttpConstant::HTTP_CLIENT_KEY);
720 Secure::SecureChar keyPasswd = Secure::SecureChar(
721 NapiUtils::GetStringPropertyUtf8(GetEnv(), clientCertValue, HttpConstant::HTTP_CLIENT_KEY_PASSWD));
722 options.SetClientCert(cert, certType, key, keyPasswd);
723 }
724
ParseMultiFormData(napi_value optionsValue)725 void RequestContext::ParseMultiFormData(napi_value optionsValue)
726 {
727 napi_env env = GetEnv();
728 if (!NapiUtils::HasNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_MULTI_FORM_DATA_LIST)) {
729 NETSTACK_LOGD("ParseMultiFormData multiFormDataList is null.");
730 return;
731 }
732 napi_value multiFormDataListValue =
733 NapiUtils::GetNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_MULTI_FORM_DATA_LIST);
734 if (NapiUtils::GetValueType(env, multiFormDataListValue) != napi_object) {
735 NETSTACK_LOGE("ParseMultiFormData multiFormDataList type is not object.");
736 return;
737 }
738 uint32_t dataLength = NapiUtils::GetArrayLength(env, multiFormDataListValue);
739 if (dataLength == 0) {
740 NETSTACK_LOGD("ParseMultiFormData multiFormDataList length is 0.");
741 return;
742 }
743 for (uint32_t i = 0; i < dataLength; i++) {
744 napi_value formDataValue = NapiUtils::GetArrayElement(env, multiFormDataListValue, i);
745 MultiFormData multiFormData = NapiValue2FormData(formDataValue);
746 options.AddMultiFormData(multiFormData);
747 }
748 }
749
NapiValue2FormData(napi_value formDataValue)750 MultiFormData RequestContext::NapiValue2FormData(napi_value formDataValue)
751 {
752 napi_env env = GetEnv();
753 MultiFormData multiFormData;
754 multiFormData.name = NapiUtils::GetStringPropertyUtf8(env, formDataValue, HttpConstant::HTTP_MULTI_FORM_DATA_NAME);
755 multiFormData.contentType =
756 NapiUtils::GetStringPropertyUtf8(env, formDataValue, HttpConstant::HTTP_MULTI_FORM_DATA_CONTENT_TYPE);
757 multiFormData.remoteFileName =
758 NapiUtils::GetStringPropertyUtf8(env, formDataValue, HttpConstant::HTTP_MULTI_FORM_DATA_REMOTE_FILE_NAME);
759 RequestContext::SaveFormData(
760 env, NapiUtils::GetNamedProperty(env, formDataValue, HttpConstant::HTTP_MULTI_FORM_DATA_DATA), multiFormData);
761 multiFormData.filePath =
762 NapiUtils::GetStringPropertyUtf8(env, formDataValue, HttpConstant::HTTP_MULTI_FORM_DATA_FILE_PATH);
763 return multiFormData;
764 }
765
NapiValue2CertPinning(napi_value certPIN)766 CertificatePinning RequestContext::NapiValue2CertPinning(napi_value certPIN)
767 {
768 napi_env env = GetEnv();
769 CertificatePinning singleCertPIN;
770 auto algorithm = NapiUtils::GetStringPropertyUtf8(env, certPIN, HttpConstant::HTTP_HASH_ALGORITHM);
771 if (algorithm == "SHA-256") {
772 singleCertPIN.hashAlgorithm = HashAlgorithm::SHA256;
773 } else {
774 singleCertPIN.hashAlgorithm = HashAlgorithm::INVALID;
775 }
776
777 singleCertPIN.publicKeyHash = NapiUtils::GetStringPropertyUtf8(env, certPIN, HttpConstant::HTTP_PUBLIC_KEY_HASH);
778 return singleCertPIN;
779 }
780
SaveFormData(napi_env env,napi_value dataValue,MultiFormData & multiFormData)781 void RequestContext::SaveFormData(napi_env env, napi_value dataValue, MultiFormData &multiFormData)
782 {
783 napi_valuetype type = NapiUtils::GetValueType(env, dataValue);
784 if (type == napi_string) {
785 multiFormData.data = NapiUtils::GetStringFromValueUtf8(GetEnv(), dataValue);
786 NETSTACK_LOGD("SaveFormData string");
787 } else if (NapiUtils::ValueIsArrayBuffer(GetEnv(), dataValue)) {
788 size_t length = 0;
789 void *data = NapiUtils::GetInfoFromArrayBufferValue(GetEnv(), dataValue, &length);
790 if (data == nullptr) {
791 return;
792 }
793 multiFormData.data = std::string(static_cast<const char *>(data), length);
794 NETSTACK_LOGD("SaveFormData ArrayBuffer");
795 } else if (type == napi_object) {
796 multiFormData.data = NapiUtils::GetStringFromValueUtf8(GetEnv(), NapiUtils::JsonStringify(GetEnv(), dataValue));
797 NETSTACK_LOGD("SaveFormData Object");
798 } else {
799 NETSTACK_LOGD("only support string, ArrayBuffer and Object");
800 }
801 }
802
ParseCertificatePinning(napi_value optionsValue)803 void RequestContext::ParseCertificatePinning(napi_value optionsValue)
804 {
805 auto env = GetEnv();
806 if (!NapiUtils::HasNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_CERTIFICATE_PINNING)) {
807 NETSTACK_LOGD("NO CertificatePinning option");
808 return;
809 }
810 napi_value certificatePin =
811 NapiUtils::GetNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_CERTIFICATE_PINNING);
812 std::stringstream certPinBuilder;
813
814 if (NapiUtils::IsArray(env, certificatePin)) {
815 auto arrayLen = NapiUtils::GetArrayLength(env, certificatePin);
816 for (uint32_t i = 0; i < arrayLen; i++) {
817 napi_value certPIN = NapiUtils::GetArrayElement(env, certificatePin, i);
818 CertificatePinning singleCertPIN = NapiValue2CertPinning(certPIN);
819 if (singleCertPIN.hashAlgorithm == HashAlgorithm::SHA256) {
820 certPinBuilder << "sha256//" << singleCertPIN.publicKeyHash << ';';
821 }
822 }
823 } else {
824 CertificatePinning singleCertPIN = NapiValue2CertPinning(certificatePin);
825 if (singleCertPIN.hashAlgorithm == HashAlgorithm::SHA256) {
826 certPinBuilder << "sha256//" << singleCertPIN.publicKeyHash << ';';
827 }
828 }
829
830 std::string pinRes = certPinBuilder.str();
831 if (!pinRes.empty()) {
832 pinRes.pop_back();
833 options.SetCertificatePinning(pinRes);
834 }
835 }
836
SetMultipart(curl_mime * multipart)837 void RequestContext::SetMultipart(curl_mime *multipart)
838 {
839 multipart_ = multipart;
840 }
841
GetTaskId() const842 int32_t RequestContext::GetTaskId() const
843 {
844 return taskId_;
845 }
846
SetModuleId(uint64_t moduleId)847 void RequestContext::SetModuleId(uint64_t moduleId)
848 {
849 moduleId_ = moduleId;
850 }
851
GetModuleId() const852 uint64_t RequestContext::GetModuleId() const
853 {
854 return moduleId_;
855 }
856
IsAtomicService() const857 bool RequestContext::IsAtomicService() const
858 {
859 return isAtomicService_;
860 }
861
SetAtomicService(bool isAtomicService)862 void RequestContext::SetAtomicService(bool isAtomicService)
863 {
864 isAtomicService_ = isAtomicService;
865 }
866
SetBundleName(const std::string & bundleName)867 void RequestContext::SetBundleName(const std::string &bundleName)
868 {
869 bundleName_ = bundleName;
870 }
871
GetBundleName() const872 std::string RequestContext::GetBundleName() const
873 {
874 return bundleName_;
875 }
876
SetTraceName(const std::string & traceName)877 void RequestContext::SetTraceName(const std::string &traceName)
878 {
879 traceName_ = traceName;
880 }
881
GetTraceName() const882 std::string RequestContext::GetTraceName() const
883 {
884 return traceName_;
885 }
886
SetCurlHandle(CURL * handle)887 void RequestContext::SetCurlHandle(CURL *handle)
888 {
889 curlHandle_ = handle;
890 }
891
SendNetworkProfiler()892 void RequestContext::SendNetworkProfiler()
893 {
894 #if HAS_NETMANAGER_BASE
895 HttpNetworkMessage networkMessage(std::to_string(GetTaskId()), options, response, curlHandle_);
896 networkProfilerUtils_->NetworkProfiling(networkMessage);
897 #endif
898 }
899
IsRootCaVerified() const900 bool RequestContext::IsRootCaVerified() const
901 {
902 return isRootCaVerified_;
903 }
904
SetRootCaVerified()905 void RequestContext::SetRootCaVerified()
906 {
907 isRootCaVerified_ = true;
908 }
909
IsRootCaVerifiedOk() const910 bool RequestContext::IsRootCaVerifiedOk() const
911 {
912 return isRootCaVerifiedOk_;
913 }
914
SetRootCaVerifiedOk(bool ok)915 void RequestContext::SetRootCaVerifiedOk(bool ok)
916 {
917 isRootCaVerifiedOk_ = ok;
918 }
919
SetPinnedPubkey(std::string & pubkey)920 void RequestContext::SetPinnedPubkey(std::string &pubkey)
921 {
922 pinnedPubkey_ = pubkey;
923 }
924
GetPinnedPubkey() const925 std::string RequestContext::GetPinnedPubkey() const
926 {
927 return pinnedPubkey_;
928 }
929
ParseAddressFamily(napi_value optionsValue)930 void RequestContext::ParseAddressFamily(napi_value optionsValue)
931 {
932 std::string addressFamily = NapiUtils::GetStringPropertyUtf8(GetEnv(), optionsValue,
933 HttpConstant::PARAM_KEY_ADDRESS_FAMILY);
934 if (!addressFamily.empty()) {
935 options.SetAddressFamily(addressFamily);
936 }
937 }
938 } // namespace OHOS::NetStack::Http
939