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