MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
regress_rpc.c
1 /*
2  * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #ifdef WIN32
29 #include <winsock2.h>
30 #include <windows.h>
31 #endif
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #ifdef HAVE_SYS_TIME_H
40 #include <sys/time.h>
41 #endif
42 #include <sys/queue.h>
43 #ifndef WIN32
44 #include <sys/socket.h>
45 #include <signal.h>
46 #include <unistd.h>
47 #include <netdb.h>
48 #endif
49 #include <fcntl.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <errno.h>
54 #include <assert.h>
55 
56 #include "event.h"
57 #include "evhttp.h"
58 #include "log.h"
59 #include "evrpc.h"
60 
61 #include "regress.gen.h"
62 
63 void rpc_suite(void);
64 
65 extern int test_ok;
66 
67 static struct evhttp *
68 http_setup(short *pport)
69 {
70  int i;
71  struct evhttp *myhttp;
72  short port = -1;
73 
74  /* Try a few different ports */
75  for (i = 0; i < 50; ++i) {
76  myhttp = evhttp_start("127.0.0.1", 8080 + i);
77  if (myhttp != NULL) {
78  port = 8080 + i;
79  break;
80  }
81  }
82 
83  if (port == -1)
84  event_errx(1, "Could not start web server");
85 
86  *pport = port;
87  return (myhttp);
88 }
89 
90 EVRPC_HEADER(Message, msg, kill);
91 EVRPC_HEADER(NeverReply, msg, kill);
92 
93 EVRPC_GENERATE(Message, msg, kill);
94 EVRPC_GENERATE(NeverReply, msg, kill);
95 
96 static int need_input_hook = 0;
97 static int need_output_hook = 0;
98 
99 static void
100 MessageCb(EVRPC_STRUCT(Message)* rpc, void *arg)
101 {
102  struct kill* kill_reply = rpc->reply;
103 
104  if (need_input_hook) {
105  struct evhttp_request* req = EVRPC_REQUEST_HTTP(rpc);
106  const char *header = evhttp_find_header(
107  req->input_headers, "X-Hook");
108  assert(strcmp(header, "input") == 0);
109  }
110 
111  /* we just want to fill in some non-sense */
112  EVTAG_ASSIGN(kill_reply, weapon, "dagger");
113  EVTAG_ASSIGN(kill_reply, action, "wave around like an idiot");
114 
115  /* no reply to the RPC */
116  EVRPC_REQUEST_DONE(rpc);
117 }
118 
119 static EVRPC_STRUCT(NeverReply) *saved_rpc;
120 
121 static void
122 NeverReplyCb(EVRPC_STRUCT(NeverReply)* rpc, void *arg)
123 {
124  test_ok += 1;
125  saved_rpc = rpc;
126 }
127 
128 static void
129 rpc_setup(struct evhttp **phttp, short *pport, struct evrpc_base **pbase)
130 {
131  short port;
132  struct evhttp *http = NULL;
133  struct evrpc_base *base = NULL;
134 
135  http = http_setup(&port);
136  base = evrpc_init(http);
137 
138  EVRPC_REGISTER(base, Message, msg, kill, MessageCb, NULL);
139  EVRPC_REGISTER(base, NeverReply, msg, kill, NeverReplyCb, NULL);
140 
141  *phttp = http;
142  *pport = port;
143  *pbase = base;
144 
145  need_input_hook = 0;
146  need_output_hook = 0;
147 }
148 
149 static void
150 rpc_teardown(struct evrpc_base *base)
151 {
152  assert(EVRPC_UNREGISTER(base, Message) == 0);
153  assert(EVRPC_UNREGISTER(base, NeverReply) == 0);
154 
155  evrpc_free(base);
156 }
157 
158 static void
159 rpc_postrequest_failure(struct evhttp_request *req, void *arg)
160 {
161  if (req->response_code != HTTP_SERVUNAVAIL) {
162 
163  fprintf(stderr, "FAILED (response code)\n");
164  exit(1);
165  }
166 
167  test_ok = 1;
168  event_loopexit(NULL);
169 }
170 
171 /*
172  * Test a malformed payload submitted as an RPC
173  */
174 
175 static void
176 rpc_basic_test(void)
177 {
178  short port;
179  struct evhttp *http = NULL;
180  struct evrpc_base *base = NULL;
181  struct evhttp_connection *evcon = NULL;
182  struct evhttp_request *req = NULL;
183 
184  fprintf(stdout, "Testing Basic RPC Support: ");
185 
186  rpc_setup(&http, &port, &base);
187 
188  evcon = evhttp_connection_new("127.0.0.1", port);
189  if (evcon == NULL) {
190  fprintf(stdout, "FAILED\n");
191  exit(1);
192  }
193 
194  /*
195  * At this point, we want to schedule an HTTP POST request
196  * server using our make request method.
197  */
198 
199  req = evhttp_request_new(rpc_postrequest_failure, NULL);
200  if (req == NULL) {
201  fprintf(stdout, "FAILED\n");
202  exit(1);
203  }
204 
205  /* Add the information that we care about */
206  evhttp_add_header(req->output_headers, "Host", "somehost");
207  evbuffer_add_printf(req->output_buffer, "Some Nonsense");
208 
209  if (evhttp_make_request(evcon, req,
210  EVHTTP_REQ_POST,
211  "/.rpc.Message") == -1) {
212  fprintf(stdout, "FAILED\n");
213  exit(1);
214  }
215 
216  test_ok = 0;
217 
218  event_dispatch();
219 
220  evhttp_connection_free(evcon);
221 
222  rpc_teardown(base);
223 
224  if (test_ok != 1) {
225  fprintf(stdout, "FAILED\n");
226  exit(1);
227  }
228 
229  fprintf(stdout, "OK\n");
230 
231  evhttp_free(http);
232 }
233 
234 static void
235 rpc_postrequest_done(struct evhttp_request *req, void *arg)
236 {
237  struct kill* kill_reply = NULL;
238 
239  if (req->response_code != HTTP_OK) {
240 
241  fprintf(stderr, "FAILED (response code)\n");
242  exit(1);
243  }
244 
245  kill_reply = kill_new();
246 
247  if ((kill_unmarshal(kill_reply, req->input_buffer)) == -1) {
248  fprintf(stderr, "FAILED (unmarshal)\n");
249  exit(1);
250  }
251 
252  kill_free(kill_reply);
253 
254  test_ok = 1;
255  event_loopexit(NULL);
256 }
257 
258 static void
259 rpc_basic_message(void)
260 {
261  short port;
262  struct evhttp *http = NULL;
263  struct evrpc_base *base = NULL;
264  struct evhttp_connection *evcon = NULL;
265  struct evhttp_request *req = NULL;
266  struct msg *msg;
267 
268  fprintf(stdout, "Testing Good RPC Post: ");
269 
270  rpc_setup(&http, &port, &base);
271 
272  evcon = evhttp_connection_new("127.0.0.1", port);
273  if (evcon == NULL) {
274  fprintf(stdout, "FAILED\n");
275  exit(1);
276  }
277 
278  /*
279  * At this point, we want to schedule an HTTP POST request
280  * server using our make request method.
281  */
282 
283  req = evhttp_request_new(rpc_postrequest_done, NULL);
284  if (req == NULL) {
285  fprintf(stdout, "FAILED\n");
286  exit(1);
287  }
288 
289  /* Add the information that we care about */
290  evhttp_add_header(req->output_headers, "Host", "somehost");
291 
292  /* set up the basic message */
293  msg = msg_new();
294  EVTAG_ASSIGN(msg, from_name, "niels");
295  EVTAG_ASSIGN(msg, to_name, "tester");
296  msg_marshal(req->output_buffer, msg);
297  msg_free(msg);
298 
299  if (evhttp_make_request(evcon, req,
300  EVHTTP_REQ_POST,
301  "/.rpc.Message") == -1) {
302  fprintf(stdout, "FAILED\n");
303  exit(1);
304  }
305 
306  test_ok = 0;
307 
308  event_dispatch();
309 
310  evhttp_connection_free(evcon);
311 
312  rpc_teardown(base);
313 
314  if (test_ok != 1) {
315  fprintf(stdout, "FAILED\n");
316  exit(1);
317  }
318 
319  fprintf(stdout, "OK\n");
320 
321  evhttp_free(http);
322 }
323 
324 static struct evrpc_pool *
325 rpc_pool_with_connection(short port)
326 {
327  struct evhttp_connection *evcon;
328  struct evrpc_pool *pool;
329 
330  pool = evrpc_pool_new(NULL);
331  assert(pool != NULL);
332 
333  evcon = evhttp_connection_new("127.0.0.1", port);
334  assert(evcon != NULL);
335 
336  evrpc_pool_add_connection(pool, evcon);
337 
338  return (pool);
339 }
340 
341 static void
342 GotKillCb(struct evrpc_status *status,
343  struct msg *msg, struct kill *kill, void *arg)
344 {
345  char *weapon;
346  char *action;
347 
348  if (need_output_hook) {
349  struct evhttp_request *req = status->http_req;
350  const char *header = evhttp_find_header(
351  req->input_headers, "X-Pool-Hook");
352  assert(strcmp(header, "ran") == 0);
353  }
354 
355  if (status->error != EVRPC_STATUS_ERR_NONE)
356  goto done;
357 
358  if (EVTAG_GET(kill, weapon, &weapon) == -1) {
359  fprintf(stderr, "get weapon\n");
360  goto done;
361  }
362  if (EVTAG_GET(kill, action, &action) == -1) {
363  fprintf(stderr, "get action\n");
364  goto done;
365  }
366 
367  if (strcmp(weapon, "dagger"))
368  goto done;
369 
370  if (strcmp(action, "wave around like an idiot"))
371  goto done;
372 
373  test_ok += 1;
374 
375 done:
376  event_loopexit(NULL);
377 }
378 
379 static void
380 GotKillCbTwo(struct evrpc_status *status,
381  struct msg *msg, struct kill *kill, void *arg)
382 {
383  char *weapon;
384  char *action;
385 
386  if (status->error != EVRPC_STATUS_ERR_NONE)
387  goto done;
388 
389  if (EVTAG_GET(kill, weapon, &weapon) == -1) {
390  fprintf(stderr, "get weapon\n");
391  goto done;
392  }
393  if (EVTAG_GET(kill, action, &action) == -1) {
394  fprintf(stderr, "get action\n");
395  goto done;
396  }
397 
398  if (strcmp(weapon, "dagger"))
399  goto done;
400 
401  if (strcmp(action, "wave around like an idiot"))
402  goto done;
403 
404  test_ok += 1;
405 
406 done:
407  if (test_ok == 2)
408  event_loopexit(NULL);
409 }
410 
411 static int
412 rpc_hook_add_header(struct evhttp_request *req,
413  struct evbuffer *evbuf, void *arg)
414 {
415  const char *hook_type = arg;
416  if (strcmp("input", hook_type) == 0)
417  evhttp_add_header(req->input_headers, "X-Hook", hook_type);
418  else
419  evhttp_add_header(req->output_headers, "X-Hook", hook_type);
420  return (0);
421 }
422 
423 static int
424 rpc_hook_remove_header(struct evhttp_request *req,
425  struct evbuffer *evbuf, void *arg)
426 {
427  const char *header = evhttp_find_header(req->input_headers, "X-Hook");
428  assert(header != NULL);
429  assert(strcmp(header, arg) == 0);
430  evhttp_remove_header(req->input_headers, "X-Hook");
431  evhttp_add_header(req->input_headers, "X-Pool-Hook", "ran");
432 
433  return (0);
434 }
435 
436 static void
437 rpc_basic_client(void)
438 {
439  short port;
440  struct evhttp *http = NULL;
441  struct evrpc_base *base = NULL;
442  struct evrpc_pool *pool = NULL;
443  struct msg *msg;
444  struct kill *kill;
445 
446  fprintf(stdout, "Testing RPC Client: ");
447 
448  rpc_setup(&http, &port, &base);
449 
450  need_input_hook = 1;
451  need_output_hook = 1;
452 
453  assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_add_header, (void*)"input")
454  != NULL);
455  assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_add_header, (void*)"output")
456  != NULL);
457 
458  pool = rpc_pool_with_connection(port);
459 
460  assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_remove_header, (void*)"output"));
461 
462  /* set up the basic message */
463  msg = msg_new();
464  EVTAG_ASSIGN(msg, from_name, "niels");
465  EVTAG_ASSIGN(msg, to_name, "tester");
466 
467  kill = kill_new();
468 
469  EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL);
470 
471  test_ok = 0;
472 
473  event_dispatch();
474 
475  if (test_ok != 1) {
476  fprintf(stdout, "FAILED (1)\n");
477  exit(1);
478  }
479 
480  /* we do it twice to make sure that reuse works correctly */
481  kill_clear(kill);
482 
483  EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL);
484 
485  event_dispatch();
486 
487  rpc_teardown(base);
488 
489  if (test_ok != 2) {
490  fprintf(stdout, "FAILED (2)\n");
491  exit(1);
492  }
493 
494  fprintf(stdout, "OK\n");
495 
496  msg_free(msg);
497  kill_free(kill);
498 
499  evrpc_pool_free(pool);
500  evhttp_free(http);
501 }
502 
503 /*
504  * We are testing that the second requests gets send over the same
505  * connection after the first RPCs completes.
506  */
507 static void
508 rpc_basic_queued_client(void)
509 {
510  short port;
511  struct evhttp *http = NULL;
512  struct evrpc_base *base = NULL;
513  struct evrpc_pool *pool = NULL;
514  struct msg *msg;
515  struct kill *kill_one, *kill_two;
516 
517  fprintf(stdout, "Testing RPC (Queued) Client: ");
518 
519  rpc_setup(&http, &port, &base);
520 
521  pool = rpc_pool_with_connection(port);
522 
523  /* set up the basic message */
524  msg = msg_new();
525  EVTAG_ASSIGN(msg, from_name, "niels");
526  EVTAG_ASSIGN(msg, to_name, "tester");
527 
528  kill_one = kill_new();
529  kill_two = kill_new();
530 
531  EVRPC_MAKE_REQUEST(Message, pool, msg, kill_one, GotKillCbTwo, NULL);
532  EVRPC_MAKE_REQUEST(Message, pool, msg, kill_two, GotKillCb, NULL);
533 
534  test_ok = 0;
535 
536  event_dispatch();
537 
538  rpc_teardown(base);
539 
540  if (test_ok != 2) {
541  fprintf(stdout, "FAILED (1)\n");
542  exit(1);
543  }
544 
545  fprintf(stdout, "OK\n");
546 
547  msg_free(msg);
548  kill_free(kill_one);
549  kill_free(kill_two);
550 
551  evrpc_pool_free(pool);
552  evhttp_free(http);
553 }
554 
555 static void
556 GotErrorCb(struct evrpc_status *status,
557  struct msg *msg, struct kill *kill, void *arg)
558 {
559  if (status->error != EVRPC_STATUS_ERR_TIMEOUT)
560  goto done;
561 
562  /* should never be complete but just to check */
563  if (kill_complete(kill) == 0)
564  goto done;
565 
566  test_ok += 1;
567 
568 done:
569  event_loopexit(NULL);
570 }
571 
572 static void
573 rpc_client_timeout(void)
574 {
575  short port;
576  struct evhttp *http = NULL;
577  struct evrpc_base *base = NULL;
578  struct evrpc_pool *pool = NULL;
579  struct msg *msg;
580  struct kill *kill;
581 
582  fprintf(stdout, "Testing RPC Client Timeout: ");
583 
584  rpc_setup(&http, &port, &base);
585 
586  pool = rpc_pool_with_connection(port);
587 
588  /* set the timeout to 5 seconds */
589  evrpc_pool_set_timeout(pool, 5);
590 
591  /* set up the basic message */
592  msg = msg_new();
593  EVTAG_ASSIGN(msg, from_name, "niels");
594  EVTAG_ASSIGN(msg, to_name, "tester");
595 
596  kill = kill_new();
597 
598  EVRPC_MAKE_REQUEST(NeverReply, pool, msg, kill, GotErrorCb, NULL);
599 
600  test_ok = 0;
601 
602  event_dispatch();
603 
604  /* free the saved RPC structure up */
605  EVRPC_REQUEST_DONE(saved_rpc);
606 
607  rpc_teardown(base);
608 
609  if (test_ok != 2) {
610  fprintf(stdout, "FAILED (1)\n");
611  exit(1);
612  }
613 
614  fprintf(stdout, "OK\n");
615 
616  msg_free(msg);
617  kill_free(kill);
618 
619  evrpc_pool_free(pool);
620  evhttp_free(http);
621 }
622 
623 void
624 rpc_suite(void)
625 {
626  rpc_basic_test();
627  rpc_basic_message();
628  rpc_basic_client();
629  rpc_basic_queued_client();
630  rpc_client_timeout();
631 }