Return to minihttptestserver.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / miniupnpc / src |
1.1 ! misho 1: /* $Id: minihttptestserver.c,v 1.25 2020/05/29 21:14:22 nanard Exp $ */ ! 2: /* Project : miniUPnP ! 3: * Author : Thomas Bernard ! 4: * Copyright (c) 2011-2018 Thomas Bernard ! 5: * This software is subject to the conditions detailed in the ! 6: * LICENCE file provided in this distribution. ! 7: * */ ! 8: #include <stdio.h> ! 9: #include <stdlib.h> ! 10: #include <string.h> ! 11: #include <unistd.h> ! 12: #include <sys/types.h> ! 13: #include <sys/socket.h> ! 14: #include <sys/wait.h> ! 15: #include <arpa/inet.h> ! 16: #include <netinet/in.h> ! 17: #include <signal.h> ! 18: #include <time.h> ! 19: #include <errno.h> ! 20: ! 21: #ifndef INADDR_LOOPBACK ! 22: #define INADDR_LOOPBACK 0x7f000001 ! 23: #endif ! 24: ! 25: #define CRAP_LENGTH (2048) ! 26: ! 27: static int server(unsigned short port, const char * expected_file_name, int ipv6); ! 28: ! 29: volatile sig_atomic_t quit = 0; ! 30: volatile sig_atomic_t child_to_wait_for = 0; ! 31: ! 32: /** ! 33: * signal handler for SIGCHLD (child status has changed) ! 34: */ ! 35: void handle_signal_chld(int sig) ! 36: { ! 37: (void)sig; ! 38: /* printf("handle_signal_chld(%d)\n", sig); */ ! 39: ++child_to_wait_for; ! 40: } ! 41: ! 42: /** ! 43: * signal handler for SIGINT (CRTL C) ! 44: */ ! 45: void handle_signal_int(int sig) ! 46: { ! 47: (void)sig; ! 48: /* printf("handle_signal_int(%d)\n", sig); */ ! 49: quit = 1; ! 50: } ! 51: ! 52: /** ! 53: * build a text/plain content of the specified length ! 54: */ ! 55: void build_content(char * p, size_t n) ! 56: { ! 57: char line_buffer[80]; ! 58: int k; ! 59: int i = 0; ! 60: ! 61: while(n > 0) { ! 62: k = snprintf(line_buffer, sizeof(line_buffer), ! 63: "%04d_ABCDEFGHIJKL_This_line_is_64_bytes_long_ABCDEFGHIJKL_%04d\r\n", ! 64: i, i); ! 65: if(k != 64) { ! 66: fprintf(stderr, "snprintf() returned %d in build_content()\n", k); ! 67: } ! 68: ++i; ! 69: if(n >= 64) { ! 70: memcpy(p, line_buffer, 64); ! 71: p += 64; ! 72: n -= 64; ! 73: } else { ! 74: memcpy(p, line_buffer, n); ! 75: p += n; ! 76: n = 0; ! 77: } ! 78: } ! 79: } ! 80: ! 81: /** ! 82: * build crappy content ! 83: */ ! 84: void build_crap(char * p, size_t n) ! 85: { ! 86: static const char crap[] = "_CRAP_\r\n"; ! 87: size_t i; ! 88: ! 89: while(n > 0) { ! 90: i = sizeof(crap) - 1; ! 91: if(i > n) ! 92: i = n; ! 93: memcpy(p, crap, i); ! 94: p += i; ! 95: n -= i; ! 96: } ! 97: } ! 98: ! 99: /** ! 100: * build chunked response. ! 101: * return a malloc'ed buffer ! 102: */ ! 103: char * build_chunked_response(size_t content_length, size_t * response_len) ! 104: { ! 105: char * response_buffer; ! 106: char * content_buffer; ! 107: size_t buffer_length; ! 108: size_t i; ! 109: unsigned int n; ! 110: ! 111: /* allocate to have some margin */ ! 112: buffer_length = 256 + content_length + (content_length >> 4); ! 113: response_buffer = malloc(buffer_length); ! 114: if(response_buffer == NULL) ! 115: return NULL; ! 116: *response_len = snprintf(response_buffer, buffer_length, ! 117: "HTTP/1.1 200 OK\r\n" ! 118: "Content-Type: text/plain\r\n" ! 119: "Transfer-Encoding: chunked\r\n" ! 120: "\r\n"); ! 121: ! 122: /* build the content */ ! 123: content_buffer = malloc(content_length); ! 124: if(content_buffer == NULL) { ! 125: free(response_buffer); ! 126: return NULL; ! 127: } ! 128: build_content(content_buffer, content_length); ! 129: ! 130: /* chunk it */ ! 131: i = 0; ! 132: while(i < content_length) { ! 133: n = (rand() % 199) + 1; ! 134: if(i + n > content_length) { ! 135: n = content_length - i; ! 136: } ! 137: /* TODO : check buffer size ! */ ! 138: *response_len += snprintf(response_buffer + *response_len, ! 139: buffer_length - *response_len, ! 140: "%x\r\n", n); ! 141: memcpy(response_buffer + *response_len, content_buffer + i, n); ! 142: *response_len += n; ! 143: i += n; ! 144: response_buffer[(*response_len)++] = '\r'; ! 145: response_buffer[(*response_len)++] = '\n'; ! 146: } ! 147: /* the last chunk : "0\r\n" a empty body and then ! 148: * the final "\r\n" */ ! 149: memcpy(response_buffer + *response_len, "0\r\n\r\n", 5); ! 150: *response_len += 5; ! 151: free(content_buffer); ! 152: ! 153: printf("resp_length=%lu buffer_length=%lu content_length=%lu\n", ! 154: *response_len, buffer_length, content_length); ! 155: return response_buffer; ! 156: } ! 157: ! 158: /* favicon.ico generator */ ! 159: #ifdef OLD_HEADER ! 160: #define FAVICON_LENGTH (6 + 16 + 12 + 8 + 32 * 4) ! 161: #else ! 162: #define FAVICON_LENGTH (6 + 16 + 40 + 8 + 32 * 4) ! 163: #endif ! 164: void build_favicon_content(unsigned char * p, size_t n) ! 165: { ! 166: int i; ! 167: if(n < FAVICON_LENGTH) ! 168: return; ! 169: /* header : 6 bytes */ ! 170: *p++ = 0; ! 171: *p++ = 0; ! 172: *p++ = 1; /* type : ICO */ ! 173: *p++ = 0; ! 174: *p++ = 1; /* number of images in file */ ! 175: *p++ = 0; ! 176: /* image directory (1 entry) : 16 bytes */ ! 177: *p++ = 16; /* width */ ! 178: *p++ = 16; /* height */ ! 179: *p++ = 2; /* number of colors in the palette. 0 = no palette */ ! 180: *p++ = 0; /* reserved */ ! 181: *p++ = 1; /* color planes */ ! 182: *p++ = 0; /* " */ ! 183: *p++ = 1; /* bpp */ ! 184: *p++ = 0; /* " */ ! 185: #ifdef OLD_HEADER ! 186: *p++ = 12 + 8 + 32 * 4; /* bmp size */ ! 187: #else ! 188: *p++ = 40 + 8 + 32 * 4; /* bmp size */ ! 189: #endif ! 190: *p++ = 0; /* " */ ! 191: *p++ = 0; /* " */ ! 192: *p++ = 0; /* " */ ! 193: *p++ = 6 + 16; /* bmp offset */ ! 194: *p++ = 0; /* " */ ! 195: *p++ = 0; /* " */ ! 196: *p++ = 0; /* " */ ! 197: /* BMP */ ! 198: #ifdef OLD_HEADER ! 199: /* BITMAPCOREHEADER */ ! 200: *p++ = 12; /* size of this header */ ! 201: *p++ = 0; /* " */ ! 202: *p++ = 0; /* " */ ! 203: *p++ = 0; /* " */ ! 204: *p++ = 16; /* width */ ! 205: *p++ = 0; /* " */ ! 206: *p++ = 16 * 2; /* height x 2 ! */ ! 207: *p++ = 0; /* " */ ! 208: *p++ = 1; /* color planes */ ! 209: *p++ = 0; /* " */ ! 210: *p++ = 1; /* bpp */ ! 211: *p++ = 0; /* " */ ! 212: #else ! 213: /* BITMAPINFOHEADER */ ! 214: *p++ = 40; /* size of this header */ ! 215: *p++ = 0; /* " */ ! 216: *p++ = 0; /* " */ ! 217: *p++ = 0; /* " */ ! 218: *p++ = 16; /* width */ ! 219: *p++ = 0; /* " */ ! 220: *p++ = 0; /* " */ ! 221: *p++ = 0; /* " */ ! 222: *p++ = 16 * 2; /* height x 2 ! */ ! 223: *p++ = 0; /* " */ ! 224: *p++ = 0; /* " */ ! 225: *p++ = 0; /* " */ ! 226: *p++ = 1; /* color planes */ ! 227: *p++ = 0; /* " */ ! 228: *p++ = 1; /* bpp */ ! 229: *p++ = 0; /* " */ ! 230: /* compression method, image size, ppm x, ppm y */ ! 231: /* colors in the palette ? */ ! 232: /* important colors */ ! 233: for(i = 4 * 6; i > 0; --i) ! 234: *p++ = 0; ! 235: #endif ! 236: /* palette */ ! 237: *p++ = 0; /* b */ ! 238: *p++ = 0; /* g */ ! 239: *p++ = 0; /* r */ ! 240: *p++ = 0; /* reserved */ ! 241: *p++ = 255; /* b */ ! 242: *p++ = 255; /* g */ ! 243: *p++ = 255; /* r */ ! 244: *p++ = 0; /* reserved */ ! 245: /* pixel data */ ! 246: for(i = 16; i > 0; --i) { ! 247: if(i & 1) { ! 248: *p++ = 0125; ! 249: *p++ = 0125; ! 250: } else { ! 251: *p++ = 0252; ! 252: *p++ = 0252; ! 253: } ! 254: *p++ = 0; ! 255: *p++ = 0; ! 256: } ! 257: /* Opacity MASK */ ! 258: for(i = 16 * 4; i > 0; --i) { ! 259: *p++ = 0; ! 260: } ! 261: } ! 262: ! 263: enum modes { ! 264: MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL, MODE_FAVICON, MODE_MALFORMED ! 265: }; ! 266: ! 267: const struct { ! 268: const enum modes mode; ! 269: const char * text; ! 270: } modes_array[] = { ! 271: {MODE_CHUNKED, "chunked"}, ! 272: {MODE_ADDCRAP, "addcrap"}, ! 273: {MODE_NORMAL, "normal"}, ! 274: {MODE_FAVICON, "favicon.ico"}, ! 275: {MODE_MALFORMED, "malformed"}, ! 276: {MODE_INVALID, NULL} ! 277: }; ! 278: ! 279: /** ! 280: * write the response with random behaviour ! ! 281: */ ! 282: void send_response(int c, const char * buffer, size_t len) ! 283: { ! 284: ssize_t n; ! 285: while(len > 0) { ! 286: n = (rand() % 99) + 1; ! 287: if((size_t)n > len) ! 288: n = len; ! 289: n = write(c, buffer, n); ! 290: if(n < 0) { ! 291: if(errno != EINTR) { ! 292: perror("write"); ! 293: return; ! 294: } ! 295: /* if errno == EINTR, try again */ ! 296: } else { ! 297: len -= n; ! 298: buffer += n; ! 299: usleep(10000); /* 10ms */ ! 300: } ! 301: } ! 302: } ! 303: ! 304: /** ! 305: * handle the HTTP connection ! 306: */ ! 307: void handle_http_connection(int c) ! 308: { ! 309: char request_buffer[2048]; ! 310: size_t request_len = 0; ! 311: int headers_found = 0; ! 312: ssize_t n, m; ! 313: size_t i; ! 314: char request_method[16]; ! 315: char request_uri[256]; ! 316: char http_version[16]; ! 317: char * p; ! 318: char * response_buffer; ! 319: size_t response_len; ! 320: enum modes mode; ! 321: size_t content_length = 16*1024; ! 322: ! 323: /* read the request */ ! 324: while(request_len < sizeof(request_buffer) && !headers_found) { ! 325: n = read(c, ! 326: request_buffer + request_len, ! 327: sizeof(request_buffer) - request_len); ! 328: if(n < 0) { ! 329: if(errno == EINTR) ! 330: continue; ! 331: perror("read"); ! 332: return; ! 333: } else if(n==0) { ! 334: /* remote host closed the connection */ ! 335: break; ! 336: } else { ! 337: request_len += n; ! 338: for(i = 0; i < request_len - 3; i++) { ! 339: if(0 == memcmp(request_buffer + i, "\r\n\r\n", 4)) { ! 340: /* found the end of headers */ ! 341: headers_found = 1; ! 342: break; ! 343: } ! 344: } ! 345: } ! 346: } ! 347: if(!headers_found) { ! 348: /* error */ ! 349: printf("no HTTP header found in the request\n"); ! 350: return; ! 351: } ! 352: printf("headers :\n%.*s", (int)request_len, request_buffer); ! 353: /* the request have been received, now parse the request line */ ! 354: p = request_buffer; ! 355: for(i = 0; i < sizeof(request_method) - 1; i++) { ! 356: if(*p == ' ' || *p == '\r') ! 357: break; ! 358: request_method[i] = *p; ! 359: ++p; ! 360: } ! 361: request_method[i] = '\0'; ! 362: while(*p == ' ') ! 363: p++; ! 364: for(i = 0; i < (int)sizeof(request_uri) - 1; i++) { ! 365: if(*p == ' ' || *p == '\r') ! 366: break; ! 367: request_uri[i] = *p; ! 368: ++p; ! 369: } ! 370: request_uri[i] = '\0'; ! 371: while(*p == ' ') ! 372: p++; ! 373: for(i = 0; i < (int)sizeof(http_version) - 1; i++) { ! 374: if(*p == ' ' || *p == '\r') ! 375: break; ! 376: http_version[i] = *p; ! 377: ++p; ! 378: } ! 379: http_version[i] = '\0'; ! 380: printf("Method = %s, URI = %s, %s\n", ! 381: request_method, request_uri, http_version); ! 382: /* check if the request method is allowed */ ! 383: if(0 != strcmp(request_method, "GET")) { ! 384: const char response405[] = "HTTP/1.1 405 Method Not Allowed\r\n" ! 385: "Allow: GET\r\n\r\n"; ! 386: const char * pc; ! 387: /* 405 Method Not Allowed */ ! 388: /* The response MUST include an Allow header containing a list ! 389: * of valid methods for the requested resource. */ ! 390: n = sizeof(response405) - 1; ! 391: pc = response405; ! 392: while(n > 0) { ! 393: m = write(c, pc, n); ! 394: if(m<0) { ! 395: if(errno != EINTR) { ! 396: perror("write"); ! 397: return; ! 398: } ! 399: } else { ! 400: n -= m; ! 401: pc += m; ! 402: } ! 403: } ! 404: return; ! 405: } ! 406: ! 407: mode = MODE_INVALID; ! 408: /* use the request URI to know what to do */ ! 409: for(i = 0; modes_array[i].mode != MODE_INVALID; i++) { ! 410: if(strstr(request_uri, modes_array[i].text)) { ! 411: mode = modes_array[i].mode; /* found */ ! 412: break; ! 413: } ! 414: } ! 415: ! 416: switch(mode) { ! 417: case MODE_MALFORMED: ! 418: response_len = 2048; ! 419: response_buffer = malloc(response_len); ! 420: if(!response_buffer) ! 421: break; ! 422: n = snprintf(response_buffer, response_len, ! 423: "HTTP/1.1 \r\n" ! 424: "\r\n" ! 425: /*"0000\r\n"*/); ! 426: for (i = n; i < response_len; i++) { ! 427: response_buffer[i] = ' '; ! 428: } ! 429: response_len = n; ! 430: break; ! 431: case MODE_CHUNKED: ! 432: response_buffer = build_chunked_response(content_length, &response_len); ! 433: break; ! 434: case MODE_ADDCRAP: ! 435: response_len = content_length+256; ! 436: response_buffer = malloc(response_len); ! 437: if(!response_buffer) ! 438: break; ! 439: n = snprintf(response_buffer, response_len, ! 440: "HTTP/1.1 200 OK\r\n" ! 441: "Server: minihttptestserver\r\n" ! 442: "Content-Type: text/plain\r\n" ! 443: "Content-Length: %lu\r\n" ! 444: "\r\n", content_length); ! 445: response_len = content_length+n+CRAP_LENGTH; ! 446: p = realloc(response_buffer, response_len); ! 447: if(p == NULL) { ! 448: /* error 500 */ ! 449: free(response_buffer); ! 450: response_buffer = NULL; ! 451: break; ! 452: } ! 453: response_buffer = p; ! 454: build_content(response_buffer + n, content_length); ! 455: build_crap(response_buffer + n + content_length, CRAP_LENGTH); ! 456: break; ! 457: case MODE_FAVICON: ! 458: content_length = FAVICON_LENGTH; ! 459: response_len = content_length + 256; ! 460: response_buffer = malloc(response_len); ! 461: if(!response_buffer) ! 462: break; ! 463: n = snprintf(response_buffer, response_len, ! 464: "HTTP/1.1 200 OK\r\n" ! 465: "Server: minihttptestserver\r\n" ! 466: "Content-Type: image/vnd.microsoft.icon\r\n" ! 467: "Content-Length: %lu\r\n" ! 468: "\r\n", content_length); ! 469: /* image/x-icon */ ! 470: build_favicon_content((unsigned char *)(response_buffer + n), content_length); ! 471: response_len = content_length + n; ! 472: break; ! 473: default: ! 474: response_len = content_length+256; ! 475: response_buffer = malloc(response_len); ! 476: if(!response_buffer) ! 477: break; ! 478: n = snprintf(response_buffer, response_len, ! 479: "HTTP/1.1 200 OK\r\n" ! 480: "Server: minihttptestserver\r\n" ! 481: "Content-Type: text/plain\r\n" ! 482: "\r\n"); ! 483: response_len = content_length+n; ! 484: p = realloc(response_buffer, response_len); ! 485: if(p == NULL) { ! 486: /* Error 500 */ ! 487: free(response_buffer); ! 488: response_buffer = NULL; ! 489: break; ! 490: } ! 491: response_buffer = p; ! 492: build_content(response_buffer + n, response_len - n); ! 493: } ! 494: ! 495: if(response_buffer) { ! 496: send_response(c, response_buffer, response_len); ! 497: free(response_buffer); ! 498: } else { ! 499: /* Error 500 */ ! 500: } ! 501: } ! 502: ! 503: /** ! 504: */ ! 505: int main(int argc, char * * argv) { ! 506: int ipv6 = 0; ! 507: int r, i; ! 508: unsigned short port = 0; ! 509: const char * expected_file_name = NULL; ! 510: ! 511: for(i = 1; i < argc; i++) { ! 512: if(argv[i][0] == '-') { ! 513: switch(argv[i][1]) { ! 514: case '6': ! 515: ipv6 = 1; ! 516: break; ! 517: case 'e': ! 518: /* write expected file ! */ ! 519: expected_file_name = argv[++i]; ! 520: break; ! 521: case 'p': ! 522: /* port */ ! 523: if(++i < argc) { ! 524: port = (unsigned short)atoi(argv[i]); ! 525: } ! 526: break; ! 527: default: ! 528: fprintf(stderr, "unknown command line switch '%s'\n", argv[i]); ! 529: } ! 530: } else { ! 531: fprintf(stderr, "unknown command line argument '%s'\n", argv[i]); ! 532: } ! 533: } ! 534: ! 535: srand(time(NULL)); ! 536: ! 537: r = server(port, expected_file_name, ipv6); ! 538: if(r != 0) { ! 539: printf("*** ERROR ***\n"); ! 540: } ! 541: return r; ! 542: } ! 543: ! 544: static int server(unsigned short port, const char * expected_file_name, int ipv6) ! 545: { ! 546: int s, c; ! 547: int i; ! 548: struct sockaddr_storage server_addr; ! 549: socklen_t server_addrlen; ! 550: struct sockaddr_storage client_addr; ! 551: socklen_t client_addrlen; ! 552: pid_t pid; ! 553: int child = 0; ! 554: int status; ! 555: struct sigaction sa; ! 556: ! 557: memset(&sa, 0, sizeof(struct sigaction)); ! 558: ! 559: /*signal(SIGCHLD, handle_signal_chld);*/ ! 560: sa.sa_handler = handle_signal_chld; ! 561: if(sigaction(SIGCHLD, &sa, NULL) < 0) { ! 562: perror("sigaction"); ! 563: return 1; ! 564: } ! 565: /*signal(SIGINT, handle_signal_int);*/ ! 566: sa.sa_handler = handle_signal_int; ! 567: if(sigaction(SIGINT, &sa, NULL) < 0) { ! 568: perror("sigaction"); ! 569: return 1; ! 570: } ! 571: ! 572: s = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0); ! 573: if(s < 0) { ! 574: perror("socket"); ! 575: return 1; ! 576: } ! 577: memset(&server_addr, 0, sizeof(struct sockaddr_storage)); ! 578: memset(&client_addr, 0, sizeof(struct sockaddr_storage)); ! 579: if(ipv6) { ! 580: struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr; ! 581: addr->sin6_family = AF_INET6; ! 582: addr->sin6_port = htons(port); ! 583: addr->sin6_addr = in6addr_loopback; ! 584: } else { ! 585: struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr; ! 586: addr->sin_family = AF_INET; ! 587: addr->sin_port = htons(port); ! 588: addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK); ! 589: } ! 590: if(bind(s, (struct sockaddr *)&server_addr, ! 591: ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) < 0) { ! 592: perror("bind"); ! 593: return 1; ! 594: } ! 595: if(listen(s, 5) < 0) { ! 596: perror("listen"); ! 597: } ! 598: if(port == 0) { ! 599: server_addrlen = sizeof(struct sockaddr_storage); ! 600: if(getsockname(s, (struct sockaddr *)&server_addr, &server_addrlen) < 0) { ! 601: perror("getsockname"); ! 602: return 1; ! 603: } ! 604: if(ipv6) { ! 605: struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr; ! 606: port = ntohs(addr->sin6_port); ! 607: } else { ! 608: struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr; ! 609: port = ntohs(addr->sin_port); ! 610: } ! 611: printf("Listening on port %hu\n", port); ! 612: fflush(stdout); ! 613: } ! 614: ! 615: /* write expected file */ ! 616: if(expected_file_name) { ! 617: FILE * f; ! 618: f = fopen(expected_file_name, "wb"); ! 619: if(f) { ! 620: char * buffer; ! 621: buffer = malloc(16*1024); ! 622: if(buffer == NULL) { ! 623: fprintf(stderr, "memory allocation error\n"); ! 624: } else { ! 625: build_content(buffer, 16*1024); ! 626: i = fwrite(buffer, 1, 16*1024, f); ! 627: if(i != 16*1024) { ! 628: fprintf(stderr, "error writing to file %s : %dbytes written (out of %d)\n", expected_file_name, i, 16*1024); ! 629: } ! 630: free(buffer); ! 631: } ! 632: fclose(f); ! 633: } else { ! 634: fprintf(stderr, "error opening file %s for writing\n", expected_file_name); ! 635: } ! 636: } ! 637: ! 638: /* fork() loop */ ! 639: while(!child && !quit) { ! 640: while(child_to_wait_for > 0) { ! 641: pid = wait(&status); ! 642: if(pid < 0) { ! 643: perror("wait"); ! 644: } else { ! 645: printf("child(%d) terminated with status %d\n", (int)pid, status); ! 646: } ! 647: --child_to_wait_for; ! 648: } ! 649: client_addrlen = sizeof(struct sockaddr_storage); ! 650: c = accept(s, (struct sockaddr *)&client_addr, ! 651: &client_addrlen); ! 652: if(c < 0) { ! 653: if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) ! 654: continue; ! 655: perror("accept"); ! 656: return 1; ! 657: } ! 658: printf("accept...\n"); ! 659: pid = fork(); ! 660: if(pid < 0) { ! 661: perror("fork"); ! 662: return 1; ! 663: } else if(pid == 0) { ! 664: /* child */ ! 665: child = 1; ! 666: close(s); ! 667: s = -1; ! 668: handle_http_connection(c); ! 669: } ! 670: close(c); ! 671: } ! 672: if(s >= 0) { ! 673: close(s); ! 674: s = -1; ! 675: } ! 676: if(!child) { ! 677: while(child_to_wait_for > 0) { ! 678: pid = wait(&status); ! 679: if(pid < 0) { ! 680: perror("wait"); ! 681: } else { ! 682: printf("child(%d) terminated with status %d\n", (int)pid, status); ! 683: } ! 684: --child_to_wait_for; ! 685: } ! 686: printf("Bye...\n"); ! 687: } ! 688: return 0; ! 689: } ! 690: