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 "http_module.h"
17
18 #include "cache_proxy.h"
19 #include "constant.h"
20 #include "event_list.h"
21 #include "http_async_work.h"
22 #include "http_exec.h"
23
24 #include "module_template.h"
25 #include "netstack_common_utils.h"
26 #include "netstack_log.h"
27
28 #define DECLARE_RESPONSE_CODE(code) \
29 DECLARE_NAPI_STATIC_PROPERTY(#code, NapiUtils::CreateUint32(env, static_cast<uint32_t>(ResponseCode::code)))
30
31 #define DECLARE_REQUEST_METHOD(method) \
32 DECLARE_NAPI_STATIC_PROPERTY(HttpConstant::method, NapiUtils::CreateStringUtf8(env, HttpConstant::method))
33
34 #define DECLARE_HTTP_PROTOCOL(protocol) \
35 DECLARE_NAPI_STATIC_PROPERTY(#protocol, NapiUtils::CreateUint32(env, static_cast<uint32_t>(HttpProtocol::protocol)))
36
37 namespace OHOS::NetStack::Http {
38 static constexpr const char *FLUSH_ASYNC_WORK_NAME = "ExecFlush";
39
40 static constexpr const char *DELETE_ASYNC_WORK_NAME = "ExecDelete";
41
42 static constexpr const char *HTTP_MODULE_NAME = "net.http";
43
44 static thread_local uint64_t g_moduleId;
45
46 static bool g_appIsAtomicService = false;
47
48 static std::string g_appBundleName;
49
50 static std::once_flag g_isAtomicServiceFlag;
51
InitHttpModule(napi_env env,napi_value exports)52 napi_value HttpModuleExports::InitHttpModule(napi_env env, napi_value exports)
53 {
54 DefineHttpRequestClass(env, exports);
55 DefineHttpResponseCacheClass(env, exports);
56 InitHttpProperties(env, exports);
57 g_moduleId = NapiUtils::CreateUvHandlerQueue(env);
58 NapiUtils::SetEnvValid(env);
59 napi_add_env_cleanup_hook(env, NapiUtils::HookForEnvCleanup, env);
60 std::call_once(g_isAtomicServiceFlag, []() {
61 g_appIsAtomicService = CommonUtils::IsAtomicService(g_appBundleName);
62 NETSTACK_LOGI("IsAtomicService bundleName is %{public}s, isAtomicService is %{public}d",
63 g_appBundleName.c_str(), g_appIsAtomicService);
64 });
65 return exports;
66 }
67
CreateHttp(napi_env env,napi_callback_info info)68 napi_value HttpModuleExports::CreateHttp(napi_env env, napi_callback_info info)
69 {
70 return ModuleTemplate::NewInstanceWithManagerWrapper(
71 env, info, INTERFACE_HTTP_REQUEST, [](napi_env, void *data, void *) {
72 NETSTACK_LOGD("http request handle is finalized");
73 auto wrapper = reinterpret_cast<EventManagerWrapper *>(data);
74 delete wrapper;
75 });
76 }
77
CreateHttpResponseCache(napi_env env,napi_callback_info info)78 napi_value HttpModuleExports::CreateHttpResponseCache(napi_env env, napi_callback_info info)
79 {
80 napi_value thisVal = nullptr;
81 size_t paramsCount = MAX_PARAM_NUM;
82 napi_value params[MAX_PARAM_NUM] = {nullptr};
83 NAPI_CALL(env, napi_get_cb_info(env, info, ¶msCount, params, &thisVal, nullptr));
84 if (paramsCount != 1 || NapiUtils::GetValueType(env, params[0]) != napi_number) {
85 CacheProxy::RunCache();
86 } else {
87 size_t size = NapiUtils::GetUint32FromValue(env, params[0]);
88 CacheProxy::RunCacheWithSize(size);
89 }
90
91 return ModuleTemplate::NewInstanceNoManager(env, info, INTERFACE_HTTP_RESPONSE_CACHE, [](napi_env, void *, void *) {
92 NETSTACK_LOGI("http response cache handle is finalized");
93 });
94 }
95
DefineHttpRequestClass(napi_env env,napi_value exports)96 void HttpModuleExports::DefineHttpRequestClass(napi_env env, napi_value exports)
97 {
98 std::initializer_list<napi_property_descriptor> properties = {
99 DECLARE_NAPI_FUNCTION(HttpRequest::FUNCTION_REQUEST, HttpRequest::Request),
100 DECLARE_NAPI_FUNCTION(HttpRequest::FUNCTION_REQUEST_IN_STREAM, HttpRequest::RequestInStream),
101 DECLARE_NAPI_FUNCTION(HttpRequest::FUNCTION_DESTROY, HttpRequest::Destroy),
102 DECLARE_NAPI_FUNCTION(HttpRequest::FUNCTION_ON, HttpRequest::On),
103 DECLARE_NAPI_FUNCTION(HttpRequest::FUNCTION_ONCE, HttpRequest::Once),
104 DECLARE_NAPI_FUNCTION(HttpRequest::FUNCTION_OFF, HttpRequest::Off),
105 };
106 ModuleTemplate::DefineClass(env, exports, properties, INTERFACE_HTTP_REQUEST);
107 }
108
DefineHttpResponseCacheClass(napi_env env,napi_value exports)109 void HttpModuleExports::DefineHttpResponseCacheClass(napi_env env, napi_value exports)
110 {
111 std::initializer_list<napi_property_descriptor> properties = {
112 DECLARE_NAPI_FUNCTION(HttpResponseCache::FUNCTION_FLUSH, HttpResponseCache::Flush),
113 DECLARE_NAPI_FUNCTION(HttpResponseCache::FUNCTION_DELETE, HttpResponseCache::Delete),
114 };
115 ModuleTemplate::DefineClass(env, exports, properties, INTERFACE_HTTP_RESPONSE_CACHE);
116 }
117
InitHttpProperties(napi_env env,napi_value exports)118 void HttpModuleExports::InitHttpProperties(napi_env env, napi_value exports)
119 {
120 std::initializer_list<napi_property_descriptor> properties = {
121 DECLARE_NAPI_FUNCTION(FUNCTION_CREATE_HTTP, CreateHttp),
122 DECLARE_NAPI_FUNCTION(FUNCTION_CREATE_HTTP_RESPONSE_CACHE, CreateHttpResponseCache),
123 };
124 NapiUtils::DefineProperties(env, exports, properties);
125
126 InitRequestMethod(env, exports);
127 InitResponseCode(env, exports);
128 InitCertType(env, exports);
129 InitHttpProtocol(env, exports);
130 InitHttpDataType(env, exports);
131 }
132
InitRequestMethod(napi_env env,napi_value exports)133 void HttpModuleExports::InitRequestMethod(napi_env env, napi_value exports)
134 {
135 std::initializer_list<napi_property_descriptor> properties = {
136 DECLARE_REQUEST_METHOD(HTTP_METHOD_OPTIONS), DECLARE_REQUEST_METHOD(HTTP_METHOD_GET),
137 DECLARE_REQUEST_METHOD(HTTP_METHOD_HEAD), DECLARE_REQUEST_METHOD(HTTP_METHOD_POST),
138 DECLARE_REQUEST_METHOD(HTTP_METHOD_PUT), DECLARE_REQUEST_METHOD(HTTP_METHOD_DELETE),
139 DECLARE_REQUEST_METHOD(HTTP_METHOD_TRACE), DECLARE_REQUEST_METHOD(HTTP_METHOD_CONNECT),
140 };
141
142 napi_value requestMethod = NapiUtils::CreateObject(env);
143 NapiUtils::DefineProperties(env, requestMethod, properties);
144
145 NapiUtils::SetNamedProperty(env, exports, INTERFACE_REQUEST_METHOD, requestMethod);
146 }
147
InitResponseCode(napi_env env,napi_value exports)148 void HttpModuleExports::InitResponseCode(napi_env env, napi_value exports)
149 {
150 std::initializer_list<napi_property_descriptor> properties = {
151 DECLARE_RESPONSE_CODE(OK),
152 DECLARE_RESPONSE_CODE(CREATED),
153 DECLARE_RESPONSE_CODE(ACCEPTED),
154 DECLARE_RESPONSE_CODE(NOT_AUTHORITATIVE),
155 DECLARE_RESPONSE_CODE(NO_CONTENT),
156 DECLARE_RESPONSE_CODE(RESET),
157 DECLARE_RESPONSE_CODE(PARTIAL),
158 DECLARE_RESPONSE_CODE(MULT_CHOICE),
159 DECLARE_RESPONSE_CODE(MOVED_PERM),
160 DECLARE_RESPONSE_CODE(MOVED_TEMP),
161 DECLARE_RESPONSE_CODE(SEE_OTHER),
162 DECLARE_RESPONSE_CODE(NOT_MODIFIED),
163 DECLARE_RESPONSE_CODE(USE_PROXY),
164 DECLARE_RESPONSE_CODE(BAD_REQUEST),
165 DECLARE_RESPONSE_CODE(UNAUTHORIZED),
166 DECLARE_RESPONSE_CODE(PAYMENT_REQUIRED),
167 DECLARE_RESPONSE_CODE(FORBIDDEN),
168 DECLARE_RESPONSE_CODE(NOT_FOUND),
169 DECLARE_RESPONSE_CODE(BAD_METHOD),
170 DECLARE_RESPONSE_CODE(NOT_ACCEPTABLE),
171 DECLARE_RESPONSE_CODE(PROXY_AUTH),
172 DECLARE_RESPONSE_CODE(CLIENT_TIMEOUT),
173 DECLARE_RESPONSE_CODE(CONFLICT),
174 DECLARE_RESPONSE_CODE(GONE),
175 DECLARE_RESPONSE_CODE(LENGTH_REQUIRED),
176 DECLARE_RESPONSE_CODE(PRECON_FAILED),
177 DECLARE_RESPONSE_CODE(ENTITY_TOO_LARGE),
178 DECLARE_RESPONSE_CODE(REQ_TOO_LONG),
179 DECLARE_RESPONSE_CODE(UNSUPPORTED_TYPE),
180 DECLARE_RESPONSE_CODE(RANGE_NOT_SATISFIABLE),
181 DECLARE_RESPONSE_CODE(INTERNAL_ERROR),
182 DECLARE_RESPONSE_CODE(NOT_IMPLEMENTED),
183 DECLARE_RESPONSE_CODE(BAD_GATEWAY),
184 DECLARE_RESPONSE_CODE(UNAVAILABLE),
185 DECLARE_RESPONSE_CODE(GATEWAY_TIMEOUT),
186 DECLARE_RESPONSE_CODE(VERSION),
187 };
188
189 napi_value responseCode = NapiUtils::CreateObject(env);
190 NapiUtils::DefineProperties(env, responseCode, properties);
191
192 NapiUtils::SetNamedProperty(env, exports, INTERFACE_RESPONSE_CODE, responseCode);
193 }
194
InitHttpProtocol(napi_env env,napi_value exports)195 void HttpModuleExports::InitHttpProtocol(napi_env env, napi_value exports)
196 {
197 std::initializer_list<napi_property_descriptor> properties = {
198 DECLARE_HTTP_PROTOCOL(HTTP1_1),
199 DECLARE_HTTP_PROTOCOL(HTTP2),
200 DECLARE_HTTP_PROTOCOL(HTTP3),
201 };
202
203 napi_value httpProtocol = NapiUtils::CreateObject(env);
204 NapiUtils::DefineProperties(env, httpProtocol, properties);
205
206 NapiUtils::SetNamedProperty(env, exports, INTERFACE_HTTP_PROTOCOL, httpProtocol);
207 }
208
InitCertType(napi_env env,napi_value exports)209 void HttpModuleExports::InitCertType(napi_env env, napi_value exports)
210 {
211 std::initializer_list<napi_property_descriptor> properties = {
212 DECLARE_NAPI_STATIC_PROPERTY(HttpConstant::HTTP_CERT_TYPE_PEM,
213 NapiUtils::CreateStringUtf8(env, HttpConstant::HTTP_CERT_TYPE_PEM)),
214 DECLARE_NAPI_STATIC_PROPERTY(HttpConstant::HTTP_CERT_TYPE_DER,
215 NapiUtils::CreateStringUtf8(env, HttpConstant::HTTP_CERT_TYPE_DER)),
216 DECLARE_NAPI_STATIC_PROPERTY(HttpConstant::HTTP_CERT_TYPE_P12,
217 NapiUtils::CreateStringUtf8(env, HttpConstant::HTTP_CERT_TYPE_P12)),
218 };
219 napi_value httpCertType = NapiUtils::CreateObject(env);
220 NapiUtils::DefineProperties(env, httpCertType, properties);
221 NapiUtils::SetNamedProperty(env, exports, INTERFACE_CERT_TYPE, httpCertType);
222 }
223
InitHttpDataType(napi_env env,napi_value exports)224 void HttpModuleExports::InitHttpDataType(napi_env env, napi_value exports)
225 {
226 std::initializer_list<napi_property_descriptor> properties = {
227 DECLARE_NAPI_STATIC_PROPERTY("STRING",
228 NapiUtils::CreateUint32(env, static_cast<uint32_t>(HttpDataType::STRING))),
229 DECLARE_NAPI_STATIC_PROPERTY("OBJECT",
230 NapiUtils::CreateUint32(env, static_cast<uint32_t>(HttpDataType::OBJECT))),
231 DECLARE_NAPI_STATIC_PROPERTY("ARRAY_BUFFER",
232 NapiUtils::CreateUint32(env, static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER))),
233 };
234 napi_value httpDataType = NapiUtils::CreateObject(env);
235 NapiUtils::DefineProperties(env, httpDataType, properties);
236 NapiUtils::SetNamedProperty(env, exports, INTERFACE_HTTP_DATA_TYPE, httpDataType);
237 }
238
Request(napi_env env,napi_callback_info info)239 napi_value HttpModuleExports::HttpRequest::Request(napi_env env, napi_callback_info info)
240 {
241 return ModuleTemplate::InterfaceWithOutAsyncWorkWithManagerWrapper<RequestContext>(
242 env, info,
243 [](napi_env, napi_value, RequestContext *context) -> bool {
244 #if !HAS_NETMANAGER_BASE
245 if (!HttpExec::Initialize()) {
246 return false;
247 }
248 #endif
249 context->SetModuleId(g_moduleId);
250 context->SetAtomicService(g_appIsAtomicService);
251 context->SetBundleName(g_appBundleName);
252 HttpExec::AsyncRunRequest(context);
253 return context->IsExecOK();
254 },
255 "Request", HttpAsyncWork::ExecRequest, HttpAsyncWork::RequestCallback);
256 }
257
RequestInStream(napi_env env,napi_callback_info info)258 napi_value HttpModuleExports::HttpRequest::RequestInStream(napi_env env, napi_callback_info info)
259 {
260 return ModuleTemplate::InterfaceWithOutAsyncWorkWithManagerWrapper<RequestContext>(
261 env, info,
262 [](napi_env, napi_value, RequestContext *context) -> bool {
263 #if !HAS_NETMANAGER_BASE
264 if (!HttpExec::Initialize()) {
265 return false;
266 }
267 #endif
268 context->SetModuleId(g_moduleId);
269 context->SetAtomicService(g_appIsAtomicService);
270 context->SetBundleName(g_appBundleName);
271 context->EnableRequestInStream();
272 HttpExec::AsyncRunRequest(context);
273 return true;
274 },
275 "RequestInStream", HttpAsyncWork::ExecRequest, HttpAsyncWork::RequestCallback);
276 }
277
Destroy(napi_env env,napi_callback_info info)278 napi_value HttpModuleExports::HttpRequest::Destroy(napi_env env, napi_callback_info info)
279 {
280 napi_value thisVal = nullptr;
281 NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVal, nullptr));
282 EventManagerWrapper *wrapper = nullptr;
283 auto napiRet = napi_unwrap(env, thisVal, reinterpret_cast<void **>(&wrapper));
284 if (napiRet != napi_ok) {
285 NETSTACK_LOGE("get event manager in napi_unwrap failed, napi_ret is %{public}d", napiRet);
286 return NapiUtils::GetUndefined(env);
287 }
288
289 if (!wrapper) {
290 return NapiUtils::GetUndefined(env);
291 }
292 auto manager = wrapper->sharedManager;
293 if (!manager) {
294 return NapiUtils::GetUndefined(env);
295 }
296 if (manager->IsEventDestroy()) {
297 NETSTACK_LOGD("js object has been destroyed");
298 return NapiUtils::GetUndefined(env);
299 }
300 manager->SetEventDestroy(true);
301 manager->DeleteEventReference(env);
302 return NapiUtils::GetUndefined(env);
303 }
304
On(napi_env env,napi_callback_info info)305 napi_value HttpModuleExports::HttpRequest::On(napi_env env, napi_callback_info info)
306 {
307 ModuleTemplate::OnManagerWrapper(
308 env, info, {ON_HEADERS_RECEIVE, ON_DATA_RECEIVE, ON_DATA_END, ON_DATA_RECEIVE_PROGRESS, ON_DATA_SEND_PROGRESS},
309 false);
310 return ModuleTemplate::OnManagerWrapper(env, info, {ON_HEADER_RECEIVE}, true);
311 }
312
Once(napi_env env,napi_callback_info info)313 napi_value HttpModuleExports::HttpRequest::Once(napi_env env, napi_callback_info info)
314 {
315 return ModuleTemplate::OnceManagerWrapper(env, info, {ON_HEADER_RECEIVE, ON_HEADERS_RECEIVE}, false);
316 }
317
Off(napi_env env,napi_callback_info info)318 napi_value HttpModuleExports::HttpRequest::Off(napi_env env, napi_callback_info info)
319 {
320 ModuleTemplate::OffManagerWrapper(
321 env, info, {ON_HEADERS_RECEIVE, ON_DATA_RECEIVE, ON_DATA_END, ON_DATA_RECEIVE_PROGRESS, ON_DATA_SEND_PROGRESS});
322 return ModuleTemplate::OffManagerWrapper(env, info, {ON_HEADER_RECEIVE});
323 }
324
Flush(napi_env env,napi_callback_info info)325 napi_value HttpModuleExports::HttpResponseCache::Flush(napi_env env, napi_callback_info info)
326 {
327 return ModuleTemplate::InterfaceWithManagerWrapper<BaseContext>(
328 env, info, FLUSH_ASYNC_WORK_NAME, nullptr, HttpAsyncWork::ExecFlush, HttpAsyncWork::FlushCallback);
329 }
330
Delete(napi_env env,napi_callback_info info)331 napi_value HttpModuleExports::HttpResponseCache::Delete(napi_env env, napi_callback_info info)
332 {
333 return ModuleTemplate::InterfaceWithManagerWrapper<BaseContext>(
334 env, info, DELETE_ASYNC_WORK_NAME, nullptr, HttpAsyncWork::ExecDelete, HttpAsyncWork::DeleteCallback);
335 }
336
337 static napi_module g_httpModule = {
338 .nm_version = 1,
339 .nm_flags = 0,
340 .nm_filename = nullptr,
341 .nm_register_func = HttpModuleExports::InitHttpModule,
342 .nm_modname = HTTP_MODULE_NAME,
343 .nm_priv = nullptr,
344 .reserved = {nullptr},
345 };
346
RegisterHttpModule(void)347 extern "C" __attribute__((constructor)) void RegisterHttpModule(void)
348 {
349 napi_module_register(&g_httpModule);
350 }
351 } // namespace OHOS::NetStack::Http
352