Benny Prijono | 0a749f1 | 2005-10-31 21:02:30 +0000 | [diff] [blame^] | 1 | /* $Header: /pjproject-0.3/pjlib/src/pjlib-test/ioq_tcp.c 4 10/29/05 10:23p Bennylp $
|
| 2 | */
|
| 3 | /*
|
| 4 | * $Log: /pjproject-0.3/pjlib/src/pjlib-test/ioq_tcp.c $
|
| 5 | *
|
| 6 | * 4 10/29/05 10:23p Bennylp
|
| 7 | * Fixed no-memory exception.
|
| 8 | *
|
| 9 | * 3 10/29/05 11:51a Bennylp
|
| 10 | * Version 0.3-pre2.
|
| 11 | *
|
| 12 | * 2 10/14/05 12:26a Bennylp
|
| 13 | * Finished error code framework, some fixes in ioqueue, etc. Pretty
|
| 14 | * major.
|
| 15 | *
|
| 16 | */
|
| 17 | #include "test.h"
|
| 18 |
|
| 19 | /**
|
| 20 | * \page page_pjlib_ioqueue_tcp_test Test: I/O Queue (TCP)
|
| 21 | *
|
| 22 | * This file provides implementation to test the
|
| 23 | * functionality of the I/O queue when TCP socket is used.
|
| 24 | *
|
| 25 | *
|
| 26 | * This file is <b>pjlib-test/ioq_tcp.c</b>
|
| 27 | *
|
| 28 | * \include pjlib-test/ioq_tcp.c
|
| 29 | */
|
| 30 |
|
| 31 |
|
| 32 | #if INCLUDE_TCP_IOQUEUE_TEST
|
| 33 |
|
| 34 | #include <pjlib.h>
|
| 35 |
|
| 36 | #if PJ_HAS_TCP
|
| 37 |
|
| 38 | #define THIS_FILE "test_tcp"
|
| 39 | #define PORT 50000
|
| 40 | #define NON_EXISTANT_PORT 50123
|
| 41 | #define LOOP 100
|
| 42 | #define BUF_MIN_SIZE 32
|
| 43 | #define BUF_MAX_SIZE 2048
|
| 44 | #define SOCK_INACTIVE_MIN (4-2)
|
| 45 | #define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2)
|
| 46 | #define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
|
| 47 |
|
| 48 | static pj_ssize_t callback_read_size,
|
| 49 | callback_write_size,
|
| 50 | callback_accept_status,
|
| 51 | callback_connect_status;
|
| 52 | static pj_ioqueue_key_t*callback_read_key,
|
| 53 | *callback_write_key,
|
| 54 | *callback_accept_key,
|
| 55 | *callback_connect_key;
|
| 56 |
|
| 57 | static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read)
|
| 58 | {
|
| 59 | callback_read_key = key;
|
| 60 | callback_read_size = bytes_read;
|
| 61 | }
|
| 62 |
|
| 63 | static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_written)
|
| 64 | {
|
| 65 | callback_write_key = key;
|
| 66 | callback_write_size = bytes_written;
|
| 67 | }
|
| 68 |
|
| 69 | static void on_ioqueue_accept(pj_ioqueue_key_t *key, pj_sock_t sock,
|
| 70 | int status)
|
| 71 | {
|
| 72 | PJ_UNUSED_ARG(sock);
|
| 73 |
|
| 74 | callback_accept_key = key;
|
| 75 | callback_accept_status = status;
|
| 76 | }
|
| 77 |
|
| 78 | static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
|
| 79 | {
|
| 80 | callback_connect_key = key;
|
| 81 | callback_connect_status = status;
|
| 82 | }
|
| 83 |
|
| 84 | static pj_ioqueue_callback test_cb =
|
| 85 | {
|
| 86 | &on_ioqueue_read,
|
| 87 | &on_ioqueue_write,
|
| 88 | &on_ioqueue_accept,
|
| 89 | &on_ioqueue_connect,
|
| 90 | };
|
| 91 |
|
| 92 | static int send_recv_test(pj_ioqueue_t *ioque,
|
| 93 | pj_ioqueue_key_t *skey,
|
| 94 | pj_ioqueue_key_t *ckey,
|
| 95 | void *send_buf,
|
| 96 | void *recv_buf,
|
| 97 | pj_ssize_t bufsize,
|
| 98 | pj_timestamp *t_elapsed)
|
| 99 | {
|
| 100 | int rc;
|
| 101 | pj_ssize_t bytes;
|
| 102 | pj_timestamp t1, t2;
|
| 103 | int pending_op = 0;
|
| 104 |
|
| 105 | // Start reading on the server side.
|
| 106 | rc = pj_ioqueue_read(ioque, skey, recv_buf, bufsize);
|
| 107 | if (rc != 0 && rc != PJ_EPENDING) {
|
| 108 | return -100;
|
| 109 | }
|
| 110 |
|
| 111 | ++pending_op;
|
| 112 |
|
| 113 | // Randomize send buffer.
|
| 114 | pj_create_random_string((char*)send_buf, bufsize);
|
| 115 |
|
| 116 | // Starts send on the client side.
|
| 117 | bytes = pj_ioqueue_write(ioque, ckey, send_buf, bufsize);
|
| 118 | if (bytes != bufsize && bytes != PJ_EPENDING) {
|
| 119 | return -120;
|
| 120 | }
|
| 121 | if (bytes == PJ_EPENDING) {
|
| 122 | ++pending_op;
|
| 123 | }
|
| 124 |
|
| 125 | // Begin time.
|
| 126 | pj_get_timestamp(&t1);
|
| 127 |
|
| 128 | // Reset indicators
|
| 129 | callback_read_size = callback_write_size = 0;
|
| 130 | callback_read_key = callback_write_key = NULL;
|
| 131 |
|
| 132 | // Poll the queue until we've got completion event in the server side.
|
| 133 | rc = 0;
|
| 134 | while (pending_op > 0) {
|
| 135 | rc = pj_ioqueue_poll(ioque, NULL);
|
| 136 | if (rc > 0) {
|
| 137 | if (callback_read_size) {
|
| 138 | if (callback_read_size != bufsize) {
|
| 139 | return -160;
|
| 140 | }
|
| 141 | if (callback_read_key != skey)
|
| 142 | return -161;
|
| 143 | }
|
| 144 | if (callback_write_size) {
|
| 145 | if (callback_write_key != ckey)
|
| 146 | return -162;
|
| 147 | }
|
| 148 | pending_op -= rc;
|
| 149 | }
|
| 150 | if (rc < 0) {
|
| 151 | return -170;
|
| 152 | }
|
| 153 | }
|
| 154 |
|
| 155 | // End time.
|
| 156 | pj_get_timestamp(&t2);
|
| 157 | t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo);
|
| 158 |
|
| 159 | if (rc < 0) {
|
| 160 | return -150;
|
| 161 | }
|
| 162 |
|
| 163 | // Compare recv buffer with send buffer.
|
| 164 | if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) {
|
| 165 | return -180;
|
| 166 | }
|
| 167 |
|
| 168 | // Success
|
| 169 | return 0;
|
| 170 | }
|
| 171 |
|
| 172 |
|
| 173 | /*
|
| 174 | * Compliance test for success scenario.
|
| 175 | */
|
| 176 | static int compliance_test_0(void)
|
| 177 | {
|
| 178 | pj_sock_t ssock=-1, csock0=-1, csock1=-1;
|
| 179 | pj_sockaddr_in addr, client_addr, rmt_addr;
|
| 180 | int client_addr_len;
|
| 181 | pj_pool_t *pool = NULL;
|
| 182 | char *send_buf, *recv_buf;
|
| 183 | pj_ioqueue_t *ioque = NULL;
|
| 184 | pj_ioqueue_key_t *skey, *ckey0, *ckey1;
|
| 185 | int bufsize = BUF_MIN_SIZE;
|
| 186 | pj_ssize_t status = -1;
|
| 187 | int pending_op = 0;
|
| 188 | pj_timestamp t_elapsed;
|
| 189 | pj_str_t s;
|
| 190 | pj_status_t rc;
|
| 191 |
|
| 192 | // Create pool.
|
| 193 | pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
|
| 194 |
|
| 195 | // Allocate buffers for send and receive.
|
| 196 | send_buf = (char*)pj_pool_alloc(pool, bufsize);
|
| 197 | recv_buf = (char*)pj_pool_alloc(pool, bufsize);
|
| 198 |
|
| 199 | // Create server socket and client socket for connecting
|
| 200 | rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &ssock);
|
| 201 | if (rc != PJ_SUCCESS) {
|
| 202 | app_perror("...error creating socket", rc);
|
| 203 | status=-1; goto on_error;
|
| 204 | }
|
| 205 |
|
| 206 | rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1);
|
| 207 | if (rc != PJ_SUCCESS) {
|
| 208 | app_perror("...error creating socket", rc);
|
| 209 | status=-1; goto on_error;
|
| 210 | }
|
| 211 |
|
| 212 | // Bind server socket.
|
| 213 | memset(&addr, 0, sizeof(addr));
|
| 214 | addr.sin_family = PJ_AF_INET;
|
| 215 | addr.sin_port = pj_htons(PORT);
|
| 216 | if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
|
| 217 | app_perror("...bind error", rc);
|
| 218 | status=-10; goto on_error;
|
| 219 | }
|
| 220 |
|
| 221 | // Create I/O Queue.
|
| 222 | rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, 0, &ioque);
|
| 223 | if (rc != PJ_SUCCESS) {
|
| 224 | app_perror("...ERROR in pj_ioqueue_create()", rc);
|
| 225 | status=-20; goto on_error;
|
| 226 | }
|
| 227 |
|
| 228 | // Register server socket and client socket.
|
| 229 | rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey);
|
| 230 | if (rc == PJ_SUCCESS)
|
| 231 | rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb,
|
| 232 | &ckey1);
|
| 233 | else
|
| 234 | ckey1 = NULL;
|
| 235 | if (rc != PJ_SUCCESS) {
|
| 236 | app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
|
| 237 | status=-23; goto on_error;
|
| 238 | }
|
| 239 |
|
| 240 | // Server socket listen().
|
| 241 | if (pj_sock_listen(ssock, 5)) {
|
| 242 | app_perror("...ERROR in pj_sock_listen()", rc);
|
| 243 | status=-25; goto on_error;
|
| 244 | }
|
| 245 |
|
| 246 | // Server socket accept()
|
| 247 | client_addr_len = sizeof(pj_sockaddr_in);
|
| 248 | status = pj_ioqueue_accept(ioque, skey, &csock0, &client_addr, &rmt_addr, &client_addr_len);
|
| 249 | if (status != PJ_EPENDING) {
|
| 250 | app_perror("...ERROR in pj_ioqueue_accept()", rc);
|
| 251 | status=-30; goto on_error;
|
| 252 | }
|
| 253 | if (status==PJ_EPENDING) {
|
| 254 | ++pending_op;
|
| 255 | }
|
| 256 |
|
| 257 | // Initialize remote address.
|
| 258 | memset(&addr, 0, sizeof(addr));
|
| 259 | addr.sin_family = PJ_AF_INET;
|
| 260 | addr.sin_port = pj_htons(PORT);
|
| 261 | addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
|
| 262 |
|
| 263 | // Client socket connect()
|
| 264 | status = pj_ioqueue_connect(ioque, ckey1, &addr, sizeof(addr));
|
| 265 | if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
|
| 266 | app_perror("...ERROR in pj_ioqueue_connect()", rc);
|
| 267 | status=-40; goto on_error;
|
| 268 | }
|
| 269 | if (status==PJ_EPENDING) {
|
| 270 | ++pending_op;
|
| 271 | }
|
| 272 |
|
| 273 | // Poll until connected
|
| 274 | callback_read_size = callback_write_size = 0;
|
| 275 | callback_accept_status = callback_connect_status = -2;
|
| 276 |
|
| 277 | callback_read_key = callback_write_key =
|
| 278 | callback_accept_key = callback_connect_key = NULL;
|
| 279 |
|
| 280 | while (pending_op) {
|
| 281 | pj_time_val timeout = {1, 0};
|
| 282 |
|
| 283 | status=pj_ioqueue_poll(ioque, &timeout);
|
| 284 | if (status > 0) {
|
| 285 | if (callback_accept_status != -2) {
|
| 286 | if (callback_accept_status != 0) {
|
| 287 | status=-41; goto on_error;
|
| 288 | }
|
| 289 | if (callback_accept_key != skey) {
|
| 290 | status=-41; goto on_error;
|
| 291 | }
|
| 292 | }
|
| 293 |
|
| 294 | if (callback_connect_status != -2) {
|
| 295 | if (callback_connect_status != 0) {
|
| 296 | status=-50; goto on_error;
|
| 297 | }
|
| 298 | if (callback_connect_key != ckey1) {
|
| 299 | status=-51; goto on_error;
|
| 300 | }
|
| 301 | }
|
| 302 |
|
| 303 | pending_op -= status;
|
| 304 |
|
| 305 | if (pending_op == 0) {
|
| 306 | status = 0;
|
| 307 | }
|
| 308 | }
|
| 309 | }
|
| 310 |
|
| 311 | // Check accepted socket.
|
| 312 | if (csock0 == PJ_INVALID_SOCKET) {
|
| 313 | status = -69;
|
| 314 | app_perror("...accept() error", pj_get_os_error());
|
| 315 | goto on_error;
|
| 316 | }
|
| 317 |
|
| 318 | // Register newly accepted socket.
|
| 319 | rc = pj_ioqueue_register_sock(pool, ioque, csock0, NULL,
|
| 320 | &test_cb, &ckey0);
|
| 321 | if (rc != PJ_SUCCESS) {
|
| 322 | app_perror("...ERROR in pj_ioqueue_register_sock", rc);
|
| 323 | status = -70;
|
| 324 | goto on_error;
|
| 325 | }
|
| 326 |
|
| 327 | // Test send and receive.
|
| 328 | t_elapsed.u32.lo = 0;
|
| 329 | status = send_recv_test(ioque, ckey0, ckey1, send_buf, recv_buf, bufsize, &t_elapsed);
|
| 330 | if (status != 0) {
|
| 331 | goto on_error;
|
| 332 | }
|
| 333 |
|
| 334 | // Success
|
| 335 | status = 0;
|
| 336 |
|
| 337 | on_error:
|
| 338 | if (ssock != PJ_INVALID_SOCKET)
|
| 339 | pj_sock_close(ssock);
|
| 340 | if (csock1 != PJ_INVALID_SOCKET)
|
| 341 | pj_sock_close(csock1);
|
| 342 | if (csock0 != PJ_INVALID_SOCKET)
|
| 343 | pj_sock_close(csock0);
|
| 344 | if (ioque != NULL)
|
| 345 | pj_ioqueue_destroy(ioque);
|
| 346 | pj_pool_release(pool);
|
| 347 | return status;
|
| 348 |
|
| 349 | }
|
| 350 |
|
| 351 | /*
|
| 352 | * Compliance test for failed scenario.
|
| 353 | * In this case, the client connects to a non-existant service.
|
| 354 | */
|
| 355 | static int compliance_test_1(void)
|
| 356 | {
|
| 357 | pj_sock_t csock1=-1;
|
| 358 | pj_sockaddr_in addr;
|
| 359 | pj_pool_t *pool = NULL;
|
| 360 | pj_ioqueue_t *ioque = NULL;
|
| 361 | pj_ioqueue_key_t *ckey1;
|
| 362 | pj_ssize_t status = -1;
|
| 363 | int pending_op = 0;
|
| 364 | pj_str_t s;
|
| 365 | pj_status_t rc;
|
| 366 |
|
| 367 | // Create pool.
|
| 368 | pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
|
| 369 |
|
| 370 | // Create I/O Queue.
|
| 371 | rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, 0, &ioque);
|
| 372 | if (!ioque) {
|
| 373 | status=-20; goto on_error;
|
| 374 | }
|
| 375 |
|
| 376 | // Create client socket
|
| 377 | rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1);
|
| 378 | if (rc != PJ_SUCCESS) {
|
| 379 | app_perror("...ERROR in pj_sock_socket()", rc);
|
| 380 | status=-1; goto on_error;
|
| 381 | }
|
| 382 |
|
| 383 | // Register client socket.
|
| 384 | rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL,
|
| 385 | &test_cb, &ckey1);
|
| 386 | if (rc != PJ_SUCCESS) {
|
| 387 | app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
|
| 388 | status=-23; goto on_error;
|
| 389 | }
|
| 390 |
|
| 391 | // Initialize remote address.
|
| 392 | memset(&addr, 0, sizeof(addr));
|
| 393 | addr.sin_family = PJ_AF_INET;
|
| 394 | addr.sin_port = pj_htons(NON_EXISTANT_PORT);
|
| 395 | addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
|
| 396 |
|
| 397 | // Client socket connect()
|
| 398 | status = pj_ioqueue_connect(ioque, ckey1, &addr, sizeof(addr));
|
| 399 | if (status==PJ_SUCCESS) {
|
| 400 | // unexpectedly success!
|
| 401 | status = -30;
|
| 402 | goto on_error;
|
| 403 | }
|
| 404 | if (status != PJ_EPENDING) {
|
| 405 | // success
|
| 406 | } else {
|
| 407 | ++pending_op;
|
| 408 | }
|
| 409 |
|
| 410 | callback_connect_status = -2;
|
| 411 | callback_connect_key = NULL;
|
| 412 |
|
| 413 | // Poll until we've got result
|
| 414 | while (pending_op) {
|
| 415 | pj_time_val timeout = {1, 0};
|
| 416 |
|
| 417 | status=pj_ioqueue_poll(ioque, &timeout);
|
| 418 | if (status > 0) {
|
| 419 | if (callback_connect_key==ckey1) {
|
| 420 | if (callback_connect_status == 0) {
|
| 421 | // unexpectedly connected!
|
| 422 | status = -50;
|
| 423 | goto on_error;
|
| 424 | }
|
| 425 | }
|
| 426 |
|
| 427 | pending_op -= status;
|
| 428 | if (pending_op == 0) {
|
| 429 | status = 0;
|
| 430 | }
|
| 431 | }
|
| 432 | }
|
| 433 |
|
| 434 | // Success
|
| 435 | status = 0;
|
| 436 |
|
| 437 | on_error:
|
| 438 | if (csock1 != PJ_INVALID_SOCKET)
|
| 439 | pj_sock_close(csock1);
|
| 440 | if (ioque != NULL)
|
| 441 | pj_ioqueue_destroy(ioque);
|
| 442 | pj_pool_release(pool);
|
| 443 | return status;
|
| 444 | }
|
| 445 |
|
| 446 | int tcp_ioqueue_test()
|
| 447 | {
|
| 448 | int status;
|
| 449 |
|
| 450 | PJ_LOG(3, (THIS_FILE, "..compliance test 0 (success scenario)"));
|
| 451 | if ((status=compliance_test_0()) != 0) {
|
| 452 | PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
|
| 453 | return status;
|
| 454 | }
|
| 455 | PJ_LOG(3, (THIS_FILE, "..compliance test 1 (failed scenario)"));
|
| 456 | if ((status=compliance_test_1()) != 0) {
|
| 457 | PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
|
| 458 | return status;
|
| 459 | }
|
| 460 |
|
| 461 | return 0;
|
| 462 | }
|
| 463 |
|
| 464 | #endif /* PJ_HAS_TCP */
|
| 465 |
|
| 466 |
|
| 467 | #else
|
| 468 | /* To prevent warning about "translation unit is empty"
|
| 469 | * when this test is disabled.
|
| 470 | */
|
| 471 | int dummy_uiq_tcp;
|
| 472 | #endif /* INCLUDE_TCP_IOQUEUE_TEST */
|
| 473 |
|
| 474 |
|