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