1/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22#include "uv.h"
23#include "task.h"
24#include <stdio.h>
25#include <stdlib.h>
26
27typedef struct {
28 uv_write_t req;
29 uv_buf_t buf;
30} write_req_t;
31
32static uv_loop_t* loop;
33
34static int server_closed;
35static stream_type serverType;
36static uv_tcp_t tcpServer;
37static uv_udp_t udpServer;
38static uv_pipe_t pipeServer;
39static uv_handle_t* server;
40
41static void after_write(uv_write_t* req, int status);
42static void after_read(uv_stream_t*, ssize_t nread, const uv_buf_t* buf);
43static void on_close(uv_handle_t* peer);
44static void on_server_close(uv_handle_t* handle);
45static void on_connection(uv_stream_t*, int status);
46
47
48static void after_write(uv_write_t* req, int status) {
49 write_req_t* wr;
50
51 /* Free the read/write buffer and the request */
52 wr = (write_req_t*) req;
53 free(wr->buf.base);
54 free(wr);
55
56 if (status == 0)
57 return;
58
59 fprintf(stderr,
60 "uv_write error: %s - %s\n",
61 uv_err_name(status),
62 uv_strerror(status));
63}
64
65
66static void after_shutdown(uv_shutdown_t* req, int status) {
67 uv_close((uv_handle_t*) req->handle, on_close);
68 free(req);
69}
70
71
72static void after_read(uv_stream_t* handle,
73 ssize_t nread,
74 const uv_buf_t* buf) {
75 int i;
76 write_req_t *wr;
77 uv_shutdown_t* sreq;
78
79 if (nread < 0) {
80 /* Error or EOF */
81 ASSERT(nread == UV_EOF);
82
83 free(buf->base);
84 sreq = malloc(sizeof* sreq);
85 ASSERT(0 == uv_shutdown(sreq, handle, after_shutdown));
86 return;
87 }
88
89 if (nread == 0) {
90 /* Everything OK, but nothing read. */
91 free(buf->base);
92 return;
93 }
94
95 /*
96 * Scan for the letter Q which signals that we should quit the server.
97 * If we get QS it means close the stream.
98 */
99 if (!server_closed) {
100 for (i = 0; i < nread; i++) {
101 if (buf->base[i] == 'Q') {
102 if (i + 1 < nread && buf->base[i + 1] == 'S') {
103 free(buf->base);
104 uv_close((uv_handle_t*)handle, on_close);
105 return;
106 } else {
107 uv_close(server, on_server_close);
108 server_closed = 1;
109 }
110 }
111 }
112 }
113
114 wr = (write_req_t*) malloc(sizeof *wr);
115 ASSERT(wr != NULL);
116 wr->buf = uv_buf_init(buf->base, nread);
117
118 if (uv_write(&wr->req, handle, &wr->buf, 1, after_write)) {
119 FATAL("uv_write failed");
120 }
121}
122
123
124static void on_close(uv_handle_t* peer) {
125 free(peer);
126}
127
128
129static void echo_alloc(uv_handle_t* handle,
130 size_t suggested_size,
131 uv_buf_t* buf) {
132 buf->base = malloc(suggested_size);
133 buf->len = suggested_size;
134}
135
136
137static void on_connection(uv_stream_t* server, int status) {
138 uv_stream_t* stream;
139 int r;
140
141 if (status != 0) {
142 fprintf(stderr, "Connect error %s\n", uv_err_name(status));
143 }
144 ASSERT(status == 0);
145
146 switch (serverType) {
147 case TCP:
148 stream = malloc(sizeof(uv_tcp_t));
149 ASSERT(stream != NULL);
150 r = uv_tcp_init(loop, (uv_tcp_t*)stream);
151 ASSERT(r == 0);
152 break;
153
154 case PIPE:
155 stream = malloc(sizeof(uv_pipe_t));
156 ASSERT(stream != NULL);
157 r = uv_pipe_init(loop, (uv_pipe_t*)stream, 0);
158 ASSERT(r == 0);
159 break;
160
161 default:
162 ASSERT(0 && "Bad serverType");
163 abort();
164 }
165
166 /* associate server with stream */
167 stream->data = server;
168
169 r = uv_accept(server, stream);
170 ASSERT(r == 0);
171
172 r = uv_read_start(stream, echo_alloc, after_read);
173 ASSERT(r == 0);
174}
175
176
177static void on_server_close(uv_handle_t* handle) {
178 ASSERT(handle == server);
179}
180
181
182static void on_send(uv_udp_send_t* req, int status);
183
184
185static void on_recv(uv_udp_t* handle,
186 ssize_t nread,
187 const uv_buf_t* rcvbuf,
188 const struct sockaddr* addr,
189 unsigned flags) {
190 uv_udp_send_t* req;
191 uv_buf_t sndbuf;
192
193 ASSERT(nread > 0);
194 ASSERT(addr->sa_family == AF_INET);
195
196 req = malloc(sizeof(*req));
197 ASSERT(req != NULL);
198
199 sndbuf = *rcvbuf;
200 ASSERT(0 == uv_udp_send(req, handle, &sndbuf, 1, addr, on_send));
201}
202
203
204static void on_send(uv_udp_send_t* req, int status) {
205 ASSERT(status == 0);
206 free(req);
207}
208
209
210static int tcp4_echo_start(int port) {
211 struct sockaddr_in addr;
212 int r;
213
214 ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr));
215
216 server = (uv_handle_t*)&tcpServer;
217 serverType = TCP;
218
219 r = uv_tcp_init(loop, &tcpServer);
220 if (r) {
221 /* TODO: Error codes */
222 fprintf(stderr, "Socket creation error\n");
223 return 1;
224 }
225
226 r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr, 0);
227 if (r) {
228 /* TODO: Error codes */
229 fprintf(stderr, "Bind error\n");
230 return 1;
231 }
232
233 r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection);
234 if (r) {
235 /* TODO: Error codes */
236 fprintf(stderr, "Listen error %s\n", uv_err_name(r));
237 return 1;
238 }
239
240 return 0;
241}
242
243
244static int tcp6_echo_start(int port) {
245 struct sockaddr_in6 addr6;
246 int r;
247
248 ASSERT(0 == uv_ip6_addr("::1", port, &addr6));
249
250 server = (uv_handle_t*)&tcpServer;
251 serverType = TCP;
252
253 r = uv_tcp_init(loop, &tcpServer);
254 if (r) {
255 /* TODO: Error codes */
256 fprintf(stderr, "Socket creation error\n");
257 return 1;
258 }
259
260 /* IPv6 is optional as not all platforms support it */
261 r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr6, 0);
262 if (r) {
263 /* show message but return OK */
264 fprintf(stderr, "IPv6 not supported\n");
265 return 0;
266 }
267
268 r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection);
269 if (r) {
270 /* TODO: Error codes */
271 fprintf(stderr, "Listen error\n");
272 return 1;
273 }
274
275 return 0;
276}
277
278
279static int udp4_echo_start(int port) {
280 int r;
281
282 server = (uv_handle_t*)&udpServer;
283 serverType = UDP;
284
285 r = uv_udp_init(loop, &udpServer);
286 if (r) {
287 fprintf(stderr, "uv_udp_init: %s\n", uv_strerror(r));
288 return 1;
289 }
290
291 r = uv_udp_recv_start(&udpServer, echo_alloc, on_recv);
292 if (r) {
293 fprintf(stderr, "uv_udp_recv_start: %s\n", uv_strerror(r));
294 return 1;
295 }
296
297 return 0;
298}
299
300
301static int pipe_echo_start(char* pipeName) {
302 int r;
303
304#ifndef _WIN32
305 {
306 uv_fs_t req;
307 uv_fs_unlink(NULL, &req, pipeName, NULL);
308 uv_fs_req_cleanup(&req);
309 }
310#endif
311
312 server = (uv_handle_t*)&pipeServer;
313 serverType = PIPE;
314
315 r = uv_pipe_init(loop, &pipeServer, 0);
316 if (r) {
317 fprintf(stderr, "uv_pipe_init: %s\n", uv_strerror(r));
318 return 1;
319 }
320
321 r = uv_pipe_bind(&pipeServer, pipeName);
322 if (r) {
323 fprintf(stderr, "uv_pipe_bind: %s\n", uv_strerror(r));
324 return 1;
325 }
326
327 r = uv_listen((uv_stream_t*)&pipeServer, SOMAXCONN, on_connection);
328 if (r) {
329 fprintf(stderr, "uv_pipe_listen: %s\n", uv_strerror(r));
330 return 1;
331 }
332
333 return 0;
334}
335
336
337HELPER_IMPL(tcp4_echo_server) {
338 loop = uv_default_loop();
339
340 if (tcp4_echo_start(TEST_PORT))
341 return 1;
342
343 notify_parent_process();
344 uv_run(loop, UV_RUN_DEFAULT);
345 return 0;
346}
347
348
349HELPER_IMPL(tcp6_echo_server) {
350 loop = uv_default_loop();
351
352 if (tcp6_echo_start(TEST_PORT))
353 return 1;
354
355 notify_parent_process();
356 uv_run(loop, UV_RUN_DEFAULT);
357 return 0;
358}
359
360
361HELPER_IMPL(pipe_echo_server) {
362 loop = uv_default_loop();
363
364 if (pipe_echo_start(TEST_PIPENAME))
365 return 1;
366
367 notify_parent_process();
368 uv_run(loop, UV_RUN_DEFAULT);
369 return 0;
370}
371
372
373HELPER_IMPL(udp4_echo_server) {
374 loop = uv_default_loop();
375
376 if (udp4_echo_start(TEST_PORT))
377 return 1;
378
379 notify_parent_process();
380 uv_run(loop, UV_RUN_DEFAULT);
381 return 0;
382}
383