PageRenderTime 70ms CodeModel.GetById 2ms app.highlight 63ms RepoModel.GetById 1ms app.codeStats 0ms

/test/test-ipc.c

http://github.com/joyent/libuv
C | 625 lines | 417 code | 162 blank | 46 comment | 113 complexity | b7d2c329246cd1db8efd15f7df0afb96 MD5 | raw file
  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
 25#include <stdio.h>
 26#include <string.h>
 27
 28static uv_pipe_t channel;
 29static uv_tcp_t tcp_server;
 30static uv_tcp_t tcp_connection;
 31
 32static int exit_cb_called;
 33static int read2_cb_called;
 34static int tcp_write_cb_called;
 35static int tcp_read_cb_called;
 36static int on_pipe_read_called;
 37static int local_conn_accepted;
 38static int remote_conn_accepted;
 39static int tcp_server_listening;
 40static uv_write_t write_req;
 41static uv_pipe_t channel;
 42static uv_tcp_t tcp_server;
 43static uv_write_t conn_notify_req;
 44static int close_cb_called;
 45static int connection_accepted;
 46static int tcp_conn_read_cb_called;
 47static int tcp_conn_write_cb_called;
 48
 49typedef struct {
 50  uv_connect_t conn_req;
 51  uv_write_t tcp_write_req;
 52  uv_tcp_t conn;
 53} tcp_conn;
 54
 55#define CONN_COUNT 100
 56
 57
 58static void close_server_conn_cb(uv_handle_t* handle) {
 59  free(handle);
 60}
 61
 62
 63static void on_connection(uv_stream_t* server, int status) {
 64  uv_tcp_t* conn;
 65  int r;
 66
 67  if (!local_conn_accepted) {
 68    /* Accept the connection and close it.  Also and close the server. */
 69    ASSERT(status == 0);
 70    ASSERT((uv_stream_t*)&tcp_server == server);
 71
 72    conn = malloc(sizeof(*conn));
 73    ASSERT(conn);
 74    r = uv_tcp_init(server->loop, conn);
 75    ASSERT(r == 0);
 76
 77    r = uv_accept(server, (uv_stream_t*)conn);
 78    ASSERT(r == 0);
 79
 80    uv_close((uv_handle_t*)conn, close_server_conn_cb);
 81    uv_close((uv_handle_t*)server, NULL);
 82    local_conn_accepted = 1;
 83  }
 84}
 85
 86
 87static void exit_cb(uv_process_t* process, int exit_status, int term_signal) {
 88  printf("exit_cb\n");
 89  exit_cb_called++;
 90  ASSERT(exit_status == 0);
 91  uv_close((uv_handle_t*)process, NULL);
 92}
 93
 94
 95static uv_buf_t on_alloc(uv_handle_t* handle, size_t suggested_size) {
 96  return uv_buf_init(malloc(suggested_size), suggested_size);
 97}
 98
 99
100static void close_client_conn_cb(uv_handle_t* handle) {
101  tcp_conn* p = (tcp_conn*)handle->data;
102  free(p);
103}
104
105
106static void connect_cb(uv_connect_t* req, int status) {
107  uv_close((uv_handle_t*)req->handle, close_client_conn_cb);
108}
109
110
111static void make_many_connections() {
112  tcp_conn* conn;
113  struct sockaddr_in addr;
114  int r, i;
115
116  for (i = 0; i < CONN_COUNT; i++) {
117    conn = malloc(sizeof(*conn));
118    ASSERT(conn);
119
120    r = uv_tcp_init(uv_default_loop(), &conn->conn);
121    ASSERT(r == 0);
122
123    addr = uv_ip4_addr("127.0.0.1", TEST_PORT);
124
125    r = uv_tcp_connect(&conn->conn_req, (uv_tcp_t*)&conn->conn, addr, connect_cb);
126    ASSERT(r == 0);
127
128    conn->conn.data = conn;
129  }
130}
131
132
133static void on_read(uv_pipe_t* pipe, ssize_t nread, uv_buf_t buf,
134    uv_handle_type pending) {
135  int r;
136  uv_buf_t outbuf;
137  uv_err_t err;
138
139  if (nread == 0) {
140    /* Everything OK, but nothing read. */
141    free(buf.base);
142    return;
143  }
144
145  if (nread < 0) {
146    err = uv_last_error(pipe->loop);
147    if (err.code == UV_EOF) {
148      free(buf.base);
149      return;
150    }
151
152    printf("error recving on channel: %s\n", uv_strerror(err));
153    abort();
154  }
155
156  fprintf(stderr, "got %d bytes\n", (int)nread);
157
158  if (!tcp_server_listening) {
159    ASSERT(nread > 0 && buf.base && pending != UV_UNKNOWN_HANDLE);
160    read2_cb_called++;
161
162    /* Accept the pending TCP server, and start listening on it. */
163    ASSERT(pending == UV_TCP);
164    r = uv_tcp_init(uv_default_loop(), &tcp_server);
165    ASSERT(r == 0);
166
167    r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server);
168    ASSERT(r == 0);
169
170    r = uv_listen((uv_stream_t*)&tcp_server, 12, on_connection);
171    ASSERT(r == 0);
172
173    tcp_server_listening = 1;
174
175    /* Make sure that the expected data is correctly multiplexed. */
176    ASSERT(memcmp("hello\n", buf.base, nread) == 0);
177
178    outbuf = uv_buf_init("world\n", 6);
179    r = uv_write(&write_req, (uv_stream_t*)pipe, &outbuf, 1, NULL);
180    ASSERT(r == 0);
181
182    /* Create a bunch of connections to get both servers to accept. */
183    make_many_connections();
184  } else if (memcmp("accepted_connection\n", buf.base, nread) == 0) {
185    /* Remote server has accepted a connection.  Close the channel. */
186    ASSERT(pending == UV_UNKNOWN_HANDLE);
187    remote_conn_accepted = 1;
188    uv_close((uv_handle_t*)&channel, NULL);
189  }
190
191  free(buf.base);
192}
193
194
195void spawn_helper(uv_pipe_t* channel,
196                  uv_process_t* process,
197                  const char* helper) {
198  uv_process_options_t options;
199  size_t exepath_size;
200  char exepath[1024];
201  char* args[3];
202  int r;
203  uv_stdio_container_t stdio[1];
204
205  r = uv_pipe_init(uv_default_loop(), channel, 1);
206  ASSERT(r == 0);
207  ASSERT(channel->ipc);
208
209  exepath_size = sizeof(exepath);
210  r = uv_exepath(exepath, &exepath_size);
211  ASSERT(r == 0);
212
213  exepath[exepath_size] = '\0';
214  args[0] = exepath;
215  args[1] = (char*)helper;
216  args[2] = NULL;
217
218  memset(&options, 0, sizeof(options));
219  options.file = exepath;
220  options.args = args;
221  options.exit_cb = exit_cb;
222
223  options.stdio = stdio;
224  options.stdio[0].flags = UV_CREATE_PIPE |
225    UV_READABLE_PIPE | UV_WRITABLE_PIPE;
226  options.stdio[0].data.stream = (uv_stream_t*)channel;
227  options.stdio_count = 1;
228
229  r = uv_spawn(uv_default_loop(), process, options);
230  ASSERT(r == 0);
231}
232
233
234static void on_tcp_write(uv_write_t* req, int status) {
235  ASSERT(status == 0);
236  ASSERT(req->handle == (uv_stream_t*)&tcp_connection);
237  tcp_write_cb_called++;
238}
239
240
241static uv_buf_t on_read_alloc(uv_handle_t* handle, size_t suggested_size) {
242  uv_buf_t buf;
243  buf.base = (char*)malloc(suggested_size);
244  buf.len = suggested_size;
245  return buf;
246}
247
248
249static void on_tcp_read(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) {
250  ASSERT(nread > 0);
251  ASSERT(memcmp("hello again\n", buf.base, nread) == 0);
252  ASSERT(tcp == (uv_stream_t*)&tcp_connection);
253  free(buf.base);
254
255  tcp_read_cb_called++;
256
257  uv_close((uv_handle_t*)tcp, NULL);
258  uv_close((uv_handle_t*)&channel, NULL);
259}
260
261
262static void on_read_connection(uv_pipe_t* pipe, ssize_t nread, uv_buf_t buf,
263    uv_handle_type pending) {
264  int r;
265  uv_buf_t outbuf;
266  uv_err_t err;
267
268  if (nread == 0) {
269    /* Everything OK, but nothing read. */
270    free(buf.base);
271    return;
272  }
273
274  if (nread < 0) {
275    err = uv_last_error(pipe->loop);
276    if (err.code == UV_EOF) {
277      free(buf.base);
278      return;
279    }
280
281    printf("error recving on channel: %s\n", uv_strerror(err));
282    abort();
283  }
284
285  fprintf(stderr, "got %d bytes\n", (int)nread);
286
287  ASSERT(nread > 0 && buf.base && pending != UV_UNKNOWN_HANDLE);
288  read2_cb_called++;
289
290  /* Accept the pending TCP connection */
291  ASSERT(pending == UV_TCP);
292  r = uv_tcp_init(uv_default_loop(), &tcp_connection);
293  ASSERT(r == 0);
294
295  r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_connection);
296  ASSERT(r == 0);
297
298  /* Make sure that the expected data is correctly multiplexed. */
299  ASSERT(memcmp("hello\n", buf.base, nread) == 0);
300
301  /* Write/read to/from the connection */
302  outbuf = uv_buf_init("world\n", 6);
303  r = uv_write(&write_req, (uv_stream_t*)&tcp_connection, &outbuf, 1,
304    on_tcp_write);
305  ASSERT(r == 0);
306
307  r = uv_read_start((uv_stream_t*)&tcp_connection, on_read_alloc, on_tcp_read);
308  ASSERT(r == 0);
309
310  free(buf.base);
311}
312
313
314static int run_ipc_test(const char* helper, uv_read2_cb read_cb) {
315  uv_process_t process;
316  int r;
317
318  spawn_helper(&channel, &process, helper);
319  uv_read2_start((uv_stream_t*)&channel, on_alloc, read_cb);
320
321  r = uv_run(uv_default_loop());
322  ASSERT(r == 0);
323
324  MAKE_VALGRIND_HAPPY();
325  return 0;
326}
327
328
329TEST_IMPL(ipc_listen_before_write) {
330  int r = run_ipc_test("ipc_helper_listen_before_write", on_read);
331  ASSERT(local_conn_accepted == 1);
332  ASSERT(remote_conn_accepted == 1);
333  ASSERT(read2_cb_called == 1);
334  ASSERT(exit_cb_called == 1);
335  return r;
336}
337
338
339TEST_IMPL(ipc_listen_after_write) {
340  int r = run_ipc_test("ipc_helper_listen_after_write", on_read);
341  ASSERT(local_conn_accepted == 1);
342  ASSERT(remote_conn_accepted == 1);
343  ASSERT(read2_cb_called == 1);
344  ASSERT(exit_cb_called == 1);
345  return r;
346}
347
348
349TEST_IMPL(ipc_tcp_connection) {
350  int r = run_ipc_test("ipc_helper_tcp_connection", on_read_connection);
351  ASSERT(read2_cb_called == 1);
352  ASSERT(tcp_write_cb_called == 1);
353  ASSERT(tcp_read_cb_called == 1);
354  ASSERT(exit_cb_called == 1);
355  return r;
356}
357
358
359#ifdef _WIN32
360TEST_IMPL(listen_with_simultaneous_accepts) {
361  uv_tcp_t server;
362  int r;
363  struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", TEST_PORT);
364
365  r = uv_tcp_init(uv_default_loop(), &server);
366  ASSERT(r == 0);
367
368  r = uv_tcp_bind(&server, addr);
369  ASSERT(r == 0);
370
371  r = uv_tcp_simultaneous_accepts(&server, 1);
372  ASSERT(r == 0);
373
374  r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL);
375  ASSERT(r == 0);
376  ASSERT(server.reqs_pending == 32);
377
378  MAKE_VALGRIND_HAPPY();
379  return 0;
380}
381
382
383TEST_IMPL(listen_no_simultaneous_accepts) {
384  uv_tcp_t server;
385  int r;
386  struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", TEST_PORT);
387
388  r = uv_tcp_init(uv_default_loop(), &server);
389  ASSERT(r == 0);
390
391  r = uv_tcp_bind(&server, addr);
392  ASSERT(r == 0);
393
394  r = uv_tcp_simultaneous_accepts(&server, 0);
395  ASSERT(r == 0);
396
397  r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL);
398  ASSERT(r == 0);
399  ASSERT(server.reqs_pending == 1);
400
401  MAKE_VALGRIND_HAPPY();
402  return 0;
403}
404#endif
405
406
407/* Everything here runs in a child process. */
408
409tcp_conn conn;
410
411
412static void close_cb(uv_handle_t* handle) {
413  close_cb_called++;
414}
415
416
417static void conn_notify_write_cb(uv_write_t* req, int status) {
418  uv_close((uv_handle_t*)&tcp_server, close_cb);
419  uv_close((uv_handle_t*)&channel, close_cb);
420}
421
422
423static void tcp_connection_write_cb(uv_write_t* req, int status) {
424  ASSERT((uv_handle_t*)&conn.conn == (uv_handle_t*)req->handle);
425  uv_close((uv_handle_t*)req->handle, close_cb);
426  uv_close((uv_handle_t*)&channel, close_cb);
427  uv_close((uv_handle_t*)&tcp_server, close_cb);
428  tcp_conn_write_cb_called++;
429}
430
431
432static void on_tcp_child_process_read(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) {
433  uv_buf_t outbuf;
434  int r;
435
436  if (nread < 0) {
437    if (uv_last_error(tcp->loop).code == UV_EOF) {
438      free(buf.base);
439      return;
440    }
441
442    printf("error recving on tcp connection: %s\n", 
443      uv_strerror(uv_last_error(tcp->loop)));
444    abort();
445  }
446
447  ASSERT(nread > 0);
448  ASSERT(memcmp("world\n", buf.base, nread) == 0);
449  on_pipe_read_called++;
450  free(buf.base);
451
452  /* Write to the socket */
453  outbuf = uv_buf_init("hello again\n", 12);
454  r = uv_write(&conn.tcp_write_req, tcp, &outbuf, 1, tcp_connection_write_cb);
455  ASSERT(r == 0);
456
457  tcp_conn_read_cb_called++;
458}
459
460
461static void connect_child_process_cb(uv_connect_t* req, int status) {
462  int r;
463
464  ASSERT(status == 0);
465  r = uv_read_start(req->handle, on_read_alloc, on_tcp_child_process_read);
466  ASSERT(r == 0);
467}
468
469
470static void ipc_on_connection(uv_stream_t* server, int status) {
471  int r;
472  uv_buf_t buf;
473
474  if (!connection_accepted) {
475    /*
476     * Accept the connection and close it.  Also let the other
477     * side know.
478     */
479    ASSERT(status == 0);
480    ASSERT((uv_stream_t*)&tcp_server == server);
481
482    r = uv_tcp_init(server->loop, &conn.conn);
483    ASSERT(r == 0);
484
485    r = uv_accept(server, (uv_stream_t*)&conn.conn);
486    ASSERT(r == 0);
487
488    uv_close((uv_handle_t*)&conn.conn, close_cb);
489
490    buf = uv_buf_init("accepted_connection\n", 20);
491    r = uv_write2(&conn_notify_req, (uv_stream_t*)&channel, &buf, 1,
492      NULL, conn_notify_write_cb);
493    ASSERT(r == 0);
494
495    connection_accepted = 1;
496  }
497}
498
499
500static void ipc_on_connection_tcp_conn(uv_stream_t* server, int status) {
501  int r;
502  uv_buf_t buf;
503  uv_tcp_t* conn;
504
505  ASSERT(status == 0);
506  ASSERT((uv_stream_t*)&tcp_server == server);
507
508  conn = malloc(sizeof(*conn));
509  ASSERT(conn);
510
511  r = uv_tcp_init(server->loop, conn);
512  ASSERT(r == 0);
513
514  r = uv_accept(server, (uv_stream_t*)conn);
515  ASSERT(r == 0);
516
517  /* Send the accepted connection to the other process */
518  buf = uv_buf_init("hello\n", 6);
519  r = uv_write2(&conn_notify_req, (uv_stream_t*)&channel, &buf, 1,
520    (uv_stream_t*)conn, NULL);
521  ASSERT(r == 0);
522
523  r = uv_read_start((uv_stream_t*)conn, on_read_alloc, on_tcp_child_process_read);
524  ASSERT(r == 0);
525
526  uv_close((uv_handle_t*)conn, close_cb);
527}
528
529
530int ipc_helper(int listen_after_write) {
531  /*
532   * This is launched from test-ipc.c. stdin is a duplex channel that we
533   * over which a handle will be transmitted.
534   */
535
536  uv_write_t write_req;
537  int r;
538  uv_buf_t buf;
539
540  r = uv_pipe_init(uv_default_loop(), &channel, 1);
541  ASSERT(r == 0);
542
543  uv_pipe_open(&channel, 0);
544
545  ASSERT(uv_is_readable((uv_stream_t*) &channel));
546  ASSERT(uv_is_writable((uv_stream_t*) &channel));
547  ASSERT(!uv_is_closing((uv_handle_t*) &channel));
548
549  r = uv_tcp_init(uv_default_loop(), &tcp_server);
550  ASSERT(r == 0);
551
552  r = uv_tcp_bind(&tcp_server, uv_ip4_addr("0.0.0.0", TEST_PORT));
553  ASSERT(r == 0);
554
555  if (!listen_after_write) {
556    r = uv_listen((uv_stream_t*)&tcp_server, 12, ipc_on_connection);
557    ASSERT(r == 0);
558  }
559
560  buf = uv_buf_init("hello\n", 6);
561  r = uv_write2(&write_req, (uv_stream_t*)&channel, &buf, 1,
562      (uv_stream_t*)&tcp_server, NULL);
563  ASSERT(r == 0);
564
565  if (listen_after_write) {
566    r = uv_listen((uv_stream_t*)&tcp_server, 12, ipc_on_connection);
567    ASSERT(r == 0);
568  }
569
570  r = uv_run(uv_default_loop());
571  ASSERT(r == 0);
572
573  ASSERT(connection_accepted == 1);
574  ASSERT(close_cb_called == 3);
575
576  MAKE_VALGRIND_HAPPY();
577  return 0;
578}
579
580
581int ipc_helper_tcp_connection() {
582  /*
583   * This is launched from test-ipc.c. stdin is a duplex channel that we
584   * over which a handle will be transmitted.
585   */
586
587  int r;
588  struct sockaddr_in addr;
589
590  r = uv_pipe_init(uv_default_loop(), &channel, 1);
591  ASSERT(r == 0);
592
593  uv_pipe_open(&channel, 0);
594
595  ASSERT(uv_is_readable((uv_stream_t*)&channel));
596  ASSERT(uv_is_writable((uv_stream_t*)&channel));
597  ASSERT(!uv_is_closing((uv_handle_t*)&channel));
598
599  r = uv_tcp_init(uv_default_loop(), &tcp_server);
600  ASSERT(r == 0);
601
602  r = uv_tcp_bind(&tcp_server, uv_ip4_addr("0.0.0.0", TEST_PORT));
603  ASSERT(r == 0);
604
605  r = uv_listen((uv_stream_t*)&tcp_server, 12, ipc_on_connection_tcp_conn);
606  ASSERT(r == 0);
607
608  /* Make a connection to the server */
609  r = uv_tcp_init(uv_default_loop(), &conn.conn);
610  ASSERT(r == 0);
611
612  addr = uv_ip4_addr("127.0.0.1", TEST_PORT);
613  r = uv_tcp_connect(&conn.conn_req, (uv_tcp_t*)&conn.conn, addr, connect_child_process_cb);
614  ASSERT(r == 0);
615
616  r = uv_run(uv_default_loop());
617  ASSERT(r == 0);
618
619  ASSERT(tcp_conn_read_cb_called == 1);
620  ASSERT(tcp_conn_write_cb_called == 1);
621  ASSERT(close_cb_called == 4);
622
623  MAKE_VALGRIND_HAPPY();
624  return 0;
625}