Annotation of embedaddon/miniupnpc/minihttptestserver.c, revision 1.1.1.2

1.1.1.2 ! misho       1: /* $Id: minihttptestserver.c,v 1.13 2012/05/29 13:03:07 nanard Exp $ */
1.1       misho       2: /* Project : miniUPnP
                      3:  * Author : Thomas Bernard
1.1.1.2 ! misho       4:  * Copyright (c) 2011-2012 Thomas Bernard
1.1       misho       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>
1.1.1.2 ! misho      19: #include <errno.h>
1.1       misho      20: 
                     21: #define CRAP_LENGTH (2048)
                     22: 
1.1.1.2 ! misho      23: volatile sig_atomic_t quit = 0;
        !            24: volatile sig_atomic_t child_to_wait_for = 0;
1.1       misho      25: 
                     26: /**
                     27:  * signal handler for SIGCHLD (child status has changed)
                     28:  */
                     29: void handle_signal_chld(int sig)
                     30: {
                     31:        printf("handle_signal_chld(%d)\n", sig);
                     32:        ++child_to_wait_for;
                     33: }
                     34: 
                     35: /**
                     36:  * signal handler for SIGINT (CRTL C)
                     37:  */
                     38: #if 0
                     39: void handle_signal_int(int sig)
                     40: {
                     41:        printf("handle_signal_int(%d)\n", sig);
                     42:        quit = 1;
                     43: }
                     44: #endif
                     45: 
                     46: /**
                     47:  * build a text/plain content of the specified length
                     48:  */
                     49: void build_content(char * p, int n)
                     50: {
                     51:        char line_buffer[80];
                     52:        int k;
                     53:        int i = 0;
                     54: 
                     55:        while(n > 0) {
                     56:                k = snprintf(line_buffer, sizeof(line_buffer),
                     57:                             "%04d_ABCDEFGHIJKL_This_line_is_64_bytes_long_ABCDEFGHIJKL_%04d\r\n",
                     58:                             i, i);
                     59:                if(k != 64) {
                     60:                        fprintf(stderr, "snprintf() returned %d in build_content()\n", k);
                     61:                }
                     62:                ++i;
                     63:                if(n >= 64) {
                     64:                        memcpy(p, line_buffer, 64);
                     65:                        p += 64;
                     66:                        n -= 64;
                     67:                } else {
                     68:                        memcpy(p, line_buffer, n);
                     69:                        p += n;
                     70:                        n = 0;
                     71:                }
                     72:        }
                     73: }
                     74: 
                     75: /**
                     76:  * build crappy content
                     77:  */
                     78: void build_crap(char * p, int n)
                     79: {
                     80:        static const char crap[] = "_CRAP_\r\n";
                     81:        int i;
                     82: 
                     83:        while(n > 0) {
                     84:                i = sizeof(crap) - 1;
                     85:                if(i > n)
                     86:                        i = n;
                     87:                memcpy(p, crap, i);
                     88:                p += i;
                     89:                n -= i;
                     90:        }
                     91: }
                     92: 
                     93: /**
                     94:  * build chunked response.
                     95:  * return a malloc'ed buffer
                     96:  */
                     97: char * build_chunked_response(int content_length, int * response_len) {
                     98:        char * response_buffer;
                     99:        char * content_buffer;
                    100:        int buffer_length;
                    101:        int i, n;
                    102: 
                    103:        /* allocate to have some margin */
                    104:        buffer_length = 256 + content_length + (content_length >> 4);
                    105:        response_buffer = malloc(buffer_length);
                    106:        *response_len = snprintf(response_buffer, buffer_length,
                    107:                                 "HTTP/1.1 200 OK\r\n"
                    108:                                 "Content-Type: text/plain\r\n"
                    109:                                 "Transfer-Encoding: chunked\r\n"
                    110:                                 "\r\n");
                    111: 
                    112:        /* build the content */
                    113:        content_buffer = malloc(content_length);
                    114:        build_content(content_buffer, content_length);
                    115: 
                    116:        /* chunk it */
                    117:        i = 0;
                    118:        while(i < content_length) {
                    119:                n = (rand() % 199) + 1;
                    120:                if(i + n > content_length) {
                    121:                        n = content_length - i;
                    122:                }
                    123:                /* TODO : check buffer size ! */
                    124:                *response_len += snprintf(response_buffer + *response_len,
                    125:                                          buffer_length - *response_len,
                    126:                                          "%x\r\n", n);
                    127:                memcpy(response_buffer + *response_len, content_buffer + i, n);
                    128:                *response_len += n;
                    129:                i += n;
                    130:                response_buffer[(*response_len)++] = '\r';
                    131:                response_buffer[(*response_len)++] = '\n';
                    132:        }
1.1.1.2 ! misho     133:        /* the last chunk : "0\r\n" a empty body and then
        !           134:         * the final "\r\n" */
        !           135:        memcpy(response_buffer + *response_len, "0\r\n\r\n", 5);
        !           136:        *response_len += 5;
1.1       misho     137:        free(content_buffer);
                    138: 
                    139:        printf("resp_length=%d buffer_length=%d content_length=%d\n",
                    140:               *response_len, buffer_length, content_length);
                    141:        return response_buffer;
                    142: }
                    143: 
                    144: enum modes { MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL };
                    145: const struct {
                    146:        const enum modes mode;
                    147:        const char * text;
                    148: } modes_array[] = {
                    149:        {MODE_CHUNKED, "chunked"},
                    150:        {MODE_ADDCRAP, "addcrap"},
                    151:        {MODE_NORMAL, "normal"},
                    152:        {MODE_INVALID, NULL}
                    153: };
                    154: 
                    155: /**
                    156:  * write the response with random behaviour !
                    157:  */
                    158: void send_response(int c, const char * buffer, int len)
                    159: {
                    160:        int n;
                    161:        while(len > 0) {
                    162:                n = (rand() % 99) + 1;
                    163:                if(n > len)
                    164:                        n = len;
                    165:                n = write(c, buffer, n);
                    166:                if(n < 0) {
1.1.1.2 ! misho     167:                        if(errno != EINTR) {
        !           168:                                perror("write");
        !           169:                                return;
        !           170:                        }
        !           171:                        /* if errno == EINTR, try again */
1.1       misho     172:                } else {
                    173:                        len -= n;
                    174:                        buffer += n;
                    175:                }
                    176:                usleep(10000); /* 10ms */
                    177:        }
                    178: }
                    179: 
                    180: /**
                    181:  * handle the HTTP connection
                    182:  */
                    183: void handle_http_connection(int c)
                    184: {
                    185:        char request_buffer[2048];
                    186:        int request_len = 0;
                    187:        int headers_found = 0;
                    188:        int n, i;
                    189:        char request_method[16];
                    190:        char request_uri[256];
                    191:        char http_version[16];
                    192:        char * p;
                    193:        char * response_buffer;
                    194:        int response_len;
                    195:        enum modes mode;
                    196:        int content_length = 16*1024;
                    197: 
                    198:        /* read the request */
1.1.1.2 ! misho     199:        while(request_len < (int)sizeof(request_buffer) && !headers_found) {
1.1       misho     200:                n = read(c,
                    201:                         request_buffer + request_len,
                    202:                         sizeof(request_buffer) - request_len);
                    203:                if(n < 0) {
                    204:                        perror("read");
                    205:                        return;
                    206:                } else if(n==0) {
                    207:                        /* remote host closed the connection */
                    208:                        break;
                    209:                } else {
                    210:                        request_len += n;
                    211:                        for(i = 0; i < request_len - 3; i++) {
                    212:                                if(0 == memcmp(request_buffer + i, "\r\n\r\n", 4)) {
                    213:                                        /* found the end of headers */
                    214:                                        headers_found = 1;
                    215:                                        break;
                    216:                                }
                    217:                        }
                    218:                }
                    219:        }
                    220:        if(!headers_found) {
                    221:                /* error */
                    222:                return;
                    223:        }
                    224:        printf("headers :\n%.*s", request_len, request_buffer);
                    225:        /* the request have been received, now parse the request line */
                    226:        p = request_buffer;
1.1.1.2 ! misho     227:        for(i = 0; i < (int)sizeof(request_method) - 1; i++) {
1.1       misho     228:                if(*p == ' ' || *p == '\r')
                    229:                        break;
                    230:                request_method[i] = *p;
                    231:                ++p;
                    232:        }
                    233:        request_method[i] = '\0';
                    234:        while(*p == ' ')
                    235:                p++;
1.1.1.2 ! misho     236:        for(i = 0; i < (int)sizeof(request_uri) - 1; i++) {
1.1       misho     237:                if(*p == ' ' || *p == '\r')
                    238:                        break;
                    239:                request_uri[i] = *p;
                    240:                ++p;
                    241:        }
                    242:        request_uri[i] = '\0';
                    243:        while(*p == ' ')
                    244:                p++;
1.1.1.2 ! misho     245:        for(i = 0; i < (int)sizeof(http_version) - 1; i++) {
1.1       misho     246:                if(*p == ' ' || *p == '\r')
                    247:                        break;
                    248:                http_version[i] = *p;
                    249:                ++p;
                    250:        }
                    251:        http_version[i] = '\0';
                    252:        printf("Method = %s, URI = %s, %s\n",
                    253:               request_method, request_uri, http_version);
                    254:        /* check if the request method is allowed */
                    255:        if(0 != strcmp(request_method, "GET")) {
                    256:                const char response405[] = "HTTP/1.1 405 Method Not Allowed\r\n"
                    257:                                           "Allow: GET\r\n\r\n";
1.1.1.2 ! misho     258:                const char * pc;
1.1       misho     259:                /* 405 Method Not Allowed */
                    260:                /* The response MUST include an Allow header containing a list
                    261:                 * of valid methods for the requested resource. */
1.1.1.2 ! misho     262:                n = sizeof(response405) - 1;
        !           263:                pc = response405;
        !           264:                while(n > 0) {
        !           265:                        i = write(c, pc, n);
        !           266:                        if(i<0) {
        !           267:                                if(errno != EINTR) {
        !           268:                                        perror("write");
        !           269:                                        return;
        !           270:                                }
        !           271:                        } else {
        !           272:                                n -= i;
        !           273:                                pc += i;
        !           274:                        }
        !           275:                }
1.1       misho     276:                return;
                    277:        }
                    278: 
                    279:        mode = MODE_INVALID;
                    280:        /* use the request URI to know what to do */
                    281:        for(i = 0; modes_array[i].mode != MODE_INVALID; i++) {
                    282:                if(strstr(request_uri, modes_array[i].text)) {
                    283:                        mode = modes_array[i].mode; /* found */
                    284:                        break;
                    285:                }
                    286:        }
                    287: 
                    288:        switch(mode) {
                    289:        case MODE_CHUNKED:
                    290:                response_buffer = build_chunked_response(content_length, &response_len);
                    291:                break;
                    292:        case MODE_ADDCRAP:
                    293:                response_len = content_length+256;
                    294:                response_buffer = malloc(response_len);
                    295:                n = snprintf(response_buffer, response_len,
                    296:                             "HTTP/1.1 200 OK\r\n"
                    297:                             "Server: minihttptestserver\r\n"
                    298:                             "Content-Type: text/plain\r\n"
                    299:                             "Content-Length: %d\r\n"
                    300:                             "\r\n", content_length);
                    301:                response_len = content_length+n+CRAP_LENGTH;
                    302:                response_buffer = realloc(response_buffer, response_len);
                    303:                build_content(response_buffer + n, content_length);
                    304:                build_crap(response_buffer + n + content_length, CRAP_LENGTH);
                    305:                break;
                    306:        default:
                    307:                response_len = content_length+256;
                    308:                response_buffer = malloc(response_len);
                    309:                n = snprintf(response_buffer, response_len,
                    310:                             "HTTP/1.1 200 OK\r\n"
                    311:                             "Server: minihttptestserver\r\n"
                    312:                             "Content-Type: text/plain\r\n"
                    313:                             "\r\n");
                    314:                response_len = content_length+n;
                    315:                response_buffer = realloc(response_buffer, response_len);
                    316:                build_content(response_buffer + n, response_len - n);
                    317:        }
                    318: 
                    319:        if(response_buffer) {
                    320:                send_response(c, response_buffer, response_len);
                    321:                free(response_buffer);
                    322:        } else {
                    323:                /* Error 500 */
                    324:        }
                    325: }
                    326: 
                    327: /**
                    328:  */
                    329: int main(int argc, char * * argv) {
                    330:        int ipv6 = 0;
                    331:        int s, c, i;
                    332:        unsigned short port = 0;
                    333:        struct sockaddr_storage server_addr;
                    334:        socklen_t server_addrlen;
                    335:        struct sockaddr_storage client_addr;
                    336:        socklen_t client_addrlen;
                    337:        pid_t pid;
                    338:        int child = 0;
                    339:        int status;
                    340:        const char * expected_file_name = NULL;
                    341: 
                    342:        for(i = 1; i < argc; i++) {
                    343:                if(argv[i][0] == '-') {
                    344:                        switch(argv[i][1]) {
                    345:                        case '6':
                    346:                                ipv6 = 1;
                    347:                                break;
                    348:                        case 'e':
                    349:                                /* write expected file ! */
                    350:                                expected_file_name = argv[++i];
                    351:                                break;
                    352:                        case 'p':
                    353:                                /* port */
                    354:                                if(++i < argc) {
                    355:                                        port = (unsigned short)atoi(argv[i]);
                    356:                                }
                    357:                                break;
                    358:                        default:
                    359:                                fprintf(stderr, "unknown command line switch '%s'\n", argv[i]);
                    360:                        }
                    361:                } else {
                    362:                        fprintf(stderr, "unkown command line argument '%s'\n", argv[i]);
                    363:                }
                    364:        }
                    365: 
                    366:        srand(time(NULL));
                    367:        signal(SIGCHLD, handle_signal_chld);
                    368: #if 0
                    369:        signal(SIGINT, handle_signal_int);
                    370: #endif
                    371: 
                    372:        s = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
                    373:        if(s < 0) {
                    374:                perror("socket");
                    375:                return 1;
                    376:        }
                    377:        memset(&server_addr, 0, sizeof(struct sockaddr_storage));
                    378:        memset(&client_addr, 0, sizeof(struct sockaddr_storage));
                    379:        if(ipv6) {
                    380:                struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
                    381:                addr->sin6_family = AF_INET6;
                    382:                addr->sin6_port = htons(port);
1.1.1.2 ! misho     383:                addr->sin6_addr = in6addr_loopback;
1.1       misho     384:        } else {
                    385:                struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
                    386:                addr->sin_family = AF_INET;
                    387:                addr->sin_port = htons(port);
1.1.1.2 ! misho     388:                addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1.1       misho     389:        }
                    390:        if(bind(s, (struct sockaddr *)&server_addr,
                    391:                ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) < 0) {
                    392:                perror("bind");
                    393:                return 1;
                    394:        }
                    395:        if(listen(s, 5) < 0) {
                    396:                perror("listen");
                    397:        }
                    398:        if(port == 0) {
                    399:                server_addrlen = sizeof(struct sockaddr_storage);
                    400:                if(getsockname(s, (struct sockaddr *)&server_addr, &server_addrlen) < 0) {
                    401:                        perror("getsockname");
                    402:                        return 1;
                    403:                }
                    404:                if(ipv6) {
                    405:                        struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
                    406:                        port = ntohs(addr->sin6_port);
                    407:                } else {
                    408:                        struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
                    409:                        port = ntohs(addr->sin_port);
                    410:                }
                    411:                printf("Listening on port %hu\n", port);
                    412:                fflush(stdout);
                    413:        }
                    414: 
                    415:        /* write expected file */
                    416:        if(expected_file_name) {
                    417:                FILE * f;
                    418:                f = fopen(expected_file_name, "wb");
                    419:                if(f) {
                    420:                        char * buffer;
                    421:                        buffer = malloc(16*1024);
                    422:                        build_content(buffer, 16*1024);
1.1.1.2 ! misho     423:                        i = fwrite(buffer, 1, 16*1024, f);
        !           424:                        if(i != 16*1024) {
        !           425:                                fprintf(stderr, "error writing to file %s : %dbytes written (out of %d)\n", expected_file_name, i, 16*1024);
        !           426:                        }
1.1       misho     427:                        free(buffer);
                    428:                        fclose(f);
1.1.1.2 ! misho     429:                } else {
        !           430:                        fprintf(stderr, "error opening file %s for writing\n", expected_file_name);
1.1       misho     431:                }
                    432:        }
                    433: 
                    434:        /* fork() loop */
                    435:        while(!child && !quit) {
                    436:                while(child_to_wait_for > 0) {
                    437:                        pid = wait(&status);
                    438:                        if(pid < 0) {
                    439:                                perror("wait");
                    440:                        } else {
                    441:                                printf("child(%d) terminated with status %d\n", pid, status);
                    442:                        }
                    443:                        --child_to_wait_for;
                    444:                }
                    445:                /* TODO : add a select() call in order to handle the case
                    446:                 * when a signal is caught */
                    447:                client_addrlen = sizeof(struct sockaddr_storage);
                    448:                c = accept(s, (struct sockaddr *)&client_addr,
                    449:                           &client_addrlen);
                    450:                if(c < 0) {
                    451:                        perror("accept");
                    452:                        return 1;
                    453:                }
                    454:                printf("accept...\n");
                    455:                pid = fork();
                    456:                if(pid < 0) {
                    457:                        perror("fork");
                    458:                        return 1;
                    459:                } else if(pid == 0) {
                    460:                        /* child */
                    461:                        child = 1;
                    462:                        close(s);
                    463:                        s = -1;
                    464:                        handle_http_connection(c);
                    465:                }
                    466:                close(c);
                    467:        }
                    468:        if(s >= 0) {
                    469:                close(s);
                    470:                s = -1;
                    471:        }
                    472:        if(!child) {
                    473:                while(child_to_wait_for > 0) {
                    474:                        pid = wait(&status);
                    475:                        if(pid < 0) {
                    476:                                perror("wait");
                    477:                        } else {
                    478:                                printf("child(%d) terminated with status %d\n", pid, status);
                    479:                        }
                    480:                        --child_to_wait_for;
                    481:                }
                    482:                printf("Bye...\n");
                    483:        }
                    484:        return 0;
                    485: }
                    486: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>