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-2016 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 
91 
93 {
94  Octstr *content;
95 
96  if ((content = octstr_read_file(octstr_get_cstr(content_file))) == NULL)
97  panic(0, "Cannot read content text");
98  debug("", 0, "body content is");
99  octstr_dump(content, 0);
100 
101  return content;
102 }
103 
104 static void start_request(HTTPCaller *caller, List *reqh, long i)
105 {
106  Octstr *url, *content = NULL;
107  long *id;
108 
109  if ((i % 1000) == 0)
110  info(0, "Starting fetch %ld", i);
111  id = gw_malloc(sizeof(long));
112  *id = i;
113  url = octstr_create(urls[i % num_urls]);
114  if (file) {
115  octstr_append(url, octstr_imm("&text="));
116  octstr_append(url, msg_text);
117  }
118 
119  /* add the extra headers that have been read from the file */
120  if (split != NULL)
121  http_header_combine(reqh, split);
122 
123  /*
124  * if a body content file has been specified, then
125  * we assume this should be a POST
126  */
127  if (content_file != NULL) {
128  content = post_content_create();
130  }
131 
132  /*
133  * if this is a POST request then pass the required content as body to
134  * the HTTP server, otherwise skip the body, the arguments will be
135  * urlencoded in the URL itself.
136  */
137  http_start_request(caller, method,
138  url, reqh, content, follow_redirect, id, ssl_client_certkey_file);
139 
140  debug("", 0, "Started request %ld with url:", *id);
141  octstr_url_decode(url);
142  octstr_dump(url, 0);
143  octstr_destroy(url);
144  octstr_destroy(msg_text);
145  octstr_destroy(content);
146 }
147 
148 
150 {
151  void *id;
152  int ret;
153  Octstr *final_url;
154  List *replyh;
155  Octstr *replyb;
156  Octstr *type;
157  Octstr *charset;
158  Octstr *os;
159 
160  id = http_receive_result(caller, &ret, &final_url, &replyh, &replyb);
161  octstr_destroy(final_url);
162  if (id == NULL || ret == -1) {
163  error(0, "http GET failed");
164  gw_free(id);
165  return -1;
166  }
167  debug("", 0, "Done with request %ld", *(long *) id);
168  gw_free(id);
169 
170  http_header_get_content_type(replyh, &type, &charset);
171  debug("", 0, "Content-type is <%s>, charset is <%s>",
172  octstr_get_cstr(type),
173  octstr_get_cstr(charset));
174  octstr_destroy(type);
175  octstr_destroy(charset);
176  if (verbose)
177  debug("", 0, "Reply headers:");
178  while ((os = gwlist_extract_first(replyh)) != NULL) {
179  if (verbose)
180  octstr_dump(os, 1);
181  octstr_destroy(os);
182  }
183  gwlist_destroy(replyh, NULL);
184  if (verbose) {
185  debug("", 0, "Reply body:");
186  octstr_dump(replyb, 1);
187  }
188  octstr_destroy(replyb);
189 
190  return 0;
191 }
192 
193 
194 static void client_thread(void *arg)
195 {
196  List *reqh;
197  unsigned long i;
198  long succeeded, failed;
200  char buf[1024];
201  long in_queue;
202  Counter *counter = NULL;
203 
204  caller = arg;
205  succeeded = 0;
206  failed = 0;
207  reqh = gwlist_create();
208  sprintf(buf, "%ld", (long) gwthread_self());
209  http_header_add(reqh, "X-Thread", buf);
210  if (auth_username != NULL && auth_password != NULL)
211  http_add_basic_auth(reqh, auth_username, auth_password);
212 
213  in_queue = 0;
214  counter = counter_create();
215 
216  for (;;) {
217  i = counter_increase(counter);
218  if (i >= max_requests)
219  goto receive_rest;
220  start_request(caller, reqh, i);
221  if (interval > 0)
223  ++in_queue;
224  if (receive_reply(caller) == -1)
225  ++failed;
226  else
227  ++succeeded;
228  --in_queue;
229  }
230 
231 receive_rest:
232  while (in_queue > 0) {
233  if (receive_reply(caller) == -1)
234  ++failed;
235  else
236  ++succeeded;
237  --in_queue;
238  }
239 
240  counter_destroy(counter);
241  http_destroy_headers(reqh);
242  http_caller_destroy(caller);
243  info(0, "This thread: %ld succeeded, %ld failed.", succeeded, failed);
244 }
245 
246 
247 static void split_headers(Octstr *headers, List **split)
248 {
249  long start;
250  long pos;
251 
252  *split = gwlist_create();
253  start = 0;
254  for (pos = 0; pos < octstr_len(headers); pos++) {
255  if (octstr_get_char(headers, pos) == '\n') {
256  Octstr *line;
257 
258  if (pos == start) {
259  /* Skip empty lines */
260  start = pos + 1;
261  continue;
262  }
263  line = octstr_copy(headers, start, pos - start);
264  start = pos + 1;
265  gwlist_append(*split, line);
266  }
267  }
268 }
269 
270 
271 static void help(void)
272 {
273  info(0, "Usage: test_http [options] url ...");
274  info(0, "where options are:");
275  info(0, "-v number");
276  info(0, " set log level for stderr logging");
277  info(0, "-q");
278  info(0, " don't print the body or headers of the HTTP response");
279  info(0, "-r number");
280  info(0, " make `number' requests, repeating URLs as necessary");
281  info(0, "-t number");
282  info(0, " run `number' threads, that make -r `number' requests");
283  info(0, "-i interval");
284  info(0, " make one request in `interval' seconds");
285  info(0, "-p domain.name");
286  info(0, " use `domain.name' as a proxy");
287  info(0, "-P portnumber");
288  info(0, " connect to proxy at port `portnumber'");
289  info(0, "-S");
290  info(0, " use HTTPS scheme to access SSL-enabled proxy server");
291  info(0, "-e domain1:domain2:...");
292  info(0, " set exception list for proxy use");
293  info(0, "-u filename");
294  info(0, " read request's &text= string from file 'filename'. It is");
295  info(0, " url encoded before it is added to the request");
296  info(0, "-H filename");
297  info(0, " read HTTP headers from file 'filename' and add them to");
298  info(0, " the request for url 'url'");
299  info(0, "-B filename");
300  info(0, " read content from file 'filename' and send it as body");
301  info(0, " of a POST method request (default: GET if no -B is set)");
302  info(0, "-m method");
303  info(0, " use a specific HTTP method for request to server");
304  info(0, "-s");
305  info(0, " use HTTPS scheme to access SSL-enabled HTTP server");
306  info(0, "-c ssl_client_cert_key_file");
307  info(0, " use this file as the SSL certificate and key file");
308  info(0, "-C ssl_ca_file");
309  info(0, " use this file as the SSL certificate authority");
310  info(0, "-f");
311  info(0, " don't follow redirects");
312 }
313 
314 int main(int argc, char **argv)
315 {
316  int i, opt, num_threads;
317  Octstr *proxy;
318  List *exceptions;
319  long proxy_port;
320  int proxy_ssl = 0;
323  Octstr *exceptions_regex;
324  char *p;
325  long threads[MAX_THREADS];
326  time_t start, end;
327  double run_time;
328  FILE *fp;
329  int ssl = 0;
330  Octstr *ca_file;
331 
332  gwlib_init();
333 
334  proxy = NULL;
335  proxy_port = -1;
336  exceptions = gwlist_create();
337  proxy_username = NULL;
338  proxy_password = NULL;
339  exceptions_regex = NULL;
340  num_threads = 1;
341  file = 0;
342  fp = NULL;
343 
344  while ((opt = getopt(argc, argv, "hv:qr:p:P:Se:t:i:a:u:sc:H:B:m:fC:")) != EOF) {
345  switch (opt) {
346  case 'v':
348  break;
349 
350  case 'q':
351  verbose = 0;
352  break;
353 
354  case 'r':
355  max_requests = atoi(optarg);
356  break;
357 
358  case 't':
359  num_threads = atoi(optarg);
360  if (num_threads > MAX_THREADS)
361  num_threads = MAX_THREADS;
362  break;
363 
364  case 'i':
365  interval = atof(optarg);
366  break;
367 
368  case 'u':
369  file = 1;
370  fp = fopen(optarg, "a");
371  if (fp == NULL)
372  panic(0, "Cannot open message text file %s", optarg);
373  msg_text = octstr_read_file(optarg);
374  if (msg_text == NULL)
375  panic(0, "Cannot read message text");
376  debug("", 0, "message text is");
377  octstr_dump(msg_text, 0);
378  octstr_url_encode(msg_text);
379  fclose(fp);
380  break;
381 
382  case 'h':
383  help();
384  exit(0);
385 
386  case 'p':
387  proxy = octstr_create(optarg);
388  break;
389 
390  case 'P':
391  proxy_port = atoi(optarg);
392  break;
393 
394  case 'S':
395  proxy_ssl = 1;
396  break;
397 
398  case 'e':
399  p = strtok(optarg, ":");
400  while (p != NULL) {
401  gwlist_append(exceptions, octstr_create(p));
402  p = strtok(NULL, ":");
403  }
404  break;
405 
406  case 'E':
407  exceptions_regex = octstr_create(optarg);
408  break;
409 
410  case 'a':
411  p = strtok(optarg, ":");
412  if (p != NULL) {
413  auth_username = octstr_create(p);
414  p = strtok(NULL, "");
415  if (p != NULL)
416  auth_password = octstr_create(p);
417  }
418  break;
419 
420  case 's':
421  ssl = 1;
422  break;
423 
424  case 'c':
425  octstr_destroy(ssl_client_certkey_file);
426  ssl_client_certkey_file = octstr_create(optarg);
427  break;
428 
429  case 'H':
430  fp = fopen(optarg, "a");
431  if (fp == NULL)
432  panic(0, "Cannot open header text file %s", optarg);
433  extra_headers = octstr_read_file(optarg);
434  if (extra_headers == NULL)
435  panic(0, "Cannot read header text");
436  debug("", 0, "headers are");
437  octstr_dump(extra_headers, 0);
438  split_headers(extra_headers, &split);
439  fclose(fp);
440  break;
441 
442  case 'B':
443  content_file = octstr_create(optarg);
444  break;
445 
446  case 'm':
447  method_name = octstr_create(optarg);
448  break;
449 
450  case 'f':
451  follow_redirect = 0;
452  break;
453 
454  case 'C':
455  ca_file = octstr_create(optarg);
456  conn_use_global_trusted_ca_file(ca_file);
457  octstr_destroy(ca_file);
458  break;
459 
460  case '?':
461  default:
462  error(0, "Invalid option %c", opt);
463  help();
464  panic(0, "Stopping.");
465  }
466  }
467 
468  if (optind == argc) {
469  help();
470  exit(0);
471  }
472 
473 #ifdef HAVE_LIBSSL
474  /*
475  * check if we are doing a SSL-enabled client version here
476  * load the required cert and key file
477  */
478  if (ssl || proxy_ssl) {
479  if (ssl_client_certkey_file != NULL) {
480  conn_use_global_client_certkey_file(ssl_client_certkey_file);
481  } else {
482  panic(0, "client certkey file need to be given!");
483  }
484  }
485 #endif
486 
487  if (method_name != NULL) {
488  method = http_name2method(method_name);
489  }
490 
491  if (proxy != NULL && proxy_port > 0) {
492  http_use_proxy(proxy, proxy_port, proxy_ssl, exceptions,
493  proxy_username, proxy_password, exceptions_regex);
494  }
495  octstr_destroy(proxy);
496  octstr_destroy(proxy_username);
497  octstr_destroy(proxy_password);
498  octstr_destroy(exceptions_regex);
499  gwlist_destroy(exceptions, octstr_destroy_item);
500 
501  urls = argv + optind;
502  num_urls = argc - optind;
503 
504  time(&start);
505  if (num_threads == 1)
507  else {
508  for (i = 0; i < num_threads; ++i)
510  for (i = 0; i < num_threads; ++i)
511  gwthread_join(threads[i]);
512  }
513  time(&end);
514 
515  run_time = difftime(end, start);
516  info(0, "%ld requests in %f seconds, %f requests/s.",
517  (max_requests * num_threads), run_time, (max_requests * num_threads) / run_time);
518 
519  octstr_destroy(ssl_client_certkey_file);
520  octstr_destroy(auth_username);
521  octstr_destroy(auth_password);
522  octstr_destroy(extra_headers);
523  octstr_destroy(content_file);
525 
526  gwlib_shutdown();
527 
528  return 0;
529 }
530 
void error(int err, const char *fmt,...)
Definition: log.c:612
void info(int err, const char *fmt,...)
Definition: log.c:636
long gwthread_self(void)
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
int ssl
void counter_destroy(Counter *counter)
Definition: counter.c:110
static void split_headers(Octstr *headers, List **split)
Definition: test_http.c:247
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:194
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
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
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:3633
static int proxy_ssl
Definition: http.c:202
#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:314
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:217
static Octstr * content_file
Definition: test_http.c:85
static void help(void)
Definition: test_http.c:271
static double interval
Definition: test_http.c:75
#define octstr_dump(ostr, level,...)
Definition: octstr.h:564
static int proxy_port
Definition: http.c:201
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:149
void gwthread_sleep(double seconds)
static int verbose
Definition: test_http.c:79
#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:690
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 gwlib_shutdown(void)
Definition: gwlib.c:94
#define gwlist_create()
Definition: list.h:136
static Octstr * post_content_create(void)
Definition: test_http.c:92
void http_caller_destroy(HTTPCaller *caller)
Definition: http.c:907
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:104
#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
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.