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

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

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