1 /* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "str.h"
5 #include "str-sanitize.h"
6 #include "hostpid.h"
7 #include "ioloop.h"
8 #include "istream.h"
9 #include "istream-chain.h"
10 #include "ostream.h"
11 #include "time-util.h"
12 #include "sleep.h"
13 #include "connection.h"
14 #include "test-common.h"
15 #include "test-subprocess.h"
16 #include "http-url.h"
17 #include "http-request.h"
18 #include "http-client.h"
19
20 #include <unistd.h>
21 #include <sys/signal.h>
22
23 #define CLIENT_PROGRESS_TIMEOUT 10
24 #define SERVER_KILL_TIMEOUT_SECS 20
25
26 static void main_deinit(void);
27
28 /*
29 * Types
30 */
31
32 struct server_connection {
33 struct connection conn;
34 void *context;
35
36 pool_t pool;
37 bool version_sent:1;
38 };
39
40 typedef void (*test_server_init_t)(unsigned int index);
41 typedef bool
42 (*test_client_init_t)(const struct http_client_settings *client_set);
43 typedef void (*test_dns_init_t)(void);
44
45 /*
46 * State
47 */
48
49 /* common */
50 static struct ip_addr bind_ip;
51 static in_port_t *bind_ports = 0;
52 static struct ioloop *ioloop;
53 static bool debug = FALSE;
54
55 /* server */
56 static struct io *io_listen;
57 static int fd_listen = -1;
58 static struct connection_list *server_conn_list;
59 static size_t server_read_max = 0;
60 static unsigned int server_index;
61 static int (*test_server_init)(struct server_connection *conn);
62 static void (*test_server_deinit)(struct server_connection *conn);
63 static void (*test_server_input)(struct server_connection *conn);
64
65 /* client */
66 static struct timeout *to_client_progress = NULL;
67 static struct http_client *http_client = NULL;
68
69 /*
70 * Forward declarations
71 */
72
73 /* server */
74 static void test_server_run(unsigned int index);
75 static void server_connection_deinit(struct server_connection **_conn);
76
77 /* client */
78 static void test_client_defaults(struct http_client_settings *http_set);
79 static void test_client_deinit(void);
80
81 /* test*/
82 static void
83 test_run_client_server(const struct http_client_settings *client_set,
84 test_client_init_t client_test,
85 test_server_init_t server_test,
86 unsigned int server_tests_count,
87 test_dns_init_t dns_test) ATTR_NULL(3);
88
89 /*
90 * Utility
91 */
92
93 static void
test_client_assert_response(const struct http_response * resp,bool condition)94 test_client_assert_response(const struct http_response *resp,
95 bool condition)
96 {
97 const char *reason = (resp->reason != NULL ? resp->reason : "<NULL>");
98
99 test_assert(resp->reason != NULL && *resp->reason != '\0');
100
101 if (!condition)
102 i_error("BAD RESPONSE: %u %s", resp->status, reason);
103 else if (debug)
104 i_debug("RESPONSE: %u %s", resp->status, resp->reason);
105 }
106
107 /*
108 * Unconfigured SSL
109 */
110
111 /* client */
112
113 struct _unconfigured_ssl {
114 unsigned int count;
115 };
116
117 static void
test_client_unconfigured_ssl_response(const struct http_response * resp,struct _unconfigured_ssl * ctx)118 test_client_unconfigured_ssl_response(const struct http_response *resp,
119 struct _unconfigured_ssl *ctx)
120 {
121 test_client_assert_response(
122 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED);
123
124 if (--ctx->count == 0) {
125 i_free(ctx);
126 io_loop_stop(ioloop);
127 }
128 }
129
130 static bool
test_client_unconfigured_ssl(const struct http_client_settings * client_set)131 test_client_unconfigured_ssl(const struct http_client_settings *client_set)
132 {
133 struct http_client_request *hreq;
134 struct _unconfigured_ssl *ctx;
135
136 ctx = i_new(struct _unconfigured_ssl, 1);
137 ctx->count = 2;
138
139 http_client = http_client_init(client_set);
140
141 hreq = http_client_request(
142 http_client, "GET", "127.0.0.1", "/unconfigured-ssl.txt",
143 test_client_unconfigured_ssl_response, ctx);
144 http_client_request_set_ssl(hreq, TRUE);
145 http_client_request_submit(hreq);
146
147 hreq = http_client_request(
148 http_client, "GET", "127.0.0.1", "/unconfigured-ssl2.txt",
149 test_client_unconfigured_ssl_response, ctx);
150 http_client_request_set_ssl(hreq, TRUE);
151 http_client_request_submit(hreq);
152
153 return TRUE;
154 }
155
156 /* test */
157
test_unconfigured_ssl(void)158 static void test_unconfigured_ssl(void)
159 {
160 struct http_client_settings http_client_set;
161
162 test_client_defaults(&http_client_set);
163
164 test_begin("unconfigured ssl");
165 test_run_client_server(&http_client_set,
166 test_client_unconfigured_ssl, NULL, 0, NULL);
167 test_end();
168 }
169
170 /*
171 * Unconfigured SSL abort
172 */
173
174 /* client */
175
176 struct _unconfigured_ssl_abort {
177 unsigned int count;
178 };
179
180 static void
test_client_unconfigured_ssl_abort_response1(const struct http_response * resp,struct _unconfigured_ssl_abort * ctx ATTR_UNUSED)181 test_client_unconfigured_ssl_abort_response1(
182 const struct http_response *resp,
183 struct _unconfigured_ssl_abort *ctx ATTR_UNUSED)
184 {
185 if (debug)
186 i_debug("RESPONSE: %u %s", resp->status, resp->reason);
187
188 test_out_quiet("inappropriate callback", FALSE);
189 }
190
191 static void
test_client_unconfigured_ssl_abort_response2(const struct http_response * resp,struct _unconfigured_ssl_abort * ctx)192 test_client_unconfigured_ssl_abort_response2(
193 const struct http_response *resp, struct _unconfigured_ssl_abort *ctx)
194 {
195 test_client_assert_response(
196 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED);
197
198 i_free(ctx);
199 io_loop_stop(ioloop);
200 }
201
202 static bool
test_client_unconfigured_ssl_abort(const struct http_client_settings * client_set)203 test_client_unconfigured_ssl_abort(
204 const struct http_client_settings *client_set)
205 {
206 struct http_client_request *hreq;
207 struct _unconfigured_ssl_abort *ctx;
208
209 ctx = i_new(struct _unconfigured_ssl_abort, 1);
210 ctx->count = 1;
211
212 http_client = http_client_init(client_set);
213
214 hreq = http_client_request(
215 http_client, "GET", "127.0.0.1", "/unconfigured-ssl.txt",
216 test_client_unconfigured_ssl_abort_response1, ctx);
217 http_client_request_set_ssl(hreq, TRUE);
218 http_client_request_submit(hreq);
219 http_client_request_abort(&hreq);
220
221 hreq = http_client_request(
222 http_client, "GET", "127.0.0.1", "/unconfigured-ssl2.txt",
223 test_client_unconfigured_ssl_abort_response2, ctx);
224 http_client_request_set_ssl(hreq, TRUE);
225 http_client_request_submit(hreq);
226
227 return TRUE;
228 }
229
230 /* test */
231
test_unconfigured_ssl_abort(void)232 static void test_unconfigured_ssl_abort(void)
233 {
234 struct http_client_settings http_client_set;
235
236 test_client_defaults(&http_client_set);
237
238 test_begin("unconfigured ssl abort");
239 test_run_client_server(&http_client_set,
240 test_client_unconfigured_ssl_abort,
241 NULL, 0, NULL);
242 test_end();
243 }
244
245 /*
246 * Invalid URL
247 */
248
249 /* client */
250
251 struct _invalid_url {
252 unsigned int count;
253 };
254
255 static void
test_client_invalid_url_response(const struct http_response * resp,struct _invalid_url * ctx)256 test_client_invalid_url_response(const struct http_response *resp,
257 struct _invalid_url *ctx)
258 {
259 test_client_assert_response(
260 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_INVALID_URL);
261
262 if (--ctx->count == 0) {
263 i_free(ctx);
264 io_loop_stop(ioloop);
265 }
266 }
267
268 static bool
test_client_invalid_url(const struct http_client_settings * client_set)269 test_client_invalid_url(const struct http_client_settings *client_set)
270 {
271 struct http_client_request *hreq;
272 struct _invalid_url *ctx;
273
274 ctx = i_new(struct _invalid_url, 1);
275 ctx->count = 2;
276
277 http_client = http_client_init(client_set);
278
279 hreq = http_client_request_url_str(
280 http_client, "GET", "imap://example.com/INBOX",
281 test_client_invalid_url_response, ctx);
282 http_client_request_submit(hreq);
283
284 hreq = http_client_request_url_str(
285 http_client, "GET", "http:/www.example.com",
286 test_client_invalid_url_response, ctx);
287 http_client_request_submit(hreq);
288
289 return TRUE;
290 }
291
292 /* test */
293
test_invalid_url(void)294 static void test_invalid_url(void)
295 {
296 struct http_client_settings http_client_set;
297
298 test_client_defaults(&http_client_set);
299
300 test_begin("invalid url");
301 test_run_client_server(&http_client_set,
302 test_client_invalid_url, NULL, 0, NULL);
303 test_end();
304 }
305
306 /*
307 * Host lookup failed
308 */
309
310 /* client */
311
312 struct _host_lookup_failed {
313 unsigned int count;
314 };
315
316 static void
test_client_host_lookup_failed_response(const struct http_response * resp,struct _host_lookup_failed * ctx)317 test_client_host_lookup_failed_response(const struct http_response *resp,
318 struct _host_lookup_failed *ctx)
319 {
320 test_client_assert_response(
321 resp,
322 resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED);
323
324 if (--ctx->count == 0) {
325 i_free(ctx);
326 io_loop_stop(ioloop);
327 }
328 }
329
330 static bool
test_client_host_lookup_failed(const struct http_client_settings * client_set)331 test_client_host_lookup_failed(const struct http_client_settings *client_set)
332 {
333 struct http_client_request *hreq;
334 struct _host_lookup_failed *ctx;
335
336 ctx = i_new(struct _host_lookup_failed, 1);
337 ctx->count = 2;
338
339 http_client = http_client_init(client_set);
340
341 hreq = http_client_request(
342 http_client, "GET", "host.in-addr.arpa",
343 "/host-lookup-failed.txt",
344 test_client_host_lookup_failed_response, ctx);
345 http_client_request_submit(hreq);
346
347 hreq = http_client_request(
348 http_client, "GET", "host.in-addr.arpa",
349 "/host-lookup-failed2.txt",
350 test_client_host_lookup_failed_response, ctx);
351 http_client_request_submit(hreq);
352
353 return TRUE;
354 }
355
356 /* test */
357
test_host_lookup_failed(void)358 static void test_host_lookup_failed(void)
359 {
360 struct http_client_settings http_client_set;
361
362 test_client_defaults(&http_client_set);
363
364 test_begin("host lookup failed");
365 test_run_client_server(&http_client_set,
366 test_client_host_lookup_failed, NULL, 0, NULL);
367 test_end();
368 }
369
370 /*
371 * Connection refused
372 */
373
374 /* server */
375
376 static void
test_server_connection_refused(unsigned int index ATTR_UNUSED)377 test_server_connection_refused(unsigned int index ATTR_UNUSED)
378 {
379 i_close_fd(&fd_listen);
380
381 test_subprocess_notify_signal_send_parent(SIGUSR1);
382 }
383
384 /* client */
385
386 struct _connection_refused {
387 unsigned int count;
388 struct timeout *to;
389 };
390
391 static void
test_client_connection_refused_response(const struct http_response * resp,struct _connection_refused * ctx)392 test_client_connection_refused_response(const struct http_response *resp,
393 struct _connection_refused *ctx)
394 {
395 test_assert(ctx->to == NULL);
396 timeout_remove(&ctx->to);
397
398 test_client_assert_response(
399 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED);
400
401 if (--ctx->count == 0) {
402 i_free(ctx);
403 io_loop_stop(ioloop);
404 }
405 }
406
407 static void
test_client_connection_refused_timeout(struct _connection_refused * ctx)408 test_client_connection_refused_timeout(struct _connection_refused *ctx)
409 {
410 if (debug)
411 i_debug("TIMEOUT (ok)");
412 timeout_remove(&ctx->to);
413 }
414
415 static bool
test_client_connection_refused(const struct http_client_settings * client_set)416 test_client_connection_refused(const struct http_client_settings *client_set)
417 {
418 struct http_client_request *hreq;
419 struct _connection_refused *ctx;
420
421 /* wait for the server side to close the socket */
422 test_subprocess_notify_signal_wait(SIGUSR1, 10000);
423
424 ctx = i_new(struct _connection_refused, 1);
425 ctx->count = 2;
426
427 if (client_set->max_connect_attempts > 0) {
428 ctx->to = timeout_add_short(250,
429 test_client_connection_refused_timeout, ctx);
430 }
431
432 http_client = http_client_init(client_set);
433
434 hreq = http_client_request(
435 http_client, "GET", net_ip2addr(&bind_ip),
436 "/connection-refused.txt",
437 test_client_connection_refused_response, ctx);
438 http_client_request_set_port(hreq, bind_ports[0]);
439 http_client_request_submit(hreq);
440
441 hreq = http_client_request(
442 http_client, "GET", net_ip2addr(&bind_ip),
443 "/connection-refused2.txt",
444 test_client_connection_refused_response, ctx);
445 http_client_request_set_port(hreq, bind_ports[0]);
446 http_client_request_submit(hreq);
447
448 return TRUE;
449 }
450
451 /* test */
452
test_connection_refused(void)453 static void test_connection_refused(void)
454 {
455 struct http_client_settings http_client_set;
456
457 test_client_defaults(&http_client_set);
458
459 test_begin("connection refused");
460 test_subprocess_notify_signal_reset(SIGUSR1);
461 test_run_client_server(&http_client_set,
462 test_client_connection_refused,
463 test_server_connection_refused, 1, NULL);
464 test_end();
465
466 http_client_set.max_connect_attempts = 3;
467
468 test_begin("connection refused backoff");
469 test_subprocess_notify_signal_reset(SIGUSR1);
470 test_run_client_server(&http_client_set,
471 test_client_connection_refused,
472 test_server_connection_refused, 1, NULL);
473 test_end();
474 }
475
476 /*
477 * Connection lost prematurely
478 */
479
480 /* server */
481
482 static void
test_server_connection_lost_prematurely_input(struct server_connection * conn)483 test_server_connection_lost_prematurely_input(struct server_connection *conn)
484 {
485 server_connection_deinit(&conn);
486 }
487
test_server_connection_lost_prematurely(unsigned int index)488 static void test_server_connection_lost_prematurely(unsigned int index)
489 {
490 test_server_input = test_server_connection_lost_prematurely_input;
491 test_server_run(index);
492 }
493
494 /* client */
495
496 struct _connection_lost_prematurely {
497 unsigned int count;
498 struct timeout *to;
499 };
500
501 static void
test_client_connection_lost_prematurely_response(const struct http_response * resp,struct _connection_lost_prematurely * ctx)502 test_client_connection_lost_prematurely_response(
503 const struct http_response *resp,
504 struct _connection_lost_prematurely *ctx)
505 {
506 test_assert(ctx->to == NULL);
507 timeout_remove(&ctx->to);
508
509 test_client_assert_response(
510 resp,
511 resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST);
512
513 if (--ctx->count == 0) {
514 i_free(ctx);
515 io_loop_stop(ioloop);
516 }
517 }
518
519 static void
test_client_connection_lost_prematurely_timeout(struct _connection_lost_prematurely * ctx)520 test_client_connection_lost_prematurely_timeout(
521 struct _connection_lost_prematurely *ctx)
522 {
523 if (debug)
524 i_debug("TIMEOUT (ok)");
525 timeout_remove(&ctx->to);
526 }
527
528 static bool
test_client_connection_lost_prematurely(const struct http_client_settings * client_set)529 test_client_connection_lost_prematurely(
530 const struct http_client_settings *client_set)
531 {
532 struct http_client_request *hreq;
533 struct _connection_lost_prematurely *ctx;
534
535 ctx = i_new(struct _connection_lost_prematurely, 1);
536 ctx->count = 2;
537
538 ctx->to = timeout_add_short(
539 250, test_client_connection_lost_prematurely_timeout, ctx);
540
541 http_client = http_client_init(client_set);
542
543 hreq = http_client_request(
544 http_client, "GET", net_ip2addr(&bind_ip),
545 "/connection-refused-retry.txt",
546 test_client_connection_lost_prematurely_response, ctx);
547 http_client_request_set_port(hreq, bind_ports[0]);
548 http_client_request_submit(hreq);
549
550 hreq = http_client_request(
551 http_client, "GET", net_ip2addr(&bind_ip),
552 "/connection-refused-retry2.txt",
553 test_client_connection_lost_prematurely_response, ctx);
554 http_client_request_set_port(hreq, bind_ports[0]);
555 http_client_request_submit(hreq);
556
557 return TRUE;
558 }
559
560 /* test */
561
test_connection_lost_prematurely(void)562 static void test_connection_lost_prematurely(void)
563 {
564 struct http_client_settings http_client_set;
565
566 test_client_defaults(&http_client_set);
567 http_client_set.max_connect_attempts = 3;
568 http_client_set.max_attempts = 3;
569
570 test_begin("connection lost prematurely");
571 test_run_client_server(&http_client_set,
572 test_client_connection_lost_prematurely,
573 test_server_connection_lost_prematurely, 1,
574 NULL);
575 test_end();
576 }
577
578 /*
579 * Connection timed out
580 */
581
582 /* client */
583
584 struct _connection_timed_out {
585 unsigned int count;
586 };
587
588 static void
test_client_connection_timed_out_response(const struct http_response * resp,struct _connection_timed_out * ctx)589 test_client_connection_timed_out_response(const struct http_response *resp,
590 struct _connection_timed_out *ctx)
591 {
592 test_client_assert_response(
593 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED);
594
595 if (--ctx->count == 0) {
596 i_free(ctx);
597 io_loop_stop(ioloop);
598 }
599 }
600
601 static bool
test_client_connection_timed_out(const struct http_client_settings * client_set)602 test_client_connection_timed_out(const struct http_client_settings *client_set)
603 {
604 struct http_client_request *hreq;
605 struct _connection_timed_out *ctx;
606
607 ctx = i_new(struct _connection_timed_out, 1);
608 ctx->count = 2;
609
610 http_client = http_client_init(client_set);
611
612 hreq = http_client_request(
613 http_client, "GET", "192.168.0.0", "/connection-timed-out.txt",
614 test_client_connection_timed_out_response, ctx);
615 http_client_request_submit(hreq);
616
617 hreq = http_client_request(
618 http_client, "GET", "192.168.0.0", "/connection-timed-out2.txt",
619 test_client_connection_timed_out_response, ctx);
620 http_client_request_submit(hreq);
621
622 return TRUE;
623 }
624
625 /* test */
626
test_connection_timed_out(void)627 static void test_connection_timed_out(void)
628 {
629 struct http_client_settings http_client_set;
630
631 test_client_defaults(&http_client_set);
632 http_client_set.connect_timeout_msecs = 1000;
633 http_client_set.max_attempts = 1;
634
635 test_begin("connection timed out");
636 test_run_client_server(&http_client_set,
637 test_client_connection_timed_out, NULL, 0, NULL);
638 test_end();
639 }
640
641 /*
642 * Invalid redirect
643 */
644
645 /* server */
646
647 /* -> not accepted */
648
test_invalid_redirect_input1(struct server_connection * conn)649 static void test_invalid_redirect_input1(struct server_connection *conn)
650 {
651 static const char *resp =
652 "HTTP/1.1 302 Redirect\r\n"
653 "Location: http://localhost:4444\r\n"
654 "\r\n";
655
656 o_stream_nsend_str(conn->conn.output, resp);
657 server_connection_deinit(&conn);
658 }
659
test_server_invalid_redirect1(unsigned int index)660 static void test_server_invalid_redirect1(unsigned int index)
661 {
662 test_server_input = test_invalid_redirect_input1;
663 test_server_run(index);
664 }
665
666 /* -> bad location */
667
test_invalid_redirect_input2(struct server_connection * conn)668 static void test_invalid_redirect_input2(struct server_connection *conn)
669 {
670 static const char *resp =
671 "HTTP/1.1 302 Redirect\r\n"
672 "Location: unix:/var/run/dovecot/auth-master\r\n"
673 "\r\n";
674
675 o_stream_nsend_str(conn->conn.output, resp);
676 server_connection_deinit(&conn);
677 }
678
test_server_invalid_redirect2(unsigned int index)679 static void test_server_invalid_redirect2(unsigned int index)
680 {
681 test_server_input = test_invalid_redirect_input2;
682 test_server_run(index);
683 }
684
685 /* -> too many */
686
test_invalid_redirect_input3(struct server_connection * conn)687 static void test_invalid_redirect_input3(struct server_connection *conn)
688 {
689 string_t *resp;
690
691 resp = t_str_new(512);
692 str_printfa(resp,
693 "HTTP/1.1 302 Redirect\r\n"
694 "Location: http://%s:%u/friep.txt\r\n"
695 "\r\n",
696 net_ip2addr(&bind_ip), bind_ports[server_index+1]);
697 o_stream_nsend(conn->conn.output, str_data(resp), str_len(resp));
698 server_connection_deinit(&conn);
699 }
700
test_server_invalid_redirect3(unsigned int index)701 static void test_server_invalid_redirect3(unsigned int index)
702 {
703 test_server_input = test_invalid_redirect_input3;
704 test_server_run(index);
705 }
706
707 /* client */
708
709 static void
test_client_invalid_redirect_response(const struct http_response * resp,void * context ATTR_UNUSED)710 test_client_invalid_redirect_response(const struct http_response *resp,
711 void *context ATTR_UNUSED)
712 {
713 test_client_assert_response(
714 resp,
715 resp->status == HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT);
716
717 io_loop_stop(ioloop);
718 }
719
720 static bool
test_client_invalid_redirect(const struct http_client_settings * client_set)721 test_client_invalid_redirect(const struct http_client_settings *client_set)
722 {
723 struct http_client_request *hreq;
724
725 http_client = http_client_init(client_set);
726
727 hreq = http_client_request(
728 http_client, "GET", net_ip2addr(&bind_ip),
729 "/invalid-redirect.txt",
730 test_client_invalid_redirect_response, NULL);
731 http_client_request_set_port(hreq, bind_ports[0]);
732 http_client_request_submit(hreq);
733
734 return TRUE;
735 }
736
737 /* test */
738
test_invalid_redirect(void)739 static void test_invalid_redirect(void)
740 {
741 struct http_client_settings http_client_set;
742
743 test_client_defaults(&http_client_set);
744
745 test_begin("invalid redirect: not accepted");
746 http_client_set.max_redirects = 0;
747 test_run_client_server(&http_client_set,
748 test_client_invalid_redirect,
749 test_server_invalid_redirect1, 1, NULL);
750 test_end();
751
752 test_begin("invalid redirect: bad location");
753 http_client_set.max_redirects = 1;
754 test_run_client_server(&http_client_set,
755 test_client_invalid_redirect,
756 test_server_invalid_redirect2, 1, NULL);
757 test_end();
758
759 test_begin("invalid redirect: too many");
760 http_client_set.max_redirects = 1;
761 test_run_client_server(&http_client_set,
762 test_client_invalid_redirect,
763 test_server_invalid_redirect3, 3, NULL);
764 test_end();
765 }
766
767 /*
768 * Unseekable redirect
769 */
770
771 /* server */
772
test_unseekable_redirect_input(struct server_connection * conn)773 static void test_unseekable_redirect_input(struct server_connection *conn)
774 {
775 string_t *resp;
776
777 resp = t_str_new(512);
778 str_printfa(resp,
779 "HTTP/1.1 302 Redirect\r\n"
780 "Location: http://%s:%u/frml.txt\r\n"
781 "\r\n",
782 net_ip2addr(&bind_ip), bind_ports[server_index+1]);
783 o_stream_nsend(conn->conn.output, str_data(resp), str_len(resp));
784 server_connection_deinit(&conn);
785 }
786
test_server_unseekable_redirect(unsigned int index)787 static void test_server_unseekable_redirect(unsigned int index)
788 {
789 test_server_input = test_unseekable_redirect_input;
790 test_server_run(index);
791 }
792
793 /* client */
794
795 static void
test_client_unseekable_redirect_response(const struct http_response * resp,void * context ATTR_UNUSED)796 test_client_unseekable_redirect_response(const struct http_response *resp,
797 void *context ATTR_UNUSED)
798 {
799 test_client_assert_response(
800 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_ABORTED);
801
802 io_loop_stop(ioloop);
803 }
804
805 static bool
test_client_unseekable_redirect(const struct http_client_settings * client_set)806 test_client_unseekable_redirect(const struct http_client_settings *client_set)
807 {
808 struct http_client_request *hreq;
809 struct istream *input;
810
811 http_client = http_client_init(client_set);
812
813 input = i_stream_create_from_data("FROP", 4);
814 input->seekable = FALSE;
815
816 hreq = http_client_request(
817 http_client, "GET", net_ip2addr(&bind_ip),
818 "/unseekable-redirect.txt",
819 test_client_unseekable_redirect_response, NULL);
820 http_client_request_set_port(hreq, bind_ports[0]);
821 http_client_request_set_payload(hreq, input, FALSE);
822 http_client_request_submit(hreq);
823
824 i_stream_unref(&input);
825 return TRUE;
826 }
827
828 /* test */
829
test_unseekable_redirect(void)830 static void test_unseekable_redirect(void)
831 {
832 struct http_client_settings http_client_set;
833
834 test_client_defaults(&http_client_set);
835 http_client_set.max_redirects = 1;
836
837 test_begin("unseekable redirect");
838 test_run_client_server(&http_client_set,
839 test_client_unseekable_redirect,
840 test_server_unseekable_redirect, 2, NULL);
841 test_end();
842 }
843
844 /*
845 * Unseekable retry
846 */
847
848 /* server */
849
test_unseekable_retry_input(struct server_connection * conn)850 static void test_unseekable_retry_input(struct server_connection *conn)
851 {
852 server_connection_deinit(&conn);
853 }
854
test_server_unseekable_retry(unsigned int index)855 static void test_server_unseekable_retry(unsigned int index)
856 {
857 test_server_input = test_unseekable_retry_input;
858 test_server_run(index);
859 }
860
861 /* client */
862
863 static void
test_client_unseekable_retry_response(const struct http_response * resp,void * context ATTR_UNUSED)864 test_client_unseekable_retry_response(const struct http_response *resp,
865 void *context ATTR_UNUSED)
866 {
867 test_client_assert_response(
868 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_ABORTED);
869
870 io_loop_stop(ioloop);
871 }
872
873 static bool
test_client_unseekable_retry(const struct http_client_settings * client_set)874 test_client_unseekable_retry(const struct http_client_settings *client_set)
875 {
876 struct http_client_request *hreq;
877 struct istream *input;
878
879 http_client = http_client_init(client_set);
880
881 input = i_stream_create_from_data("FROP", 4);
882 input->seekable = FALSE;
883
884 hreq = http_client_request(
885 http_client, "GET", net_ip2addr(&bind_ip),
886 "/unseekable-retry.txt",
887 test_client_unseekable_retry_response, NULL);
888 http_client_request_set_port(hreq, bind_ports[0]);
889 http_client_request_set_payload(hreq, input, FALSE);
890 http_client_request_submit(hreq);
891
892 i_stream_unref(&input);
893 return TRUE;
894 }
895
896 /* test */
897
test_unseekable_retry(void)898 static void test_unseekable_retry(void)
899 {
900 struct http_client_settings http_client_set;
901
902 test_client_defaults(&http_client_set);
903 http_client_set.max_attempts = 3;
904
905 test_begin("unseekable retry");
906 test_run_client_server(&http_client_set,
907 test_client_unseekable_retry,
908 test_server_unseekable_retry, 2, NULL);
909 test_end();
910 }
911
912 /*
913 * Broken payload
914 */
915
916 /* server */
917
test_broken_payload_input(struct server_connection * conn)918 static void test_broken_payload_input(struct server_connection *conn)
919 {
920 static const char *resp =
921 "HTTP/1.1 200 OK\r\n"
922 "Content-Length: 18\r\n"
923 "\r\n"
924 "Everything is OK\r\n";
925
926 o_stream_nsend_str(conn->conn.output, resp);
927 server_connection_deinit(&conn);
928 }
929
test_server_broken_payload(unsigned int index)930 static void test_server_broken_payload(unsigned int index)
931 {
932 test_server_input = test_broken_payload_input;
933 test_server_run(index);
934 }
935
936 /* client */
937
938 static void
test_client_broken_payload_response(const struct http_response * resp,void * context ATTR_UNUSED)939 test_client_broken_payload_response(const struct http_response *resp,
940 void *context ATTR_UNUSED)
941 {
942 test_client_assert_response(
943 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_BROKEN_PAYLOAD);
944
945 io_loop_stop(ioloop);
946 }
947
948 static bool
test_client_broken_payload(const struct http_client_settings * client_set)949 test_client_broken_payload(const struct http_client_settings *client_set)
950 {
951 struct http_client_request *hreq;
952 struct istream *input;
953
954 test_expect_errors(1);
955
956 http_client = http_client_init(client_set);
957
958 input = i_stream_create_error_str(EIO, "Moehahahaha!!");
959 i_stream_set_name(input, "PURE EVIL");
960
961 hreq = http_client_request(
962 http_client, "GET", net_ip2addr(&bind_ip),
963 "/broken-payload.txt",
964 test_client_broken_payload_response, NULL);
965 http_client_request_set_port(hreq, bind_ports[0]);
966 http_client_request_set_payload(hreq, input, FALSE);
967 http_client_request_submit(hreq);
968
969 i_stream_unref(&input);
970 return TRUE;
971 }
972
973 /* test */
974
test_broken_payload(void)975 static void test_broken_payload(void)
976 {
977 struct http_client_settings http_client_set;
978
979 test_client_defaults(&http_client_set);
980
981 test_begin("broken payload");
982 test_run_client_server(&http_client_set,
983 test_client_broken_payload,
984 test_server_broken_payload, 1, NULL);
985 test_end();
986 }
987
988 /*
989 * Retry payload
990 */
991
992 /* server */
993
994 struct _retry_payload_sctx {
995 bool eoh;
996 };
997
test_retry_payload_init(struct server_connection * conn)998 static int test_retry_payload_init(struct server_connection *conn)
999 {
1000 struct _retry_payload_sctx *ctx;
1001
1002 ctx = p_new(conn->pool, struct _retry_payload_sctx, 1);
1003 conn->context = ctx;
1004 return 0;
1005 }
1006
test_retry_payload_input(struct server_connection * conn)1007 static void test_retry_payload_input(struct server_connection *conn)
1008 {
1009 struct _retry_payload_sctx *ctx = conn->context;
1010 const char *line;
1011
1012 while ((line = i_stream_read_next_line(conn->conn.input)) != NULL) {
1013 if (*line == '\0') {
1014 ctx->eoh = TRUE;
1015 continue;
1016 }
1017 if (ctx->eoh)
1018 break;
1019 }
1020
1021 if (conn->conn.input->stream_errno != 0) {
1022 i_fatal("server: Stream error: %s",
1023 i_stream_get_error(conn->conn.input));
1024 }
1025 if (line == NULL) {
1026 if (conn->conn.input->eof)
1027 i_fatal("server: Client stream ended prematurely");
1028 return;
1029 }
1030
1031 i_assert(ctx->eoh);
1032
1033 if (strcmp(line, "This is the payload we expect.") == 0) {
1034 if (debug)
1035 i_debug("Expected payload received");
1036 o_stream_nsend_str(conn->conn.output,
1037 "HTTP/1.1 500 Oh no!\r\n"
1038 "Connection: close\r\n"
1039 "Content-Length: 17\r\n"
1040 "\r\n"
1041 "Expected result\r\n");
1042 } else {
1043 i_error("Unexpected payload received: `%s'",
1044 str_sanitize(line, 128));
1045 o_stream_nsend_str(conn->conn.output,
1046 "HTTP/1.1 501 Oh no!\r\n"
1047 "Connection: close\r\n"
1048 "Content-Length: 19\r\n"
1049 "\r\n"
1050 "Unexpected result\r\n");
1051 }
1052 server_connection_deinit(&conn);
1053 }
1054
test_server_retry_payload(unsigned int index)1055 static void test_server_retry_payload(unsigned int index)
1056 {
1057 test_server_init = test_retry_payload_init;
1058 test_server_input = test_retry_payload_input;
1059 test_server_run(index);
1060 }
1061
1062 /* client */
1063
1064 struct _retry_payload_ctx {
1065 unsigned int count;
1066 };
1067
1068 struct _retry_payload_request_ctx {
1069 struct _retry_payload_ctx *ctx;
1070 struct http_client_request *req;
1071 };
1072
1073 static void
test_client_retry_payload_response(const struct http_response * resp,struct _retry_payload_request_ctx * rctx)1074 test_client_retry_payload_response(const struct http_response *resp,
1075 struct _retry_payload_request_ctx *rctx)
1076 {
1077 struct _retry_payload_ctx *ctx = rctx->ctx;
1078
1079 test_client_assert_response(resp, resp->status == 500);
1080
1081 if (http_client_request_try_retry(rctx->req)) {
1082 if (debug)
1083 i_debug("retrying");
1084 return;
1085 }
1086 i_free(rctx);
1087
1088 if (--ctx->count == 0) {
1089 i_free(ctx);
1090 io_loop_stop(ioloop);
1091 }
1092 }
1093
1094 static bool
test_client_retry_payload(const struct http_client_settings * client_set)1095 test_client_retry_payload(const struct http_client_settings *client_set)
1096 {
1097 static const char payload[] = "This is the payload we expect.\r\n";
1098 struct _retry_payload_ctx *ctx;
1099 struct _retry_payload_request_ctx *rctx;
1100 struct http_client_request *hreq;
1101 struct istream *input;
1102
1103 ctx = i_new(struct _retry_payload_ctx, 1);
1104 ctx->count = 1;
1105
1106 http_client = http_client_init(client_set);
1107
1108 input = i_stream_create_from_data(payload, sizeof(payload)-1);
1109
1110 rctx = i_new(struct _retry_payload_request_ctx, 1);
1111 rctx->ctx = ctx;
1112
1113 rctx->req = hreq = http_client_request(
1114 http_client, "GET", net_ip2addr(&bind_ip), "/retry-payload.txt",
1115 test_client_retry_payload_response, rctx);
1116 http_client_request_set_port(hreq, bind_ports[0]);
1117 http_client_request_set_payload(hreq, input, FALSE);
1118 http_client_request_submit(hreq);
1119
1120 i_stream_unref(&input);
1121 return TRUE;
1122 }
1123
1124 /* test */
1125
test_retry_payload(void)1126 static void test_retry_payload(void)
1127 {
1128 struct http_client_settings http_client_set;
1129
1130 test_client_defaults(&http_client_set);
1131 http_client_set.max_attempts = 2;
1132
1133 server_read_max = 0;
1134
1135 test_begin("retry payload");
1136 test_run_client_server(&http_client_set,
1137 test_client_retry_payload,
1138 test_server_retry_payload, 1, NULL);
1139 test_end();
1140 }
1141
1142 /*
1143 * Connection lost
1144 */
1145
1146 /* server */
1147
test_connection_lost_input(struct server_connection * conn)1148 static void test_connection_lost_input(struct server_connection *conn)
1149 {
1150 ssize_t ret;
1151
1152 if (server_read_max == 0) {
1153 server_connection_deinit(&conn);
1154 return;
1155 }
1156
1157 i_stream_set_max_buffer_size(conn->conn.input, server_read_max);
1158 ret = i_stream_read(conn->conn.input);
1159 if (ret == -2) {
1160 server_connection_deinit(&conn);
1161 return;
1162 }
1163 if (ret < 0) {
1164 i_assert(conn->conn.input->eof);
1165 if (conn->conn.input->stream_errno == 0)
1166 i_fatal("server: Client stream ended prematurely");
1167 else
1168 i_fatal("server: Stream error: %s",
1169 i_stream_get_error(conn->conn.input));
1170 }
1171 }
1172
test_server_connection_lost(unsigned int index)1173 static void test_server_connection_lost(unsigned int index)
1174 {
1175 test_server_input = test_connection_lost_input;
1176 test_server_run(index);
1177 }
1178
1179 /* client */
1180
1181 struct _connection_lost_ctx {
1182 unsigned int count;
1183 };
1184
1185 struct _connection_lost_request_ctx {
1186 struct _connection_lost_ctx *ctx;
1187 struct http_client_request *req;
1188 };
1189
1190 static void
test_client_connection_lost_response(const struct http_response * resp,struct _connection_lost_request_ctx * rctx)1191 test_client_connection_lost_response(const struct http_response *resp,
1192 struct _connection_lost_request_ctx *rctx)
1193 {
1194 struct _connection_lost_ctx *ctx = rctx->ctx;
1195
1196 test_client_assert_response(
1197 resp,
1198 resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST);
1199
1200 if (http_client_request_try_retry(rctx->req)) {
1201 if (debug)
1202 i_debug("retrying");
1203 return;
1204 }
1205 i_free(rctx);
1206
1207 if (--ctx->count == 0) {
1208 i_free(ctx);
1209 io_loop_stop(ioloop);
1210 }
1211 }
1212
1213 static bool
test_client_connection_lost(const struct http_client_settings * client_set)1214 test_client_connection_lost(const struct http_client_settings *client_set)
1215 {
1216 static const char payload[] =
1217 "This is a useless payload that only serves as a means to give "
1218 "the server the opportunity to close the connection before the "
1219 "payload is finished.";
1220 struct _connection_lost_ctx *ctx;
1221 struct _connection_lost_request_ctx *rctx;
1222 struct http_client_request *hreq;
1223 struct istream *input;
1224
1225 ctx = i_new(struct _connection_lost_ctx, 1);
1226 ctx->count = 2;
1227
1228 http_client = http_client_init(client_set);
1229
1230 input = i_stream_create_from_data(payload, sizeof(payload)-1);
1231
1232 rctx = i_new(struct _connection_lost_request_ctx, 1);
1233 rctx->ctx = ctx;
1234
1235 rctx->req = hreq = http_client_request(
1236 http_client, "GET", net_ip2addr(&bind_ip),
1237 "/connection-lost.txt",
1238 test_client_connection_lost_response, rctx);
1239 http_client_request_set_port(hreq, bind_ports[0]);
1240 http_client_request_set_payload(hreq, input, FALSE);
1241 http_client_request_submit(hreq);
1242
1243 rctx = i_new(struct _connection_lost_request_ctx, 1);
1244 rctx->ctx = ctx;
1245
1246 rctx->req = hreq = http_client_request(
1247 http_client, "GET", net_ip2addr(&bind_ip),
1248 "/connection-lost2.txt",
1249 test_client_connection_lost_response, rctx);
1250 http_client_request_set_port(hreq, bind_ports[0]);
1251 http_client_request_submit(hreq);
1252
1253 i_stream_unref(&input);
1254 return TRUE;
1255 }
1256
1257 /* test */
1258
test_connection_lost(void)1259 static void test_connection_lost(void)
1260 {
1261 struct http_client_settings http_client_set;
1262
1263 test_client_defaults(&http_client_set);
1264
1265 server_read_max = 0;
1266
1267 test_begin("connection lost: one attempt");
1268 http_client_set.max_attempts = 1;
1269 test_run_client_server(&http_client_set,
1270 test_client_connection_lost,
1271 test_server_connection_lost, 1, NULL);
1272 test_end();
1273
1274 test_begin("connection lost: two attempts");
1275 http_client_set.max_attempts = 2;
1276 test_run_client_server(&http_client_set,
1277 test_client_connection_lost,
1278 test_server_connection_lost, 1, NULL);
1279 test_end();
1280
1281 test_begin("connection lost: three attempts");
1282 http_client_set.max_attempts = 3;
1283 test_run_client_server(&http_client_set,
1284 test_client_connection_lost,
1285 test_server_connection_lost, 1, NULL);
1286 test_end();
1287
1288 test_begin("connection lost: manual retry");
1289 http_client_set.max_attempts = 3;
1290 http_client_set.no_auto_retry = TRUE;
1291 test_run_client_server(&http_client_set,
1292 test_client_connection_lost,
1293 test_server_connection_lost, 1, NULL);
1294 test_end();
1295 }
1296
1297 /*
1298 * Connection lost after 100-continue
1299 */
1300
1301 /* server */
1302
test_connection_lost_100_input(struct server_connection * conn)1303 static void test_connection_lost_100_input(struct server_connection *conn)
1304 {
1305 static const char *resp =
1306 "HTTP/1.1 100 Continue\r\n"
1307 "\r\n";
1308
1309 o_stream_nsend_str(conn->conn.output, resp);
1310 server_connection_deinit(&conn);
1311 }
1312
test_server_connection_lost_100(unsigned int index)1313 static void test_server_connection_lost_100(unsigned int index)
1314 {
1315 test_server_input = test_connection_lost_100_input;
1316 test_server_run(index);
1317 }
1318
1319 /* client */
1320
1321 struct _connection_lost_100_ctx {
1322 unsigned int count;
1323 };
1324
1325 static void
test_client_connection_lost_100_response(const struct http_response * resp,struct _connection_lost_100_ctx * ctx)1326 test_client_connection_lost_100_response(const struct http_response *resp,
1327 struct _connection_lost_100_ctx *ctx)
1328 {
1329 test_client_assert_response(
1330 resp,
1331 resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST);
1332
1333 if (--ctx->count == 0) {
1334 i_free(ctx);
1335 io_loop_stop(ioloop);
1336 }
1337 }
1338
1339 static bool
test_client_connection_lost_100(const struct http_client_settings * client_set)1340 test_client_connection_lost_100(
1341 const struct http_client_settings *client_set)
1342 {
1343 static const char payload[] =
1344 "This is a useless payload that only serves as a means to give "
1345 "the server the opportunity to close the connection before the "
1346 "payload is finished.";
1347 struct _connection_lost_100_ctx *ctx;
1348 struct http_client_request *hreq;
1349 struct istream *input;
1350
1351 ctx = i_new(struct _connection_lost_100_ctx, 1);
1352 ctx->count = 2;
1353
1354 http_client = http_client_init(client_set);
1355
1356 input = i_stream_create_from_data(payload, sizeof(payload)-1);
1357
1358 hreq = http_client_request(
1359 http_client, "GET", net_ip2addr(&bind_ip),
1360 "/connection-lost.txt",
1361 test_client_connection_lost_100_response, ctx);
1362 http_client_request_set_port(hreq, bind_ports[0]);
1363 http_client_request_set_payload(hreq, input, TRUE);
1364 http_client_request_submit(hreq);
1365
1366 hreq = http_client_request(
1367 http_client, "GET", net_ip2addr(&bind_ip),
1368 "/connection-lost2.txt",
1369 test_client_connection_lost_100_response, ctx);
1370 http_client_request_set_port(hreq, bind_ports[0]);
1371 http_client_request_set_payload(hreq, input, TRUE);
1372 http_client_request_submit(hreq);
1373
1374 i_stream_unref(&input);
1375 return TRUE;
1376 }
1377
1378 /* test */
1379
test_connection_lost_100(void)1380 static void test_connection_lost_100(void)
1381 {
1382 struct http_client_settings http_client_set;
1383
1384 test_client_defaults(&http_client_set);
1385
1386 server_read_max = 0;
1387
1388 test_begin("connection lost after 100-continue");
1389 http_client_set.max_attempts = 1;
1390 test_run_client_server(&http_client_set,
1391 test_client_connection_lost_100,
1392 test_server_connection_lost_100, 1, NULL);
1393 test_end();
1394 }
1395
1396 /*
1397 * Connection lost in sub-ioloop
1398 */
1399
1400 /* server */
1401
1402 static void
test_connection_lost_sub_ioloop_input(struct server_connection * conn)1403 test_connection_lost_sub_ioloop_input(struct server_connection *conn)
1404 {
1405 static const char *resp =
1406 "HTTP/1.1 200 OK\r\n"
1407 "Content-Length: 0\r\n"
1408 "\r\n";
1409
1410 o_stream_nsend_str(conn->conn.output, resp);
1411 server_connection_deinit(&conn);
1412 }
1413
test_server_connection_lost_sub_ioloop(unsigned int index)1414 static void test_server_connection_lost_sub_ioloop(unsigned int index)
1415 {
1416 test_server_input = test_connection_lost_sub_ioloop_input;
1417 test_server_run(index);
1418 }
1419
1420 /* client */
1421
1422 struct _connection_lost_sub_ioloop_ctx {
1423 unsigned int count;
1424 };
1425
1426 static void
test_client_connection_lost_sub_ioloop_response2(const struct http_response * resp,struct ioloop * sub_ioloop)1427 test_client_connection_lost_sub_ioloop_response2(
1428 const struct http_response *resp, struct ioloop *sub_ioloop)
1429 {
1430 test_client_assert_response(
1431 resp,
1432 (resp->status == 200 ||
1433 resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST));
1434
1435 io_loop_stop(sub_ioloop);
1436 }
1437
1438 static void
test_client_connection_lost_sub_ioloop_response(const struct http_response * resp,struct _connection_lost_sub_ioloop_ctx * ctx)1439 test_client_connection_lost_sub_ioloop_response(
1440 const struct http_response *resp,
1441 struct _connection_lost_sub_ioloop_ctx *ctx)
1442 {
1443 struct http_client_request *hreq;
1444 struct ioloop *sub_ioloop;
1445
1446 if (debug)
1447 i_debug("RESPONSE: %u %s", resp->status, resp->reason);
1448
1449 test_assert(resp->status == 200 ||
1450 resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST);
1451 test_assert(resp->reason != NULL && *resp->reason != '\0');
1452
1453 sub_ioloop = io_loop_create();
1454 http_client_switch_ioloop(http_client);
1455
1456 hreq = http_client_request(
1457 http_client, "GET", net_ip2addr(&bind_ip),
1458 "/connection-lost-sub-ioloop3.txt",
1459 test_client_connection_lost_sub_ioloop_response2, sub_ioloop);
1460 http_client_request_set_port(hreq, bind_ports[1]);
1461 http_client_request_submit(hreq);
1462
1463 io_loop_run(sub_ioloop);
1464 io_loop_set_current(ioloop);
1465 http_client_switch_ioloop(http_client);
1466 io_loop_set_current(sub_ioloop);
1467 io_loop_destroy(&sub_ioloop);
1468
1469 if (--ctx->count == 0) {
1470 i_free(ctx);
1471 io_loop_stop(ioloop);
1472 }
1473 }
1474
1475 static bool
test_client_connection_lost_sub_ioloop(const struct http_client_settings * client_set)1476 test_client_connection_lost_sub_ioloop(
1477 const struct http_client_settings *client_set)
1478 {
1479 static const char payload[] =
1480 "This is a useless payload that only serves as a means to give "
1481 "the server the opportunity to close the connection before the "
1482 "payload is finished.";
1483 struct _connection_lost_sub_ioloop_ctx *ctx;
1484 struct http_client_request *hreq;
1485 struct istream *input;
1486
1487 ctx = i_new(struct _connection_lost_sub_ioloop_ctx, 1);
1488 ctx->count = 2;
1489
1490 http_client = http_client_init(client_set);
1491
1492 input = i_stream_create_from_data(payload, sizeof(payload)-1);
1493
1494 hreq = http_client_request(
1495 http_client, "GET", net_ip2addr(&bind_ip),
1496 "/connection-lost-sub-ioloop.txt",
1497 test_client_connection_lost_sub_ioloop_response, ctx);
1498 http_client_request_set_port(hreq, bind_ports[0]);
1499 http_client_request_set_payload(hreq, input, TRUE);
1500 http_client_request_submit(hreq);
1501
1502 hreq = http_client_request(
1503 http_client, "GET", net_ip2addr(&bind_ip),
1504 "/connection-lost-sub-ioloop2.txt",
1505 test_client_connection_lost_sub_ioloop_response, ctx);
1506 http_client_request_set_port(hreq, bind_ports[0]);
1507 http_client_request_set_payload(hreq, input, TRUE);
1508 http_client_request_submit(hreq);
1509
1510 i_stream_unref(&input);
1511 return TRUE;
1512 }
1513
1514 /* test */
1515
test_connection_lost_sub_ioloop(void)1516 static void test_connection_lost_sub_ioloop(void)
1517 {
1518 struct http_client_settings http_client_set;
1519
1520 test_client_defaults(&http_client_set);
1521
1522 server_read_max = 0;
1523
1524 test_begin("connection lost while running sub-ioloop");
1525 http_client_set.max_attempts = 1;
1526 test_run_client_server(&http_client_set,
1527 test_client_connection_lost_sub_ioloop,
1528 test_server_connection_lost_sub_ioloop, 2, NULL);
1529 test_end();
1530 }
1531
1532 /*
1533 * Early success
1534 */
1535
1536 /* server */
1537
test_early_success_input(struct server_connection * conn)1538 static void test_early_success_input(struct server_connection *conn)
1539 {
1540 static const char *resp =
1541 "HTTP/1.1 200 OK\r\n"
1542 "Content-Length: 18\r\n"
1543 "\r\n"
1544 "Everything is OK\r\n";
1545
1546 i_sleep_msecs(200);
1547 o_stream_nsend_str(conn->conn.output, resp);
1548 server_connection_deinit(&conn);
1549 }
1550
test_server_early_success(unsigned int index)1551 static void test_server_early_success(unsigned int index)
1552 {
1553 test_server_input = test_early_success_input;
1554 test_server_run(index);
1555 }
1556
1557 /* client */
1558
1559 struct _early_success_ctx {
1560 unsigned int count;
1561 };
1562
1563 static void
test_client_early_success_response(const struct http_response * resp,struct _early_success_ctx * ctx)1564 test_client_early_success_response(const struct http_response *resp,
1565 struct _early_success_ctx *ctx)
1566 {
1567 if (ctx->count == 2) {
1568 test_client_assert_response(
1569 resp,
1570 resp->status == HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE);
1571 } else {
1572 test_client_assert_response(resp, resp->status == 200);
1573 }
1574
1575 if (--ctx->count == 0) {
1576 io_loop_stop(ioloop);
1577 i_free(ctx);
1578 }
1579 }
1580
1581 static bool
test_client_early_success(const struct http_client_settings * client_set)1582 test_client_early_success(const struct http_client_settings *client_set)
1583 {
1584 struct http_client_request *hreq;
1585 struct _early_success_ctx *ctx;
1586 string_t *payload;
1587 unsigned int i;
1588
1589 ctx = i_new(struct _early_success_ctx, 1);
1590 ctx->count = 2;
1591
1592 http_client = http_client_init(client_set);
1593
1594 hreq = http_client_request(
1595 http_client, "GET", net_ip2addr(&bind_ip),
1596 "/early-success.txt",
1597 test_client_early_success_response, ctx);
1598 http_client_request_set_port(hreq, bind_ports[0]);
1599
1600 T_BEGIN {
1601 struct istream_chain *chain;
1602 struct istream *input, *chain_input;
1603
1604 payload = t_str_new(64*3000);
1605 for (i = 0; i < 3000; i++) {
1606 str_append(payload,
1607 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
1608 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n");
1609 }
1610
1611 chain_input = i_stream_create_chain(&chain);
1612
1613 input = i_stream_create_copy_from_data(str_data(payload),
1614 str_len(payload));
1615 i_stream_chain_append(chain, input);
1616 i_stream_unref(&input);
1617
1618 http_client_request_set_payload(hreq, chain_input, FALSE);
1619 i_stream_unref(&chain_input);
1620 } T_END;
1621 http_client_request_submit(hreq);
1622
1623 hreq = http_client_request(
1624 http_client, "GET", net_ip2addr(&bind_ip),
1625 "/early-success2.txt",
1626 test_client_early_success_response, ctx);
1627 http_client_request_set_port(hreq, bind_ports[0]);
1628 http_client_request_submit(hreq);
1629
1630 return TRUE;
1631 }
1632
1633 /* test */
1634
test_early_success(void)1635 static void test_early_success(void)
1636 {
1637 struct http_client_settings http_client_set;
1638
1639 test_client_defaults(&http_client_set);
1640 http_client_set.socket_send_buffer_size = 4096;
1641
1642 test_begin("early succes");
1643 test_run_client_server(&http_client_set,
1644 test_client_early_success,
1645 test_server_early_success, 1, NULL);
1646 test_end();
1647 }
1648
1649 /*
1650 * Bad response
1651 */
1652
1653 /* server */
1654
test_bad_response_input(struct server_connection * conn)1655 static void test_bad_response_input(struct server_connection *conn)
1656 {
1657 static const char *resp =
1658 "HTTP/1.1 666 Really bad response\r\n"
1659 "\r\n";
1660
1661 o_stream_nsend_str(conn->conn.output, resp);
1662 server_connection_deinit(&conn);
1663 }
1664
test_server_bad_response(unsigned int index)1665 static void test_server_bad_response(unsigned int index)
1666 {
1667 test_server_input = test_bad_response_input;
1668 test_server_run(index);
1669 }
1670
1671 /* client */
1672
1673 struct _bad_response_ctx {
1674 unsigned int count;
1675 };
1676
1677 static void
test_client_bad_response_response(const struct http_response * resp,struct _bad_response_ctx * ctx)1678 test_client_bad_response_response(const struct http_response *resp,
1679 struct _bad_response_ctx *ctx)
1680 {
1681 test_client_assert_response(
1682 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE);
1683
1684 if (--ctx->count == 0) {
1685 i_free(ctx);
1686 io_loop_stop(ioloop);
1687 }
1688 }
1689
1690 static bool
test_client_bad_response(const struct http_client_settings * client_set)1691 test_client_bad_response(const struct http_client_settings *client_set)
1692 {
1693 struct http_client_request *hreq;
1694 struct _bad_response_ctx *ctx;
1695
1696 ctx = i_new(struct _bad_response_ctx, 1);
1697 ctx->count = 2;
1698
1699 http_client = http_client_init(client_set);
1700
1701 hreq = http_client_request(
1702 http_client, "GET", net_ip2addr(&bind_ip),
1703 "/bad-response.txt",
1704 test_client_bad_response_response, ctx);
1705 http_client_request_set_port(hreq, bind_ports[0]);
1706 http_client_request_submit(hreq);
1707
1708 hreq = http_client_request(
1709 http_client, "GET", net_ip2addr(&bind_ip),
1710 "/bad-response2.txt",
1711 test_client_bad_response_response, ctx);
1712 http_client_request_set_port(hreq, bind_ports[0]);
1713 http_client_request_submit(hreq);
1714
1715 return TRUE;
1716 }
1717
1718 /* test */
1719
test_bad_response(void)1720 static void test_bad_response(void)
1721 {
1722 struct http_client_settings http_client_set;
1723
1724 test_client_defaults(&http_client_set);
1725
1726 test_begin("bad response");
1727 test_run_client_server(&http_client_set,
1728 test_client_bad_response,
1729 test_server_bad_response, 1, NULL);
1730 test_end();
1731 }
1732
1733 /*
1734 * Request timed out
1735 */
1736
1737 /* server */
1738
1739 static void
test_request_timed_out_input(struct server_connection * conn ATTR_UNUSED)1740 test_request_timed_out_input(struct server_connection *conn ATTR_UNUSED)
1741 {
1742 /* do nothing */
1743 }
1744
test_server_request_timed_out(unsigned int index)1745 static void test_server_request_timed_out(unsigned int index)
1746 {
1747 test_server_input = test_request_timed_out_input;
1748 test_server_run(index);
1749 }
1750
1751 /* client */
1752
1753 struct _request_timed_out1_ctx {
1754 unsigned int count;
1755 };
1756
1757 static void
test_client_request_timed_out1_response(const struct http_response * resp,struct _request_timed_out1_ctx * ctx)1758 test_client_request_timed_out1_response(const struct http_response *resp,
1759 struct _request_timed_out1_ctx *ctx)
1760 {
1761 test_client_assert_response(
1762 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT);
1763
1764 if (--ctx->count == 0) {
1765 i_free(ctx);
1766 io_loop_stop(ioloop);
1767 }
1768 }
1769
1770 static bool
test_client_request_timed_out1(const struct http_client_settings * client_set)1771 test_client_request_timed_out1(const struct http_client_settings *client_set)
1772 {
1773 struct http_client_request *hreq;
1774 struct _request_timed_out1_ctx *ctx;
1775
1776 ctx = i_new(struct _request_timed_out1_ctx, 1);
1777 ctx->count = 2;
1778
1779 http_client = http_client_init(client_set);
1780
1781 hreq = http_client_request(
1782 http_client, "GET", net_ip2addr(&bind_ip),
1783 "/request-timed-out1-1.txt",
1784 test_client_request_timed_out1_response, ctx);
1785 http_client_request_set_port(hreq, bind_ports[0]);
1786 http_client_request_submit(hreq);
1787
1788 hreq = http_client_request(
1789 http_client, "GET", net_ip2addr(&bind_ip),
1790 "/request-timed-out1-2.txt",
1791 test_client_request_timed_out1_response, ctx);
1792 http_client_request_set_port(hreq, bind_ports[0]);
1793 http_client_request_submit(hreq);
1794
1795 return TRUE;
1796 }
1797
1798 struct _request_timed_out2_ctx {
1799 struct timeout *to;
1800 unsigned int count;
1801 unsigned int max_parallel_connections;
1802 };
1803
1804 static void
test_client_request_timed_out2_timeout(struct _request_timed_out2_ctx * ctx)1805 test_client_request_timed_out2_timeout(struct _request_timed_out2_ctx *ctx)
1806 {
1807 timeout_remove(&ctx->to);
1808 i_debug("TIMEOUT");
1809 }
1810
1811 static void
test_client_request_timed_out2_response(const struct http_response * resp,struct _request_timed_out2_ctx * ctx)1812 test_client_request_timed_out2_response(const struct http_response *resp,
1813 struct _request_timed_out2_ctx *ctx)
1814 {
1815 test_client_assert_response(
1816 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT);
1817 test_assert(ctx->to != NULL);
1818
1819 if (--ctx->count > 0) {
1820 if (ctx->to != NULL && ctx->max_parallel_connections <= 1)
1821 timeout_reset(ctx->to);
1822 } else {
1823 timeout_remove(&ctx->to);
1824 i_free(ctx);
1825 io_loop_stop(ioloop);
1826 }
1827 }
1828
1829 static bool
test_client_request_timed_out2(const struct http_client_settings * client_set)1830 test_client_request_timed_out2(const struct http_client_settings *client_set)
1831 {
1832 struct http_client_request *hreq;
1833 struct _request_timed_out2_ctx *ctx;
1834
1835 ctx = i_new(struct _request_timed_out2_ctx, 1);
1836 ctx->count = 2;
1837 ctx->max_parallel_connections =
1838 client_set->max_parallel_connections;
1839
1840 ctx->to = timeout_add(2000,
1841 test_client_request_timed_out2_timeout, ctx);
1842
1843 http_client = http_client_init(client_set);
1844
1845 hreq = http_client_request(
1846 http_client, "GET", net_ip2addr(&bind_ip),
1847 "/request-timed-out2-1.txt",
1848 test_client_request_timed_out2_response, ctx);
1849 http_client_request_set_port(hreq, bind_ports[0]);
1850 http_client_request_set_attempt_timeout_msecs(hreq, 1000);
1851 http_client_request_submit(hreq);
1852
1853 hreq = http_client_request(
1854 http_client, "GET", net_ip2addr(&bind_ip),
1855 "/request-timed-out2-2.txt",
1856 test_client_request_timed_out2_response, ctx);
1857 http_client_request_set_port(hreq, bind_ports[0]);
1858 http_client_request_set_attempt_timeout_msecs(hreq, 1000);
1859 http_client_request_submit(hreq);
1860
1861 return TRUE;
1862 }
1863
1864 /* test */
1865
test_request_timed_out(void)1866 static void test_request_timed_out(void)
1867 {
1868 struct http_client_settings http_client_set;
1869
1870 test_client_defaults(&http_client_set);
1871
1872 test_begin("request timed out: one attempt");
1873 http_client_set.request_timeout_msecs = 1000;
1874 http_client_set.max_attempts = 1;
1875 test_run_client_server(&http_client_set,
1876 test_client_request_timed_out1,
1877 test_server_request_timed_out, 1, NULL);
1878 test_end();
1879
1880 test_begin("request timed out: two attempts");
1881 http_client_set.request_timeout_msecs = 1000;
1882 http_client_set.max_attempts = 1;
1883 test_run_client_server(&http_client_set,
1884 test_client_request_timed_out1,
1885 test_server_request_timed_out, 1, NULL);
1886 test_end();
1887
1888 test_begin("request absolutely timed out");
1889 http_client_set.request_timeout_msecs = 0;
1890 http_client_set.request_absolute_timeout_msecs = 2000;
1891 http_client_set.max_attempts = 3;
1892 test_run_client_server(&http_client_set,
1893 test_client_request_timed_out1,
1894 test_server_request_timed_out, 1, NULL);
1895 test_end();
1896
1897 test_begin("request double timed out");
1898 http_client_set.request_timeout_msecs = 500;
1899 http_client_set.request_absolute_timeout_msecs = 2000;
1900 http_client_set.max_attempts = 3;
1901 test_run_client_server(&http_client_set,
1902 test_client_request_timed_out1,
1903 test_server_request_timed_out, 1, NULL);
1904 test_end();
1905
1906 test_begin("request timed out: specific timeout");
1907 http_client_set.request_timeout_msecs = 3000;
1908 http_client_set.request_absolute_timeout_msecs = 0;
1909 http_client_set.max_attempts = 1;
1910 http_client_set.max_parallel_connections = 1;
1911 test_run_client_server(&http_client_set,
1912 test_client_request_timed_out2,
1913 test_server_request_timed_out, 1, NULL);
1914 test_end();
1915
1916 test_begin("request timed out: specific timeout (parallel)");
1917 http_client_set.request_timeout_msecs = 3000;
1918 http_client_set.request_absolute_timeout_msecs = 0;
1919 http_client_set.max_attempts = 1;
1920 http_client_set.max_parallel_connections = 4;
1921 test_run_client_server(&http_client_set,
1922 test_client_request_timed_out2,
1923 test_server_request_timed_out, 1, NULL);
1924 test_end();
1925 }
1926
1927 /*
1928 * Request aborted early
1929 */
1930
1931 /* server */
1932
1933 static void
test_request_aborted_early_input(struct server_connection * conn ATTR_UNUSED)1934 test_request_aborted_early_input(struct server_connection *conn ATTR_UNUSED)
1935 {
1936 static const char *resp =
1937 "HTTP/1.1 404 Not Found\r\n"
1938 "\r\n";
1939
1940 /* wait one second to respond */
1941 i_sleep_intr_secs(1);
1942
1943 /* respond */
1944 o_stream_nsend_str(conn->conn.output, resp);
1945 server_connection_deinit(&conn);
1946 }
1947
test_server_request_aborted_early(unsigned int index)1948 static void test_server_request_aborted_early(unsigned int index)
1949 {
1950 test_server_input = test_request_aborted_early_input;
1951 test_server_run(index);
1952 }
1953
1954 /* client */
1955
1956 struct _request_aborted_early_ctx {
1957 struct http_client_request *req1, *req2;
1958 struct timeout *to;
1959 };
1960
1961 static void
test_client_request_aborted_early_response(const struct http_response * resp,struct _request_aborted_early_ctx * ctx ATTR_UNUSED)1962 test_client_request_aborted_early_response(
1963 const struct http_response *resp,
1964 struct _request_aborted_early_ctx *ctx ATTR_UNUSED)
1965 {
1966 if (debug)
1967 i_debug("RESPONSE: %u %s", resp->status, resp->reason);
1968
1969 /* abort does not trigger callback */
1970 test_assert(FALSE);
1971 }
1972
1973 static void
test_client_request_aborted_early_timeout(struct _request_aborted_early_ctx * ctx)1974 test_client_request_aborted_early_timeout(
1975 struct _request_aborted_early_ctx *ctx)
1976 {
1977 timeout_remove(&ctx->to);
1978
1979 if (ctx->req1 != NULL) {
1980 /* abort early */
1981 http_client_request_abort(&ctx->req1); /* sent */
1982 http_client_request_abort(&ctx->req2); /* only queued */
1983
1984 /* wait a little for server to actually respond to an
1985 already aborted request */
1986 ctx->to = timeout_add_short(
1987 1000, test_client_request_aborted_early_timeout, ctx);
1988 } else {
1989 /* all done */
1990 i_free(ctx);
1991 io_loop_stop(ioloop);
1992 }
1993 }
1994
1995 static bool
test_client_request_aborted_early(const struct http_client_settings * client_set)1996 test_client_request_aborted_early(const struct http_client_settings *client_set)
1997 {
1998 struct http_client_request *hreq;
1999 struct _request_aborted_early_ctx *ctx;
2000
2001 ctx = i_new(struct _request_aborted_early_ctx, 1);
2002
2003 http_client = http_client_init(client_set);
2004
2005 hreq = ctx->req1 = http_client_request(
2006 http_client, "GET", net_ip2addr(&bind_ip),
2007 "/request-aborted-early.txt",
2008 test_client_request_aborted_early_response, ctx);
2009 http_client_request_set_port(hreq, bind_ports[0]);
2010 http_client_request_submit(hreq);
2011
2012 hreq = ctx->req2 = http_client_request(
2013 http_client, "GET", net_ip2addr(&bind_ip),
2014 "/request-aborted-early2.txt",
2015 test_client_request_aborted_early_response, ctx);
2016 http_client_request_set_port(hreq, bind_ports[0]);
2017 http_client_request_submit(hreq);
2018
2019 ctx->to = timeout_add_short(
2020 500, test_client_request_aborted_early_timeout, ctx);
2021 return TRUE;
2022 }
2023
2024 /* test */
2025
test_request_aborted_early(void)2026 static void test_request_aborted_early(void)
2027 {
2028 struct http_client_settings http_client_set;
2029
2030 test_client_defaults(&http_client_set);
2031
2032 test_begin("request aborted early");
2033 test_run_client_server(&http_client_set,
2034 test_client_request_aborted_early,
2035 test_server_request_aborted_early, 1, NULL);
2036 test_end();
2037 }
2038
2039 /*
2040 * Request failed blocking
2041 */
2042
2043 /* server */
2044
2045 static void
test_request_failed_blocking_input(struct server_connection * conn)2046 test_request_failed_blocking_input(struct server_connection *conn)
2047 {
2048 static const char *resp =
2049 "HTTP/1.1 500 Internal Server Error\r\n"
2050 "\r\n";
2051
2052 /* respond */
2053 o_stream_nsend_str(conn->conn.output, resp);
2054 i_sleep_intr_secs(10);
2055 server_connection_deinit(&conn);
2056 }
2057
test_server_request_failed_blocking(unsigned int index)2058 static void test_server_request_failed_blocking(unsigned int index)
2059 {
2060 test_server_input = test_request_failed_blocking_input;
2061 test_server_run(index);
2062 }
2063
2064 /* client */
2065
2066 struct _request_failed_blocking_ctx {
2067 struct http_client_request *req;
2068 };
2069
2070 static void
test_client_request_failed_blocking_response(const struct http_response * resp,struct _request_failed_blocking_ctx * ctx ATTR_UNUSED)2071 test_client_request_failed_blocking_response(
2072 const struct http_response *resp,
2073 struct _request_failed_blocking_ctx *ctx ATTR_UNUSED)
2074 {
2075 test_client_assert_response(resp, resp->status == 500);
2076 }
2077
2078 static bool
test_client_request_failed_blocking(const struct http_client_settings * client_set)2079 test_client_request_failed_blocking(
2080 const struct http_client_settings *client_set)
2081 {
2082 static const char *payload = "This a test payload!";
2083 struct http_client_request *hreq;
2084 struct _request_failed_blocking_ctx *ctx;
2085 unsigned int n;
2086 string_t *data;
2087
2088 data = str_new(default_pool, 1000000);
2089 for (n = 0; n < 50000; n++)
2090 str_append(data, payload);
2091
2092 ctx = i_new(struct _request_failed_blocking_ctx, 1);
2093
2094 http_client = http_client_init(client_set);
2095
2096 hreq = ctx->req = http_client_request(
2097 http_client, "GET", net_ip2addr(&bind_ip),
2098 "/request-failed-blocking.txt",
2099 test_client_request_failed_blocking_response, ctx);
2100 http_client_request_set_port(hreq, bind_ports[0]);
2101
2102 test_assert(http_client_request_send_payload(&hreq,
2103 str_data(data), str_len(data)) < 0);
2104 i_assert(hreq == NULL);
2105
2106 str_free(&data);
2107 i_free(ctx);
2108 return FALSE;
2109 }
2110
2111 /* test */
2112
test_request_failed_blocking(void)2113 static void test_request_failed_blocking(void)
2114 {
2115 struct http_client_settings http_client_set;
2116
2117 test_client_defaults(&http_client_set);
2118 http_client_set.socket_send_buffer_size = 4096;
2119
2120 test_begin("request failed blocking");
2121 test_run_client_server(&http_client_set,
2122 test_client_request_failed_blocking,
2123 test_server_request_failed_blocking, 1, NULL);
2124 test_end();
2125 }
2126
2127 /*
2128 * Client deinit early
2129 */
2130
2131 /* server */
2132
2133 static void
test_client_deinit_early_input(struct server_connection * conn ATTR_UNUSED)2134 test_client_deinit_early_input(struct server_connection *conn ATTR_UNUSED)
2135 {
2136 static const char *resp =
2137 "HTTP/1.1 404 Not Found\r\n"
2138 "\r\n";
2139
2140 /* wait one second to respond */
2141 i_sleep_intr_secs(1);
2142
2143 /* respond */
2144 o_stream_nsend_str(conn->conn.output, resp);
2145 server_connection_deinit(&conn);
2146 }
2147
test_server_client_deinit_early(unsigned int index)2148 static void test_server_client_deinit_early(unsigned int index)
2149 {
2150 test_server_input = test_client_deinit_early_input;
2151 test_server_run(index);
2152 }
2153
2154 /* client */
2155
2156 struct _client_deinit_early_ctx {
2157 struct timeout *to;
2158 };
2159
2160 static void
test_client_client_deinit_early_response(const struct http_response * resp,struct _client_deinit_early_ctx * ctx ATTR_UNUSED)2161 test_client_client_deinit_early_response(
2162 const struct http_response *resp,
2163 struct _client_deinit_early_ctx *ctx ATTR_UNUSED)
2164 {
2165 if (debug)
2166 i_debug("RESPONSE: %u %s", resp->status, resp->reason);
2167
2168 /* abort does not trigger callback */
2169 test_assert(FALSE);
2170 }
2171
2172 static void
test_client_client_deinit_early_timeout(struct _client_deinit_early_ctx * ctx)2173 test_client_client_deinit_early_timeout(struct _client_deinit_early_ctx *ctx)
2174 {
2175 timeout_remove(&ctx->to);
2176
2177 /* deinit early */
2178 http_client_deinit(&http_client);
2179
2180 /* all done */
2181 i_free(ctx);
2182 io_loop_stop(ioloop);
2183 }
2184
2185 static bool
test_client_client_deinit_early(const struct http_client_settings * client_set)2186 test_client_client_deinit_early(const struct http_client_settings *client_set)
2187 {
2188 struct http_client_request *hreq;
2189 struct _client_deinit_early_ctx *ctx;
2190
2191 ctx = i_new(struct _client_deinit_early_ctx, 1);
2192
2193 http_client = http_client_init(client_set);
2194
2195 hreq = http_client_request(
2196 http_client, "GET", net_ip2addr(&bind_ip),
2197 "/client-deinit-early.txt",
2198 test_client_client_deinit_early_response, ctx);
2199 http_client_request_set_port(hreq, bind_ports[0]);
2200 http_client_request_submit(hreq);
2201
2202 hreq = http_client_request(
2203 http_client, "GET", net_ip2addr(&bind_ip),
2204 "/client-deinit-early2.txt",
2205 test_client_client_deinit_early_response, ctx);
2206 http_client_request_set_port(hreq, bind_ports[0]);
2207 http_client_request_submit(hreq);
2208
2209 ctx->to = timeout_add_short(
2210 500, test_client_client_deinit_early_timeout, ctx);
2211 return TRUE;
2212 }
2213
2214 /* test */
2215
test_client_deinit_early(void)2216 static void test_client_deinit_early(void)
2217 {
2218 struct http_client_settings http_client_set;
2219
2220 test_client_defaults(&http_client_set);
2221
2222 test_begin("client deinit early");
2223 test_run_client_server(&http_client_set,
2224 test_client_client_deinit_early,
2225 test_server_client_deinit_early, 1, NULL);
2226 test_end();
2227 }
2228
2229 /*
2230 * Retry with delay
2231 */
2232
2233 /* server */
2234
test_retry_with_delay_input(struct server_connection * conn)2235 static void test_retry_with_delay_input(struct server_connection *conn)
2236 {
2237 string_t *resp;
2238
2239 resp = t_str_new(512);
2240 str_printfa(resp,
2241 "HTTP/1.1 500 Internal Server Error\r\n"
2242 "\r\n");
2243 o_stream_nsend(conn->conn.output, str_data(resp), str_len(resp));
2244 server_connection_deinit(&conn);
2245 }
2246
test_server_retry_with_delay(unsigned int index)2247 static void test_server_retry_with_delay(unsigned int index)
2248 {
2249 test_server_input = test_retry_with_delay_input;
2250 test_server_run(index);
2251 }
2252
2253 /* client */
2254
2255 struct _client_retry_with_delay_ctx {
2256 struct http_client_request *req;
2257 unsigned int retries;
2258 struct timeval time;
2259 };
2260
2261 static void
test_client_retry_with_delay_response(const struct http_response * resp,struct _client_retry_with_delay_ctx * ctx)2262 test_client_retry_with_delay_response(
2263 const struct http_response *resp,
2264 struct _client_retry_with_delay_ctx *ctx)
2265 {
2266 int real_delay, exp_delay;
2267
2268 test_client_assert_response(resp, resp->status == 500);
2269
2270 if (ctx->retries > 0) {
2271 /* check delay */
2272 real_delay = timeval_diff_msecs(&ioloop_timeval, &ctx->time);
2273 exp_delay = (1 << (ctx->retries-1)) * 50;
2274 if (real_delay < exp_delay-2) {
2275 i_fatal("Retry delay is too short %d < %d",
2276 real_delay, exp_delay);
2277 }
2278 }
2279
2280 http_client_request_delay_msecs(ctx->req, (1 << ctx->retries) * 50);
2281 ctx->time = ioloop_timeval;
2282 if (http_client_request_try_retry(ctx->req)) {
2283 ctx->retries++;
2284 if (debug)
2285 i_debug("retrying");
2286 return;
2287 }
2288
2289 i_free(ctx);
2290 io_loop_stop(ioloop);
2291 }
2292
2293 static bool
test_client_retry_with_delay(const struct http_client_settings * client_set)2294 test_client_retry_with_delay(const struct http_client_settings *client_set)
2295 {
2296 struct http_client_request *hreq;
2297 struct _client_retry_with_delay_ctx *ctx;
2298
2299 ctx = i_new(struct _client_retry_with_delay_ctx, 1);
2300 ctx->time = ioloop_timeval;
2301
2302 http_client = http_client_init(client_set);
2303
2304 ctx->req = hreq = http_client_request(
2305 http_client, "GET", net_ip2addr(&bind_ip),
2306 "/retry-with-delay.txt",
2307 test_client_retry_with_delay_response, ctx);
2308 http_client_request_set_port(hreq, bind_ports[0]);
2309 http_client_request_submit(hreq);
2310
2311 return TRUE;
2312 }
2313
2314 /* test */
2315
test_retry_with_delay(void)2316 static void test_retry_with_delay(void)
2317 {
2318 struct http_client_settings http_client_set;
2319
2320 test_client_defaults(&http_client_set);
2321 http_client_set.max_attempts = 3;
2322
2323 test_begin("retry with delay");
2324 test_run_client_server(&http_client_set,
2325 test_client_retry_with_delay,
2326 test_server_retry_with_delay, 1, NULL);
2327 test_end();
2328 }
2329
2330 /*
2331 * DNS service failure
2332 */
2333
2334 /* client */
2335
2336 struct _dns_service_failure {
2337 unsigned int count;
2338 };
2339
2340 static void
test_client_dns_service_failure_response(const struct http_response * resp,struct _dns_service_failure * ctx)2341 test_client_dns_service_failure_response(
2342 const struct http_response *resp,
2343 struct _dns_service_failure *ctx)
2344 {
2345 test_client_assert_response(
2346 resp,
2347 resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED);
2348
2349 if (--ctx->count == 0) {
2350 i_free(ctx);
2351 io_loop_stop(ioloop);
2352 }
2353 }
2354
2355 static bool
test_client_dns_service_failure(const struct http_client_settings * client_set)2356 test_client_dns_service_failure(const struct http_client_settings *client_set)
2357 {
2358 struct http_client_request *hreq;
2359 struct _dns_service_failure *ctx;
2360
2361 ctx = i_new(struct _dns_service_failure, 1);
2362 ctx->count = 2;
2363
2364 http_client = http_client_init(client_set);
2365
2366 hreq = http_client_request(
2367 http_client, "GET", "example.com", "/dns-service-failure.txt",
2368 test_client_dns_service_failure_response, ctx);
2369 http_client_request_set_port(hreq, 80);
2370 http_client_request_submit(hreq);
2371
2372 hreq = http_client_request(
2373 http_client, "GET", "example.com", "/dns-service-failure2.txt",
2374 test_client_dns_service_failure_response, ctx);
2375 http_client_request_set_port(hreq, 80);
2376 http_client_request_submit(hreq);
2377
2378 return TRUE;
2379 }
2380
2381 /* test */
2382
test_dns_service_failure(void)2383 static void test_dns_service_failure(void)
2384 {
2385 struct http_client_settings http_client_set;
2386
2387 test_client_defaults(&http_client_set);
2388 http_client_set.dns_client_socket_path = "./frop";
2389
2390 test_begin("dns service failure");
2391 test_run_client_server(&http_client_set,
2392 test_client_dns_service_failure,
2393 NULL, 0, NULL);
2394 test_end();
2395 }
2396
2397 /*
2398 * DNS timeout
2399 */
2400
2401 /* dns */
2402
test_dns_timeout_input(struct server_connection * conn ATTR_UNUSED)2403 static void test_dns_timeout_input(struct server_connection *conn ATTR_UNUSED)
2404 {
2405 /* hang */
2406 i_sleep_intr_secs(100);
2407 server_connection_deinit(&conn);
2408 }
2409
test_dns_dns_timeout(void)2410 static void test_dns_dns_timeout(void)
2411 {
2412 test_server_input = test_dns_timeout_input;
2413 test_server_run(0);
2414 }
2415
2416 /* client */
2417
2418 struct _dns_timeout {
2419 unsigned int count;
2420 };
2421
2422 static void
test_client_dns_timeout_response(const struct http_response * resp,struct _dns_timeout * ctx)2423 test_client_dns_timeout_response(
2424 const struct http_response *resp,
2425 struct _dns_timeout *ctx)
2426 {
2427 test_client_assert_response(
2428 resp,
2429 resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED);
2430
2431 if (--ctx->count == 0) {
2432 i_free(ctx);
2433 io_loop_stop(ioloop);
2434 }
2435 }
2436
2437 static bool
test_client_dns_timeout(const struct http_client_settings * client_set)2438 test_client_dns_timeout(const struct http_client_settings *client_set)
2439 {
2440 struct http_client_request *hreq;
2441 struct _dns_timeout *ctx;
2442
2443 ctx = i_new(struct _dns_timeout, 1);
2444 ctx->count = 2;
2445
2446 http_client = http_client_init(client_set);
2447
2448 hreq = http_client_request(
2449 http_client, "GET", "example.com", "/dns-timeout.txt",
2450 test_client_dns_timeout_response, ctx);
2451 http_client_request_set_port(hreq, 80);
2452 http_client_request_submit(hreq);
2453
2454 hreq = http_client_request(
2455 http_client, "GET", "example.com", "/dns-timeout2.txt",
2456 test_client_dns_timeout_response, ctx);
2457 http_client_request_set_port(hreq, 80);
2458 http_client_request_submit(hreq);
2459
2460 return TRUE;
2461 }
2462
2463 /* test */
2464
test_dns_timeout(void)2465 static void test_dns_timeout(void)
2466 {
2467 struct http_client_settings http_client_set;
2468
2469 test_client_defaults(&http_client_set);
2470 http_client_set.request_timeout_msecs = 2000;
2471 http_client_set.connect_timeout_msecs = 2000;
2472 http_client_set.dns_client_socket_path = "./dns-test";
2473
2474 test_begin("dns timeout");
2475 test_run_client_server(&http_client_set,
2476 test_client_dns_timeout, NULL, 0,
2477 test_dns_dns_timeout);
2478 test_end();
2479 }
2480
2481 /*
2482 * DNS lookup failure
2483 */
2484
2485 /* dns */
2486
2487 static void
test_dns_lookup_failure_input(struct server_connection * conn)2488 test_dns_lookup_failure_input(struct server_connection *conn)
2489 {
2490 if (!conn->version_sent) {
2491 conn->version_sent = TRUE;
2492 o_stream_nsend_str(conn->conn.output, "VERSION\tdns\t1\t0\n");
2493 }
2494
2495 o_stream_nsend_str(conn->conn.output,
2496 t_strdup_printf("%d\tFAIL\n", EAI_FAIL));
2497 server_connection_deinit(&conn);
2498 }
2499
test_dns_dns_lookup_failure(void)2500 static void test_dns_dns_lookup_failure(void)
2501 {
2502 test_server_input = test_dns_lookup_failure_input;
2503 test_server_run(0);
2504 }
2505
2506 /* client */
2507
2508 struct _dns_lookup_failure {
2509 unsigned int count;
2510 };
2511
2512 static void
test_client_dns_lookup_failure_response(const struct http_response * resp,struct _dns_lookup_failure * ctx)2513 test_client_dns_lookup_failure_response(const struct http_response *resp,
2514 struct _dns_lookup_failure *ctx)
2515 {
2516 test_client_assert_response(
2517 resp,
2518 resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED);
2519
2520 if (--ctx->count == 0) {
2521 i_free(ctx);
2522 io_loop_stop(ioloop);
2523 }
2524 }
2525
2526 static bool
test_client_dns_lookup_failure(const struct http_client_settings * client_set)2527 test_client_dns_lookup_failure(const struct http_client_settings *client_set)
2528 {
2529 struct http_client_request *hreq;
2530 struct _dns_lookup_failure *ctx;
2531
2532 ctx = i_new(struct _dns_lookup_failure, 1);
2533 ctx->count = 2;
2534
2535 http_client = http_client_init(client_set);
2536
2537 hreq = http_client_request(
2538 http_client, "GET", "example.com", "/dns-lookup-failure.txt",
2539 test_client_dns_lookup_failure_response, ctx);
2540 http_client_request_set_port(hreq, 80);
2541 http_client_request_submit(hreq);
2542
2543 hreq = http_client_request(
2544 http_client, "GET", "example.com", "/dns-lookup-failure2.txt",
2545 test_client_dns_lookup_failure_response, ctx);
2546 http_client_request_set_port(hreq, 80);
2547 http_client_request_submit(hreq);
2548
2549 return TRUE;
2550 }
2551
2552 /* test */
2553
test_dns_lookup_failure(void)2554 static void test_dns_lookup_failure(void)
2555 {
2556 struct http_client_settings http_client_set;
2557
2558 test_client_defaults(&http_client_set);
2559 http_client_set.dns_client_socket_path = "./dns-test";
2560
2561 test_begin("dns lookup failure");
2562 test_run_client_server(&http_client_set,
2563 test_client_dns_lookup_failure, NULL, 0,
2564 test_dns_dns_lookup_failure);
2565 test_end();
2566 }
2567
2568 /*
2569 * DNS lookup ttl
2570 */
2571
2572 /* dns */
2573
2574 static void
test_dns_lookup_ttl_input(struct server_connection * conn)2575 test_dns_lookup_ttl_input(struct server_connection *conn)
2576 {
2577 static unsigned int count = 0;
2578 const char *line;
2579
2580 if (!conn->version_sent) {
2581 conn->version_sent = TRUE;
2582 o_stream_nsend_str(conn->conn.output, "VERSION\tdns\t1\t0\n");
2583 }
2584
2585 while ((line = i_stream_read_next_line(conn->conn.input)) != NULL) {
2586 if (str_begins(line, "VERSION"))
2587 continue;
2588 if (debug)
2589 i_debug("DNS REQUEST %u: %s", count, line);
2590
2591 if (count == 0) {
2592 o_stream_nsend_str(conn->conn.output,
2593 "0\t127.0.0.1\n");
2594 } else {
2595 o_stream_nsend_str(
2596 conn->conn.output,
2597 t_strdup_printf("%d\tFAIL\n", EAI_FAIL));
2598 if (count > 4) {
2599 server_connection_deinit(&conn);
2600 return;
2601 }
2602 }
2603 count++;
2604 }
2605 }
2606
test_dns_dns_lookup_ttl(void)2607 static void test_dns_dns_lookup_ttl(void)
2608 {
2609 test_server_input = test_dns_lookup_ttl_input;
2610 test_server_run(0);
2611 }
2612
2613 /* server */
2614
2615 static void
test_server_dns_lookup_ttl_input(struct server_connection * conn)2616 test_server_dns_lookup_ttl_input(struct server_connection *conn)
2617 {
2618 string_t *resp;
2619
2620 resp = t_str_new(512);
2621 str_printfa(resp,
2622 "HTTP/1.1 200 OK\r\n"
2623 "Connection: close\r\n"
2624 "\r\n");
2625 o_stream_nsend(conn->conn.output, str_data(resp), str_len(resp));
2626 server_connection_deinit(&conn);
2627 }
2628
test_server_dns_lookup_ttl(unsigned int index)2629 static void test_server_dns_lookup_ttl(unsigned int index)
2630 {
2631 test_server_input = test_server_dns_lookup_ttl_input;
2632 test_server_run(index);
2633 }
2634
2635 /* client */
2636
2637 struct _dns_lookup_ttl {
2638 struct http_client *client;
2639 unsigned int count;
2640 struct timeout *to;
2641 };
2642
2643 static void
test_client_dns_lookup_ttl_response_stage2(const struct http_response * resp,struct _dns_lookup_ttl * ctx)2644 test_client_dns_lookup_ttl_response_stage2(const struct http_response *resp,
2645 struct _dns_lookup_ttl *ctx)
2646 {
2647 test_client_assert_response(
2648 resp,
2649 resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED);
2650
2651 if (--ctx->count == 0) {
2652 i_free(ctx);
2653 io_loop_stop(ioloop);
2654 }
2655 }
2656
test_client_dns_lookup_ttl_stage2_start(struct _dns_lookup_ttl * ctx)2657 static void test_client_dns_lookup_ttl_stage2_start(struct _dns_lookup_ttl *ctx)
2658 {
2659 struct http_client_request *hreq;
2660
2661 timeout_remove(&ctx->to);
2662
2663 ctx->count = 2;
2664
2665 hreq = http_client_request(
2666 ctx->client, "GET", "example.com",
2667 "/dns-lookup-ttl-stage2.txt",
2668 test_client_dns_lookup_ttl_response_stage2, ctx);
2669 http_client_request_set_port(hreq, bind_ports[0]);
2670 http_client_request_submit(hreq);
2671
2672 hreq = http_client_request(
2673 ctx->client, "GET", "example.com",
2674 "/dns-lookup-ttl2-stage2.txt",
2675 test_client_dns_lookup_ttl_response_stage2, ctx);
2676 http_client_request_set_port(hreq, bind_ports[0]);
2677 http_client_request_submit(hreq);
2678 }
2679
2680 static void
test_client_dns_lookup_ttl_response_stage1(const struct http_response * resp,struct _dns_lookup_ttl * ctx)2681 test_client_dns_lookup_ttl_response_stage1(const struct http_response *resp,
2682 struct _dns_lookup_ttl *ctx)
2683 {
2684 test_client_assert_response(resp, resp->status == 200);
2685
2686 if (--ctx->count == 0) {
2687 ctx->to = timeout_add(2000,
2688 test_client_dns_lookup_ttl_stage2_start, ctx);
2689 }
2690 }
2691
2692 static bool
test_client_dns_lookup_ttl(const struct http_client_settings * client_set)2693 test_client_dns_lookup_ttl(const struct http_client_settings *client_set)
2694 {
2695 struct http_client_request *hreq;
2696 struct _dns_lookup_ttl *ctx;
2697
2698 ctx = i_new(struct _dns_lookup_ttl, 1);
2699 ctx->count = 2;
2700
2701 ctx->client = http_client = http_client_init(client_set);
2702
2703 hreq = http_client_request(
2704 ctx->client, "GET", "example.com",
2705 "/dns-lookup-ttl-stage1.txt",
2706 test_client_dns_lookup_ttl_response_stage1, ctx);
2707 http_client_request_set_port(hreq, bind_ports[0]);
2708 http_client_request_submit(hreq);
2709
2710 hreq = http_client_request(
2711 ctx->client, "GET", "example.com",
2712 "/dns-lookup-ttl2-stage1.txt",
2713 test_client_dns_lookup_ttl_response_stage1, ctx);
2714 http_client_request_set_port(hreq, bind_ports[0]);
2715 http_client_request_submit(hreq);
2716
2717 return TRUE;
2718 }
2719
2720 /* test */
2721
test_dns_lookup_ttl(void)2722 static void test_dns_lookup_ttl(void)
2723 {
2724 struct http_client_settings http_client_set;
2725
2726 test_client_defaults(&http_client_set);
2727 http_client_set.dns_client_socket_path = "./dns-test";
2728 http_client_set.dns_ttl_msecs = 1000;
2729
2730 test_begin("dns lookup ttl");
2731 test_run_client_server(&http_client_set,
2732 test_client_dns_lookup_ttl,
2733 test_server_dns_lookup_ttl, 1,
2734 test_dns_dns_lookup_ttl);
2735 test_end();
2736 }
2737
2738 /*
2739 * Peer reuse failure
2740 */
2741
2742 /* server */
2743
test_peer_reuse_failure_input(struct server_connection * conn)2744 static void test_peer_reuse_failure_input(struct server_connection *conn)
2745 {
2746 static unsigned int seq = 0;
2747 static const char *resp =
2748 "HTTP/1.1 200 OK\r\n"
2749 "\r\n";
2750
2751 o_stream_nsend_str(conn->conn.output, resp);
2752 if (seq++ > 2) {
2753 server_connection_deinit(&conn);
2754 io_loop_stop(current_ioloop);
2755 }
2756 }
2757
test_server_peer_reuse_failure(unsigned int index)2758 static void test_server_peer_reuse_failure(unsigned int index)
2759 {
2760 test_server_input = test_peer_reuse_failure_input;
2761 test_server_run(index);
2762 }
2763
2764 /* client */
2765
2766 struct _peer_reuse_failure {
2767 struct timeout *to;
2768 bool first:1;
2769 };
2770
2771 static void
test_client_peer_reuse_failure_response2(const struct http_response * resp,struct _peer_reuse_failure * ctx)2772 test_client_peer_reuse_failure_response2(const struct http_response *resp,
2773 struct _peer_reuse_failure *ctx)
2774 {
2775 test_client_assert_response(
2776 resp, http_response_is_internal_error(resp));
2777
2778 i_free(ctx);
2779 io_loop_stop(ioloop);
2780 }
2781
2782 static void
test_client_peer_reuse_failure_next(struct _peer_reuse_failure * ctx)2783 test_client_peer_reuse_failure_next(struct _peer_reuse_failure *ctx)
2784 {
2785 struct http_client_request *hreq;
2786
2787 timeout_remove(&ctx->to);
2788
2789 hreq = http_client_request(
2790 http_client, "GET", net_ip2addr(&bind_ip),
2791 "/peer-reuse-next.txt",
2792 test_client_peer_reuse_failure_response2, ctx);
2793 http_client_request_set_port(hreq, bind_ports[0]);
2794 http_client_request_submit(hreq);
2795 }
2796
2797 static void
test_client_peer_reuse_failure_response1(const struct http_response * resp,struct _peer_reuse_failure * ctx)2798 test_client_peer_reuse_failure_response1(const struct http_response *resp,
2799 struct _peer_reuse_failure *ctx)
2800 {
2801 if (ctx->first) {
2802 test_client_assert_response(resp, resp->status == 200);
2803
2804 ctx->first = FALSE;
2805 ctx->to = timeout_add_short(
2806 500, test_client_peer_reuse_failure_next, ctx);
2807 } else {
2808 test_client_assert_response(
2809 resp, http_response_is_internal_error(resp));
2810 }
2811
2812 test_assert(resp->reason != NULL && *resp->reason != '\0');
2813 }
2814
2815 static bool
test_client_peer_reuse_failure(const struct http_client_settings * client_set)2816 test_client_peer_reuse_failure(const struct http_client_settings *client_set)
2817 {
2818 struct http_client_request *hreq;
2819 struct _peer_reuse_failure *ctx;
2820
2821 ctx = i_new(struct _peer_reuse_failure, 1);
2822 ctx->first = TRUE;
2823
2824 http_client = http_client_init(client_set);
2825
2826 hreq = http_client_request(
2827 http_client, "GET", net_ip2addr(&bind_ip), "/peer-reuse.txt",
2828 test_client_peer_reuse_failure_response1, ctx);
2829 http_client_request_set_port(hreq, bind_ports[0]);
2830 http_client_request_submit(hreq);
2831
2832 hreq = http_client_request(
2833 http_client, "GET", net_ip2addr(&bind_ip), "/peer-reuse.txt",
2834 test_client_peer_reuse_failure_response1, ctx);
2835 http_client_request_set_port(hreq, bind_ports[0]);
2836 http_client_request_submit(hreq);
2837
2838 hreq = http_client_request(
2839 http_client, "GET", net_ip2addr(&bind_ip), "/peer-reuse.txt",
2840 test_client_peer_reuse_failure_response1, ctx);
2841 http_client_request_set_port(hreq, bind_ports[0]);
2842 http_client_request_submit(hreq);
2843
2844 return TRUE;
2845 }
2846
2847 /* test */
2848
test_peer_reuse_failure(void)2849 static void test_peer_reuse_failure(void)
2850 {
2851 struct http_client_settings http_client_set;
2852
2853 test_client_defaults(&http_client_set);
2854 http_client_set.max_connect_attempts = 1;
2855 http_client_set.max_idle_time_msecs = 500;
2856
2857 test_begin("peer reuse failure");
2858 test_run_client_server(&http_client_set,
2859 test_client_peer_reuse_failure,
2860 test_server_peer_reuse_failure, 1, NULL);
2861 test_end();
2862 }
2863
2864 /*
2865 * Reconnect failure
2866 */
2867
2868 /* dns */
2869
test_dns_reconnect_failure_input(struct server_connection * conn)2870 static void test_dns_reconnect_failure_input(struct server_connection *conn)
2871 {
2872 static unsigned int count = 0;
2873 const char *line;
2874
2875 if (!conn->version_sent) {
2876 conn->version_sent = TRUE;
2877 o_stream_nsend_str(conn->conn.output, "VERSION\tdns\t1\t0\n");
2878 }
2879
2880 while ((line = i_stream_read_next_line(conn->conn.input)) != NULL) {
2881 if (str_begins(line, "VERSION"))
2882 continue;
2883 if (debug)
2884 i_debug("DNS REQUEST %u: %s", count, line);
2885
2886 if (count == 0) {
2887 o_stream_nsend_str(conn->conn.output,
2888 "0\t127.0.0.1\n");
2889 } else {
2890 o_stream_nsend_str(
2891 conn->conn.output,
2892 t_strdup_printf("%d\tFAIL\n", EAI_FAIL));
2893 if (count > 4) {
2894 server_connection_deinit(&conn);
2895 return;
2896 }
2897 }
2898 count++;
2899 }
2900 }
2901
test_dns_reconnect_failure(void)2902 static void test_dns_reconnect_failure(void)
2903 {
2904 test_server_input = test_dns_reconnect_failure_input;
2905 test_server_run(0);
2906 }
2907
2908 /* server */
2909
test_reconnect_failure_input(struct server_connection * conn)2910 static void test_reconnect_failure_input(struct server_connection *conn)
2911 {
2912 static const char *resp =
2913 "HTTP/1.1 200 OK\r\n"
2914 "Content-Length: 18\r\n"
2915 "\r\n"
2916 "Everything is OK\r\n";
2917
2918 o_stream_nsend_str(conn->conn.output, resp);
2919 io_loop_stop(current_ioloop);
2920 io_remove(&io_listen);
2921 i_close_fd(&fd_listen);
2922 server_connection_deinit(&conn);
2923 }
2924
test_server_reconnect_failure(unsigned int index)2925 static void test_server_reconnect_failure(unsigned int index)
2926 {
2927 test_server_input = test_reconnect_failure_input;
2928 test_server_run(index);
2929 }
2930
2931 /* client */
2932
2933 struct _reconnect_failure_ctx {
2934 struct timeout *to;
2935 };
2936
2937 static void
test_client_reconnect_failure_response2(const struct http_response * resp,struct _reconnect_failure_ctx * ctx)2938 test_client_reconnect_failure_response2(const struct http_response *resp,
2939 struct _reconnect_failure_ctx *ctx)
2940 {
2941 test_client_assert_response(
2942 resp, resp->status == HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED);
2943
2944 io_loop_stop(ioloop);
2945 i_free(ctx);
2946 }
2947
2948 static void
test_client_reconnect_failure_next(struct _reconnect_failure_ctx * ctx)2949 test_client_reconnect_failure_next(struct _reconnect_failure_ctx *ctx)
2950 {
2951 struct http_client_request *hreq;
2952
2953 if (debug)
2954 i_debug("NEXT REQUEST");
2955
2956 timeout_remove(&ctx->to);
2957
2958 hreq = http_client_request(
2959 http_client, "GET", "example.com", "/reconnect-failure-2.txt",
2960 test_client_reconnect_failure_response2, ctx);
2961 http_client_request_set_port(hreq, bind_ports[0]);
2962 http_client_request_submit(hreq);
2963 }
2964
2965 static void
test_client_reconnect_failure_response1(const struct http_response * resp,struct _reconnect_failure_ctx * ctx)2966 test_client_reconnect_failure_response1(const struct http_response *resp,
2967 struct _reconnect_failure_ctx *ctx)
2968 {
2969 test_client_assert_response(resp, resp->status == 200);
2970
2971 ctx->to = timeout_add_short(
2972 5000, test_client_reconnect_failure_next, ctx);
2973 }
2974
2975 static bool
test_client_reconnect_failure(const struct http_client_settings * client_set)2976 test_client_reconnect_failure(const struct http_client_settings *client_set)
2977 {
2978 struct http_client_request *hreq;
2979 struct _reconnect_failure_ctx *ctx;
2980
2981 ctx = i_new(struct _reconnect_failure_ctx, 1);
2982
2983 http_client = http_client_init(client_set);
2984
2985 hreq = http_client_request(
2986 http_client, "GET", "example.com", "/reconnect-failure-1.txt",
2987 test_client_reconnect_failure_response1, ctx);
2988 http_client_request_set_port(hreq, bind_ports[0]);
2989 http_client_request_submit(hreq);
2990
2991 return TRUE;
2992 }
2993
2994 /* test */
2995
test_reconnect_failure(void)2996 static void test_reconnect_failure(void)
2997 {
2998 struct http_client_settings http_client_set;
2999
3000 test_client_defaults(&http_client_set);
3001 http_client_set.dns_client_socket_path = "./dns-test";
3002 http_client_set.dns_ttl_msecs = 10000;
3003 http_client_set.max_idle_time_msecs = 1000;
3004 http_client_set.max_attempts = 1;
3005 http_client_set.request_timeout_msecs = 1000;
3006
3007 test_begin("reconnect failure");
3008 test_run_client_server(&http_client_set,
3009 test_client_reconnect_failure,
3010 test_server_reconnect_failure, 1,
3011 test_dns_reconnect_failure);
3012 test_end();
3013 }
3014
3015 /*
3016 * Multi IP attempts
3017 */
3018
3019 /* dns */
3020
test_multi_ip_attempts_input(struct server_connection * conn)3021 static void test_multi_ip_attempts_input(struct server_connection *conn)
3022 {
3023 unsigned int count = 0;
3024 const char *line;
3025
3026 if (!conn->version_sent) {
3027 conn->version_sent = TRUE;
3028 o_stream_nsend_str(conn->conn.output, "VERSION\tdns\t1\t0\n");
3029 }
3030
3031 while ((line = i_stream_read_next_line(conn->conn.input)) != NULL) {
3032 if (str_begins(line, "VERSION"))
3033 continue;
3034 if (debug)
3035 i_debug("DNS REQUEST %u: %s", count, line);
3036
3037 if (strcmp(line, "IP\ttest1.local") == 0) {
3038 o_stream_nsend_str(conn->conn.output,
3039 "0\t127.0.0.4\t127.0.0.3\t"
3040 "127.0.0.2\t127.0.0.1\n");
3041 continue;
3042 }
3043
3044 o_stream_nsend_str(conn->conn.output,
3045 "0\t10.255.255.1\t192.168.0.0\t"
3046 "192.168.255.255\t127.0.0.1\n");
3047 }
3048 }
3049
test_dns_multi_ip_attempts(void)3050 static void test_dns_multi_ip_attempts(void)
3051 {
3052 test_server_input = test_multi_ip_attempts_input;
3053 test_server_run(0);
3054 }
3055
3056 /* server */
3057
test_server_multi_ip_attempts_input(struct server_connection * conn)3058 static void test_server_multi_ip_attempts_input(struct server_connection *conn)
3059 {
3060 string_t *resp;
3061
3062 resp = t_str_new(512);
3063 str_printfa(resp,
3064 "HTTP/1.1 200 OK\r\n"
3065 "Connection: close\r\n"
3066 "\r\n");
3067 o_stream_nsend(conn->conn.output, str_data(resp), str_len(resp));
3068 server_connection_deinit(&conn);
3069 }
3070
test_server_multi_ip_attempts(unsigned int index)3071 static void test_server_multi_ip_attempts(unsigned int index)
3072 {
3073 test_server_input = test_server_multi_ip_attempts_input;
3074 test_server_run(index);
3075 }
3076
3077 /* client */
3078
3079 struct _multi_ip_attempts {
3080 unsigned int count;
3081 };
3082
3083 static void
test_client_multi_ip_attempts_response(const struct http_response * resp,struct _multi_ip_attempts * ctx)3084 test_client_multi_ip_attempts_response(const struct http_response *resp,
3085 struct _multi_ip_attempts *ctx)
3086 {
3087 test_client_assert_response(resp, resp->status == 200);
3088
3089 if (--ctx->count == 0) {
3090 i_free(ctx);
3091 io_loop_stop(ioloop);
3092 }
3093 }
3094
3095 static bool
test_client_multi_ip_attempts1(const struct http_client_settings * client_set)3096 test_client_multi_ip_attempts1(const struct http_client_settings *client_set)
3097 {
3098 struct http_client_request *hreq;
3099 struct _multi_ip_attempts *ctx;
3100
3101 ctx = i_new(struct _multi_ip_attempts, 1);
3102 ctx->count = 2;
3103
3104 http_client = http_client_init(client_set);
3105
3106 hreq = http_client_request(
3107 http_client, "GET", "test1.local", "/multi-ip-attempts.txt",
3108 test_client_multi_ip_attempts_response, ctx);
3109 http_client_request_set_port(hreq, bind_ports[0]);
3110 http_client_request_submit(hreq);
3111
3112 hreq = http_client_request(
3113 http_client, "GET", "test1.local", "/multi-ip-attempts2.txt",
3114 test_client_multi_ip_attempts_response, ctx);
3115 http_client_request_set_port(hreq, bind_ports[0]);
3116 http_client_request_submit(hreq);
3117
3118 return TRUE;
3119 }
3120
3121 static bool
test_client_multi_ip_attempts2(const struct http_client_settings * client_set)3122 test_client_multi_ip_attempts2(const struct http_client_settings *client_set)
3123 {
3124 struct http_client_request *hreq;
3125 struct _multi_ip_attempts *ctx;
3126
3127 ctx = i_new(struct _multi_ip_attempts, 1);
3128 ctx->count = 2;
3129
3130 http_client = http_client_init(client_set);
3131
3132 hreq = http_client_request(
3133 http_client, "GET", "test2.local", "/multi-ip-attempts.txt",
3134 test_client_multi_ip_attempts_response, ctx);
3135 http_client_request_set_port(hreq, bind_ports[0]);
3136 http_client_request_submit(hreq);
3137
3138 hreq = http_client_request(
3139 http_client, "GET", "test2.local", "/multi-ip-attempts2.txt",
3140 test_client_multi_ip_attempts_response, ctx);
3141 http_client_request_set_port(hreq, bind_ports[0]);
3142 http_client_request_submit(hreq);
3143
3144 return TRUE;
3145 }
3146
3147 /* test */
3148
test_multi_ip_attempts(void)3149 static void test_multi_ip_attempts(void)
3150 {
3151 struct http_client_settings http_client_set;
3152
3153 test_client_defaults(&http_client_set);
3154 http_client_set.connect_timeout_msecs = 1000;
3155 http_client_set.request_timeout_msecs = 1000;
3156 http_client_set.dns_client_socket_path = "./dns-test";
3157 http_client_set.max_connect_attempts = 4;
3158
3159 test_begin("multi IP attempts (connection refused)");
3160 test_run_client_server(&http_client_set,
3161 test_client_multi_ip_attempts1,
3162 test_server_multi_ip_attempts, 1,
3163 test_dns_multi_ip_attempts);
3164 test_end();
3165
3166 test_begin("multi IP attempts (connect timeout)");
3167 test_run_client_server(&http_client_set,
3168 test_client_multi_ip_attempts2,
3169 test_server_multi_ip_attempts, 1,
3170 test_dns_multi_ip_attempts);
3171 test_end();
3172
3173 http_client_set.soft_connect_timeout_msecs = 100;
3174
3175 test_begin("multi IP attempts (soft connect timeout)");
3176 test_run_client_server(&http_client_set,
3177 test_client_multi_ip_attempts2,
3178 test_server_multi_ip_attempts, 1,
3179 test_dns_multi_ip_attempts);
3180 test_end();
3181 }
3182
3183 /*
3184 * Idle connections
3185 */
3186
3187 /* server */
3188
3189 struct _idle_connections_sctx {
3190 bool eoh;
3191 };
3192
test_idle_connections_init(struct server_connection * conn)3193 static int test_idle_connections_init(struct server_connection *conn)
3194 {
3195 struct _idle_connections_sctx *ctx;
3196
3197 ctx = p_new(conn->pool, struct _idle_connections_sctx, 1);
3198 conn->context = ctx;
3199 return 0;
3200 }
3201
test_idle_connections_input(struct server_connection * conn)3202 static void test_idle_connections_input(struct server_connection *conn)
3203 {
3204 struct _idle_connections_sctx *ctx = conn->context;
3205 const char *line;
3206
3207 while ((line = i_stream_read_next_line(conn->conn.input)) != NULL) {
3208 if (*line == '\0') {
3209 ctx->eoh = TRUE;
3210 break;
3211 }
3212 }
3213
3214 if (conn->conn.input->stream_errno != 0) {
3215 i_fatal("server: Stream error: %s",
3216 i_stream_get_error(conn->conn.input));
3217 }
3218 if (line == NULL) {
3219 if (conn->conn.input->eof)
3220 server_connection_deinit(&conn);
3221 return;
3222 }
3223
3224 i_assert(ctx->eoh);
3225 ctx->eoh = FALSE;
3226
3227 string_t *resp;
3228
3229 resp = t_str_new(512);
3230 str_printfa(resp,
3231 "HTTP/1.1 200 OK\r\n"
3232 "Content-Length: 0\r\n"
3233 "\r\n");
3234 o_stream_nsend(conn->conn.output, str_data(resp), str_len(resp));
3235 if (o_stream_flush(conn->conn.output) < 0) {
3236 i_fatal("server: Flush error: %s",
3237 o_stream_get_error(conn->conn.output));
3238 }
3239 }
3240
test_server_idle_connections(unsigned int index)3241 static void test_server_idle_connections(unsigned int index)
3242 {
3243 test_server_init = test_idle_connections_init;
3244 test_server_input = test_idle_connections_input;
3245 test_server_run(index);
3246 }
3247
3248 /* client */
3249
3250 struct _idle_connections {
3251 struct http_client *client;
3252 unsigned int max, count;
3253 struct timeout *to;
3254 };
3255
3256 static void
test_client_idle_connections_response_stage2(const struct http_response * resp,struct _idle_connections * ctx)3257 test_client_idle_connections_response_stage2(const struct http_response *resp,
3258 struct _idle_connections *ctx)
3259 {
3260 test_client_assert_response(
3261 resp, resp->status == 200);
3262
3263 if (--ctx->count == 0) {
3264 i_free(ctx);
3265 io_loop_stop(ioloop);
3266 }
3267 }
3268
test_client_idle_connections_stage2_start(struct _idle_connections * ctx)3269 static void test_client_idle_connections_stage2_start(struct _idle_connections *ctx)
3270 {
3271 struct http_client_request *hreq;
3272 unsigned int i;
3273
3274 if (debug)
3275 i_debug("STAGE 2");
3276
3277 timeout_remove(&ctx->to);
3278
3279 ctx->count = ctx->max;
3280
3281 for (i = 0; i < ctx->count; i++) {
3282 hreq = http_client_request(
3283 ctx->client, "GET", net_ip2addr(&bind_ip),
3284 t_strdup_printf("/idle-connections-stage2-%d.txt", i),
3285 test_client_idle_connections_response_stage2, ctx);
3286 http_client_request_set_port(hreq, bind_ports[0]);
3287 http_client_request_submit(hreq);
3288 }
3289 }
3290
3291 static void
test_client_idle_connections_response_stage1(const struct http_response * resp,struct _idle_connections * ctx)3292 test_client_idle_connections_response_stage1(const struct http_response *resp,
3293 struct _idle_connections *ctx)
3294 {
3295 test_client_assert_response(resp, resp->status == 200);
3296
3297 if (--ctx->count == 0) {
3298 if (debug)
3299 i_debug("START STAGE 2");
3300 ctx->to = timeout_add_short(
3301 550, test_client_idle_connections_stage2_start, ctx);
3302 }
3303 }
3304
3305 static bool
test_client_idle_connections(const struct http_client_settings * client_set)3306 test_client_idle_connections(const struct http_client_settings *client_set)
3307 {
3308 struct http_client_request *hreq;
3309 struct _idle_connections *ctx;
3310 unsigned int i;
3311
3312 if (debug)
3313 i_debug("STAGE 1");
3314
3315 ctx = i_new(struct _idle_connections, 1);
3316 ctx->max = client_set->max_parallel_connections;
3317 ctx->count = client_set->max_parallel_connections;
3318
3319 ctx->client = http_client = http_client_init(client_set);
3320
3321 for (i = 0; i < ctx->count; i++) {
3322 hreq = http_client_request(
3323 ctx->client, "GET", net_ip2addr(&bind_ip),
3324 t_strdup_printf("/idle-connections-stage1-%d.txt", i),
3325 test_client_idle_connections_response_stage1, ctx);
3326 http_client_request_set_port(hreq, bind_ports[0]);
3327 http_client_request_submit(hreq);
3328 }
3329
3330 return TRUE;
3331 }
3332
3333 /* test */
3334
test_idle_connections(void)3335 static void test_idle_connections(void)
3336 {
3337 struct http_client_settings http_client_set;
3338
3339 test_client_defaults(&http_client_set);
3340 http_client_set.max_idle_time_msecs = 1000;
3341
3342 test_begin("idle connections (max 1)");
3343 http_client_set.max_parallel_connections = 1;
3344 test_run_client_server(&http_client_set,
3345 test_client_idle_connections,
3346 test_server_idle_connections, 1, NULL);
3347 test_end();
3348
3349 test_begin("idle connections (max 2)");
3350 http_client_set.max_parallel_connections = 2;
3351 test_run_client_server(&http_client_set,
3352 test_client_idle_connections,
3353 test_server_idle_connections, 1, NULL);
3354 test_end();
3355
3356 test_begin("idle connections (max 4)");
3357 http_client_set.max_parallel_connections = 4;
3358 test_run_client_server(&http_client_set,
3359 test_client_idle_connections,
3360 test_server_idle_connections, 1, NULL);
3361 test_end();
3362
3363 test_begin("idle connections (max 8)");
3364 http_client_set.max_parallel_connections = 8;
3365 test_run_client_server(&http_client_set,
3366 test_client_idle_connections,
3367 test_server_idle_connections, 1, NULL);
3368 test_end();
3369 }
3370
3371 /*
3372 * Idle hosts
3373 */
3374
3375 /* dns */
3376
3377 static void
test_dns_idle_hosts_input(struct server_connection * conn)3378 test_dns_idle_hosts_input(struct server_connection *conn)
3379 {
3380 const char *line;
3381
3382 if (!conn->version_sent) {
3383 conn->version_sent = TRUE;
3384 o_stream_nsend_str(conn->conn.output, "VERSION\tdns\t1\t0\n");
3385 }
3386
3387 while ((line = i_stream_read_next_line(conn->conn.input)) != NULL) {
3388 if (str_begins(line, "VERSION"))
3389 continue;
3390 if (debug)
3391 i_debug("DNS REQUEST: %s", line);
3392
3393 if (strcmp(line, "IP\thosta") == 0) {
3394 o_stream_nsend_str(conn->conn.output,
3395 "0\t127.0.0.1\n");
3396 } else {
3397 i_sleep_msecs(300);
3398 o_stream_nsend_str(
3399 conn->conn.output,
3400 t_strdup_printf("%d\tFAIL\n", EAI_FAIL));
3401 }
3402 }
3403 }
3404
test_dns_idle_hosts(void)3405 static void test_dns_idle_hosts(void)
3406 {
3407 test_server_input = test_dns_idle_hosts_input;
3408 test_server_run(0);
3409 }
3410
3411 /* server */
3412
3413 struct _idle_hosts_sctx {
3414 bool eoh;
3415 };
3416
test_idle_hosts_init(struct server_connection * conn)3417 static int test_idle_hosts_init(struct server_connection *conn)
3418 {
3419 struct _idle_hosts_sctx *ctx;
3420
3421 ctx = p_new(conn->pool, struct _idle_hosts_sctx, 1);
3422 conn->context = ctx;
3423 return 0;
3424 }
3425
test_idle_hosts_input(struct server_connection * conn)3426 static void test_idle_hosts_input(struct server_connection *conn)
3427 {
3428 struct _idle_hosts_sctx *ctx = conn->context;
3429 const char *line;
3430
3431 while ((line = i_stream_read_next_line(conn->conn.input)) != NULL) {
3432 if (*line == '\0') {
3433 ctx->eoh = TRUE;
3434 break;
3435 }
3436 }
3437
3438 if (conn->conn.input->stream_errno != 0) {
3439 i_fatal("server: Stream error: %s",
3440 i_stream_get_error(conn->conn.input));
3441 }
3442 if (line == NULL) {
3443 if (conn->conn.input->eof)
3444 server_connection_deinit(&conn);
3445 return;
3446 }
3447
3448 i_assert(ctx->eoh);
3449 ctx->eoh = FALSE;
3450
3451 string_t *resp;
3452
3453 resp = t_str_new(512);
3454 str_printfa(resp,
3455 "HTTP/1.1 200 OK\r\n"
3456 "Content-Length: 0\r\n"
3457 "\r\n");
3458 o_stream_nsend(conn->conn.output, str_data(resp), str_len(resp));
3459 if (o_stream_flush(conn->conn.output) < 0) {
3460 i_fatal("server: Flush error: %s",
3461 o_stream_get_error(conn->conn.output));
3462 }
3463 }
3464
test_server_idle_hosts(unsigned int index)3465 static void test_server_idle_hosts(unsigned int index)
3466 {
3467 test_server_init = test_idle_hosts_init;
3468 test_server_input = test_idle_hosts_input;
3469 test_server_run(index);
3470 }
3471
3472 /* client */
3473
3474 struct _idle_hosts {
3475 struct http_client *client;
3476 struct http_client_request *hostb_req;
3477 unsigned int count;
3478 };
3479
3480 static void
test_client_idle_hosts_response_hosta(const struct http_response * resp,struct _idle_hosts * ctx)3481 test_client_idle_hosts_response_hosta(const struct http_response *resp,
3482 struct _idle_hosts *ctx)
3483 {
3484 test_client_assert_response(resp, resp->status == 200);
3485
3486 if (--ctx->count == 0) {
3487 i_free(ctx);
3488 io_loop_stop(ioloop);
3489 }
3490 }
3491
3492 static void
test_client_idle_hosts_response_hostb(const struct http_response * resp,struct _idle_hosts * ctx)3493 test_client_idle_hosts_response_hostb(const struct http_response *resp,
3494 struct _idle_hosts *ctx)
3495 {
3496 test_client_assert_response(resp,
3497 resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED);
3498
3499 if (http_client_request_try_retry(ctx->hostb_req)) {
3500 if (debug)
3501 i_debug("retrying");
3502 return;
3503 }
3504
3505 ctx->hostb_req = NULL;
3506 }
3507
3508 static bool
test_client_idle_hosts(const struct http_client_settings * client_set)3509 test_client_idle_hosts(const struct http_client_settings *client_set)
3510 {
3511 struct http_client_request *hreq;
3512 struct _idle_hosts *ctx;
3513
3514 ctx = i_new(struct _idle_hosts, 1);
3515 ctx->count = 2;
3516
3517 ctx->client = http_client = http_client_init(client_set);
3518
3519 hreq = http_client_request(
3520 ctx->client, "GET", "hosta",
3521 t_strdup_printf("/idle-hosts-a1.txt"),
3522 test_client_idle_hosts_response_hosta, ctx);
3523 http_client_request_set_port(hreq, bind_ports[0]);
3524 http_client_request_submit(hreq);
3525
3526 hreq = ctx->hostb_req = http_client_request(
3527 ctx->client, "GET", "hostb",
3528 t_strdup_printf("/idle-hosts-b.txt"),
3529 test_client_idle_hosts_response_hostb, ctx);
3530 http_client_request_set_port(hreq, bind_ports[0]);
3531 http_client_request_submit(hreq);
3532
3533 hreq = http_client_request(
3534 ctx->client, "GET", "hosta",
3535 t_strdup_printf("/idle-hosts-a2.txt"),
3536 test_client_idle_hosts_response_hosta, ctx);
3537 http_client_request_set_port(hreq, bind_ports[0]);
3538 http_client_request_delay_msecs(hreq, 600);
3539 http_client_request_submit(hreq);
3540
3541 return TRUE;
3542 }
3543
3544 /* test */
3545
test_idle_hosts(void)3546 static void test_idle_hosts(void)
3547 {
3548 struct http_client_settings http_client_set;
3549
3550 test_client_defaults(&http_client_set);
3551 http_client_set.dns_client_socket_path = "./dns-test";
3552 http_client_set.dns_ttl_msecs = 400;
3553 http_client_set.max_parallel_connections = 1;
3554 http_client_set.max_idle_time_msecs = 100;
3555 http_client_set.max_attempts = 2;
3556
3557 test_begin("idle hosts");
3558 test_run_client_server(&http_client_set,
3559 test_client_idle_hosts,
3560 test_server_idle_hosts, 1,
3561 test_dns_idle_hosts);
3562 test_end();
3563 }
3564
3565 /*
3566 * All tests
3567 */
3568
3569 static void (*const test_functions[])(void) = {
3570 test_unconfigured_ssl,
3571 test_unconfigured_ssl_abort,
3572 test_invalid_url,
3573 test_host_lookup_failed,
3574 test_connection_refused,
3575 test_connection_lost_prematurely,
3576 test_connection_timed_out,
3577 test_invalid_redirect,
3578 test_unseekable_redirect,
3579 test_unseekable_retry,
3580 test_broken_payload,
3581 test_retry_payload,
3582 test_connection_lost,
3583 test_connection_lost_100,
3584 test_connection_lost_sub_ioloop,
3585 test_early_success,
3586 test_bad_response,
3587 test_request_timed_out,
3588 test_request_aborted_early,
3589 test_request_failed_blocking,
3590 test_client_deinit_early,
3591 test_retry_with_delay,
3592 test_dns_service_failure,
3593 test_dns_timeout,
3594 test_dns_lookup_failure,
3595 test_dns_lookup_ttl,
3596 test_peer_reuse_failure,
3597 test_reconnect_failure,
3598 test_multi_ip_attempts,
3599 test_idle_connections,
3600 test_idle_hosts,
3601 NULL
3602 };
3603
3604 /*
3605 * Test client
3606 */
3607
test_client_defaults(struct http_client_settings * http_set)3608 static void test_client_defaults(struct http_client_settings *http_set)
3609 {
3610 /* client settings */
3611 i_zero(http_set);
3612 http_set->max_idle_time_msecs = 5*1000;
3613 http_set->max_parallel_connections = 1;
3614 http_set->max_pipelined_requests = 1;
3615 http_set->max_redirects = 0;
3616 http_set->max_attempts = 1;
3617 http_set->debug = debug;
3618 }
3619
test_client_progress_timeout(void * context ATTR_UNUSED)3620 static void test_client_progress_timeout(void *context ATTR_UNUSED)
3621 {
3622 /* Terminate test due to lack of progress */
3623 test_assert(FALSE);
3624 timeout_remove(&to_client_progress);
3625 io_loop_stop(current_ioloop);
3626 }
3627
3628 static bool
test_client_init(test_client_init_t client_test,const struct http_client_settings * client_set)3629 test_client_init(test_client_init_t client_test,
3630 const struct http_client_settings *client_set)
3631 {
3632 i_assert(client_test != NULL);
3633 if (!client_test(client_set))
3634 return FALSE;
3635
3636 to_client_progress = timeout_add(CLIENT_PROGRESS_TIMEOUT*1000,
3637 test_client_progress_timeout, NULL);
3638 return TRUE;
3639 }
3640
test_client_deinit(void)3641 static void test_client_deinit(void)
3642 {
3643 timeout_remove(&to_client_progress);
3644
3645 if (http_client != NULL)
3646 http_client_deinit(&http_client);
3647 }
3648
3649 static void
test_client_run(test_client_init_t client_test,const struct http_client_settings * client_set)3650 test_client_run(test_client_init_t client_test,
3651 const struct http_client_settings *client_set)
3652 {
3653 if (test_client_init(client_test, client_set))
3654 io_loop_run(ioloop);
3655 test_client_deinit();
3656 }
3657
3658 /*
3659 * Test server
3660 */
3661
3662 /* client connection */
3663
server_connection_input(struct connection * _conn)3664 static void server_connection_input(struct connection *_conn)
3665 {
3666 struct server_connection *conn = (struct server_connection *)_conn;
3667
3668 test_server_input(conn);
3669 }
3670
server_connection_init(int fd)3671 static void server_connection_init(int fd)
3672 {
3673 struct server_connection *conn;
3674 pool_t pool;
3675
3676 net_set_nonblock(fd, TRUE);
3677
3678 pool = pool_alloconly_create("server connection", 512);
3679 conn = p_new(pool, struct server_connection, 1);
3680 conn->pool = pool;
3681
3682 connection_init_server(server_conn_list, &conn->conn,
3683 "server connection", fd, fd);
3684
3685 if (test_server_init != NULL) {
3686 if (test_server_init(conn) != 0)
3687 return;
3688 }
3689 }
3690
server_connection_deinit(struct server_connection ** _conn)3691 static void server_connection_deinit(struct server_connection **_conn)
3692 {
3693 struct server_connection *conn = *_conn;
3694
3695 *_conn = NULL;
3696
3697 if (test_server_deinit != NULL)
3698 test_server_deinit(conn);
3699
3700 connection_deinit(&conn->conn);
3701 pool_unref(&conn->pool);
3702 }
3703
server_connection_destroy(struct connection * _conn)3704 static void server_connection_destroy(struct connection *_conn)
3705 {
3706 struct server_connection *conn = (struct server_connection *)_conn;
3707
3708 server_connection_deinit(&conn);
3709 }
3710
server_connection_accept(void * context ATTR_UNUSED)3711 static void server_connection_accept(void *context ATTR_UNUSED)
3712 {
3713 int fd;
3714
3715 /* accept new client */
3716 fd = net_accept(fd_listen, NULL, NULL);
3717 if (fd == -1)
3718 return;
3719 if (fd == -2) {
3720 i_fatal("test server: accept() failed: %m");
3721 }
3722
3723 server_connection_init(fd);
3724 }
3725
3726 /* */
3727
3728 static struct connection_settings server_connection_set = {
3729 .input_max_size = SIZE_MAX,
3730 .output_max_size = SIZE_MAX,
3731 .client = FALSE
3732 };
3733
3734 static const struct connection_vfuncs server_connection_vfuncs = {
3735 .destroy = server_connection_destroy,
3736 .input = server_connection_input
3737 };
3738
test_server_run(unsigned int index)3739 static void test_server_run(unsigned int index)
3740 {
3741 server_index = index;
3742
3743 /* open server socket */
3744 io_listen = io_add(fd_listen, IO_READ, server_connection_accept, NULL);
3745
3746 server_conn_list = connection_list_init(&server_connection_set,
3747 &server_connection_vfuncs);
3748
3749 io_loop_run(ioloop);
3750
3751 /* close server socket */
3752 io_remove(&io_listen);
3753
3754 connection_list_deinit(&server_conn_list);
3755 }
3756
3757 /*
3758 * Tests
3759 */
3760
3761 struct test_server_data {
3762 unsigned int index;
3763 test_server_init_t server_test;
3764 };
3765
test_open_server_fd(in_port_t * bind_port)3766 static int test_open_server_fd(in_port_t *bind_port)
3767 {
3768 int fd = net_listen(&bind_ip, bind_port, 128);
3769 if (debug)
3770 i_debug("server listening on %u", *bind_port);
3771 if (fd == -1) {
3772 i_fatal("listen(%s:%u) failed: %m",
3773 net_ip2addr(&bind_ip), *bind_port);
3774 }
3775 return fd;
3776 }
3777
test_run_server(struct test_server_data * data)3778 static int test_run_server(struct test_server_data *data)
3779 {
3780 i_set_failure_prefix("SERVER[%u]: ", data->index + 1);
3781
3782 if (debug)
3783 i_debug("PID=%s", my_pid);
3784
3785 test_subprocess_notify_signal_send_parent(SIGHUP);
3786 ioloop = io_loop_create();
3787 data->server_test(data->index);
3788 io_loop_destroy(&ioloop);
3789
3790 if (debug)
3791 i_debug("Terminated");
3792
3793 i_close_fd(&fd_listen);
3794 i_free(bind_ports);
3795 main_deinit();
3796 return 0;
3797 }
3798
test_run_dns(test_dns_init_t dns_test)3799 static int test_run_dns(test_dns_init_t dns_test)
3800 {
3801 i_set_failure_prefix("DNS: ");
3802
3803 if (debug)
3804 i_debug("PID=%s", my_pid);
3805
3806 ioloop = io_loop_create();
3807 dns_test();
3808 io_loop_destroy(&ioloop);
3809
3810 if (debug)
3811 i_debug("Terminated");
3812
3813 i_close_fd(&fd_listen);
3814 i_free(bind_ports);
3815 main_deinit();
3816 return 0;
3817 }
3818
3819 static void
test_run_client(const struct http_client_settings * client_set,test_client_init_t client_test)3820 test_run_client(const struct http_client_settings *client_set,
3821 test_client_init_t client_test)
3822 {
3823 i_set_failure_prefix("CLIENT: ");
3824
3825 if (debug)
3826 i_debug("PID=%s", my_pid);
3827
3828 ioloop = io_loop_create();
3829 test_client_run(client_test, client_set);
3830 io_loop_destroy(&ioloop);
3831
3832 if (debug)
3833 i_debug("Terminated");
3834 }
3835
3836 static void
test_run_client_server(const struct http_client_settings * client_set,test_client_init_t client_test,test_server_init_t server_test,unsigned int server_tests_count,test_dns_init_t dns_test)3837 test_run_client_server(const struct http_client_settings *client_set,
3838 test_client_init_t client_test,
3839 test_server_init_t server_test,
3840 unsigned int server_tests_count,
3841 test_dns_init_t dns_test)
3842 {
3843 unsigned int i;
3844
3845 test_subprocess_notify_signal_reset(SIGHUP);
3846 test_server_init = NULL;
3847 test_server_deinit = NULL;
3848 test_server_input = NULL;
3849
3850 if (server_tests_count > 0) {
3851 int fds[server_tests_count];
3852
3853 bind_ports = i_new(in_port_t, server_tests_count);
3854 for (i = 0; i < server_tests_count; i++)
3855 fds[i] = test_open_server_fd(&bind_ports[i]);
3856
3857 for (i = 0; i < server_tests_count; i++) {
3858 struct test_server_data data;
3859
3860 i_zero(&data);
3861 data.index = i;
3862 data.server_test = server_test;
3863
3864 /* Fork server */
3865 fd_listen = fds[i];
3866 test_subprocess_fork(test_run_server, &data, FALSE);
3867 i_close_fd(&fd_listen);
3868 test_subprocess_notify_signal_wait(SIGHUP, 10000);
3869 test_subprocess_notify_signal_reset(SIGHUP);
3870 }
3871 }
3872
3873 if (dns_test != NULL) {
3874 int fd;
3875
3876 i_unlink_if_exists("./dns-test");
3877 fd = net_listen_unix("./dns-test", 128);
3878 if (fd == -1) {
3879 i_fatal("listen(./dns-test) failed: %m");
3880 }
3881
3882 /* Fork DNS service */
3883 fd_listen = fd;
3884 test_subprocess_fork(test_run_dns, dns_test, FALSE);
3885 i_close_fd(&fd_listen);
3886 }
3887
3888 /* Run client */
3889 test_run_client(client_set, client_test);
3890
3891 i_unset_failure_prefix();
3892 test_subprocess_kill_all(SERVER_KILL_TIMEOUT_SECS);
3893 i_free(bind_ports);
3894
3895 i_unlink_if_exists("./dns-test");
3896 }
3897
3898 /*
3899 * Main
3900 */
3901
main_init(void)3902 static void main_init(void)
3903 {
3904 /* nothing yet */
3905 }
3906
main_deinit(void)3907 static void main_deinit(void)
3908 {
3909 /* nothing yet; also called from sub-processes */
3910 }
3911
main(int argc,char * argv[])3912 int main(int argc, char *argv[])
3913 {
3914 int c;
3915 int ret;
3916
3917 lib_init();
3918 main_init();
3919
3920 while ((c = getopt(argc, argv, "D")) > 0) {
3921 switch (c) {
3922 case 'D':
3923 debug = TRUE;
3924 break;
3925 default:
3926 i_fatal("Usage: %s [-D]", argv[0]);
3927 }
3928 }
3929
3930 test_subprocesses_init(debug);
3931
3932 /* listen on localhost */
3933 i_zero(&bind_ip);
3934 bind_ip.family = AF_INET;
3935 bind_ip.u.ip4.s_addr = htonl(INADDR_LOOPBACK);
3936
3937 ret = test_run(test_functions);
3938
3939 test_subprocesses_deinit();
3940 main_deinit();
3941 lib_deinit();
3942
3943 return ret;
3944 }
3945