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, &paramsCount, 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