1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2014 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_xmlrpc.c: A simple program to test XML-RPC parsing
59  *
60  * Stipe Tolj <stolj@wapme.de>
61  */
62 
63 
64 #include <string.h>
65 #include <stdlib.h>
66 #include <unistd.h>
67 #include <stdio.h>
68 
69 #include "gwlib/gwlib.h"
70 #include "gwlib/http.h"
71 #include "gwlib/xmlrpc.h"
72 
73 #define MAX_THREADS 1024
74 #define MAX_IN_QUEUE 128
75 
76 static Counter *counter = NULL;
77 static long max_requests = 1;
78 /*static int verbose = 1;*/
79 static Octstr *auth_username = NULL;
80 static Octstr *auth_password = NULL;
81 static Octstr *ssl_client_certkey_file = NULL;
82 static Octstr *extra_headers = NULL;
83 static Octstr *content_file = NULL;
84 static Octstr *url = NULL;
85 static int file = 0;
86 static XMLRPCDocument *msg;
87 
88 
start_request(HTTPCaller * caller,List * reqh,long i)89 static void start_request(HTTPCaller *caller, List *reqh, long i)
90 {
91     long *id;
92 
93     if ((i % 1000) == 0)
94 	   info(0, "Starting fetch %ld", i);
95     id = gw_malloc(sizeof(long));
96     *id = i;
97 
98     /*
99      * not semd the XML-RPC document contained in msg to
100      * the URL 'url' using the POST method
101      */
102     xmlrpc_send_call(msg, caller, url, reqh, id);
103 
104     debug("", 0, "Started request %ld.", *id);
105     /*
106     debug("", 0, "Started request %ld with url:", *id);
107     octstr_url_decode(url);
108     octstr_dump(url, 0);
109     */
110 }
111 
112 
receive_reply(HTTPCaller * caller)113 static int receive_reply(HTTPCaller *caller)
114 {
115     void *id;
116     int ret;
117     Octstr *final_url;
118     List *replyh;
119     Octstr *replyb;
120     Octstr *output;
121     XMLRPCDocument *xrdoc;
122     /*
123     Octstr *type, *os_xrdoc, *os;
124     Octstr *charset;
125     */
126 
127     id = http_receive_result(caller, &ret, &final_url, &replyh, &replyb);
128     octstr_destroy(final_url);
129     if (id == NULL || ret == -1) {
130         error(0, "http POST failed");
131         gw_free(id);
132         return -1;
133     }
134     debug("", 0, "Done with request %ld", *(long *) id);
135     gw_free(id);
136 
137 /*
138     http_header_get_content_type(replyh, &type, &charset);
139     debug("", 0, "Content-type is <%s>, charset is <%s>",
140           octstr_get_cstr(type), octstr_get_cstr(charset));
141     octstr_destroy(type);
142     octstr_destroy(charset);
143     if (verbose)
144         debug("", 0, "Reply headers:");
145     while ((os = gwlist_extract_first(replyh)) != NULL) {
146         if (verbose)
147             octstr_dump(os, 1);
148         octstr_destroy(os);
149     }
150     if (verbose) {
151         debug("", 0, "Reply body:");
152         octstr_dump(replyb, 1);
153     }
154 */
155     xrdoc = xmlrpc_parse_response(replyb);
156     debug("", 0, "Parsed xmlrpc");
157 
158     if ((xmlrpc_parse_status(xrdoc) != XMLRPC_COMPILE_OK) &&
159         ((output = xmlrpc_parse_error(xrdoc)) != NULL)) {
160         /* parse failure */
161         error(0, "%s", octstr_get_cstr(output));
162         octstr_destroy(output);
163         return -1;
164     } else {
165         /*parse proper xmlrpc */
166         if (xmlrpc_is_fault(xrdoc)) {
167             Octstr *fstring = xmlrpc_get_faultstring(xrdoc);
168             debug("xr", 0, "Got fault response with code:%ld and description: %s",
169                            xmlrpc_get_faultcode(xrdoc),
170                            octstr_get_cstr(fstring));
171             octstr_destroy(fstring);
172             http_destroy_headers(replyh);
173             octstr_destroy(replyb);
174             return -1;
175         }
176         /*
177         os_xrdoc = xmlrpc_print_response(xrdoc);
178         debug("xr", 0, "XMLRPC response:");
179         octstr_dump(os_xrdoc, 0);
180         */
181     }
182     http_destroy_headers(replyh);
183     octstr_destroy(replyb);
184     return 0;
185 }
186 
187 
client_thread(void * arg)188 static void client_thread(void *arg)
189 {
190     List *reqh;
191     long i, succeeded, failed;
192     HTTPCaller *caller;
193     char buf[1024];
194     long in_queue;
195 
196     caller = arg;
197     succeeded = 0;
198     failed = 0;
199     reqh = gwlist_create();
200 
201     sprintf(buf, "%ld", (long) gwthread_self());
202     http_header_add(reqh, "X-Thread", buf);
203     if (auth_username != NULL && auth_password != NULL)
204         http_add_basic_auth(reqh, auth_username, auth_password);
205 
206     in_queue = 0;
207 
208     for (;;) {
209         while (in_queue < MAX_IN_QUEUE) {
210             i = counter_increase(counter);
211             if (i >= max_requests)
212                 goto receive_rest;
213             start_request(caller, reqh, i);
214 #if 1
215             gwthread_sleep(0.1);
216 #endif
217             ++in_queue;
218         }
219         while (in_queue >= MAX_IN_QUEUE) {
220             if (receive_reply(caller) == -1)
221                 ++failed;
222             else
223                 ++succeeded;
224             --in_queue;
225         }
226     }
227 
228 receive_rest:
229     while (in_queue > 0) {
230         if (receive_reply(caller) == -1)
231             ++failed;
232         else
233             ++succeeded;
234         --in_queue;
235     }
236 
237     http_caller_destroy(caller);
238     info(0, "This thread: %ld succeeded, %ld failed.", succeeded, failed);
239 }
240 
241 
help(void)242 static void help(void)
243 {
244     info(0, "Usage: test_xmlrpc [options] xml_source");
245     info(0, "where options are:");
246     info(0, "-u URL");
247     info(0, "    send XML-RPC source as POST HTTP request to URL");
248     info(0, "-v number");
249     info(0, "    set log level for stderr logging");
250     info(0, "-q");
251     info(0, "    don't print the body or headers of the HTTP response");
252     info(0, "-r number");
253     info(0, "    make `number' requests, repeating URLs as necessary");
254     info(0, "-p domain.name");
255     info(0, "    use `domain.name' as a proxy");
256     info(0, "-P portnumber");
257     info(0, "    connect to proxy at port `portnumber'");
258     info(0, "-S");
259     info(0, "    use HTTPS scheme to access SSL-enabled proxy server");
260     info(0, "-e domain1:domain2:...");
261     info(0, "    set exception list for proxy use");
262     info(0, "-s");
263     info(0, "    use HTTPS scheme to access SSL-enabled HTTP server");
264     info(0, "-c ssl_client_cert_key_file");
265     info(0, "    use this file as the SSL certificate and key file");
266 }
267 
268 
main(int argc,char ** argv)269 int main(int argc, char **argv)
270 {
271     int i, opt, num_threads;
272     Octstr *proxy;
273     List *exceptions;
274     long proxy_port;
275     int proxy_ssl = 0;
276     Octstr *proxy_username;
277     Octstr *proxy_password;
278     Octstr *exceptions_regex;
279     char *p;
280     long threads[MAX_THREADS];
281     time_t start, end;
282     double run_time;
283     Octstr *output, *xml_doc;
284     int ssl = 0;
285 
286     gwlib_init();
287 
288     proxy = NULL;
289     proxy_port = -1;
290     exceptions = gwlist_create();
291     proxy_username = NULL;
292     proxy_password = NULL;
293     exceptions_regex = NULL;
294     num_threads = 0;
295     file = 0;
296 
297     while ((opt = getopt(argc, argv, "hvr:t:p:u:P:Se:a:sc:")) != EOF) {
298         switch (opt) {
299             case 'h':
300                 help();
301                 exit(1);
302                 break;
303 
304             case 'v':
305                 log_set_output_level(atoi(optarg));
306                 break;
307 
308             case 'r':
309                 max_requests = atoi(optarg);
310                 break;
311 
312             case 't':
313                 num_threads = atoi(optarg);
314                 if (num_threads > MAX_THREADS)
315                     num_threads = MAX_THREADS;
316                 break;
317 
318             case 'p':
319                 proxy = octstr_create(optarg);
320                 break;
321 
322             case 'u':
323                 url = octstr_create(optarg);
324                 break;
325 
326             case 'P':
327                 proxy_port = atoi(optarg);
328                 break;
329 
330         	case 'S':
331                 proxy_ssl = 1;
332                 break;
333 
334             case 'e':
335                 p = strtok(optarg, ":");
336                 while (p != NULL) {
337                     gwlist_append(exceptions, octstr_create(p));
338                     p = strtok(NULL, ":");
339                 }
340                 break;
341 
342             case 'E':
343                 exceptions_regex = octstr_create(optarg);
344                 break;
345 
346             case 'a':
347                 p = strtok(optarg, ":");
348                 if (p != NULL) {
349                     auth_username = octstr_create(p);
350                     p = strtok(NULL, "");
351                     if (p != NULL)
352                         auth_password = octstr_create(p);
353                 }
354                 break;
355 
356             case 's':
357                 ssl = 1;
358                 break;
359 
360             case 'c':
361 	           octstr_destroy(ssl_client_certkey_file);
362 	           ssl_client_certkey_file = octstr_create(optarg);
363                 break;
364 
365             case '?':
366             default:
367                 error(0, "Invalid option %c", opt);
368                 help();
369                 panic(0, "Stopping");
370                 break;
371         }
372     }
373 
374     if (optind >= argc) {
375         error(0, "Missing arguments");
376         help();
377         panic(0, "Stopping");
378     }
379 
380 #ifdef HAVE_LIBSSL
381     /*
382      * check if we are doing a SSL-enabled client version here
383      * load the required cert and key file
384      */
385     if (ssl || proxy_ssl) {
386         if (ssl_client_certkey_file != NULL) {
387             use_global_client_certkey_file(ssl_client_certkey_file);
388         } else {
389             panic(0, "client certkey file need to be given!");
390         }
391     }
392 #endif
393 
394     if (proxy != NULL && proxy_port > 0) {
395         http_use_proxy(proxy, proxy_port, proxy_ssl, exceptions,
396                        proxy_username, proxy_password,
397                        exceptions_regex);
398     }
399     octstr_destroy(proxy);
400     octstr_destroy(proxy_username);
401     octstr_destroy(proxy_password);
402     octstr_destroy(exceptions_regex);
403     gwlist_destroy(exceptions, octstr_destroy_item);
404 
405     counter = counter_create();
406 
407     xml_doc = octstr_read_file(argv[optind]);
408     if (xml_doc == NULL)
409         panic(0, "Cannot read the XML document");
410 
411     /*
412      * parse the XML source
413      */
414     msg = xmlrpc_parse_call(xml_doc);
415 
416     if ((xmlrpc_parse_status(msg) != XMLRPC_COMPILE_OK) &&
417         ((output = xmlrpc_parse_error(msg)) != NULL)) {
418         /* parse failure */
419         error(0, "%s", octstr_get_cstr(output));
420         octstr_destroy(output);
421     }
422 
423     /*
424      * if no POST is desired then dump the re-formated XML
425      */
426     if (url != NULL) {
427 
428         time(&start);
429         if (num_threads == 0)
430 	       client_thread(http_caller_create());
431         else {
432             for (i = 0; i < num_threads; ++i)
433                 threads[i] = gwthread_create(client_thread, http_caller_create());
434             for (i = 0; i < num_threads; ++i)
435                 gwthread_join(threads[i]);
436         }
437         time(&end);
438 
439 
440         run_time = difftime(end, start);
441         info(0, "%ld requests in %f seconds, %f requests/s.",
442 	         max_requests, run_time, max_requests / run_time);
443 
444         octstr_destroy(url);
445 
446     } else {
447         output = xmlrpc_print_call(msg);
448         if (output != NULL) {
449             octstr_print(stderr, output);
450             octstr_destroy(output);
451         }
452     }
453 
454     counter_destroy(counter);
455     octstr_destroy(auth_username);
456     octstr_destroy(auth_password);
457     octstr_destroy(ssl_client_certkey_file);
458     octstr_destroy(extra_headers);
459     octstr_destroy(content_file);
460 
461 
462     xmlrpc_destroy_call(msg);
463     octstr_destroy(xml_doc);
464 
465     gwlib_shutdown();
466 
467     return 0;
468 }
469 
470