Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

test_http_server.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * test_http.c - a simple program to test the http library, server end
00059  *
00060  * Lars Wirzenius
00061  */
00062 
00063 #include <string.h>
00064 #include <stdlib.h>
00065 #include <unistd.h>
00066 #include <signal.h>
00067 
00068 #include "gwlib/gwlib.h"
00069 #include "gwlib/http.h"
00070 
00071 #define MAX_THREADS 1024
00072 
00073 Octstr *whitelist, *blacklist;
00074 Octstr *reply_text = NULL;
00075 
00076 int verbose, run, port;
00077 int ssl = 0;   /* indicate if SSL-enabled server should be used */
00078 
00079 static void client_thread(void *arg) 
00080 {
00081     HTTPClient *client;
00082     Octstr *body, *url, *ip;
00083     List *headers, *resph, *cgivars;
00084     HTTPCGIVar *v;
00085     Octstr *reply_body, *reply_type;
00086     unsigned long n = 0;
00087     int status, i;
00088 
00089     while (run) {
00090         client = http_accept_request(port, &ip, &url, &headers, &body, &cgivars);
00091 
00092         n++;
00093         if (client == NULL)
00094             break;
00095 
00096         info(0, "Request for <%s> from <%s>", 
00097              octstr_get_cstr(url), octstr_get_cstr(ip));
00098         if (verbose)
00099             debug("test.http", 0, "CGI vars were");
00100 
00101         /*
00102          * Don't use gwlist_extract() here, otherwise we don't have a chance
00103          * to re-use the cgivars later on.
00104          */
00105         for (i = 0; i < gwlist_len(cgivars); i++) {
00106             if ((v = gwlist_get(cgivars, i)) != NULL && verbose) {
00107                 octstr_dump(v->name, 0);
00108                 octstr_dump(v->value, 0);
00109             }
00110         }
00111     
00112         if (arg == NULL) {
00113             reply_body = octstr_duplicate(reply_text);
00114             reply_type = octstr_create("Content-Type: text/plain; "
00115                                        "charset=\"UTF-8\"");
00116         } else {
00117             reply_body = octstr_duplicate(arg);
00118             reply_type = octstr_create("Content-Type: text/vnd.wap.wml");
00119         }
00120 
00121         resph = gwlist_create();
00122         gwlist_append(resph, reply_type);
00123 
00124         status = HTTP_OK;
00125 
00126         /* check for special URIs and handle those */
00127         if (octstr_compare(url, octstr_imm("/quit")) == 0) {
00128            run = 0;
00129         } else if (octstr_compare(url, octstr_imm("/whitelist")) == 0) {
00130            octstr_destroy(reply_body);
00131             if (whitelist != NULL) {
00132                 if (verbose) {
00133                     debug("test.http.server", 0, "we send a white list");
00134                     octstr_dump(whitelist, 0);
00135                 }
00136                 reply_body = octstr_duplicate(whitelist);
00137             } else {
00138                reply_body = octstr_imm("");
00139            }
00140         } else if (octstr_compare(url, octstr_imm("/blacklist")) == 0) {
00141             octstr_destroy(reply_body);
00142             if (blacklist != NULL) {
00143                 if (verbose) {
00144                     debug("test.http.server", 0, "we send a blacklist");
00145                     octstr_dump(blacklist, 0);
00146                 }
00147                 reply_body = octstr_duplicate(blacklist);
00148             } else {
00149                 reply_body = octstr_imm("");
00150             } 
00151         } else if (octstr_compare(url, octstr_imm("/save")) == 0) {
00152             /* safe the body into a temporary file */
00153             pid_t pid = getpid();
00154             FILE *f = fopen(octstr_get_cstr(octstr_format("/tmp/body.%ld.%ld", pid, n)), "w");
00155             octstr_print(f, body);
00156             fclose(f);
00157         } else if (octstr_compare(url, octstr_imm("/redirect/")) == 0) {
00158             /* provide us with a HTTP 302 redirection response
00159              * will return /redirect/<pid> for the location header 
00160              * and will return /redirect/ if cgivar loop is set to allow looping
00161              */
00162             Octstr *redirect_header, *scheme, *uri, *l;
00163             pid_t pid = getpid();
00164 
00165             uri = ((l = http_cgi_variable(cgivars, "loop")) != NULL) ?
00166                 octstr_format("%s?loop=%s", octstr_get_cstr(url), 
00167                               octstr_get_cstr(l)) : 
00168                 octstr_format("%s%ld", octstr_get_cstr(url), pid);
00169 
00170             octstr_destroy(reply_body);
00171             reply_body = octstr_imm("Here you got a redirection URL that you should follow.");
00172             scheme = ssl ? octstr_imm("https://") : octstr_imm("http://");
00173             redirect_header = octstr_format("Location: %s%s%s", 
00174                 octstr_get_cstr(scheme),
00175                 octstr_get_cstr(http_header_value(headers, octstr_imm("Host"))),
00176                 octstr_get_cstr(uri));
00177             gwlist_append(resph, redirect_header);
00178             status = HTTP_FOUND; /* will provide 302 */
00179             octstr_destroy(uri);
00180         } else if (octstr_compare(url, octstr_imm("/mmsc")) == 0) {
00181             /* fake a M-Send.conf PDU which is using MMSEncapsulation as body */
00182             pid_t pid = getpid();
00183             FILE *f;
00184             gwlist_destroy(resph, octstr_destroy_item);
00185             octstr_destroy(reply_body);
00186             reply_type = octstr_create("Content-Type: application/vnd.wap.mms-message");
00187             reply_body = octstr_create("");
00188             octstr_append_from_hex(reply_body, 
00189                 "8c81"              /* X-Mms-Message-Type: m-send-conf */
00190                 "98632d3862343300"  /* X-Mms-Transaction-ID: c-8b43 */
00191                 "8d90"              /* X-Mms-MMS-Version: 1.0 */
00192                 "9280"              /* Response-status: Ok */
00193                 "8b313331373939353434393639383434313731323400"
00194             );                      /* Message-Id: 13179954496984417124 */
00195             resph = gwlist_create();
00196             gwlist_append(resph, reply_type);
00197             /* safe the M-Send.req body into a temporary file */
00198             f = fopen(octstr_get_cstr(octstr_format("/tmp/mms-body.%ld.%ld", pid, n)), "w");
00199             octstr_print(f, body);
00200             fclose(f);
00201         }        
00202             
00203         if (verbose) {
00204             debug("test.http", 0, "request headers were");
00205             http_header_dump(headers);
00206             if (body != NULL) {
00207                 debug("test.http", 0, "request body was");
00208                 octstr_dump(body, 0);
00209             }
00210         }
00211 
00212         /* return response to client */
00213         http_send_reply(client, status, resph, reply_body);
00214 
00215         octstr_destroy(ip);
00216         octstr_destroy(url);
00217         octstr_destroy(body);
00218         octstr_destroy(reply_body);
00219         http_destroy_cgiargs(cgivars);
00220         gwlist_destroy(headers, octstr_destroy_item);
00221         gwlist_destroy(resph, octstr_destroy_item);
00222     }
00223 
00224     octstr_destroy(whitelist);
00225     octstr_destroy(blacklist);
00226     debug("test.http", 0, "Working thread 'client_thread' terminates");
00227     http_close_all_ports();
00228 }
00229 
00230 static void help(void) {
00231     info(0, "Usage: test_http_server [options...]");
00232     info(0, "where options are:");
00233     info(0, "-t number");
00234     info(0, "    set number of working threads to use (default: 1)");
00235     info(0, "-v number");
00236     info(0, "    set log level for stderr logging (default: 0 - debug)");
00237     info(0, "-l logfile");
00238     info(0, "    log all output to a file");
00239     info(0, "-f file");
00240     info(0, "    use a specific file content for the response body");
00241     info(0, "-r reply_text");
00242     info(0, "    defines which static text to use for replies");
00243     info(0, "-h");
00244     info(0, "    provides this usage help information");
00245     info(0, "-q");
00246     info(0, "    don't be too verbose with output");
00247     info(0, "-p port");
00248     info(0, "    bind server to a specific port");
00249     info(0, "-s");
00250     info(0, "    be an SSL-enabled server");
00251     info(0, "-c ssl_cert");
00252     info(0, "    file of the SSL certificate to use");
00253     info(0, "-k ssl_key");
00254     info(0, "    file of the SSL private key to use");
00255     info(0, "-w white_list");
00256     info(0, "    file that is used for whitelist");
00257     info(0, "-b black_list");
00258     info(0, "    file that is used for blacklist");
00259     info(0, "specific URIs with special functions are:");
00260     info(0, "  /quite - shutdown the HTTP server");
00261     info(0, "  /whitelist - provides the -w whitelist as response");
00262     info(0, "  /blacklist - provides the -b blacklist as response");
00263     info(0, "  /save - save a HTTP POST request body to a file /tmp/body.<pid>.<n>");
00264     info(0, "    where <pid> is the process id and <n> is the received request number");
00265     info(0, "  /redirect/ - respond with HTTP 302 and the location /redirect/<pid>");
00266     info(0, "    where <pid> is the process id. if a cgivar loop=<something> is given");
00267     info(0, "    then HTTP reponses will end up in a loop.");
00268     info(0, "  /mmsc - fake a MMSC HTTP interface for M-Send.req PDUs send by a");
00269     info(0, "    mobile MMS-capable device, responds with a M-Send.conf PDU and");
00270     info(0, "    saves the M-Send.req body to a file /tmp/mms-body.<pid>.<n> in");
00271     info(0, "    MMSEncapsulation encoded binary format");
00272      
00273 }
00274 
00275 static void sigterm(int signo) {
00276     run = 0;
00277     http_close_all_ports();
00278     debug("test.gwlib", 0, "Signal %d received, quitting.", signo);
00279 }
00280 
00281 int main(int argc, char **argv) {
00282     int i, opt, use_threads;
00283     struct sigaction act;
00284     char *filename;
00285     Octstr *log_filename;
00286     Octstr *file_contents;
00287 #ifdef HAVE_LIBSSL
00288     Octstr *ssl_server_cert_file = NULL;
00289     Octstr *ssl_server_key_file = NULL;
00290 #endif
00291     char *whitelist_name;
00292     char *blacklist_name;
00293     int white_asked,
00294         black_asked;
00295     long threads[MAX_THREADS];
00296 
00297     gwlib_init();
00298 
00299     act.sa_handler = sigterm;
00300     sigemptyset(&act.sa_mask);
00301     act.sa_flags = 0;
00302     sigaction(SIGTERM, &act, NULL);
00303 
00304     port = 8080;
00305     use_threads = 1;
00306     verbose = 1;
00307     run = 1;
00308     filename = NULL;
00309     log_filename = NULL;
00310     blacklist_name = NULL;
00311     whitelist_name = NULL;
00312     white_asked = 0;
00313     black_asked = 0;
00314 
00315     reply_text = octstr_create("Sent.");
00316 
00317     while ((opt = getopt(argc, argv, "hqv:p:t:f:l:sc:k:b:w:r:")) != EOF) {
00318     switch (opt) {
00319     case 'v':
00320         log_set_output_level(atoi(optarg));
00321         break;
00322 
00323         case 'q':
00324         verbose = 0;                                           
00325         break;
00326 
00327     case 'h':
00328         help();
00329         exit(0);
00330 
00331     case 'p':
00332         port = atoi(optarg);
00333         break;
00334 
00335     case 't':
00336         use_threads = atoi(optarg);
00337         if (use_threads > MAX_THREADS)
00338             use_threads = MAX_THREADS;
00339         break;
00340 
00341         case 'c':
00342 #ifdef HAVE_LIBSSL
00343         octstr_destroy(ssl_server_cert_file);
00344         ssl_server_cert_file = octstr_create(optarg);
00345 #endif
00346         break;
00347 
00348         case 'k':
00349 #ifdef HAVE_LIBSSL
00350         octstr_destroy(ssl_server_key_file);
00351         ssl_server_key_file = octstr_create(optarg);
00352 #endif
00353         break;
00354 
00355     case 's':
00356 #ifdef HAVE_LIBSSL
00357         ssl = 1;
00358 #endif   
00359         break;
00360 
00361     case 'f':
00362         filename = optarg;
00363         break;
00364 
00365     case 'l':
00366         octstr_destroy(log_filename);
00367         log_filename = octstr_create(optarg);
00368     break;
00369 
00370     case 'w':
00371         whitelist_name = optarg;
00372         if (whitelist_name == NULL)
00373             whitelist_name = "";
00374         white_asked = 1;
00375     break;
00376 
00377     case 'b':
00378         blacklist_name = optarg;
00379         if (blacklist_name == NULL)
00380             blacklist_name = "";
00381         black_asked = 1;
00382     break;
00383 
00384     case 'r':
00385         octstr_destroy(reply_text);
00386         reply_text = octstr_create(optarg);
00387         break;
00388 
00389     case '?':
00390     default:
00391         error(0, "Invalid option %c", opt);
00392         help();
00393         panic(0, "Stopping.");
00394     }
00395     }
00396 
00397     if (log_filename != NULL) {
00398         log_open(octstr_get_cstr(log_filename), GW_DEBUG, GW_NON_EXCL);
00399         octstr_destroy(log_filename);
00400     }
00401 
00402     if (filename == NULL)
00403         file_contents = NULL;
00404     else
00405         file_contents = octstr_read_file(filename);
00406 
00407     if (white_asked) {
00408         whitelist = octstr_read_file(whitelist_name);
00409         if (whitelist == NULL)
00410             panic(0, "Cannot read the whitelist");
00411     }
00412     
00413     if (black_asked) {
00414         blacklist = octstr_read_file(blacklist_name);
00415         if (blacklist == NULL)
00416             panic(0, "Cannot read the blacklist");
00417     }
00418 
00419 #ifdef HAVE_LIBSSL
00420     /*
00421      * check if we are doing a SSL-enabled server version here
00422      * load the required cert and key file
00423      */
00424     if (ssl) {
00425         if (ssl_server_cert_file != NULL && ssl_server_key_file != NULL) {
00426             use_global_server_certkey_file(ssl_server_cert_file, ssl_server_key_file);
00427             octstr_destroy(ssl_server_cert_file);
00428             octstr_destroy(ssl_server_key_file);
00429         } else {
00430             panic(0, "certificate and public key need to be given!");
00431         }
00432     }
00433 #endif
00434      
00435     if (http_open_port(port, ssl) == -1)
00436         panic(0, "http_open_server failed");
00437 
00438     /*
00439      * Do the real work in a separate thread so that the main
00440      * thread can catch signals safely.
00441      */
00442     for (i = 0; i < use_threads; ++i) 
00443         threads[i] = gwthread_create(client_thread, file_contents);
00444 
00445     /* wait for all working threads */
00446     for (i = 0; i < use_threads; ++i)
00447         gwthread_join(threads[i]);
00448 
00449     debug("test.http", 0, "Program exiting normally.");
00450     gwlib_shutdown();
00451     return 0;
00452 }
00453 
00454 
00455 
00456 
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.