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 
ParseParams(napi_value * params,size_t paramsCount)104 void RequestContext::ParseParams(napi_value *params, size_t paramsCount)
105 {
106     bool valid = CheckParamsType(params, paramsCount);
107     if (!valid) {
108         if (paramsCount == PARAM_JUST_URL_OR_CALLBACK) {
109             if (NapiUtils::GetValueType(GetEnv(), params[0]) == napi_function) {
110                 SetCallback(params[0]);
111             }
112             return;
113         }
114         if (paramsCount == PARAM_URL_AND_OPTIONS_OR_CALLBACK) {
115             if (NapiUtils::GetValueType(GetEnv(), params[1]) == napi_function) {
116                 SetCallback(params[1]);
117             }
118             return;
119         }
120         if (paramsCount == PARAM_URL_AND_OPTIONS_AND_CALLBACK) {
121             if (NapiUtils::GetValueType(GetEnv(), params[PARAM_URL_AND_OPTIONS_AND_CALLBACK - 1]) == napi_function) {
122                 SetCallback(params[PARAM_URL_AND_OPTIONS_AND_CALLBACK - 1]);
123             }
124             return;
125         }
126         return;
127     }
128 
129     if (paramsCount == PARAM_JUST_URL) {
130         options.SetUrl(NapiUtils::GetStringFromValueUtf8(GetEnv(), params[0]));
131         SetParseOK(true);
132         return;
133     }
134 
135     if (paramsCount == PARAM_URL_AND_OPTIONS_OR_CALLBACK) {
136         napi_valuetype type = NapiUtils::GetValueType(GetEnv(), params[1]);
137         if (type == napi_function) {
138             options.SetUrl(NapiUtils::GetStringFromValueUtf8(GetEnv(), params[0]));
139             SetParseOK(SetCallback(params[1]) == napi_ok);
140             return;
141         }
142         if (type == napi_object) {
143             UrlAndOptions(params[0], params[1]);
144             return;
145         }
146         return;
147     }
148 
149     if (paramsCount == PARAM_URL_AND_OPTIONS_AND_CALLBACK) {
150         if (SetCallback(params[PARAM_URL_AND_OPTIONS_AND_CALLBACK - 1]) != napi_ok) {
151             return;
152         }
153         UrlAndOptions(params[0], params[1]);
154     }
155 }
156 
CheckParamsType(napi_value * params,size_t paramsCount)157 bool RequestContext::CheckParamsType(napi_value *params, size_t paramsCount)
158 {
159     if (paramsCount == PARAM_JUST_URL) {
160         // just url
161         return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string;
162     }
163     if (paramsCount == PARAM_URL_AND_OPTIONS_OR_CALLBACK) {
164         // should be url, callback or url, options
165         napi_valuetype type = NapiUtils::GetValueType(GetEnv(), params[1]);
166         return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string &&
167                (type == napi_function || type == napi_object);
168     }
169     if (paramsCount == PARAM_URL_AND_OPTIONS_AND_CALLBACK) {
170         // should be url options and callback
171         return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string &&
172                NapiUtils::GetValueType(GetEnv(), params[1]) == napi_object &&
173                NapiUtils::GetValueType(GetEnv(), params[PARAM_URL_AND_OPTIONS_AND_CALLBACK - 1]) == napi_function;
174     }
175     return false;
176 }
177 
ParseNumberOptions(napi_value optionsValue)178 void RequestContext::ParseNumberOptions(napi_value optionsValue)
179 {
180     if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_READ_TIMEOUT)) {
181         options.SetReadTimeout(
182             NapiUtils::GetUint32Property(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_READ_TIMEOUT));
183     }
184 
185     if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_MAX_LIMIT)) {
186         options.SetMaxLimit(NapiUtils::GetUint32Property(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_MAX_LIMIT));
187     }
188 
189     if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_CONNECT_TIMEOUT)) {
190         options.SetConnectTimeout(
191             NapiUtils::GetUint32Property(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_CONNECT_TIMEOUT));
192     }
193 
194     if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_CACHE)) {
195         napi_value value = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_CACHE);
196         if (NapiUtils::GetValueType(GetEnv(), value) == napi_boolean) {
197             usingCache_ = NapiUtils::GetBooleanFromValue(GetEnv(), value);
198         }
199     }
200 
201     if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_PROTOCOL)) {
202         napi_value value = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_PROTOCOL);
203         if (NapiUtils::GetValueType(GetEnv(), value) == napi_number) {
204             uint32_t number = NapiUtils::GetUint32FromValue(GetEnv(), value);
205             if (number == static_cast<uint32_t>(HttpProtocol::HTTP1_1) ||
206                 number == static_cast<uint32_t>(HttpProtocol::HTTP2) ||
207                 number == static_cast<uint32_t>(HttpProtocol::HTTP3)) {
208                 options.SetUsingProtocol(static_cast<HttpProtocol>(number));
209             }
210         }
211     }
212     if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_EXPECT_DATA_TYPE)) {
213         napi_value value =
214             NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_EXPECT_DATA_TYPE);
215         if (NapiUtils::GetValueType(GetEnv(), value) == napi_number) {
216             uint32_t type = NapiUtils::GetUint32FromValue(GetEnv(), value);
217             options.SetHttpDataType(static_cast<HttpDataType>(type));
218         }
219     }
220 
221     if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_PRIORITY)) {
222         napi_value value = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_PRIORITY);
223         if (NapiUtils::GetValueType(GetEnv(), value) == napi_number) {
224             uint32_t priority = NapiUtils::GetUint32FromValue(GetEnv(), value);
225             options.SetPriority(priority);
226         }
227     }
228 }
229 
ParseHeader(napi_value optionsValue)230 void RequestContext::ParseHeader(napi_value optionsValue)
231 {
232     if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_HEADER)) {
233         return;
234     }
235     napi_value header = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_HEADER);
236     if (NapiUtils::GetValueType(GetEnv(), header) != napi_object) {
237         return;
238     }
239     if (HttpExec::MethodForPost(options.GetMethod())) {
240         options.SetHeader(CommonUtils::ToLower(HttpConstant::HTTP_CONTENT_TYPE),
241                           HttpConstant::HTTP_CONTENT_TYPE_JSON); // default
242     }
243     auto names = NapiUtils::GetPropertyNames(GetEnv(), header);
244     std::for_each(names.begin(), names.end(), [header, this](const std::string &name) {
245         napi_value value = NapiUtils::GetNamedProperty(GetEnv(), header, name);
246         std::string valueStr = NapiUtils::NapiValueToString(GetEnv(), value);
247         options.SetHeader(CommonUtils::ToLower(name), valueStr);
248     });
249 }
250 
HandleMethodForGet(napi_value extraData)251 bool RequestContext::HandleMethodForGet(napi_value extraData)
252 {
253     std::string url = options.GetUrl();
254     std::string param;
255     auto index = url.find(HttpConstant::HTTP_URL_PARAM_START);
256     if (index != std::string::npos) {
257         param = url.substr(index + 1);
258         url.resize(index);
259     }
260 
261     napi_valuetype type = NapiUtils::GetValueType(GetEnv(), extraData);
262     if (type == napi_string) {
263         std::string extraParam = NapiUtils::GetStringFromValueUtf8(GetEnv(), extraData);
264 
265         options.SetUrl(HttpExec::MakeUrl(url, param, extraParam));
266         return true;
267     }
268     if (type != napi_object) {
269         return true;
270     }
271 
272     std::string extraParam;
273     auto names = NapiUtils::GetPropertyNames(GetEnv(), extraData);
274     std::for_each(names.begin(), names.end(), [this, extraData, &extraParam](std::string name) {
275         auto value = NapiUtils::GetStringPropertyUtf8(GetEnv(), extraData, name);
276         if (!name.empty() && !value.empty()) {
277             bool encodeName = HttpExec::EncodeUrlParam(name);
278             bool encodeValue = HttpExec::EncodeUrlParam(value);
279             if (encodeName || encodeValue) {
280                 options.SetHeader(CommonUtils::ToLower(HttpConstant::HTTP_CONTENT_TYPE),
281                                   HttpConstant::HTTP_CONTENT_TYPE_URL_ENCODE);
282             }
283             extraParam +=
284                 name + HttpConstant::HTTP_URL_NAME_VALUE_SEPARATOR + value + HttpConstant::HTTP_URL_PARAM_SEPARATOR;
285         }
286     });
287     if (!extraParam.empty()) {
288         extraParam.pop_back(); // remove the last &
289     }
290 
291     options.SetUrl(HttpExec::MakeUrl(url, param, extraParam));
292     return true;
293 }
294 
ParseExtraData(napi_value optionsValue)295 bool RequestContext::ParseExtraData(napi_value optionsValue)
296 {
297     if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_EXTRA_DATA)) {
298         NETSTACK_LOGD("no extraData");
299         return true;
300     }
301 
302     napi_value extraData = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_EXTRA_DATA);
303     if (NapiUtils::GetValueType(GetEnv(), extraData) == napi_undefined ||
304         NapiUtils::GetValueType(GetEnv(), extraData) == napi_null) {
305         NETSTACK_LOGD("extraData is undefined or null");
306         return true;
307     }
308 
309     if (HttpExec::MethodForGet(options.GetMethod())) {
310         return HandleMethodForGet(extraData);
311     }
312 
313     if (HttpExec::MethodForPost(options.GetMethod())) {
314         return GetRequestBody(extraData);
315     }
316     return false;
317 }
318 
ParseUsingHttpProxy(napi_value optionsValue)319 void RequestContext::ParseUsingHttpProxy(napi_value optionsValue)
320 {
321     if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_HTTP_PROXY)) {
322         NETSTACK_LOGD("Use default proxy");
323         return;
324     }
325     napi_value httpProxyValue =
326         NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_USING_HTTP_PROXY);
327     napi_valuetype type = NapiUtils::GetValueType(GetEnv(), httpProxyValue);
328     if (type == napi_boolean) {
329         bool usingProxy = NapiUtils::GetBooleanFromValue(GetEnv(), httpProxyValue);
330         UsingHttpProxyType usingType = usingProxy ? UsingHttpProxyType::USE_DEFAULT : UsingHttpProxyType::NOT_USE;
331         options.SetUsingHttpProxyType(usingType);
332         return;
333     }
334     if (type != napi_object) {
335         return;
336     }
337     std::string host = NapiUtils::GetStringPropertyUtf8(GetEnv(), httpProxyValue, HttpConstant::HTTP_PROXY_KEY_HOST);
338     int32_t port = NapiUtils::GetInt32Property(GetEnv(), httpProxyValue, HttpConstant::HTTP_PROXY_KEY_PORT);
339     std::string exclusionList;
340     if (NapiUtils::HasNamedProperty(GetEnv(), httpProxyValue, HttpConstant::HTTP_PROXY_KEY_EXCLUSION_LIST)) {
341         napi_value exclusionListValue =
342             NapiUtils::GetNamedProperty(GetEnv(), httpProxyValue, HttpConstant::HTTP_PROXY_KEY_EXCLUSION_LIST);
343         uint32_t listLength = NapiUtils::GetArrayLength(GetEnv(), exclusionListValue);
344         for (uint32_t index = 0; index < listLength; ++index) {
345             napi_value exclusionValue = NapiUtils::GetArrayElement(GetEnv(), exclusionListValue, index);
346             std::string exclusion = NapiUtils::GetStringFromValueUtf8(GetEnv(), exclusionValue);
347             if (index != 0) {
348                 exclusionList = exclusionList + HttpConstant::HTTP_PROXY_EXCLUSIONS_SEPARATOR;
349             }
350             exclusionList += exclusion;
351         }
352     }
353     options.SetSpecifiedHttpProxy(host, port, exclusionList);
354     options.SetUsingHttpProxyType(UsingHttpProxyType::USE_SPECIFIED);
355 }
356 
GetRequestBody(napi_value extraData)357 bool RequestContext::GetRequestBody(napi_value extraData)
358 {
359     /* if body is empty, return false, or curl will wait for body */
360 
361     napi_valuetype type = NapiUtils::GetValueType(GetEnv(), extraData);
362     if (type == napi_string) {
363         auto body = NapiUtils::GetStringFromValueUtf8(GetEnv(), extraData);
364         if (body.empty()) {
365             return false;
366         }
367         options.SetBody(body.c_str(), body.size());
368         return true;
369     }
370 
371     if (NapiUtils::ValueIsArrayBuffer(GetEnv(), extraData)) {
372         size_t length = 0;
373         void *data = NapiUtils::GetInfoFromArrayBufferValue(GetEnv(), extraData, &length);
374         if (data == nullptr) {
375             return false;
376         }
377         options.SetBody(data, length);
378         return true;
379     }
380 
381     if (type == napi_object) {
382         std::string body = NapiUtils::GetStringFromValueUtf8(GetEnv(), NapiUtils::JsonStringify(GetEnv(), extraData));
383         if (body.empty()) {
384             return false;
385         }
386         options.SetBody(body.c_str(), body.length());
387         return true;
388     }
389 
390     NETSTACK_LOGE("only support string arraybuffer and object");
391     return false;
392 }
393 
ParseCaPath(napi_value optionsValue)394 void RequestContext::ParseCaPath(napi_value optionsValue)
395 {
396     std::string caPath = NapiUtils::GetStringPropertyUtf8(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_CA_PATH);
397     if (!caPath.empty()) {
398         options.SetCaPath(caPath);
399     }
400 }
401 
ParseDohUrl(napi_value optionsValue)402 void RequestContext::ParseDohUrl(napi_value optionsValue)
403 {
404     std::string dohUrl = NapiUtils::GetStringPropertyUtf8(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_DOH_URL);
405     if (!dohUrl.empty()) {
406         options.SetDohUrl(dohUrl);
407     }
408 }
409 
ParseResumeFromToNumber(napi_value optionsValue)410 void RequestContext::ParseResumeFromToNumber(napi_value optionsValue)
411 {
412     napi_env env = GetEnv();
413     int64_t from = NapiUtils::GetInt64Property(env, optionsValue, HttpConstant::PARAM_KEY_RESUME_FROM);
414     int64_t to = NapiUtils::GetInt64Property(env, optionsValue, HttpConstant::PARAM_KEY_RESUME_TO);
415     options.SetRangeNumber(from, to);
416 }
417 
UrlAndOptions(napi_value urlValue,napi_value optionsValue)418 void RequestContext::UrlAndOptions(napi_value urlValue, napi_value optionsValue)
419 {
420     options.SetUrl(NapiUtils::GetStringFromValueUtf8(GetEnv(), urlValue));
421 
422     if (NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_METHOD)) {
423         napi_value requestMethod = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_METHOD);
424         if (NapiUtils::GetValueType(GetEnv(), requestMethod) == napi_string) {
425             options.SetMethod(NapiUtils::GetStringPropertyUtf8(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_METHOD));
426         }
427     }
428 
429     ParseNumberOptions(optionsValue);
430     ParseUsingHttpProxy(optionsValue);
431     ParseClientCert(optionsValue);
432 
433     /* parse extra data here to recover header */
434     if (!ParseExtraData(optionsValue)) {
435         return;
436     }
437 
438     ParseHeader(optionsValue);
439     ParseCaPath(optionsValue);
440     ParseDohUrl(optionsValue);
441     ParseResumeFromToNumber(optionsValue);
442     ParseDnsServers(optionsValue);
443     ParseMultiFormData(optionsValue);
444     ParseCertificatePinning(optionsValue);
445     SetParseOK(true);
446 }
447 
IsUsingCache() const448 bool RequestContext::IsUsingCache() const
449 {
450     return usingCache_;
451 }
452 
SetCurlHeaderList(curl_slist * curlHeaderList)453 void RequestContext::SetCurlHeaderList(curl_slist *curlHeaderList)
454 {
455     curlHeaderList_ = curlHeaderList;
456 }
457 
GetCurlHeaderList()458 curl_slist *RequestContext::GetCurlHeaderList()
459 {
460     return curlHeaderList_;
461 }
462 
SetCurlHostList(curl_slist * curlHostList)463 void RequestContext::SetCurlHostList(curl_slist *curlHostList)
464 {
465     curlHostList_ = curlHostList;
466 }
467 
GetCurlHostList()468 curl_slist *RequestContext::GetCurlHostList()
469 {
470     return curlHostList_;
471 }
472 
~RequestContext()473 RequestContext::~RequestContext()
474 {
475     if (curlHeaderList_ != nullptr) {
476         curl_slist_free_all(curlHeaderList_);
477     }
478     if (curlHostList_ != nullptr) {
479         curl_slist_free_all(curlHostList_);
480     }
481     if (multipart_ != nullptr) {
482         curl_mime_free(multipart_);
483         multipart_ = nullptr;
484     }
485     NETSTACK_LOGD("the destructor of request context is invoked");
486 }
487 
SetCacheResponse(const HttpResponse & cacheResponse)488 void RequestContext::SetCacheResponse(const HttpResponse &cacheResponse)
489 {
490     cacheResponse_ = cacheResponse;
491 }
SetResponseByCache()492 void RequestContext::SetResponseByCache()
493 {
494     response = cacheResponse_;
495 }
496 
GetErrorCode() const497 int32_t RequestContext::GetErrorCode() const
498 {
499     auto err = BaseContext::GetErrorCode();
500     if (err == PARSE_ERROR_CODE) {
501         return PARSE_ERROR_CODE;
502     }
503 
504     if (BaseContext::IsPermissionDenied()) {
505         return PERMISSION_DENIED_CODE;
506     }
507 
508     if (BaseContext::IsNoAllowedHost()) {
509         return HTTP_NOT_ALLOWED_HOST;
510     }
511 
512     if (HTTP_ERR_MAP.find(err + HTTP_ERROR_CODE_BASE) != HTTP_ERR_MAP.end()) {
513         return err + HTTP_ERROR_CODE_BASE;
514     }
515     return HTTP_UNKNOWN_OTHER_ERROR;
516 }
517 
GetErrorMessage() const518 std::string RequestContext::GetErrorMessage() const
519 {
520     auto err = BaseContext::GetErrorCode();
521     if (err == PARSE_ERROR_CODE) {
522         return PARSE_ERROR_MSG;
523     }
524 
525     if (BaseContext::IsPermissionDenied()) {
526         return PERMISSION_DENIED_MSG;
527     }
528 
529     if (BaseContext::IsNoAllowedHost()) {
530         return HTTP_ERR_MAP.at(HTTP_NOT_ALLOWED_HOST);
531     }
532 
533     auto pos = HTTP_ERR_MAP.find(err + HTTP_ERROR_CODE_BASE);
534     if (pos != HTTP_ERR_MAP.end()) {
535         return pos->second;
536     }
537     return HTTP_ERR_MAP.at(HTTP_UNKNOWN_OTHER_ERROR);
538 }
539 
EnableRequestInStream()540 void RequestContext::EnableRequestInStream()
541 {
542     requestInStream_ = true;
543 }
544 
IsRequestInStream() const545 bool RequestContext::IsRequestInStream() const
546 {
547     return requestInStream_;
548 }
549 
SetDlLen(curl_off_t nowLen,curl_off_t totalLen)550 void RequestContext::SetDlLen(curl_off_t nowLen, curl_off_t totalLen)
551 {
552     std::lock_guard<std::mutex> lock(dlLenLock_);
553     LoadBytes dlBytes{nowLen, totalLen};
554     dlBytes_.push(dlBytes);
555 }
556 
SetCertsPath(std::vector<std::string> && certPathList,const std::string & certFile)557 void RequestContext::SetCertsPath(std::vector<std::string> &&certPathList, const std::string &certFile)
558 {
559     certsPath_.certPathList = std::move(certPathList);
560     certsPath_.certFile = certFile;
561 }
562 
GetCertsPath()563 const CertsPath &RequestContext::GetCertsPath()
564 {
565     return certsPath_;
566 }
567 
GetDlLen()568 LoadBytes RequestContext::GetDlLen()
569 {
570     std::lock_guard<std::mutex> lock(dlLenLock_);
571     LoadBytes dlBytes;
572     if (!dlBytes_.empty()) {
573         dlBytes.nLen = dlBytes_.front().nLen;
574         dlBytes.tLen = dlBytes_.front().tLen;
575         dlBytes_.pop();
576     }
577     return dlBytes;
578 }
579 
SetUlLen(curl_off_t nowLen,curl_off_t totalLen)580 void RequestContext::SetUlLen(curl_off_t nowLen, curl_off_t totalLen)
581 {
582     std::lock_guard<std::mutex> lock(ulLenLock_);
583     if (!ulBytes_.empty()) {
584         ulBytes_.pop();
585     }
586     LoadBytes ulBytes{nowLen, totalLen};
587     ulBytes_.push(ulBytes);
588 }
589 
GetUlLen()590 LoadBytes RequestContext::GetUlLen()
591 {
592     std::lock_guard<std::mutex> lock(ulLenLock_);
593     LoadBytes ulBytes;
594     if (!ulBytes_.empty()) {
595         ulBytes.nLen = ulBytes_.back().nLen;
596         ulBytes.tLen = ulBytes_.back().tLen;
597     }
598     return ulBytes;
599 }
600 
CompareWithLastElement(curl_off_t nowLen,curl_off_t totalLen)601 bool RequestContext::CompareWithLastElement(curl_off_t nowLen, curl_off_t totalLen)
602 {
603     std::lock_guard<std::mutex> lock(ulLenLock_);
604     if (ulBytes_.empty()) {
605         return false;
606     }
607     const LoadBytes &lastElement = ulBytes_.back();
608     return nowLen == lastElement.nLen && totalLen == lastElement.tLen;
609 }
610 
SetTempData(const void * data,size_t size)611 void RequestContext::SetTempData(const void *data, size_t size)
612 {
613     std::lock_guard<std::mutex> lock(tempDataLock_);
614     std::string tempString;
615     tempString.append(reinterpret_cast<const char *>(data), size);
616     tempData_.push(tempString);
617 }
618 
GetTempData()619 std::string RequestContext::GetTempData()
620 {
621     std::lock_guard<std::mutex> lock(tempDataLock_);
622     if (!tempData_.empty()) {
623         return tempData_.front();
624     }
625     return {};
626 }
627 
PopTempData()628 void RequestContext::PopTempData()
629 {
630     std::lock_guard<std::mutex> lock(tempDataLock_);
631     if (!tempData_.empty()) {
632         tempData_.pop();
633     }
634 }
635 
ParseDnsServers(napi_value optionsValue)636 void RequestContext::ParseDnsServers(napi_value optionsValue)
637 {
638     napi_env env = GetEnv();
639     if (!NapiUtils::HasNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_DNS_SERVERS)) {
640         NETSTACK_LOGD("ParseDnsServers no data");
641         return;
642     }
643     napi_value dnsServerValue = NapiUtils::GetNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_DNS_SERVERS);
644     if (NapiUtils::GetValueType(env, dnsServerValue) != napi_object) {
645         return;
646     }
647     uint32_t dnsLength = NapiUtils::GetArrayLength(env, dnsServerValue);
648     if (dnsLength == 0) {
649         return;
650     }
651     std::vector<std::string> dnsServers;
652     uint32_t dnsSize = 0;
653     for (uint32_t i = 0; i < dnsLength && dnsSize < DNS_SERVER_SIZE; i++) {
654         napi_value element = NapiUtils::GetArrayElement(env, dnsServerValue, i);
655         std::string dnsServer = NapiUtils::GetStringFromValueUtf8(env, element);
656         if (dnsServer.length() == 0) {
657             continue;
658         }
659         if (!CommonUtils::IsValidIPV4(dnsServer) && !CommonUtils::IsValidIPV6(dnsServer)) {
660             continue;
661         }
662         dnsServers.push_back(dnsServer);
663         dnsSize++;
664     }
665     if (dnsSize == 0 || dnsServers.data() == nullptr || dnsServers.empty()) {
666         NETSTACK_LOGD("dnsServersArray is empty.");
667         return;
668     }
669     options.SetDnsServers(dnsServers);
670     NETSTACK_LOGD("SetDnsServers success");
671 }
672 
CachePerformanceTimingItem(const std::string & key,double value)673 void RequestContext::CachePerformanceTimingItem(const std::string &key, double value)
674 {
675     performanceTimingMap_[key] = value;
676 }
677 
StopAndCacheNapiPerformanceTiming(const char * key)678 void RequestContext::StopAndCacheNapiPerformanceTiming(const char *key)
679 {
680     Timing::Timer &timer = timerMap_.RecieveTimer(key);
681     timer.Stop();
682     CachePerformanceTimingItem(key, timer.Elapsed());
683 }
684 
SetPerformanceTimingToResult(napi_value result)685 void RequestContext::SetPerformanceTimingToResult(napi_value result)
686 {
687     if (performanceTimingMap_.empty()) {
688         NETSTACK_LOGD("Get performanceTiming data is empty.");
689         return;
690     }
691     napi_value performanceTimingValue;
692     napi_env env = GetEnv();
693     napi_create_object(env, &performanceTimingValue);
694     for (const auto &pair : performanceTimingMap_) {
695         NapiUtils::SetDoubleProperty(env, performanceTimingValue, pair.first, pair.second);
696     }
697     NapiUtils::SetNamedProperty(env, result, HttpConstant::RESPONSE_PERFORMANCE_TIMING, performanceTimingValue);
698 }
699 
ParseClientCert(napi_value optionsValue)700 void RequestContext::ParseClientCert(napi_value optionsValue)
701 {
702     if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_CLIENT_CERT)) {
703         return;
704     }
705     napi_value clientCertValue =
706         NapiUtils::GetNamedProperty(GetEnv(), optionsValue, HttpConstant::PARAM_KEY_CLIENT_CERT);
707     napi_valuetype type = NapiUtils::GetValueType(GetEnv(), clientCertValue);
708     if (type != napi_object) {
709         return;
710     }
711     std::string cert = NapiUtils::GetStringPropertyUtf8(GetEnv(), clientCertValue, HttpConstant::HTTP_CLIENT_CERT);
712     std::string certType =
713         NapiUtils::GetStringPropertyUtf8(GetEnv(), clientCertValue, HttpConstant::HTTP_CLIENT_CERT_TYPE);
714     std::string key = NapiUtils::GetStringPropertyUtf8(GetEnv(), clientCertValue, HttpConstant::HTTP_CLIENT_KEY);
715     Secure::SecureChar keyPasswd = Secure::SecureChar(
716         NapiUtils::GetStringPropertyUtf8(GetEnv(), clientCertValue, HttpConstant::HTTP_CLIENT_KEY_PASSWD));
717     options.SetClientCert(cert, certType, key, keyPasswd);
718 }
719 
ParseMultiFormData(napi_value optionsValue)720 void RequestContext::ParseMultiFormData(napi_value optionsValue)
721 {
722     napi_env env = GetEnv();
723     if (!NapiUtils::HasNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_MULTI_FORM_DATA_LIST)) {
724         NETSTACK_LOGD("ParseMultiFormData multiFormDataList is null.");
725         return;
726     }
727     napi_value multiFormDataListValue =
728         NapiUtils::GetNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_MULTI_FORM_DATA_LIST);
729     if (NapiUtils::GetValueType(env, multiFormDataListValue) != napi_object) {
730         NETSTACK_LOGE("ParseMultiFormData multiFormDataList type is not object.");
731         return;
732     }
733     uint32_t dataLength = NapiUtils::GetArrayLength(env, multiFormDataListValue);
734     if (dataLength == 0) {
735         NETSTACK_LOGD("ParseMultiFormData multiFormDataList length is 0.");
736         return;
737     }
738     for (uint32_t i = 0; i < dataLength; i++) {
739         napi_value formDataValue = NapiUtils::GetArrayElement(env, multiFormDataListValue, i);
740         MultiFormData multiFormData = NapiValue2FormData(formDataValue);
741         options.AddMultiFormData(multiFormData);
742     }
743 }
744 
NapiValue2FormData(napi_value formDataValue)745 MultiFormData RequestContext::NapiValue2FormData(napi_value formDataValue)
746 {
747     napi_env env = GetEnv();
748     MultiFormData multiFormData;
749     multiFormData.name = NapiUtils::GetStringPropertyUtf8(env, formDataValue, HttpConstant::HTTP_MULTI_FORM_DATA_NAME);
750     multiFormData.contentType =
751         NapiUtils::GetStringPropertyUtf8(env, formDataValue, HttpConstant::HTTP_MULTI_FORM_DATA_CONTENT_TYPE);
752     multiFormData.remoteFileName =
753         NapiUtils::GetStringPropertyUtf8(env, formDataValue, HttpConstant::HTTP_MULTI_FORM_DATA_REMOTE_FILE_NAME);
754     RequestContext::SaveFormData(
755         env, NapiUtils::GetNamedProperty(env, formDataValue, HttpConstant::HTTP_MULTI_FORM_DATA_DATA), multiFormData);
756     multiFormData.filePath =
757         NapiUtils::GetStringPropertyUtf8(env, formDataValue, HttpConstant::HTTP_MULTI_FORM_DATA_FILE_PATH);
758     return multiFormData;
759 }
760 
NapiValue2CertPinning(napi_value certPIN)761 CertificatePinning RequestContext::NapiValue2CertPinning(napi_value certPIN)
762 {
763     napi_env env = GetEnv();
764     CertificatePinning singleCertPIN;
765     auto algorithm = NapiUtils::GetStringPropertyUtf8(env, certPIN, HttpConstant::HTTP_HASH_ALGORITHM);
766     if (algorithm == "SHA-256") {
767         singleCertPIN.hashAlgorithm = HashAlgorithm::SHA256;
768     } else {
769         singleCertPIN.hashAlgorithm = HashAlgorithm::INVALID;
770     }
771 
772     singleCertPIN.publicKeyHash = NapiUtils::GetStringPropertyUtf8(env, certPIN, HttpConstant::HTTP_PUBLIC_KEY_HASH);
773     return singleCertPIN;
774 }
775 
SaveFormData(napi_env env,napi_value dataValue,MultiFormData & multiFormData)776 void RequestContext::SaveFormData(napi_env env, napi_value dataValue, MultiFormData &multiFormData)
777 {
778     napi_valuetype type = NapiUtils::GetValueType(env, dataValue);
779     if (type == napi_string) {
780         multiFormData.data = NapiUtils::GetStringFromValueUtf8(GetEnv(), dataValue);
781         NETSTACK_LOGD("SaveFormData string");
782     } else if (NapiUtils::ValueIsArrayBuffer(GetEnv(), dataValue)) {
783         size_t length = 0;
784         void *data = NapiUtils::GetInfoFromArrayBufferValue(GetEnv(), dataValue, &length);
785         if (data == nullptr) {
786             return;
787         }
788         multiFormData.data = std::string(static_cast<const char *>(data), length);
789         NETSTACK_LOGD("SaveFormData ArrayBuffer");
790     } else if (type == napi_object) {
791         multiFormData.data = NapiUtils::GetStringFromValueUtf8(GetEnv(), NapiUtils::JsonStringify(GetEnv(), dataValue));
792         NETSTACK_LOGD("SaveFormData Object");
793     } else {
794         NETSTACK_LOGD("only support string, ArrayBuffer and Object");
795     }
796 }
797 
ParseCertificatePinning(napi_value optionsValue)798 void RequestContext::ParseCertificatePinning(napi_value optionsValue)
799 {
800     auto env = GetEnv();
801     if (!NapiUtils::HasNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_CERTIFICATE_PINNING)) {
802         NETSTACK_LOGD("NO CertificatePinning option");
803         return;
804     }
805     napi_value certificatePin =
806         NapiUtils::GetNamedProperty(env, optionsValue, HttpConstant::PARAM_KEY_CERTIFICATE_PINNING);
807     std::stringstream certPinBuilder;
808 
809     if (NapiUtils::IsArray(env, certificatePin)) {
810         auto arrayLen = NapiUtils::GetArrayLength(env, certificatePin);
811         for (uint32_t i = 0; i < arrayLen; i++) {
812             napi_value certPIN = NapiUtils::GetArrayElement(env, certificatePin, i);
813             CertificatePinning singleCertPIN = NapiValue2CertPinning(certPIN);
814             if (singleCertPIN.hashAlgorithm == HashAlgorithm::SHA256) {
815                 certPinBuilder << "sha256//" << singleCertPIN.publicKeyHash << ';';
816             }
817         }
818     } else {
819         CertificatePinning singleCertPIN = NapiValue2CertPinning(certificatePin);
820         if (singleCertPIN.hashAlgorithm == HashAlgorithm::SHA256) {
821             certPinBuilder << "sha256//" << singleCertPIN.publicKeyHash << ';';
822         }
823     }
824 
825     std::string pinRes = certPinBuilder.str();
826     if (!pinRes.empty()) {
827         pinRes.pop_back();
828         options.SetCertificatePinning(pinRes);
829     }
830 }
831 
SetMultipart(curl_mime * multipart)832 void RequestContext::SetMultipart(curl_mime *multipart)
833 {
834     multipart_ = multipart;
835 }
836 
GetTaskId() const837 int32_t RequestContext::GetTaskId() const
838 {
839     return taskId_;
840 }
841 
SetModuleId(uint64_t moduleId)842 void RequestContext::SetModuleId(uint64_t moduleId)
843 {
844     moduleId_ = moduleId;
845 }
846 
GetModuleId() const847 uint64_t RequestContext::GetModuleId() const
848 {
849     return moduleId_;
850 }
851 
IsAtomicService() const852 bool RequestContext::IsAtomicService() const
853 {
854     return isAtomicService_;
855 }
856 
SetAtomicService(bool isAtomicService)857 void RequestContext::SetAtomicService(bool isAtomicService)
858 {
859     isAtomicService_ = isAtomicService;
860 }
861 
SetBundleName(const std::string & bundleName)862 void RequestContext::SetBundleName(const std::string &bundleName)
863 {
864     bundleName_ = bundleName;
865 }
866 
GetBundleName() const867 std::string RequestContext::GetBundleName() const
868 {
869     return bundleName_;
870 }
871 
SetTraceName(const std::string & traceName)872 void RequestContext::SetTraceName(const std::string &traceName)
873 {
874     traceName_ = traceName;
875 }
876 
GetTraceName() const877 std::string RequestContext::GetTraceName() const
878 {
879     return traceName_;
880 }
881 
SetCurlHandle(CURL * handle)882 void RequestContext::SetCurlHandle(CURL *handle)
883 {
884     curlHandle_ = handle;
885 }
886 
SendNetworkProfiler()887 void RequestContext::SendNetworkProfiler()
888 {
889 #if HAS_NETMANAGER_BASE
890     HttpNetworkMessage networkMessage(std::to_string(GetTaskId()), options, response, curlHandle_);
891     networkProfilerUtils_->NetworkProfiling(networkMessage);
892 #endif
893 }
894 } // namespace OHOS::NetStack::Http
895