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