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