echo-server.c revision 1.1.1.1
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;
40static uv_udp_send_t* send_freelist;
41
42static void after_write(uv_write_t* req, int status);
43static void after_read(uv_stream_t*, ssize_t nread, const uv_buf_t* buf);
44static void on_close(uv_handle_t* peer);
45static void on_server_close(uv_handle_t* handle);
46static void on_connection(uv_stream_t*, int status);
47
48
49static void after_write(uv_write_t* req, int status) {
50  write_req_t* wr;
51
52  /* Free the read/write buffer and the request */
53  wr = (write_req_t*) req;
54  free(wr->buf.base);
55  free(wr);
56
57  if (status == 0)
58    return;
59
60  fprintf(stderr,
61          "uv_write error: %s - %s\n",
62          uv_err_name(status),
63          uv_strerror(status));
64}
65
66
67static void after_shutdown(uv_shutdown_t* req, int status) {
68  uv_close((uv_handle_t*) req->handle, on_close);
69  free(req);
70}
71
72
73static void after_read(uv_stream_t* handle,
74                       ssize_t nread,
75                       const uv_buf_t* buf) {
76  int i;
77  write_req_t *wr;
78  uv_shutdown_t* sreq;
79
80  if (nread < 0) {
81    /* Error or EOF */
82    ASSERT(nread == UV_EOF);
83
84    free(buf->base);
85    sreq = malloc(sizeof* sreq);
86    ASSERT(0 == uv_shutdown(sreq, handle, after_shutdown));
87    return;
88  }
89
90  if (nread == 0) {
91    /* Everything OK, but nothing read. */
92    free(buf->base);
93    return;
94  }
95
96  /*
97   * Scan for the letter Q which signals that we should quit the server.
98   * If we get QS it means close the stream.
99   */
100  if (!server_closed) {
101    for (i = 0; i < nread; i++) {
102      if (buf->base[i] == 'Q') {
103        if (i + 1 < nread && buf->base[i + 1] == 'S') {
104          free(buf->base);
105          uv_close((uv_handle_t*)handle, on_close);
106          return;
107        } else {
108          uv_close(server, on_server_close);
109          server_closed = 1;
110        }
111      }
112    }
113  }
114
115  wr = (write_req_t*) malloc(sizeof *wr);
116  ASSERT(wr != NULL);
117  wr->buf = uv_buf_init(buf->base, nread);
118
119  if (uv_write(&wr->req, handle, &wr->buf, 1, after_write)) {
120    FATAL("uv_write failed");
121  }
122}
123
124
125static void on_close(uv_handle_t* peer) {
126  free(peer);
127}
128
129
130static void echo_alloc(uv_handle_t* handle,
131                       size_t suggested_size,
132                       uv_buf_t* buf) {
133  buf->base = malloc(suggested_size);
134  buf->len = suggested_size;
135}
136
137static void slab_alloc(uv_handle_t* handle,
138                       size_t suggested_size,
139                       uv_buf_t* buf) {
140  /* up to 16 datagrams at once */
141  static char slab[16 * 64 * 1024];
142  buf->base = slab;
143  buf->len = sizeof(slab);
144}
145
146static void on_connection(uv_stream_t* server, int status) {
147  uv_stream_t* stream;
148  int r;
149
150  if (status != 0) {
151    fprintf(stderr, "Connect error %s\n", uv_err_name(status));
152  }
153  ASSERT(status == 0);
154
155  switch (serverType) {
156  case TCP:
157    stream = malloc(sizeof(uv_tcp_t));
158    ASSERT(stream != NULL);
159    r = uv_tcp_init(loop, (uv_tcp_t*)stream);
160    ASSERT(r == 0);
161    break;
162
163  case PIPE:
164    stream = malloc(sizeof(uv_pipe_t));
165    ASSERT(stream != NULL);
166    r = uv_pipe_init(loop, (uv_pipe_t*)stream, 0);
167    ASSERT(r == 0);
168    break;
169
170  default:
171    ASSERT(0 && "Bad serverType");
172    abort();
173  }
174
175  /* associate server with stream */
176  stream->data = server;
177
178  r = uv_accept(server, stream);
179  ASSERT(r == 0);
180
181  r = uv_read_start(stream, echo_alloc, after_read);
182  ASSERT(r == 0);
183}
184
185
186static void on_server_close(uv_handle_t* handle) {
187  ASSERT(handle == server);
188}
189
190static uv_udp_send_t* send_alloc(void) {
191  uv_udp_send_t* req = send_freelist;
192  if (req != NULL)
193    send_freelist = req->data;
194  else
195    req = malloc(sizeof(*req));
196  return req;
197}
198
199static void on_send(uv_udp_send_t* req, int status) {
200  ASSERT(req != NULL);
201  ASSERT(status == 0);
202  req->data = send_freelist;
203  send_freelist = req;
204}
205
206static void on_recv(uv_udp_t* handle,
207                    ssize_t nread,
208                    const uv_buf_t* rcvbuf,
209                    const struct sockaddr* addr,
210                    unsigned flags) {
211  uv_buf_t sndbuf;
212
213  if (nread == 0) {
214    /* Everything OK, but nothing read. */
215    return;
216  }
217
218  ASSERT(nread > 0);
219  ASSERT(addr->sa_family == AF_INET);
220
221  uv_udp_send_t* req = send_alloc();
222  ASSERT(req != NULL);
223  sndbuf = uv_buf_init(rcvbuf->base, nread);
224  ASSERT(0 <= uv_udp_send(req, handle, &sndbuf, 1, addr, on_send));
225}
226
227static int tcp4_echo_start(int port) {
228  struct sockaddr_in addr;
229  int r;
230
231  ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr));
232
233  server = (uv_handle_t*)&tcpServer;
234  serverType = TCP;
235
236  r = uv_tcp_init(loop, &tcpServer);
237  if (r) {
238    /* TODO: Error codes */
239    fprintf(stderr, "Socket creation error\n");
240    return 1;
241  }
242
243  r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr, 0);
244  if (r) {
245    /* TODO: Error codes */
246    fprintf(stderr, "Bind error\n");
247    return 1;
248  }
249
250  r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection);
251  if (r) {
252    /* TODO: Error codes */
253    fprintf(stderr, "Listen error %s\n", uv_err_name(r));
254    return 1;
255  }
256
257  return 0;
258}
259
260
261static int tcp6_echo_start(int port) {
262  struct sockaddr_in6 addr6;
263  int r;
264
265  ASSERT(0 == uv_ip6_addr("::1", port, &addr6));
266
267  server = (uv_handle_t*)&tcpServer;
268  serverType = TCP;
269
270  r = uv_tcp_init(loop, &tcpServer);
271  if (r) {
272    /* TODO: Error codes */
273    fprintf(stderr, "Socket creation error\n");
274    return 1;
275  }
276
277  /* IPv6 is optional as not all platforms support it */
278  r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &addr6, 0);
279  if (r) {
280    /* show message but return OK */
281    fprintf(stderr, "IPv6 not supported\n");
282    return 0;
283  }
284
285  r = uv_listen((uv_stream_t*)&tcpServer, SOMAXCONN, on_connection);
286  if (r) {
287    /* TODO: Error codes */
288    fprintf(stderr, "Listen error\n");
289    return 1;
290  }
291
292  return 0;
293}
294
295
296static int udp4_echo_start(int port) {
297  struct sockaddr_in addr;
298  int r;
299
300  ASSERT(0 == uv_ip4_addr("127.0.0.1", port, &addr));
301  server = (uv_handle_t*)&udpServer;
302  serverType = UDP;
303
304  r = uv_udp_init(loop, &udpServer);
305  if (r) {
306    fprintf(stderr, "uv_udp_init: %s\n", uv_strerror(r));
307    return 1;
308  }
309
310  r = uv_udp_bind(&udpServer, (const struct sockaddr*) &addr, 0);
311  if (r) {
312    fprintf(stderr, "uv_udp_bind: %s\n", uv_strerror(r));
313    return 1;
314  }
315
316  r = uv_udp_recv_start(&udpServer, slab_alloc, on_recv);
317  if (r) {
318    fprintf(stderr, "uv_udp_recv_start: %s\n", uv_strerror(r));
319    return 1;
320  }
321
322  return 0;
323}
324
325
326static int pipe_echo_start(char* pipeName) {
327  int r;
328
329#ifndef _WIN32
330  {
331    uv_fs_t req;
332    uv_fs_unlink(NULL, &req, pipeName, NULL);
333    uv_fs_req_cleanup(&req);
334  }
335#endif
336
337  server = (uv_handle_t*)&pipeServer;
338  serverType = PIPE;
339
340  r = uv_pipe_init(loop, &pipeServer, 0);
341  if (r) {
342    fprintf(stderr, "uv_pipe_init: %s\n", uv_strerror(r));
343    return 1;
344  }
345
346  r = uv_pipe_bind(&pipeServer, pipeName);
347  if (r) {
348    fprintf(stderr, "uv_pipe_bind: %s\n", uv_strerror(r));
349    return 1;
350  }
351
352  r = uv_listen((uv_stream_t*)&pipeServer, SOMAXCONN, on_connection);
353  if (r) {
354    fprintf(stderr, "uv_pipe_listen: %s\n", uv_strerror(r));
355    return 1;
356  }
357
358  return 0;
359}
360
361
362HELPER_IMPL(tcp4_echo_server) {
363  loop = uv_default_loop();
364
365  if (tcp4_echo_start(TEST_PORT))
366    return 1;
367
368  notify_parent_process();
369  uv_run(loop, UV_RUN_DEFAULT);
370  return 0;
371}
372
373
374HELPER_IMPL(tcp6_echo_server) {
375  loop = uv_default_loop();
376
377  if (tcp6_echo_start(TEST_PORT))
378    return 1;
379
380  notify_parent_process();
381  uv_run(loop, UV_RUN_DEFAULT);
382  return 0;
383}
384
385
386HELPER_IMPL(pipe_echo_server) {
387  loop = uv_default_loop();
388
389  if (pipe_echo_start(TEST_PIPENAME))
390    return 1;
391
392  notify_parent_process();
393  uv_run(loop, UV_RUN_DEFAULT);
394  return 0;
395}
396
397
398HELPER_IMPL(udp4_echo_server) {
399  loop = uv_default_loop();
400
401  if (udp4_echo_start(TEST_PORT))
402    return 1;
403
404  notify_parent_process();
405  uv_run(loop, UV_RUN_DEFAULT);
406  return 0;
407}
408