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