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