Kannel: Open Source WAP and SMS gateway  $Revision: 5037 $
test_http.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2018 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Kannel Group (http://www.kannel.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  * endorse or promote products derived from this software without
29  * prior written permission. For written permission, please
30  * contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  * nor may "Kannel" appear in their name, without prior written
34  * permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group. For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * test_http.c - a simple program to test the new http library
59  *
60  * Lars Wirzenius
61  */
62 
63 #include <string.h>
64 #include <stdlib.h>
65 #include <unistd.h>
66 #include <stdio.h>
67 
68 #include "gwlib/gwlib.h"
69 #include "gwlib/http.h"
70 
71 #define MAX_THREADS 1024
72 #define MAX_IN_QUEUE 128
73 
74 static long max_requests = 1;
75 static double interval = 0;
76 static int method = HTTP_METHOD_GET;
77 static char **urls = NULL;
78 static int num_urls = 0;
79 static int verbose = 1;
80 static Octstr *auth_username = NULL;
81 static Octstr *auth_password = NULL;
82 static Octstr *msg_text = NULL;
84 static Octstr *extra_headers = NULL;
85 static Octstr *content_file = NULL; /* if set use POST method */
86 static Octstr *method_name = NULL;
87 static int file = 0;
88 static List *split = NULL;
89 static int follow_redirect = 1;
90 static int escape_codes = 0;
91 
92 
94 {
95  Octstr *content;
96 
97  if ((content = octstr_read_file(octstr_get_cstr(content_file))) == NULL)
98  panic(0, "Cannot read content text");
99  debug("", 0, "body content is");
100  octstr_dump(content, 0);
101 
102  return content;
103 }
104 
105 
106 static void url_pattern(Octstr *url)
107 {
108  Octstr *temp;
109  const char *pattern;
110  size_t n;
111 
112  temp = octstr_duplicate(url);
113  octstr_truncate(url, 0);
114 
115  pattern = octstr_get_cstr(temp);
116 
117  while (*pattern != '\0') {
118  n = strcspn(pattern, "%");
119  octstr_append_data(url, pattern, n);
120  pattern += n;
121  gw_assert(*pattern == '%' || *pattern == '\0');
122  if (*pattern == '\0')
123  break;
124 
125  pattern++;
126 
127  switch (*pattern) {
128 
129  case 'r':
130  octstr_format_append(url, "%ld", gw_rand());
131  break;
132 
133  case 'I':
134  {
135  uuid_t uid;
136  char id[UUID_STR_LEN + 1];
137  uuid_generate(uid);
138  uuid_unparse(uid, id);
139  octstr_append_cstr(url, id);
140  }
141  break;
142 
143  /* XXX add more here if needed */
144 
145  case '%':
146  octstr_format_append(url, "%%");
147  break;
148 
149  default:
150  warning(0, "Unknown escape code (%%%c) within URL, skipping!", *pattern);
151  octstr_format_append(url, "%%%c", *pattern);
152  break;
153  } /* switch(...) */
154 
155  pattern++;
156  } /* while ... */
157 
158  octstr_destroy(temp);
159 }
160 
161 static void start_request(HTTPCaller *caller, List *reqh, long i)
162 {
163  Octstr *url, *content = NULL;
164  long *id;
165 
166  if ((i % 1000) == 0)
167  info(0, "Starting fetch %ld", i);
168  id = gw_malloc(sizeof(long));
169  *id = i;
170  url = octstr_create(urls[i % num_urls]);
171  if (file) {
172  octstr_append(url, octstr_imm("&text="));
173  octstr_append(url, msg_text);
174  }
175 
176  /* add the extra headers that have been read from the file */
177  if (split != NULL)
178  http_header_combine(reqh, split);
179 
180  /*
181  * if a body content file has been specified, then
182  * we assume this should be a POST
183  */
184  if (content_file != NULL) {
185  content = post_content_create();
187  }
188 
189  /* apply any escape codes */
190  if (escape_codes)
191  url_pattern(url);
192 
193  /*
194  * if this is a POST request then pass the required content as body to
195  * the HTTP server, otherwise skip the body, the arguments will be
196  * urlencoded in the URL itself.
197  */
198  http_start_request(caller, method,
199  url, reqh, content, follow_redirect, id, ssl_client_certkey_file);
200 
201  debug("", 0, "Started request %ld with url:", *id);
202  octstr_url_decode(url);
203  octstr_dump(url, 0);
204  octstr_destroy(url);
205  octstr_destroy(msg_text);
206  octstr_destroy(content);
207 }
208 
209 
211 {
212  void *id;
213  int ret;
214  Octstr *final_url;
215  List *replyh;
216  Octstr *replyb;
217  Octstr *type;
218  Octstr *charset;
219  Octstr *os;
220 
221  id = http_receive_result(caller, &ret, &final_url, &replyh, &replyb);
222  octstr_destroy(final_url);
223  if (id == NULL || ret == -1) {
224  error(0, "http GET failed");
225  gw_free(id);
226  return -1;
227  }
228  debug("", 0, "Done with request %ld", *(long *) id);
229  gw_free(id);
230 
231  http_header_get_content_type(replyh, &type, &charset);
232  debug("", 0, "Content-type is <%s>, charset is <%s>",
233  octstr_get_cstr(type),
234  octstr_get_cstr(charset));
235  octstr_destroy(type);
236  octstr_destroy(charset);
237  if (verbose)
238  debug("", 0, "Reply headers:");
239  while ((os = gwlist_extract_first(replyh)) != NULL) {
240  if (verbose)
241  octstr_dump(os, 1);
242  octstr_destroy(os);
243  }
244  gwlist_destroy(replyh, NULL);
245  if (verbose) {
246  debug("", 0, "Reply body:");
247  octstr_dump(replyb, 1);
248  }
249  octstr_destroy(replyb);
250 
251  return 0;
252 }
253 
254 
255 static void client_thread(void *arg)
256 {
257  List *reqh;
258  unsigned long i;
259  long succeeded, failed;
261  char buf[1024];
262  long in_queue;
263  Counter *counter = NULL;
264 
265  caller = arg;
266  succeeded = 0;
267  failed = 0;
268  reqh = gwlist_create();
269  sprintf(buf, "%ld", (long) gwthread_self());
270  http_header_add(reqh, "X-Thread", buf);
271  if (auth_username != NULL && auth_password != NULL)
272  http_add_basic_auth(reqh, auth_username, auth_password);
273 
274  in_queue = 0;
275  counter = counter_create();
276 
277  for (;;) {
278  i = counter_increase(counter);
279  if (i >= max_requests)
280  goto receive_rest;
281  start_request(caller, reqh, i);
282  if (interval > 0)
284  ++in_queue;
285  if (receive_reply(caller) == -1)
286  ++failed;
287  else
288  ++succeeded;
289  --in_queue;
290  }
291 
292 receive_rest:
293  while (in_queue > 0) {
294  if (receive_reply(caller) == -1)
295  ++failed;
296  else
297  ++succeeded;
298  --in_queue;
299  }
300 
301  counter_destroy(counter);
302  http_destroy_headers(reqh);
303  http_caller_destroy(caller);
304  info(0, "This thread: %ld succeeded, %ld failed.", succeeded, failed);
305 }
306 
307 
308 static void split_headers(Octstr *headers, List **split)
309 {
310  long start;
311  long pos;
312 
313  *split = gwlist_create();
314  start = 0;
315  for (pos = 0; pos < octstr_len(headers); pos++) {
316  if (octstr_get_char(headers, pos) == '\n') {
317  Octstr *line;
318 
319  if (pos == start) {
320  /* Skip empty lines */
321  start = pos + 1;
322  continue;
323  }
324  line = octstr_copy(headers, start, pos - start);
325  start = pos + 1;
326  gwlist_append(*split, line);
327  }
328  }
329 }
330 
331 
332 static void help(void)
333 {
334  info(0, "Usage: test_http [options] url ...");
335  info(0, "where options are:");
336  info(0, "-v number");
337  info(0, " set log level for stderr logging");
338  info(0, "-q");
339  info(0, " don't print the body or headers of the HTTP response");
340  info(0, "-r number");
341  info(0, " make `number' requests, repeating URLs as necessary");
342  info(0, "-t number");
343  info(0, " run `number' threads, that make -r `number' requests");
344  info(0, "-i interval");
345  info(0, " make one request in `interval' seconds");
346  info(0, "-p domain.name");
347  info(0, " use `domain.name' as a proxy");
348  info(0, "-P portnumber");
349  info(0, " connect to proxy at port `portnumber'");
350  info(0, "-E regex");
351  info(0, " proxy exceptions as regex value");
352  info(0, "-S");
353  info(0, " use HTTPS scheme to access SSL-enabled proxy server");
354  info(0, "-e domain1:domain2:...");
355  info(0, " set exception list for proxy use");
356  info(0, "-u filename");
357  info(0, " read request's &text= string from file 'filename'. It is");
358  info(0, " url encoded before it is added to the request");
359  info(0, "-H filename");
360  info(0, " read HTTP headers from file 'filename' and add them to");
361  info(0, " the request for url 'url'");
362  info(0, "-B filename");
363  info(0, " read content from file 'filename' and send it as body");
364  info(0, " of a POST method request (default: GET if no -B is set)");
365  info(0, "-m method");
366  info(0, " use a specific HTTP method for request to server");
367  info(0, "-s");
368  info(0, " use HTTPS scheme to access SSL-enabled HTTP server");
369  info(0, "-c ssl_client_cert_key_file");
370  info(0, " use this file as the SSL certificate and key file");
371  info(0, "-C ssl_ca_file");
372  info(0, " use this file as the SSL certificate authority");
373  info(0, "-f");
374  info(0, " don't follow redirects");
375  info(0, "-V");
376  info(0, " evaluate for URL escape code patterns (%r - random number,");
377  info(0, " %I - UUID string)");
378 }
379 
380 int main(int argc, char **argv)
381 {
382  int i, opt, num_threads;
383  Octstr *proxy;
384  List *exceptions;
385  long proxy_port;
386  int proxy_ssl = 0;
389  Octstr *exceptions_regex;
390  char *p;
391  long threads[MAX_THREADS];
392  time_t start, end;
393  double run_time;
394  FILE *fp;
395  int ssl = 0;
396  Octstr *ca_file;
397 
398  gwlib_init();
399 
400  proxy = NULL;
401  proxy_port = -1;
402  exceptions = gwlist_create();
403  proxy_username = NULL;
404  proxy_password = NULL;
405  exceptions_regex = NULL;
406  num_threads = 1;
407  file = 0;
408  fp = NULL;
409 
410  while ((opt = getopt(argc, argv, "hv:qr:p:P:Se:t:i:a:u:sc:H:B:m:fVC:")) != EOF) {
411  switch (opt) {
412  case 'v':
414  break;
415 
416  case 'q':
417  verbose = 0;
418  break;
419 
420  case 'r':
421  max_requests = atoi(optarg);
422  break;
423 
424  case 't':
425  num_threads = atoi(optarg);
426  if (num_threads > MAX_THREADS)
427  num_threads = MAX_THREADS;
428  break;
429 
430  case 'i':
431  interval = atof(optarg);
432  break;
433 
434  case 'u':
435  file = 1;
436  fp = fopen(optarg, "a");
437  if (fp == NULL)
438  panic(0, "Cannot open message text file %s", optarg);
439  msg_text = octstr_read_file(optarg);
440  if (msg_text == NULL)
441  panic(0, "Cannot read message text");
442  debug("", 0, "message text is");
443  octstr_dump(msg_text, 0);
444  octstr_url_encode(msg_text);
445  fclose(fp);
446  break;
447 
448  case 'h':
449  help();
450  exit(0);
451 
452  case 'p':
453  proxy = octstr_create(optarg);
454  break;
455 
456  case 'P':
457  proxy_port = atoi(optarg);
458  break;
459 
460  case 'S':
461  proxy_ssl = 1;
462  break;
463 
464  case 'e':
465  p = strtok(optarg, ":");
466  while (p != NULL) {
467  gwlist_append(exceptions, octstr_create(p));
468  p = strtok(NULL, ":");
469  }
470  break;
471 
472  case 'E':
473  exceptions_regex = octstr_create(optarg);
474  break;
475 
476  case 'a':
477  p = strtok(optarg, ":");
478  if (p != NULL) {
479  auth_username = octstr_create(p);
480  p = strtok(NULL, "");
481  if (p != NULL)
482  auth_password = octstr_create(p);
483  }
484  break;
485 
486  case 's':
487  ssl = 1;
488  break;
489 
490  case 'c':
491  octstr_destroy(ssl_client_certkey_file);
492  ssl_client_certkey_file = octstr_create(optarg);
493  break;
494 
495  case 'H':
496  fp = fopen(optarg, "a");
497  if (fp == NULL)
498  panic(0, "Cannot open header text file %s", optarg);
499  extra_headers = octstr_read_file(optarg);
500  if (extra_headers == NULL)
501  panic(0, "Cannot read header text");
502  debug("", 0, "headers are");
503  octstr_dump(extra_headers, 0);
504  split_headers(extra_headers, &split);
505  fclose(fp);
506  break;
507 
508  case 'B':
509  content_file = octstr_create(optarg);
510  break;
511 
512  case 'm':
513  method_name = octstr_create(optarg);
514  break;
515 
516  case 'f':
517  follow_redirect = 0;
518  break;
519 
520  case 'V':
521  escape_codes = 1;
522  break;
523 
524 #ifdef HAVE_LIBSSL
525  case 'C':
526  ca_file = octstr_create(optarg);
527  conn_use_global_trusted_ca_file(ca_file);
528  octstr_destroy(ca_file);
529  break;
530 #endif
531 
532  case '?':
533  default:
534  error(0, "Invalid option %c", opt);
535  help();
536  panic(0, "Stopping.");
537  }
538  }
539 
540  if (optind == argc) {
541  help();
542  exit(0);
543  }
544 
545 #ifdef HAVE_LIBSSL
546  /*
547  * check if we are doing a SSL-enabled client version here
548  * load the required cert and key file
549  */
550  if (ssl || proxy_ssl) {
551  if (ssl_client_certkey_file != NULL) {
552  conn_use_global_client_certkey_file(ssl_client_certkey_file);
553  } else {
554  panic(0, "client certkey file need to be given!");
555  }
556  }
557 #endif
558 
559  if (method_name != NULL) {
560  method = http_name2method(method_name);
561  }
562 
563  if (proxy != NULL && proxy_port > 0) {
564  http_use_proxy(proxy, proxy_port, proxy_ssl, exceptions,
565  proxy_username, proxy_password, exceptions_regex);
566  }
567  octstr_destroy(proxy);
568  octstr_destroy(proxy_username);
569  octstr_destroy(proxy_password);
570  octstr_destroy(exceptions_regex);
571  gwlist_destroy(exceptions, octstr_destroy_item);
572 
573  urls = argv + optind;
574  num_urls = argc - optind;
575 
576  time(&start);
577  if (num_threads == 1)
579  else {
580  for (i = 0; i < num_threads; ++i)
582  for (i = 0; i < num_threads; ++i)
583  gwthread_join(threads[i]);
584  }
585  time(&end);
586 
587  run_time = difftime(end, start);
588  info(0, "%ld requests in %f seconds, %f requests/s.",
589  (max_requests * num_threads), run_time, (max_requests * num_threads) / run_time);
590 
591  octstr_destroy(ssl_client_certkey_file);
592  octstr_destroy(auth_username);
593  octstr_destroy(auth_password);
594  octstr_destroy(extra_headers);
595  octstr_destroy(content_file);
597 
598  gwlib_shutdown();
599 
600  return 0;
601 }
602 
void error(int err, const char *fmt,...)
Definition: log.c:648
void info(int err, const char *fmt,...)
Definition: log.c:672
long gwthread_self(void)
void octstr_append_data(Octstr *ostr, const char *data, long len)
Definition: octstr.c:1495
void http_header_add(List *headers, char *name, char *contents)
Definition: http.c:2863
int threads
Definition: fakewap.c:239
static Octstr * method_name
Definition: test_http.c:86
gw_assert(wtls_machine->packet_to_send !=NULL)
int ssl
void counter_destroy(Counter *counter)
Definition: counter.c:110
static void split_headers(Octstr *headers, List **split)
Definition: test_http.c:308
void gwlist_append(List *list, void *item)
Definition: list.c:179
void octstr_append(Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:1502
static Octstr * auth_password
Definition: test_http.c:81
void gwthread_join(long thread)
static HTTPCaller * caller
Definition: smsbox.c:429
static void client_thread(void *arg)
Definition: test_http.c:255
void http_header_combine(List *old_headers, List *new_headers)
Definition: http.c:3045
static Octstr * extra_headers
Definition: test_http.c:84
int octstr_url_decode(Octstr *ostr)
Definition: octstr.c:1744
static int escape_codes
Definition: test_http.c:90
void http_add_basic_auth(List *headers, Octstr *username, Octstr *password)
Definition: http.c:3492
int type
Definition: smsc_cimd2.c:215
int optind
Definition: attgetopt.c:80
void uuid_unparse(const uuid_t uu, char *out)
Definition: gw_uuid.c:562
void uuid_generate(uuid_t out)
Definition: gw_uuid.c:393
static Octstr * msg_text
Definition: test_http.c:82
void http_header_get_content_type(List *headers, Octstr **type, Octstr **charset)
Definition: http.c:3202
int http_name2method(Octstr *method)
Definition: http.c:3631
void octstr_append_cstr(Octstr *ostr, const char *cstr)
Definition: octstr.c:1509
static int proxy_ssl
Definition: http.c:202
static void url_pattern(Octstr *url)
Definition: test_http.c:106
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
#define octstr_copy(ostr, from, len)
Definition: octstr.h:178
unsigned long counter_increase(Counter *counter)
Definition: counter.c:123
Octstr * charset
Definition: test_ota.c:68
int main(int argc, char **argv)
Definition: test_http.c:380
void http_destroy_headers(List *headers)
Definition: http.c:2856
static Octstr * ssl_client_certkey_file
Definition: test_http.c:83
static int follow_redirect
Definition: test_http.c:89
int getopt(int argc, char **argv, char *opts)
Definition: attgetopt.c:84
void http_start_request(HTTPCaller *caller, int method, Octstr *url, List *headers, Octstr *body, int follow, void *id, Octstr *certkeyfile)
Definition: http.c:1745
Octstr * octstr_imm(const char *cstr)
Definition: octstr.c:281
Counter * counter_create(void)
Definition: counter.c:94
static Octstr * auth_username
Definition: test_http.c:80
void * gwlist_extract_first(List *list)
Definition: list.c:305
void log_set_output_level(enum output_level level)
Definition: log.c:253
static Octstr * content_file
Definition: test_http.c:85
static void help(void)
Definition: test_http.c:332
static double interval
Definition: test_http.c:75
#define octstr_duplicate(ostr)
Definition: octstr.h:187
#define octstr_dump(ostr, level,...)
Definition: octstr.h:564
static int proxy_port
Definition: http.c:201
void warning(int err, const char *fmt,...)
Definition: log.c:660
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:322
static Octstr * proxy_username
Definition: http.c:203
#define gwthread_create(func, arg)
Definition: gwthread.h:90
#define octstr_create(cstr)
Definition: octstr.h:125
static int method
Definition: test_http.c:76
void octstr_destroy_item(void *os)
Definition: octstr.c:334
static int receive_reply(HTTPCaller *caller)
Definition: test_http.c:210
void gwthread_sleep(double seconds)
static int verbose
Definition: test_http.c:79
#define UUID_STR_LEN
Definition: gw_uuid.h:19
#define http_receive_result(caller, status, final_url, headers, body)
Definition: http.h:383
Octstr * octstr_read_file(const char *filename)
Definition: octstr.c:1546
long octstr_len(const Octstr *ostr)
Definition: octstr.c:340
static int num_urls
Definition: test_http.c:78
Definition: octstr.c:118
void http_use_proxy(Octstr *hostname, int port, int ssl, List *exceptions, Octstr *username, Octstr *password, Octstr *exceptions_regex)
Definition: http.c:268
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:726
char * optarg
Definition: attgetopt.c:82
#define panic
Definition: log.h:87
static List * split
Definition: test_http.c:88
HTTPCaller * http_caller_create(void)
Definition: http.c:897
void octstr_format_append(Octstr *os, const char *fmt,...)
Definition: octstr.c:2505
void gwlib_shutdown(void)
Definition: gwlib.c:94
#define gwlist_create()
Definition: list.h:136
static Octstr * post_content_create(void)
Definition: test_http.c:93
void octstr_truncate(Octstr *ostr, int new_len)
Definition: octstr.c:1325
void http_caller_destroy(HTTPCaller *caller)
Definition: http.c:907
unsigned char uuid_t[16]
Definition: gw_uuid.h:32
void gwlib_init(void)
Definition: gwlib.c:78
static int file
Definition: test_http.c:87
static Octstr * content
Definition: mtbatch.c:87
static void start_request(HTTPCaller *caller, List *reqh, long i)
Definition: test_http.c:161
#define MAX_THREADS
Definition: test_http.c:71
static Octstr * url
Definition: test_xmlrpc.c:84
int octstr_get_char(const Octstr *ostr, long pos)
Definition: octstr.c:404
static char ** urls
Definition: test_http.c:77
static long max_requests
Definition: test_http.c:74
int gw_rand(void)
Definition: protected.c:174
Definition: list.c:102
static int start
static Counter * counter
void octstr_url_encode(Octstr *ostr)
Definition: octstr.c:1671
static Octstr * proxy_password
Definition: http.c:204
void gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)
Definition: list.c:145
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.