blob: 8f2d74d081cbf7e4f76ad94655a4a0f3a5ec8e4b [file] [log] [blame]
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001/* $Id$ */
2/*
3 * Copyright (C)2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include "test.h"
20
21/**
22 * \page page_pjlib_ioqueue_tcp_test Test: I/O Queue (TCP)
23 *
24 * This file provides implementation to test the
25 * functionality of the I/O queue when TCP socket is used.
26 *
27 *
28 * This file is <b>pjlib-test/ioq_tcp.c</b>
29 *
30 * \include pjlib-test/ioq_tcp.c
31 */
32
33
34#if INCLUDE_TCP_IOQUEUE_TEST
35
36#include <pjlib.h>
37
38#if PJ_HAS_TCP
39
40#define THIS_FILE "test_tcp"
41#define PORT 50000
42#define NON_EXISTANT_PORT 50123
43#define LOOP 100
44#define BUF_MIN_SIZE 32
45#define BUF_MAX_SIZE 2048
46#define SOCK_INACTIVE_MIN (4-2)
47#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2)
48#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
49
50static pj_ssize_t callback_read_size,
51 callback_write_size,
52 callback_accept_status,
53 callback_connect_status;
54static pj_ioqueue_key_t *callback_read_key,
55 *callback_write_key,
56 *callback_accept_key,
57 *callback_connect_key;
58static pj_ioqueue_op_key_t *callback_read_op,
59 *callback_write_op,
60 *callback_accept_op;
61
62static void on_ioqueue_read(pj_ioqueue_key_t *key,
63 pj_ioqueue_op_key_t *op_key,
64 pj_ssize_t bytes_read)
65{
66 callback_read_key = key;
67 callback_read_op = op_key;
68 callback_read_size = bytes_read;
69}
70
71static void on_ioqueue_write(pj_ioqueue_key_t *key,
72 pj_ioqueue_op_key_t *op_key,
73 pj_ssize_t bytes_written)
74{
75 callback_write_key = key;
76 callback_write_op = op_key;
77 callback_write_size = bytes_written;
78}
79
80static void on_ioqueue_accept(pj_ioqueue_key_t *key,
81 pj_ioqueue_op_key_t *op_key,
82 pj_sock_t sock,
83 int status)
84{
85 PJ_UNUSED_ARG(sock);
86
87 callback_accept_key = key;
88 callback_accept_op = op_key;
89 callback_accept_status = status;
90}
91
92static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
93{
94 callback_connect_key = key;
95 callback_connect_status = status;
96}
97
98static pj_ioqueue_callback test_cb =
99{
100 &on_ioqueue_read,
101 &on_ioqueue_write,
102 &on_ioqueue_accept,
103 &on_ioqueue_connect,
104};
105
106static int send_recv_test(pj_ioqueue_t *ioque,
107 pj_ioqueue_key_t *skey,
108 pj_ioqueue_key_t *ckey,
109 void *send_buf,
110 void *recv_buf,
111 pj_ssize_t bufsize,
112 pj_timestamp *t_elapsed)
113{
114 pj_status_t status;
115 pj_ssize_t bytes;
116 pj_time_val timeout;
117 pj_timestamp t1, t2;
118 int pending_op = 0;
119 pj_ioqueue_op_key_t read_op, write_op;
120
121 // Start reading on the server side.
122 bytes = bufsize;
123 status = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0);
124 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
125 app_perror("...pj_ioqueue_recv error", status);
126 return -100;
127 }
128
129 if (status == PJ_EPENDING)
130 ++pending_op;
131 else {
132 /* Does not expect to return error or immediate data. */
133 return -115;
134 }
135
136 // Randomize send buffer.
137 pj_create_random_string((char*)send_buf, bufsize);
138
139 // Starts send on the client side.
140 bytes = bufsize;
141 status = pj_ioqueue_send(ckey, &write_op, send_buf, &bytes, 0);
142 if (status != PJ_SUCCESS && bytes != PJ_EPENDING) {
143 return -120;
144 }
145 if (status == PJ_EPENDING) {
146 ++pending_op;
147 }
148
149 // Begin time.
150 pj_get_timestamp(&t1);
151
152 // Reset indicators
153 callback_read_size = callback_write_size = 0;
154 callback_read_key = callback_write_key = NULL;
155 callback_read_op = callback_write_op = NULL;
156
157 // Poll the queue until we've got completion event in the server side.
158 status = 0;
159 while (pending_op > 0) {
160 timeout.sec = 1; timeout.msec = 0;
161 status = pj_ioqueue_poll(ioque, &timeout);
162 if (status > 0) {
163 if (callback_read_size) {
164 if (callback_read_size != bufsize)
165 return -160;
166 if (callback_read_key != skey)
167 return -161;
168 if (callback_read_op != &read_op)
169 return -162;
170 }
171 if (callback_write_size) {
172 if (callback_write_key != ckey)
173 return -163;
174 if (callback_write_op != &write_op)
175 return -164;
176 }
177 pending_op -= status;
178 }
179 if (status == 0) {
180 PJ_LOG(3,("", "...error: timed out"));
181 }
182 if (status < 0) {
183 return -170;
184 }
185 }
186
187 // Pending op is zero.
188 // Subsequent poll should yield zero too.
189 timeout.sec = timeout.msec = 0;
190 status = pj_ioqueue_poll(ioque, &timeout);
191 if (status != 0)
192 return -173;
193
194 // End time.
195 pj_get_timestamp(&t2);
196 t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo);
197
198 // Compare recv buffer with send buffer.
199 if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) {
200 return -180;
201 }
202
203 // Success
204 return 0;
205}
206
207
208/*
209 * Compliance test for success scenario.
210 */
211static int compliance_test_0(void)
212{
213 pj_sock_t ssock=-1, csock0=-1, csock1=-1;
214 pj_sockaddr_in addr, client_addr, rmt_addr;
215 int client_addr_len;
216 pj_pool_t *pool = NULL;
217 char *send_buf, *recv_buf;
218 pj_ioqueue_t *ioque = NULL;
219 pj_ioqueue_key_t *skey, *ckey0, *ckey1;
220 pj_ioqueue_op_key_t accept_op;
221 int bufsize = BUF_MIN_SIZE;
222 pj_ssize_t status = -1;
223 int pending_op = 0;
224 pj_timestamp t_elapsed;
225 pj_str_t s;
226 pj_status_t rc;
227
228 // Create pool.
229 pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
230
231 // Allocate buffers for send and receive.
232 send_buf = (char*)pj_pool_alloc(pool, bufsize);
233 recv_buf = (char*)pj_pool_alloc(pool, bufsize);
234
235 // Create server socket and client socket for connecting
236 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &ssock);
237 if (rc != PJ_SUCCESS) {
238 app_perror("...error creating socket", rc);
239 status=-1; goto on_error;
240 }
241
242 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1);
243 if (rc != PJ_SUCCESS) {
244 app_perror("...error creating socket", rc);
245 status=-1; goto on_error;
246 }
247
248 // Bind server socket.
249 memset(&addr, 0, sizeof(addr));
250 addr.sin_family = PJ_AF_INET;
251 addr.sin_port = pj_htons(PORT);
252 if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
253 app_perror("...bind error", rc);
254 status=-10; goto on_error;
255 }
256
257 // Create I/O Queue.
258 rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
259 if (rc != PJ_SUCCESS) {
260 app_perror("...ERROR in pj_ioqueue_create()", rc);
261 status=-20; goto on_error;
262 }
263
264 // Register server socket and client socket.
265 rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey);
266 if (rc == PJ_SUCCESS)
267 rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb,
268 &ckey1);
269 else
270 ckey1 = NULL;
271 if (rc != PJ_SUCCESS) {
272 app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
273 status=-23; goto on_error;
274 }
275
276 // Server socket listen().
277 if (pj_sock_listen(ssock, 5)) {
278 app_perror("...ERROR in pj_sock_listen()", rc);
279 status=-25; goto on_error;
280 }
281
282 // Server socket accept()
283 client_addr_len = sizeof(pj_sockaddr_in);
284 status = pj_ioqueue_accept(skey, &accept_op, &csock0,
285 &client_addr, &rmt_addr, &client_addr_len);
286 if (status != PJ_EPENDING) {
287 app_perror("...ERROR in pj_ioqueue_accept()", rc);
288 status=-30; goto on_error;
289 }
290 if (status==PJ_EPENDING) {
291 ++pending_op;
292 }
293
294 // Initialize remote address.
295 memset(&addr, 0, sizeof(addr));
296 addr.sin_family = PJ_AF_INET;
297 addr.sin_port = pj_htons(PORT);
298 addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
299
300 // Client socket connect()
301 status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr));
302 if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
303 app_perror("...ERROR in pj_ioqueue_connect()", rc);
304 status=-40; goto on_error;
305 }
306 if (status==PJ_EPENDING) {
307 ++pending_op;
308 }
309
310 // Poll until connected
311 callback_read_size = callback_write_size = 0;
312 callback_accept_status = callback_connect_status = -2;
313
314 callback_read_key = callback_write_key =
315 callback_accept_key = callback_connect_key = NULL;
316 callback_accept_op = callback_read_op = callback_write_op = NULL;
317
318 while (pending_op) {
319 pj_time_val timeout = {1, 0};
320
321 status=pj_ioqueue_poll(ioque, &timeout);
322 if (status > 0) {
323 if (callback_accept_status != -2) {
324 if (callback_accept_status != 0) {
325 status=-41; goto on_error;
326 }
327 if (callback_accept_key != skey) {
328 status=-42; goto on_error;
329 }
330 if (callback_accept_op != &accept_op) {
331 status=-43; goto on_error;
332 }
333 callback_accept_status = -2;
334 }
335
336 if (callback_connect_status != -2) {
337 if (callback_connect_status != 0) {
338 status=-50; goto on_error;
339 }
340 if (callback_connect_key != ckey1) {
341 status=-51; goto on_error;
342 }
343 callback_connect_status = -2;
344 }
345
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000346 if (status > pending_op) {
347 PJ_LOG(3,(THIS_FILE,
348 "...error: pj_ioqueue_poll() returned %d "
349 "(only expecting %d)",
350 status, pending_op));
351 return -52;
352 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000353 pending_op -= status;
354
355 if (pending_op == 0) {
356 status = 0;
357 }
358 }
359 }
360
361 // There's no pending operation.
362 // When we poll the ioqueue, there must not be events.
363 if (pending_op == 0) {
364 pj_time_val timeout = {1, 0};
365 status = pj_ioqueue_poll(ioque, &timeout);
366 if (status != 0) {
367 status=-60; goto on_error;
368 }
369 }
370
371 // Check accepted socket.
372 if (csock0 == PJ_INVALID_SOCKET) {
373 status = -69;
374 app_perror("...accept() error", pj_get_os_error());
375 goto on_error;
376 }
377
378 // Register newly accepted socket.
379 rc = pj_ioqueue_register_sock(pool, ioque, csock0, NULL,
380 &test_cb, &ckey0);
381 if (rc != PJ_SUCCESS) {
382 app_perror("...ERROR in pj_ioqueue_register_sock", rc);
383 status = -70;
384 goto on_error;
385 }
386
387 // Test send and receive.
388 t_elapsed.u32.lo = 0;
389 status = send_recv_test(ioque, ckey0, ckey1, send_buf,
390 recv_buf, bufsize, &t_elapsed);
391 if (status != 0) {
392 goto on_error;
393 }
394
395 // Success
396 status = 0;
397
398on_error:
399 if (ssock != PJ_INVALID_SOCKET)
400 pj_sock_close(ssock);
401 if (csock1 != PJ_INVALID_SOCKET)
402 pj_sock_close(csock1);
403 if (csock0 != PJ_INVALID_SOCKET)
404 pj_sock_close(csock0);
405 if (ioque != NULL)
406 pj_ioqueue_destroy(ioque);
407 pj_pool_release(pool);
408 return status;
409
410}
411
412/*
413 * Compliance test for failed scenario.
414 * In this case, the client connects to a non-existant service.
415 */
416static int compliance_test_1(void)
417{
418 pj_sock_t csock1=-1;
419 pj_sockaddr_in addr;
420 pj_pool_t *pool = NULL;
421 pj_ioqueue_t *ioque = NULL;
422 pj_ioqueue_key_t *ckey1;
423 pj_ssize_t status = -1;
424 int pending_op = 0;
425 pj_str_t s;
426 pj_status_t rc;
427
428 // Create pool.
429 pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
430
431 // Create I/O Queue.
432 rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
433 if (!ioque) {
434 status=-20; goto on_error;
435 }
436
437 // Create client socket
438 rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1);
439 if (rc != PJ_SUCCESS) {
440 app_perror("...ERROR in pj_sock_socket()", rc);
441 status=-1; goto on_error;
442 }
443
444 // Register client socket.
445 rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL,
446 &test_cb, &ckey1);
447 if (rc != PJ_SUCCESS) {
448 app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
449 status=-23; goto on_error;
450 }
451
452 // Initialize remote address.
453 memset(&addr, 0, sizeof(addr));
454 addr.sin_family = PJ_AF_INET;
455 addr.sin_port = pj_htons(NON_EXISTANT_PORT);
456 addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
457
458 // Client socket connect()
459 status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr));
460 if (status==PJ_SUCCESS) {
461 // unexpectedly success!
462 status = -30;
463 goto on_error;
464 }
465 if (status != PJ_EPENDING) {
466 // success
467 } else {
468 ++pending_op;
469 }
470
471 callback_connect_status = -2;
472 callback_connect_key = NULL;
473
474 // Poll until we've got result
475 while (pending_op) {
476 pj_time_val timeout = {1, 0};
477
478 status=pj_ioqueue_poll(ioque, &timeout);
479 if (status > 0) {
480 if (callback_connect_key==ckey1) {
481 if (callback_connect_status == 0) {
482 // unexpectedly connected!
483 status = -50;
484 goto on_error;
485 }
486 }
487
Benny Prijono42c5b9e2006-05-10 19:24:40 +0000488 if (status > pending_op) {
489 PJ_LOG(3,(THIS_FILE,
490 "...error: pj_ioqueue_poll() returned %d "
491 "(only expecting %d)",
492 status, pending_op));
493 return -552;
494 }
495
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000496 pending_op -= status;
497 if (pending_op == 0) {
498 status = 0;
499 }
500 }
501 }
502
503 // There's no pending operation.
504 // When we poll the ioqueue, there must not be events.
505 if (pending_op == 0) {
506 pj_time_val timeout = {1, 0};
507 status = pj_ioqueue_poll(ioque, &timeout);
508 if (status != 0) {
509 status=-60; goto on_error;
510 }
511 }
512
513 // Success
514 status = 0;
515
516on_error:
517 if (csock1 != PJ_INVALID_SOCKET)
518 pj_sock_close(csock1);
519 if (ioque != NULL)
520 pj_ioqueue_destroy(ioque);
521 pj_pool_release(pool);
522 return status;
523}
524
525int tcp_ioqueue_test()
526{
527 int status;
528
529 PJ_LOG(3, (THIS_FILE, "..%s compliance test 0 (success scenario)",
530 pj_ioqueue_name()));
531 if ((status=compliance_test_0()) != 0) {
532 PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
533 return status;
534 }
535 PJ_LOG(3, (THIS_FILE, "..%s compliance test 1 (failed scenario)",
536 pj_ioqueue_name()));
537 if ((status=compliance_test_1()) != 0) {
538 PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
539 return status;
540 }
541
542 return 0;
543}
544
545#endif /* PJ_HAS_TCP */
546
547
548#else
549/* To prevent warning about "translation unit is empty"
550 * when this test is disabled.
551 */
552int dummy_uiq_tcp;
553#endif /* INCLUDE_TCP_IOQUEUE_TEST */
554
555