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 <memory>
17 
18 #include "http_async_callback.h"
19 #include "http_constant.h"
20 #include "http_request_utils.h"
21 #include "js_async_work.h"
22 #include "securec.h"
23 
24 #include "fetch_module.h"
25 
26 namespace OHOS {
27 namespace ACELite {
28 
InitFetchModule(JSIValue exports)29 void InitFetchModule(JSIValue exports)
30 {
31     JSI::SetModuleAPI(exports, FetchModule::HTTP_API_KEY_FETCH, FetchModule::Fetch);
32 }
33 
FreeString(char * s)34 static void FreeString(char *s)
35 {
36     JSI::ReleaseString(s);
37 }
38 
39 const char *const FetchModule::HTTP_API_KEY_FETCH = "fetch";
40 const char *const FetchModule::HTTP_API_KEY_STRING_TO_ARRAY_BUFFER = "stringToArrayBuffer";
41 
Fetch(const JSIValue thisVal,const JSIValue * args,uint8_t argsNum)42 JSIValue FetchModule::Fetch(const JSIValue thisVal, const JSIValue *args, uint8_t argsNum)
43 {
44     if (argsNum < 1) {
45         return JSI::CreateUndefined();
46     }
47 
48     auto asyncCallback = new HttpAsyncCallback(thisVal);
49     if (JsObjectToRequestData(args[0], &asyncCallback->requestData)) {
50         asyncCallback->responseCallback[CB_SUCCESS] = JSI::GetNamedProperty(args[0], CB_SUCCESS);
51         asyncCallback->responseCallback[CB_FAIL] = JSI::GetNamedProperty(args[0], CB_FAIL);
52         asyncCallback->responseCallback[CB_COMPLETE] = JSI::GetNamedProperty(args[0], CB_COMPLETE);
53         if (!JsAsyncWork::DispatchAsyncWork(HttpAsyncCallback::AsyncExecHttpRequest,
54                                             static_cast<void *>(asyncCallback))) {
55             delete asyncCallback;
56             return JSI::CreateUndefined();
57         }
58     } else {
59         delete asyncCallback;
60     }
61 
62     return JSI::CreateUndefined();
63 }
64 
JsObjectToRequestData(JSIValue options,RequestData * req)65 bool FetchModule::JsObjectToRequestData(JSIValue options, RequestData *req)
66 {
67     if (options == nullptr || JSI::ValueIsUndefined(options) || !JSI::ValueIsObject(options)) {
68         return false;
69     }
70 
71     std::unique_ptr<char, decltype(&FreeString)> urlString(
72         JSI::GetStringProperty(options, HttpConstant::KEY_HTTP_REQUEST_URL), FreeString);
73     if (urlString == nullptr) {
74         return false;
75     }
76     HTTP_REQUEST_INFO("request url is ...");
77     req->SetUrl(urlString.get());
78 
79     req->SetMethod(GetMethodFromOptions(options));
80     HTTP_REQUEST_INFO("request method is %s", req->GetMethod().c_str());
81 
82     req->SetResponseType(GetResponseTypeFromOptions(options));
83 
84     std::unique_ptr<JSIVal, decltype(&JSI::ReleaseValue)> jsHeaders(
85         JSI::GetNamedProperty(options, HttpConstant::KEY_HTTP_REQUEST_HEADER), JSI::ReleaseValue);
86     if (jsHeaders != nullptr && !JSI::ValueIsUndefined(jsHeaders.get()) && JSI::ValueIsObject(jsHeaders.get())) {
87         GetNameValue(jsHeaders.get(), const_cast<std::map<std::string, std::string> &>(req->GetHeader()));
88     }
89 
90     ParseExtraData(options, req);
91 
92     return true;
93 }
94 
GetNameValue(JSIValue nv,std::map<std::string,std::string> & map)95 void FetchModule::GetNameValue(JSIValue nv, std::map<std::string, std::string> &map)
96 {
97     std::unique_ptr<JSIVal, decltype(&JSI::ReleaseValue)> keys(JSI::GetObjectKeys(nv), JSI::ReleaseValue);
98     if (keys == nullptr || JSI::ValueIsUndefined(keys.get()) || !JSI::ValueIsArray(keys.get())) {
99         HTTP_REQUEST_ERROR("get name value failed");
100         return;
101     }
102 
103     uint32_t length = JSI::GetArrayLength(keys.get());
104     for (uint32_t index = 0; index < length; ++index) {
105         std::unique_ptr<JSIVal, decltype(&JSI::ReleaseValue)> key(JSI::GetPropertyByIndex(keys.get(), index),
106                                                                   JSI::ReleaseValue);
107         if (key == nullptr || JSI::ValueIsUndefined(key.get()) || !JSI::ValueIsString(key.get())) {
108             continue;
109         }
110 
111         std::unique_ptr<char, decltype(&FreeString)> keyStr(JSI::ValueToString(key.get()), FreeString);
112         if (keyStr == nullptr) {
113             HTTP_REQUEST_ERROR("key to string failed");
114             continue;
115         }
116 
117         std::unique_ptr<char, decltype(&FreeString)> valStr(JSI::GetStringProperty(nv, keyStr.get()), FreeString);
118         if (valStr == nullptr) {
119             HTTP_REQUEST_ERROR("get val failed");
120             continue;
121         }
122 
123         map[keyStr.get()] = valStr.get();
124     }
125 }
126 
GetRequestBody(JSIValue options,RequestData * requestData)127 void FetchModule::GetRequestBody(JSIValue options, RequestData *requestData)
128 {
129     std::unique_ptr<JSIVal, decltype(&JSI::ReleaseValue)> body(
130         JSI::GetNamedProperty(options, HttpConstant::KEY_HTTP_REQUEST_DATA), JSI::ReleaseValue);
131     if (body == nullptr || JSI::ValueIsUndefined(body.get())) {
132         HTTP_REQUEST_ERROR("get body failed");
133         return;
134     }
135 
136     if (JSI::ValueIsString(body.get())) {
137         size_t size = 0;
138         std::unique_ptr<char, decltype(&FreeString)> bodyStr(JSI::ValueToStringWithBufferSize(body.get(), size),
139                                                              FreeString);
140         if (bodyStr == nullptr) {
141             HTTP_REQUEST_ERROR("get body str failed");
142             return;
143         }
144         std::string str(bodyStr.get(), size);
145         requestData->SetBody(str);
146         HTTP_REQUEST_INFO("body is string, size %zu", str.size());
147         return;
148     }
149 
150     if (JSI::ValueIsObject(body.get())) {
151         std::unique_ptr<char, decltype(&FreeString)> jsonString(JSI::JsonStringify(body.get()), FreeString);
152         if (jsonString == nullptr) {
153             return;
154         }
155         requestData->SetBody(jsonString.get());
156         HTTP_REQUEST_INFO("body is object, stringify size %zu", requestData->GetBody().size());
157         requestData->SetHeader(HttpConstant::HTTP_HEADER_KEY_CONTENT_TYPE, HttpConstant::HTTP_CONTENT_TYPE_JSON);
158         return;
159     }
160 
161     HTTP_REQUEST_ERROR("body should be string or object");
162 }
163 
ParseExtraData(JSIValue options,RequestData * requestData)164 void FetchModule::ParseExtraData(JSIValue options, RequestData *requestData)
165 {
166     if (MethodForGet(requestData->GetMethod())) {
167         std::unique_ptr<JSIVal, decltype(&JSI::ReleaseValue)> extraData(
168             JSI::GetNamedProperty(options, HttpConstant::KEY_HTTP_REQUEST_DATA), JSI::ReleaseValue);
169         if (extraData == nullptr || JSI::ValueIsUndefined(extraData.get())) {
170             return;
171         }
172 
173         std::string url = requestData->GetUrl();
174         std::string param;
175         auto index = url.find(HttpConstant::HTTP_URL_PARAM_SEPARATOR);
176         if (index != std::string::npos) {
177             param = url.substr(index + 1);
178             url.resize(index);
179         }
180 
181         if (JSI::ValueIsString(extraData.get())) {
182             std::unique_ptr<char, decltype(&FreeString)> dataStr(JSI::ValueToString(extraData.get()), FreeString);
183             if (dataStr == nullptr) {
184                 return;
185             }
186             std::string extraParam(dataStr.get());
187 
188             requestData->SetUrl(MakeUrl(url, param, extraParam));
189             return;
190         }
191 
192         if (!JSI::ValueIsObject(extraData.get())) {
193             return;
194         }
195 
196         std::map<std::string, std::string> urlPara;
197         GetNameValue(extraData.get(), urlPara);
198         std::string extraParam;
199         for (const auto &p : urlPara) {
200             extraParam += p.first + "=" + p.second + HttpConstant::HTTP_URL_PARAM_DELIMITER;
201         }
202         if (!extraParam.empty()) {
203             extraParam.pop_back(); // delete the last &
204         }
205 
206         requestData->SetUrl(MakeUrl(url, param, extraParam));
207         return;
208     }
209 
210     if (MethodForPost(requestData->GetMethod())) {
211         GetRequestBody(options, requestData);
212     }
213 }
214 
GetMethodFromOptions(JSIValue options)215 std::string FetchModule::GetMethodFromOptions(JSIValue options)
216 {
217     std::unique_ptr<JSIVal, decltype(&JSI::ReleaseValue)> value(
218         JSI::GetNamedProperty(options, HttpConstant::KEY_HTTP_REQUEST_METHOD), JSI::ReleaseValue);
219     if (value == nullptr || JSI::ValueIsUndefined(value.get()) || !JSI::ValueIsString(value.get())) {
220         return HttpConstant::HTTP_METHOD_GET;
221     }
222 
223     std::unique_ptr<char, decltype(&FreeString)> methodStr(JSI::ValueToString(value.get()), FreeString);
224     return methodStr == nullptr ? HttpConstant::HTTP_METHOD_GET : methodStr.get();
225 }
226 
StringToArrayBuffer(const JSIValue thisVal,const JSIValue * args,uint8_t argsNum)227 JSIValue FetchModule::StringToArrayBuffer(const JSIValue thisVal, const JSIValue *args, uint8_t argsNum)
228 {
229     (void)thisVal;
230 
231     if (argsNum < 1 || args[0] == nullptr || JSI::ValueIsUndefined(args[0]) || !JSI::ValueIsString(args[0])) {
232         return JSI::CreateUndefined();
233     }
234 
235     size_t size = 0;
236     std::unique_ptr<char, decltype(&FreeString)> str(JSI::ValueToStringWithBufferSize(args[0], size), FreeString);
237     if (str == nullptr || size == 0) {
238         return JSI::CreateUndefined();
239     }
240 
241     uint8_t *buffer = nullptr;
242     JSIValue arrayBuffer = JSI::CreateArrayBuffer(size, buffer);
243     if (buffer == nullptr || arrayBuffer == nullptr) {
244         return JSI::CreateUndefined();
245     }
246 
247     (void)memcpy_s(buffer, size, str.get(), size);
248     return arrayBuffer;
249 }
250 
GetResponseTypeFromOptions(JSIValue options)251 std::string FetchModule::GetResponseTypeFromOptions(JSIValue options)
252 {
253     std::unique_ptr<JSIVal, decltype(&JSI::ReleaseValue)> value(
254         JSI::GetNamedProperty(options, HttpConstant::KEY_HTTP_REQUEST_RESPONSE_TYPE), JSI::ReleaseValue);
255     if (value == nullptr || JSI::ValueIsUndefined(value.get()) || !JSI::ValueIsString(value.get())) {
256         return "";
257     }
258 
259     std::unique_ptr<char, decltype(&FreeString)> responseType(JSI::ValueToString(value.get()), FreeString);
260     return responseType == nullptr ? "" : responseType.get();
261 }
262 
263 } // namespace ACELite
264 } // namespace OHOS