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

drive_wapbox.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 /* drive_wapbox.c - test wapbox through its bearerbox and http interfaces
00058  *
00059  * This program starts a wapbox and pretends to be both the bearer box
00060  * and the http server, so that it can test and benchmark the wapbox in
00061  * isolation.
00062  *
00063  * Richard Braakman
00064  */
00065 
00066 #include <string.h>
00067 #include <stdlib.h>
00068 #include <unistd.h>
00069 #include <errno.h>
00070 #include <signal.h>
00071 
00072 #include <sys/time.h>
00073 #include <sys/types.h>
00074 #include <sys/socket.h>
00075 
00076 #include "gwlib/gwlib.h"
00077 #include "gw/msg.h"
00078 
00079 /* These should really be in a header file */
00080 enum wsp_types {
00081         Bad_PDU = -1,
00082         Connect_PDU = 0x01,
00083         ConnectReply_PDU = 0x02,
00084         Redirect_PDU = 0x03,
00085         Reply_PDU = 0x04,
00086         Disconnect_PDU = 0x05,
00087         Push_PDU = 0x06,
00088         ConfirmedPush_PDU = 0x07,
00089         Suspend_PDU = 0x08,
00090         Resume_PDU = 0x09,
00091         Get_PDU = 0x40,
00092         Options_PDU = 0x41,
00093         Head_PDU = 0x42,
00094         Delete_PDU = 0x43,
00095         Trace_PDU = 0x44,
00096         Post_PDU = 0x60,
00097         Put_PDU = 0x61
00098 };
00099 
00100 enum wtp_types {
00101     INVOKE = 1,
00102     RESULT = 2,
00103     ACK = 3
00104 };
00105 
00106 #define WSP_VERSION 0x10
00107 
00108 #define TIMEOUT 10.0  /* seconds */
00109 
00110 static long max_requests = 1;
00111 static long max_clients = 1;
00112 static long req_per_session = 1;
00113 static unsigned short http_port;
00114 static int wapbox_port = 30188;
00115 static Octstr *http_url = NULL;
00116 
00117 static int verbose_debug = 0;
00118 static int user_ack = 0;
00119 
00120 static long requests_complete = 0;
00121 static volatile sig_atomic_t dying = 0;
00122 
00123 enum WTP_type {
00124     TR_Invoke = 1,
00125     TR_Result = 2,
00126     TR_Ack = 3,
00127     TR_Abort = 4
00128 };
00129 
00130 struct client_status {
00131     /* True if we expect a WTP reply */
00132     int wtp_invoked;
00133 
00134     /* Transaction number for WTP level */
00135     int wtp_tid; /* current tid if wtp_invoked, else next tid to use */
00136 
00137     /* True if we're connected at the WSP level */
00138     /* Equal to 2 if we're trying to disconnect */
00139     int wsp_connected;
00140 
00141     /* -1 if we're not connected */
00142     long wsp_session_id;
00143 
00144     /* Number of successful page fetches this session */
00145     int pages_fetched;
00146 
00147     /* Source port to use for this client; should be unique. */
00148     unsigned short port;
00149 };
00150 typedef struct client_status Client;
00151     
00152 static Client *clients;
00153 List *ready_clients;
00154 
00155 static unsigned long get_varint(Octstr *pdu, int pos) {
00156     int c;
00157     long result = 0;
00158 
00159     do {
00160         c = octstr_get_char(pdu, pos++);
00161         result = (result << 7) | (c & 0x7f);
00162     } while (c & 0x80);
00163 
00164     return c;
00165 }
00166 
00167 static void http_thread(void *arg) {
00168     HTTPClient *client;
00169     Octstr *ip;
00170     Octstr *url;
00171     List *headers;
00172     Octstr *body;
00173     List *cgivars;
00174     Octstr *reply_body = octstr_create(
00175         "<?xml version=\"1.0\"?>\n"
00176         "<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\"\n"
00177         " \"http://www.wapforum.org/DTD/wml_1.1.xml\">\n"
00178         "<wml>\n"
00179         "<card id=\"main\" title=\"Hello, world\" newcontext=\"true\">\n"
00180         "        <p>Hello, world.</p>\n"
00181         "</card></wml>\n");
00182     List *reply_headers = gwlist_create();
00183     int port;
00184 
00185         port = *(int *) arg;
00186     gw_free(arg);
00187 
00188     gwlist_append(reply_headers,
00189         octstr_create("Content-Type: text/vnd.wap.wml"));
00190 
00191     for (;!dying;) {
00192         client = http_accept_request(port, &ip, &url, &headers, 
00193                                      &body, &cgivars);
00194         if (client == NULL)
00195             break;
00196         http_send_reply(client, HTTP_OK, reply_headers, reply_body);
00197         http_destroy_headers(headers);
00198         octstr_destroy(ip);
00199         octstr_destroy(url);
00200         octstr_destroy(body);
00201         http_destroy_cgiargs(cgivars);
00202     }
00203 
00204     octstr_destroy(reply_body);
00205         http_destroy_headers(reply_headers);
00206 }
00207     
00208 
00209 static long http_thread_id;
00210 
00211 static int start_http_thread(void) {
00212     unsigned short port;
00213     int *port_copy;
00214     int ssl = 0;   /* indicate if SSL-enabled server should be used */
00215 
00216     for (port = 40000; port < 41000; port += 13) {
00217         if (http_open_port(port, ssl) != -1)
00218             break;
00219     }
00220     if (port == 41000)
00221         panic(0, "No ports available for http server");
00222 
00223         port_copy = gw_malloc(sizeof(*port_copy));
00224     *port_copy = port;
00225     http_thread_id = gwthread_create(http_thread, port_copy);
00226     if (http_thread_id == -1) 
00227         panic(0, "Cannot start http thread");
00228     return port;
00229 }
00230 
00231 static Connection *start_wapbox(void) {
00232     int wap_socket;
00233     int wapbox;
00234 
00235     wap_socket = make_server_socket(wapbox_port, NULL);
00236     if (wap_socket < 0)
00237         panic(0, "Couldn't make wapbox port\n");
00238 
00239     wapbox = accept(wap_socket, NULL, NULL);
00240     if (wapbox < 0)
00241         panic(errno, "Wapbox could not connect\n");
00242 
00243     close(wap_socket);
00244 
00245     return conn_wrap_fd(wapbox, 0);
00246 }
00247 
00248 static void initialize_clients(void) {
00249     long i;
00250 
00251     ready_clients = gwlist_create();
00252 
00253     clients = gw_malloc(max_clients * sizeof(*clients));
00254     for (i = 0; i < max_clients; i++) {
00255         clients[i].wtp_invoked = 0;
00256         clients[i].wtp_tid = 0;
00257         clients[i].wsp_connected = 0;
00258         clients[i].wsp_session_id = -1;
00259         clients[i].pages_fetched = 0;
00260         clients[i].port = i;
00261         gwlist_append(ready_clients, &clients[i]);
00262     }
00263 }
00264 
00265 static void destroy_clients(void) {
00266     gw_free(clients);
00267     gwlist_destroy(ready_clients, NULL);
00268 }
00269 
00270 static Client *find_client(unsigned short port) {
00271     /* It's easy and fast since we assign ports in linear order */
00272     if (port >= max_clients)
00273         return NULL;
00274 
00275     return clients + port;
00276 }
00277 
00278 static void client_done(Client *client) {
00279     requests_complete++;
00280     gwlist_append(ready_clients, client);
00281 }
00282 
00283 static void increment_tid(Client *client) {
00284     if (client->wtp_tid == 0x7fff) 
00285         client->wtp_tid = 0;
00286     else
00287         client->wtp_tid++;
00288 }
00289 
00290 /* Set the U/P flag on an Invoke PDU */
00291 static void set_user_ack(Octstr *pdu) {
00292     octstr_set_bits(pdu, 3 * 8 + 3, 1, 1);
00293 }
00294 
00295 static Octstr *wtp_invoke_create(int class) {
00296     Octstr *pdu;
00297     /* data describes a TR-Invoke PDU, with GTR=1 and TTR=1 (segmentation
00298      * not supported), and Transaction class 0 (which we replace below) */
00299     static unsigned char data[] = { 0x0e, 0x00, 0x00, 0x00 };
00300     gw_assert(class >= 0);
00301     gw_assert(class <= 2);
00302     pdu = octstr_create_from_data(data, sizeof(data));
00303     octstr_set_char(pdu, 3, class);
00304 
00305     if (user_ack)
00306         set_user_ack(pdu);
00307 
00308     return pdu;
00309 }
00310 
00311 static Octstr *wtp_ack_create(void) {
00312     static unsigned char data[] = { 0x18, 0x00, 0x00 };
00313     return octstr_create_from_data(data, sizeof(data));
00314 }
00315 
00316 static void add_wsp_connect(Octstr *pdu) {
00317     static unsigned char data[] = { Connect_PDU, WSP_VERSION, 68, 0x00,
00318     0x03, 0x80, 0x90, 0x00, 0x03, 0x81, 0x90, 0x00,
00319     0x02, 0x82, 0x30, 0x02, 0x83, 0x01, 0x02, 0x84,
00320     0x01, 0x28, 0x85, 0x50, 0x58, 0x2d, 0x55, 0x50,
00321     0x2d, 0x41, 0x47, 0x45, 0x54, 0x00, 0x51, 0x58,
00322     0x2d, 0x55, 0x50, 0x2d, 0x47, 0x45, 0x54, 0x4e,
00323     0x4f, 0x54, 0x49, 0x46, 0x59, 0x00, 0x70, 0x58,
00324     0x2d, 0x55, 0x50, 0x2d, 0x41, 0x50, 0x4f, 0x53,
00325     0x54, 0x00, 0x09, 0x86, 0x02, 0x78, 0x2d, 0x75,
00326     0x70, 0x2d, 0x31, 0x00 };
00327     octstr_append_data(pdu, data, sizeof(data));
00328 }
00329 
00330 static void add_wsp_get(Octstr *pdu) {
00331         Octstr *urlbuf;
00332 
00333     octstr_append_char(pdu, Get_PDU);
00334     if (http_url) {
00335         octstr_append_uintvar(pdu, octstr_len(http_url));
00336         octstr_append(pdu, http_url);
00337     } else {
00338         urlbuf = octstr_format("http://localhost:%ld/hello.wml",
00339                        (long) http_port);
00340         octstr_append_uintvar(pdu, octstr_len(urlbuf));
00341         octstr_append(pdu, urlbuf);
00342         octstr_destroy(urlbuf);
00343     }
00344 }
00345 
00346 static void add_wsp_disconnect(Octstr *pdu, long session_id) {
00347     octstr_append_char(pdu, Disconnect_PDU);
00348     octstr_append_uintvar(pdu, session_id);
00349 }
00350 
00351 static void set_tid(Octstr *pdu, int tid) {
00352     int c;
00353 
00354     /* Tid wraps at 15 bits. */
00355     tid &= 0x7fff;
00356 
00357     c = octstr_get_char(pdu, 1);
00358     c = (tid >> 8) | (c & 0x80);
00359     octstr_set_char(pdu, 1, c);
00360     octstr_set_char(pdu, 2, tid & 0xff);
00361 }
00362 
00363 static int get_tid(Octstr *pdu) {
00364     return octstr_get_bits(pdu, 8, 16);
00365 }
00366 
00367 static int wtp_type(Octstr *pdu) {
00368     return octstr_get_bits(pdu, 1, 4);
00369 }
00370 
00371 static Msg *wdp_create(Octstr *data, Client *client) {
00372     Msg *msg;
00373 
00374     msg = msg_create(wdp_datagram);
00375     msg->wdp_datagram.source_address = octstr_create("127.0.0.1");
00376     msg->wdp_datagram.source_port = client->port;
00377     msg->wdp_datagram.destination_address = octstr_create("127.0.0.1");
00378     msg->wdp_datagram.destination_port = 9201;
00379     msg->wdp_datagram.user_data = octstr_duplicate(data);
00380 
00381     return msg;
00382 }
00383 
00384 static void send_pdu(Octstr *pdu, Connection *boxc, Client *client) {
00385     Msg *msg;
00386     Octstr *data;
00387 
00388     if (verbose_debug) {
00389         debug("test", 0, "Sending:");
00390         octstr_dump(pdu, 0);
00391     }
00392 
00393     msg = wdp_create(pdu, client);
00394     data = msg_pack(msg);
00395     conn_write_withlen(boxc, data);
00396 
00397     octstr_destroy(data);
00398     msg_destroy(msg);
00399 }
00400 
00401 static void send_invoke_connect(Connection *boxc, Client *client) {
00402     Octstr *pdu;
00403     
00404     gw_assert(client != NULL);
00405     gw_assert(client->wtp_invoked == 0);
00406     gw_assert(client->wsp_connected == 0);
00407 
00408     pdu = wtp_invoke_create(2);
00409     set_tid(pdu, client->wtp_tid);
00410     add_wsp_connect(pdu);
00411 
00412     send_pdu(pdu, boxc, client);
00413     octstr_destroy(pdu);
00414 
00415     client->wtp_invoked = 1;
00416 }
00417 
00418 static void send_invoke_get(Connection *boxc, Client *client) {
00419     Octstr *pdu;
00420 
00421     gw_assert(client != NULL);
00422     gw_assert(client->wtp_invoked == 0);
00423     gw_assert(client->wsp_connected == 1);
00424 
00425     pdu = wtp_invoke_create(2);
00426     set_tid(pdu, client->wtp_tid);
00427     add_wsp_get(pdu);
00428 
00429     send_pdu(pdu, boxc, client);
00430     octstr_destroy(pdu);
00431 
00432     client->wtp_invoked = 1;
00433 }
00434 
00435 static void record_disconnect(Client *client) {
00436     client->wsp_connected = 0;
00437     client->wsp_session_id = -1;
00438     client->pages_fetched = 0;
00439     increment_tid(client);
00440 }
00441 
00442 static void send_invoke_disconnect(Connection *boxc, Client *client) {
00443     Octstr *pdu;
00444 
00445     gw_assert(client != NULL);
00446     gw_assert(client->wtp_invoked == 0);
00447     gw_assert(client->wsp_connected == 1);
00448 
00449     /* Kannel can't handle it as class 1 yet, so send class 0 */
00450     pdu = wtp_invoke_create(0);
00451     set_tid(pdu, client->wtp_tid);
00452     add_wsp_disconnect(pdu, client->wsp_session_id);
00453 
00454     send_pdu(pdu, boxc, client);
00455     octstr_destroy(pdu);
00456 
00457     record_disconnect(client);
00458     client_done(client);
00459 }
00460 
00461 static void handle_connect_reply(Connection *boxc, Client *client, Octstr *pdu) {
00462     Octstr *ack;
00463 
00464     gw_assert(client);
00465     gw_assert(client->wtp_invoked);
00466     gw_assert(!client->wsp_connected);
00467 
00468     if (octstr_get_char(pdu, 3) != ConnectReply_PDU) {
00469         error(0, "Unexpected CONNECT reply");
00470         octstr_dump(pdu, 0);
00471         return;
00472     }
00473 
00474     ack = wtp_ack_create();
00475     set_tid(ack, client->wtp_tid);
00476     send_pdu(ack, boxc, client);
00477     octstr_destroy(ack);
00478 
00479     client->wtp_invoked = 0;
00480     increment_tid(client);
00481     client->wsp_connected = 1;
00482     client->wsp_session_id = get_varint(pdu, 4);
00483 
00484     send_invoke_get(boxc, client);
00485 }
00486 
00487 static void handle_get_reply(Connection *boxc, Client *client, Octstr *pdu) {
00488     Octstr *ack;
00489 
00490     gw_assert(client);
00491     gw_assert(client->wtp_invoked);
00492     gw_assert(client->wsp_connected);
00493 
00494     if (octstr_get_char(pdu, 3) != Reply_PDU) {
00495         error(0, "Unexpected GET reply");
00496         octstr_dump(pdu, 0);
00497         return;
00498     }
00499 
00500     ack = wtp_ack_create();
00501     set_tid(ack, client->wtp_tid);
00502     send_pdu(ack, boxc, client);
00503     octstr_destroy(ack);
00504 
00505     client->wtp_invoked = 0;
00506     increment_tid(client);
00507     client->pages_fetched++;
00508 
00509     if (client->pages_fetched == req_per_session) {
00510         send_invoke_disconnect(boxc, client);
00511     } else {
00512         client_done(client);
00513     }
00514 }
00515 
00516 static void handle_reply(Connection *boxc, Msg *reply) {
00517     Client *client;
00518     Octstr *wtp;
00519     int type;
00520     int dumped = 0;
00521 
00522     gw_assert(reply != NULL);
00523     gw_assert(reply->type == wdp_datagram);
00524 
00525     client = find_client(reply->wdp_datagram.destination_port);
00526     if (client == NULL)
00527         panic(0, "got packet for nonexisting client %ld",
00528             (long) reply->wdp_datagram.destination_port);
00529 
00530     wtp = reply->wdp_datagram.user_data;
00531     type = wtp_type(wtp);
00532 
00533     if (verbose_debug) {
00534         debug("test", 0, "Received:");
00535         octstr_dump(wtp, 0);
00536         dumped = 1;
00537     }
00538 
00539     if (client->wtp_invoked == 0) {
00540         error(0, "Got packet for client that wasn't waiting");
00541         if (!dumped) {
00542             octstr_dump(wtp, 0);
00543             dumped = 1;
00544         }
00545         return;
00546     }
00547 
00548     /* Server should invert the MSB of the tid in its replies */
00549     if (get_tid(wtp) != (client->wtp_tid ^ 0x8000)) {
00550         error(0, "Got packet with wrong tid %d, expected %d.",
00551             get_tid(wtp), client->wtp_tid ^ 0x8000);
00552         if (!dumped) {
00553             octstr_dump(wtp, 0);
00554             dumped = 1;
00555         }
00556         return;
00557     }
00558 
00559     /* We're going to be stupid here, and assume that replies that
00560      * look vaguely like what we expect are actually what we wanted. */
00561     if (client->wsp_connected == 0 && type == RESULT) {
00562         handle_connect_reply(boxc, client, wtp);
00563     } else if (client->wsp_connected == 1 && type == RESULT) {
00564         handle_get_reply(boxc, client, wtp);
00565     } else if (client->wsp_connected == 2 && type == ACK) {
00566         record_disconnect(client);
00567         client_done(client);
00568     } else {
00569         error(0, "Got unexpected packet");
00570         if (!dumped) {
00571             octstr_dump(wtp, 0);
00572             dumped = 1;
00573         }
00574     }
00575 }
00576 
00577 static void start_request(Connection *boxc, Client *client) {
00578     gw_assert(client != NULL);
00579     gw_assert(client->wsp_connected != 2);
00580     gw_assert(client->wtp_invoked == 0);
00581 
00582     if (client->wsp_connected == 0) {
00583         send_invoke_connect(boxc, client); 
00584     } else {
00585         send_invoke_get(boxc, client);
00586     }
00587 }
00588 
00589 static long run_requests(Connection *boxc) {
00590     int requests_sent;
00591     Octstr *data;
00592     Msg *msg;
00593     int ret;
00594 
00595     requests_sent = 0;
00596     requests_complete = 0;
00597 
00598     while (requests_complete < max_requests) {
00599         data = conn_read_withlen(boxc);
00600         if (!data) {
00601             Client *client;
00602 
00603             if (requests_sent < max_requests
00604                 && (client = gwlist_extract_first(ready_clients))) {
00605                 start_request(boxc, client);
00606                 requests_sent++;
00607             }
00608             ret = conn_wait(boxc, TIMEOUT);
00609             if (ret < 0 || conn_eof(boxc))
00610                 panic(0, "Wapbox dead.");
00611             if (ret == 1)
00612                 break; /* Timed out. */
00613         } else {
00614             msg = msg_unpack(data);
00615             if (!msg) {
00616                 octstr_dump(data, 0);
00617                 panic(0, "Received bad data from wapbox.");
00618             }
00619             if (msg->type == wdp_datagram)
00620                 handle_reply(boxc, msg);
00621             msg_destroy(msg);
00622         }
00623         octstr_destroy(data);
00624     }
00625 
00626     if (requests_complete < max_requests)
00627         info(0, "Timeout.  %ld requests unsatisfied.",
00628             max_requests - requests_complete);
00629 
00630     return requests_complete;
00631 }
00632 
00633 static void help(void) {
00634     info(0, "Usage: drive_wapbox [options...]\n");
00635     info(0, "  -r requests  Stop after this many; default 1.");
00636     info(0, "  -c clients   # of concurrent clients; default 1.");
00637     info(0, "  -w wapport   Port wapbox should connect to; default 30188");
00638     info(0, "  -u url       Use this url instead of internal http server");
00639     info(0, "  -g requests  Number of requests per WSP session; default 1");
00640     info(0, "  -U           Set the User ack flag on all WTP transactions");
00641 }
00642 
00643 int main(int argc, char **argv) {
00644     int opt;
00645     struct timeval start, end;
00646     Connection *boxc;
00647     long completed;
00648     double run_time;
00649 
00650     gwlib_init();
00651 
00652     while ((opt = getopt(argc, argv, "hv:r:c:w:du:Ug:")) != EOF) {
00653         switch (opt) {
00654         case 'v':
00655             log_set_output_level(atoi(optarg));
00656             break;
00657 
00658         case 'r':
00659             max_requests = atol(optarg);
00660             break;
00661 
00662         case 'c':
00663             max_clients = atol(optarg);
00664             break;
00665 
00666         case 'w':
00667             wapbox_port = atoi(optarg);
00668             break;
00669 
00670         case 'u':
00671             http_url = octstr_create(optarg);
00672             break;
00673 
00674         case 'U':
00675             user_ack = 1;
00676             break;
00677 
00678         case 'h':
00679             help();
00680             exit(0);
00681 
00682         case 'd':
00683             verbose_debug = 1;
00684             break;
00685 
00686         case 'g':
00687             req_per_session = atoi(optarg);
00688             break;
00689 
00690         case '?':
00691         default:
00692             error(0, "Invalid option %c", opt);
00693             help();
00694             panic(0, "Stopping.");
00695         }
00696     }
00697 
00698     if (!http_url)
00699         http_port = start_http_thread();
00700     boxc = start_wapbox();
00701 
00702     initialize_clients();
00703 
00704     if (gettimeofday(&start, NULL) < 0)
00705         panic(errno, "gettimeofday failed");
00706     completed = run_requests(boxc);
00707     if (gettimeofday(&end, NULL) < 0)
00708         panic(errno, "gettimeofday failed");
00709 
00710     conn_destroy(boxc);
00711 
00712     run_time = end.tv_sec - start.tv_sec;
00713     run_time += (double) (end.tv_usec - start.tv_usec) / 1000000.0;
00714 
00715     /* We must have timed out.  Don't count the waiting time. */
00716     if (completed < max_requests)
00717         run_time -= TIMEOUT;
00718 
00719     info(0, "%ld request%s in %0.1f seconds, %0.1f requests/s.",
00720         completed, completed != 1 ? "s" : "",
00721         run_time, max_requests / run_time);
00722 
00723     dying = 1;
00724     http_close_all_ports();
00725     if (!http_url)
00726         gwthread_join(http_thread_id);
00727 
00728     destroy_clients();
00729     octstr_destroy(http_url);
00730     
00731     gwlib_shutdown();
00732 
00733     return 0;
00734 }
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.