1 /*
2  * Copyright (c) 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 "fetch_context.h"
17 
18 #include "constant.h"
19 #include "fetch_exec.h"
20 #include "netstack_common_utils.h"
21 #include "netstack_log.h"
22 #include "napi_utils.h"
23 
24 namespace OHOS::NetStack::Fetch {
FetchContext(napi_env env,EventManager * manager)25 FetchContext::FetchContext(napi_env env, EventManager *manager)
26     : BaseContext(env, manager), successCallback_(nullptr), failCallback_(nullptr), completeCallback_(nullptr)
27 {
28 }
29 
ParseParams(napi_value * params,size_t paramsCount)30 void FetchContext::ParseParams(napi_value *params, size_t paramsCount)
31 {
32     SetNeedPromise(false);
33 
34     bool valid = CheckParamsType(params, paramsCount);
35     if (!valid) {
36         NETSTACK_LOGE("fetch CheckParamsType failed");
37         return;
38     }
39 
40     request.SetUrl(NapiUtils::GetStringPropertyUtf8(GetEnv(), params[0], FetchConstant::PARAM_KEY_URL));
41     request.SetMethod(NapiUtils::GetStringPropertyUtf8(GetEnv(), params[0], FetchConstant::PARAM_KEY_METHOD));
42     if (request.GetMethod().empty()) {
43         request.SetMethod(FetchConstant::HTTP_METHOD_GET);
44     }
45     if (NapiUtils::HasNamedProperty(GetEnv(), params[0], FetchConstant::PARAM_KEY_RESPONSE_TYPE)) {
46         responseType_ = NapiUtils::GetStringPropertyUtf8(GetEnv(), params[0], FetchConstant::PARAM_KEY_RESPONSE_TYPE);
47     }
48     ParseHeader(params[0]);
49     ParseData(params[0]);
50 
51     napi_value success = NapiUtils::GetNamedProperty(GetEnv(), params[0], FetchConstant::PARAM_KEY_SUCCESS);
52     if (NapiUtils::GetValueType(GetEnv(), success) == napi_function) {
53         if (SetSuccessCallback(success) != napi_ok) {
54             NETSTACK_LOGE("SetSuccessCallback failed");
55             return;
56         }
57     }
58     napi_value fail = NapiUtils::GetNamedProperty(GetEnv(), params[0], FetchConstant::PARAM_KEY_FAIL);
59     if (NapiUtils::GetValueType(GetEnv(), fail) == napi_function) {
60         if (SetFailCallback(fail) != napi_ok) {
61             NETSTACK_LOGE("SetFailCallback failed");
62             return;
63         }
64     }
65     napi_value complete = NapiUtils::GetNamedProperty(GetEnv(), params[0], FetchConstant::PARAM_KEY_COMPLETE);
66     if (NapiUtils::GetValueType(GetEnv(), complete) == napi_function) {
67         if (SetCompleteCallback(complete) != napi_ok) {
68             NETSTACK_LOGE("SetCompleteCallback failed");
69             return;
70         }
71     }
72     NETSTACK_LOGI("fetch parse OK");
73     SetParseOK(true);
74 }
75 
CheckParamsType(napi_value * params,size_t paramsCount)76 bool FetchContext::CheckParamsType(napi_value *params, size_t paramsCount)
77 {
78     if (paramsCount == 1) {
79         return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_object;
80     }
81     return false;
82 }
83 
ParseHeader(napi_value optionsValue)84 void FetchContext::ParseHeader(napi_value optionsValue)
85 {
86     if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, FetchConstant::PARAM_KEY_HEADER)) {
87         return;
88     }
89     napi_value header = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, FetchConstant::PARAM_KEY_HEADER);
90     if (NapiUtils::GetValueType(GetEnv(), header) != napi_object) {
91         return;
92     }
93     auto names = NapiUtils::GetPropertyNames(GetEnv(), header);
94     std::for_each(names.begin(), names.end(), [header, this](const std::string &name) {
95         auto value = NapiUtils::GetStringPropertyUtf8(GetEnv(), header, name);
96         if (!value.empty()) {
97             request.SetHeader(CommonUtils::ToLower(name), value);
98         }
99     });
100 }
101 
ParseData(napi_value optionsValue)102 bool FetchContext::ParseData(napi_value optionsValue)
103 {
104     if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, FetchConstant::PARAM_KEY_DATA)) {
105         NETSTACK_LOGI("no extraData");
106         return true;
107     }
108     napi_value data = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, FetchConstant::PARAM_KEY_DATA);
109 
110     if (FetchExec::MethodForGet(request.GetMethod())) {
111         return GetUrlParam(data);
112     }
113     if (FetchExec::MethodForPost(request.GetMethod())) {
114         return GetRequestBody(data);
115     }
116 
117     NETSTACK_LOGE("ParseData failed, method is not supported %{public}s", request.GetMethod().c_str());
118     return false;
119 }
120 
GetRequestBody(napi_value data)121 bool FetchContext::GetRequestBody(napi_value data)
122 {
123     /* if body is empty, return false, or curl will wait for body */
124 
125     napi_valuetype type = NapiUtils::GetValueType(GetEnv(), data);
126     if (type == napi_string) {
127         auto body = NapiUtils::GetStringFromValueUtf8(GetEnv(), data);
128         if (body.empty()) {
129             NETSTACK_LOGE("body napi_string is empty");
130             return false;
131         }
132         request.SetBody(body.c_str(), body.size());
133         return true;
134     }
135     if (type == napi_object) {
136         std::string body = NapiUtils::GetStringFromValueUtf8(GetEnv(), NapiUtils::JsonStringify(GetEnv(), data));
137         if (body.empty()) {
138             NETSTACK_LOGE("body napi_object is empty");
139             return false;
140         }
141         request.SetBody(body.c_str(), body.size());
142         return true;
143     }
144 
145     NETSTACK_LOGE("Body just support string and object");
146     return false;
147 }
148 
GetUrlParam(napi_value data)149 bool FetchContext::GetUrlParam(napi_value data)
150 {
151     std::string url = request.GetUrl();
152     if (url.empty()) {
153         NETSTACK_LOGE("url is empty!");
154         return false;
155     }
156 
157     std::string param;
158     auto index = url.find(FetchConstant::HTTP_URL_PARAM_START);
159     if (index != std::string::npos) {
160         param = url.substr(index + 1);
161         url = url.substr(0, index);
162     }
163 
164     napi_valuetype type = NapiUtils::GetValueType(GetEnv(), data);
165     if (type == napi_string) {
166         std::string extraParam = NapiUtils::GetStringFromValueUtf8(GetEnv(), data);
167 
168         request.SetUrl(FetchExec::MakeUrl(url, param, extraParam));
169         return true;
170     }
171     if (type != napi_object) {
172         return true;
173     }
174 
175     std::string extraParam;
176     auto names = NapiUtils::GetPropertyNames(GetEnv(), data);
177     std::for_each(names.begin(), names.end(), [this, data, &extraParam](std::string name) {
178         auto value = NapiUtils::GetStringPropertyUtf8(GetEnv(), data, name);
179         if (!name.empty() && !value.empty()) {
180             bool encodeName = FetchExec::EncodeUrlParam(name);
181             bool encodeValue = FetchExec::EncodeUrlParam(value);
182             if (encodeName || encodeValue) {
183                 request.SetHeader(CommonUtils::ToLower(FetchConstant::HTTP_CONTENT_TYPE),
184                                   FetchConstant::HTTP_CONTENT_TYPE_URL_ENCODE);
185             }
186             extraParam +=
187                 name + FetchConstant::HTTP_URL_NAME_VALUE_SEPARATOR + value + FetchConstant::HTTP_URL_PARAM_SEPARATOR;
188         }
189     });
190     if (!extraParam.empty()) {
191         extraParam.pop_back(); // remove the last &
192     }
193 
194     request.SetUrl(FetchExec::MakeUrl(url, param, extraParam));
195     return true;
196 }
197 
SetSuccessCallback(napi_value callback)198 napi_status FetchContext::SetSuccessCallback(napi_value callback)
199 {
200     if (successCallback_ != nullptr) {
201         (void)napi_delete_reference(GetEnv(), successCallback_);
202     }
203     return napi_create_reference(GetEnv(), callback, 1, &successCallback_);
204 }
205 
SetFailCallback(napi_value callback)206 napi_status FetchContext::SetFailCallback(napi_value callback)
207 {
208     if (failCallback_ != nullptr) {
209         (void)napi_delete_reference(GetEnv(), failCallback_);
210     }
211     return napi_create_reference(GetEnv(), callback, 1, &failCallback_);
212 }
213 
SetCompleteCallback(napi_value callback)214 napi_status FetchContext::SetCompleteCallback(napi_value callback)
215 {
216     if (completeCallback_ != nullptr) {
217         (void)napi_delete_reference(GetEnv(), completeCallback_);
218     }
219     return napi_create_reference(GetEnv(), callback, 1, &completeCallback_);
220 }
221 
GetSuccessCallback() const222 napi_value FetchContext::GetSuccessCallback() const
223 {
224     if (successCallback_ == nullptr) {
225         return nullptr;
226     }
227     napi_value callback = nullptr;
228     NAPI_CALL(GetEnv(), napi_get_reference_value(GetEnv(), successCallback_, &callback));
229     return callback;
230 }
231 
GetFailCallback() const232 napi_value FetchContext::GetFailCallback() const
233 {
234     if (failCallback_ == nullptr) {
235         return nullptr;
236     }
237     napi_value callback = nullptr;
238     NAPI_CALL(GetEnv(), napi_get_reference_value(GetEnv(), failCallback_, &callback));
239     return callback;
240 }
241 
GetCompleteCallback() const242 napi_value FetchContext::GetCompleteCallback() const
243 {
244     if (completeCallback_ == nullptr) {
245         return nullptr;
246     }
247     napi_value callback = nullptr;
248     NAPI_CALL(GetEnv(), napi_get_reference_value(GetEnv(), completeCallback_, &callback));
249     return callback;
250 }
251 
GetResponseType() const252 std::string FetchContext::GetResponseType() const
253 {
254     return responseType_;
255 }
256 } // namespace OHOS::NetStack::Fetch
257