1/*
2 * Copyright (c) 2021 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 "netserver.h"
17
18#include "utils/log.h"
19
20#include <cstdlib>
21
22using namespace std;
23
24NetServer::NetServer(napi_env env, napi_value thisVar) : EventTarget(env, thisVar)
25{
26    napi_get_uv_event_loop(env, &loop_);
27    tcpServer_ = { 0 };
28    tcpServer_.data = this;
29    serverClosed_ = false;
30    clients_ = nullptr;
31}
32
33NetServer::~NetServer() {}
34
35int NetServer::Start(int port)
36{
37    struct sockaddr_in addr;
38    int result = 0;
39
40    uv_ip4_addr("0.0.0.0", port, &addr);
41
42    result = uv_tcp_init(loop_, &tcpServer_);
43    if (result) {
44        this->Emit("error", nullptr);
45        return -1;
46    }
47
48    result = uv_tcp_bind(&tcpServer_, (const struct sockaddr*)&addr, 0);
49    if (result) {
50        this->Emit("error", nullptr);
51        return -1;
52    }
53
54    result = uv_listen((uv_stream_t*)&tcpServer_, SOMAXCONN, OnConnection);
55    if (result) {
56        this->Emit("error", nullptr);
57        return -1;
58    }
59
60    Emit("started", nullptr);
61
62    return 0;
63}
64
65void NetServer::Stop()
66{
67    Emit("closed", nullptr);
68    uint32_t thisRefCount = 0;
69    napi_reference_unref(env_, thisVarRef_, &thisRefCount);
70}
71
72void NetServer::OnClose(uv_handle_t* peer)
73{
74    if (peer == nullptr) {
75        HILOG_ERROR("peer is null");
76        return;
77    }
78
79    NetServer* that = (NetServer*)peer->data;
80    that->Emit("disconnect", nullptr);
81    free(peer);
82}
83
84void NetServer::OnConnection(uv_stream_t* server, int status)
85{
86    if (server == nullptr) {
87        HILOG_ERROR("server is null");
88        return;
89    }
90
91    NetServer* that = (NetServer*)server->data;
92
93    if (status != 0) {
94        that->Emit("error", nullptr);
95    }
96
97    if (that->clients_ == nullptr) {
98        that->clients_ = new NetClient();
99    } else {
100        auto tmp = new NetClient();
101        tmp->next = that->clients_;
102        that->clients_ = tmp;
103    }
104
105    uv_tcp_init(that->loop_, (uv_tcp_t*)&that->clients_->tcp);
106    that->clients_->tcp.data = server->data;
107    uv_accept(server, (uv_stream_t*)&that->clients_->tcp);
108    uv_read_start((uv_stream_t*)&that->clients_->tcp, EchoAlloc, AfterRead);
109
110    that->Emit("connect", nullptr);
111}
112
113void NetServer::OnServerClose(uv_handle_t* handle)
114{
115    if (handle == nullptr) {
116        HILOG_ERROR("handle is null");
117        return;
118    }
119
120    NetServer* that = (NetServer*)handle->data;
121
122    for (NetClient* i = that->clients_; i != nullptr; i = i->next) {
123        uv_close((uv_handle_t*)&i->tcp, nullptr);
124    }
125
126    uint32_t thisRefCount = 0;
127    napi_reference_unref(that->env_, that->thisVarRef_, &thisRefCount);
128}
129
130void NetServer::AfterWrite(uv_write_t* req, int status)
131{
132    if (req == nullptr) {
133        HILOG_ERROR("req is null");
134        return;
135    }
136
137    NetServer* that = (NetServer*)req->data;
138
139    WriteReq* wr = (WriteReq*)req;
140
141    free(wr->buf.base);
142    free(wr);
143
144    if (status == 0) {
145        that->Emit("write", nullptr);
146        return;
147    }
148
149    that->Emit("error", nullptr);
150}
151
152void NetServer::TakeDiffactionsByStatus(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf, NetServer* that)
153{
154    WriteReq* wr = nullptr;
155    uv_shutdown_t* sreq = nullptr;
156    if (nread < 0) {
157        free(buf->base);
158        sreq = (uv_shutdown_t*)malloc(sizeof(*sreq));
159        if (sreq == nullptr) {
160            HILOG_ERROR("sreq is null");
161            return;
162        }
163        sreq->data = that;
164        uv_shutdown(sreq, handle, AfterShutdown);
165        return;
166    }
167
168    if (!that->serverClosed_) {
169        for (int i = 0; i < nread; i++) {
170            if (buf->base[i] != 'Q') {
171                continue;
172            }
173            if (i + 1 < nread && buf->base[i + 1] == 'S') {
174                free(buf->base);
175                uv_close((uv_handle_t*)handle, OnClose);
176                return;
177            } else {
178                uv_close((uv_handle_t*)&that->tcpServer_, OnServerClose);
179                that->serverClosed_ = 1;
180                return;
181            }
182        }
183    }
184
185    that->Emit("read", nullptr);
186    wr = (WriteReq*)malloc(sizeof(WriteReq));
187    if (wr == nullptr) {
188        HILOG_ERROR("wr is null");
189        free(buf->base);
190        return;
191    }
192
193    wr->buf = uv_buf_init(buf->base, nread);
194    wr->req.data = that;
195    if (uv_write(&wr->req, handle, &wr->buf, 1, AfterWrite) != 0) {
196        that->Emit("error", nullptr);
197    }
198}
199
200void NetServer::AfterRead(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf)
201{
202    if (handle == nullptr) {
203        HILOG_ERROR("handle is null");
204        return;
205    }
206
207    if (buf == nullptr) {
208        HILOG_ERROR("buf is null");
209        return;
210    }
211
212    NetServer* that = (NetServer*)handle->data;
213    if (nread == 0) {
214        free(buf->base);
215        return;
216    }
217
218    TakeDiffactionsByStatus(handle, nread, buf, that);
219}
220
221void NetServer::AfterShutdown(uv_shutdown_t* req, int status)
222{
223    if (req == nullptr) {
224        HILOG_ERROR("req is null");
225        return;
226    }
227
228    uv_close((uv_handle_t*)req->handle, OnClose);
229    free(req);
230}
231
232void NetServer::EchoAlloc(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf)
233{
234    if (handle == nullptr) {
235        HILOG_ERROR("handle is null");
236        return;
237    }
238
239    if (buf == nullptr) {
240        HILOG_ERROR("buf is null");
241        return;
242    }
243
244    buf->base = (char*)malloc(suggestedSize);
245    if (buf->base != nullptr) {
246        HILOG_ERROR("buf->base is null");
247        return;
248    }
249
250    buf->len = suggestedSize;
251}
252