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