comparison src/lib-http/http-client-connection.c @ 15394:107c8b2c9594

lib-http: Added initial HTTP client implementation.
author Stephan Bosch <stephan@rename-it.nl>
date Sat, 24 Nov 2012 00:30:14 +0200
parents
children 9cab24687819
comparison
equal deleted inserted replaced
15393:b40bda50541c 15394:107c8b2c9594
1 /* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "net.h"
5 #include "str.h"
6 #include "hash.h"
7 #include "array.h"
8 #include "ioloop.h"
9 #include "istream.h"
10 #include "ostream.h"
11 #include "iostream-rawlog.h"
12 #include "iostream-ssl.h"
13 #include "http-response-parser.h"
14
15 #include "http-client-private.h"
16
17 /*
18 * Logging
19 */
20
21 static inline void
22 http_client_connection_debug(struct http_client_connection *conn,
23 const char *format, ...) ATTR_FORMAT(2, 3);
24 static inline void
25 http_client_connection_error(struct http_client_connection *conn,
26 const char *format, ...) ATTR_FORMAT(2, 3);
27
28 static inline void
29 http_client_connection_debug(struct http_client_connection *conn,
30 const char *format, ...)
31 {
32 va_list args;
33
34 if (conn->client->set.debug) {
35
36 va_start(args, format);
37 i_debug("http-client: conn %s: %s",
38 http_client_connection_label(conn), t_strdup_vprintf(format, args));
39 va_end(args);
40 }
41 }
42
43 static inline void
44 http_client_connection_error(struct http_client_connection *conn,
45 const char *format, ...)
46 {
47 va_list args;
48
49 va_start(args, format);
50 i_error("http-client: conn %s: %s",
51 http_client_connection_label(conn), t_strdup_vprintf(format, args));
52 va_end(args);
53 }
54
55
56 /*
57 * Connection
58 */
59
60 static void http_client_connection_input(struct connection *_conn);
61
62 bool http_client_connection_is_ready(struct http_client_connection *conn)
63 {
64 return (conn->connected && !conn->output_locked &&
65 array_count(&conn->request_wait_list) <
66 conn->client->set.max_pipelined_requests);
67 }
68
69 static void
70 http_client_connection_retry_requests(struct http_client_connection *conn,
71 unsigned int status, const char *error)
72 {
73 struct http_client_request **req;
74
75 array_foreach_modifiable(&conn->request_wait_list, req) {
76 http_client_request_retry(*req, status, error);
77 }
78 array_clear(&conn->request_wait_list);
79 }
80
81 static void
82 http_client_connection_server_close(struct http_client_connection **_conn)
83 {
84 struct http_client_connection *conn = *_conn;
85 struct http_client_request **req;
86
87 conn->connected = FALSE;
88 conn->closing = TRUE;
89
90 http_client_connection_debug(conn,
91 "Server explicitly closed connection");
92
93 array_foreach_modifiable(&conn->request_wait_list, req) {
94 http_client_request_resubmit(*req);
95 }
96 array_clear(&conn->request_wait_list);
97
98 if (conn->client->ioloop != NULL)
99 io_loop_stop(conn->client->ioloop);
100
101 http_client_connection_free(_conn);
102 }
103
104 static void
105 http_client_connection_abort_temp_error(struct http_client_connection **_conn,
106 unsigned int status, const char *error)
107 {
108 struct http_client_connection *conn = *_conn;
109
110 conn->connected = FALSE;
111 conn->closing = TRUE;
112
113 http_client_connection_retry_requests(conn, status, error);
114 http_client_connection_free(_conn);
115 }
116
117 static void
118 http_client_connection_abort_error(struct http_client_connection **_conn,
119 unsigned int status, const char *error)
120 {
121 struct http_client_connection *conn = *_conn;
122 struct http_client_request **req;
123
124 conn->connected = FALSE;
125 conn->closing = TRUE;
126
127 array_foreach_modifiable(&conn->request_wait_list, req) {
128 http_client_request_error(*req, status, error);
129 }
130 array_clear(&conn->request_wait_list);
131 http_client_connection_free(_conn);
132 }
133
134 static void
135 http_client_connection_idle_timeout(struct http_client_connection *conn)
136 {
137 http_client_connection_debug(conn, "Idle connection timed out");
138
139 http_client_connection_free(&conn);
140 }
141
142 static void
143 http_client_connection_check_idle(struct http_client_connection *conn)
144 {
145 unsigned int timeout, count;
146
147 if (array_count(&conn->request_wait_list) == 0 &&
148 conn->incoming_payload == NULL &&
149 conn->client->set.max_idle_time_msecs > 0) {
150
151 if (conn->to_idle != NULL) {
152 /* timeout already set */
153 return;
154 }
155
156 http_client_connection_debug(conn,
157 "No more requests queued; going idle");
158
159 if (conn->client->ioloop != NULL)
160 io_loop_stop(conn->client->ioloop);
161
162 count = array_count(&conn->peer->conns);
163 i_assert(count > 0);
164
165 /* set timeout for this connection */
166 if (count > conn->client->set.max_parallel_connections) {
167 /* instant death for (urgent) connections above limit */
168 timeout = 0;
169 } else {
170 /* kill duplicate connections quicker;
171 linearly based on the number of connections */
172 timeout = (conn->client->set.max_parallel_connections - count) *
173 (conn->client->set.max_idle_time_msecs /
174 conn->client->set.max_parallel_connections);
175 }
176
177 conn->to_idle =
178 timeout_add(timeout, http_client_connection_idle_timeout, conn);
179
180 } else {
181 /* there should be no idle timeout */
182 i_assert(conn->to_idle == NULL);
183 }
184 }
185
186 static void
187 http_client_connection_continue_timeout(struct http_client_connection *conn)
188 {
189 struct http_client_request *const *req_idx;
190 struct http_client_request *req;
191
192 if (conn->to_response != NULL)
193 timeout_remove(&conn->to_response);
194 conn->peer->no_payload_sync = TRUE;
195
196 http_client_connection_debug(conn,
197 "Expected 100-continue response timed out; sending payload anyway");
198
199 req_idx = array_idx(&conn->request_wait_list, 0);
200 req = req_idx[0];
201
202 // FIXME: we should mark this in the peer object for next requests
203 conn->payload_continue = TRUE;
204 if (http_client_request_send_more(req) < 0) {
205 http_client_connection_abort_temp_error(&conn,
206 HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST, "Failed to send request");
207 }
208 }
209
210 bool http_client_connection_next_request(struct http_client_connection *conn)
211 {
212 struct http_client_request *req = NULL;
213
214 if (!http_client_connection_is_ready(conn)) {
215
216 http_client_connection_debug(conn, "Not ready for next request");
217 return FALSE;
218 }
219
220 /* claim request, but no urgent request can be second in line */
221 req = http_client_peer_claim_request(conn->peer,
222 array_count(&conn->request_wait_list) > 0);
223 if (req == NULL) {
224 http_client_connection_check_idle(conn);
225 return FALSE;
226 }
227
228 if (conn->to_idle != NULL)
229 timeout_remove(&conn->to_idle);
230
231 req->conn = conn;
232 conn->payload_continue = FALSE;
233 if (conn->peer->no_payload_sync)
234 req->payload_sync = FALSE;
235 array_append(&conn->request_wait_list, &req, 1);
236 http_client_request_ref(req);
237
238 if (http_client_request_send(req) < 0) {
239 http_client_connection_abort_temp_error(&conn,
240 HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST,
241 "Failed to send request");
242 return FALSE;
243 }
244
245 /* https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21;
246 Section 6.1.2.1:
247
248 Because of the presence of older implementations, the protocol allows
249 ambiguous situations in which a client might send "Expect: 100-continue"
250 without receiving either a 417 (Expectation Failed) or a 100 (Continue)
251 status code. Therefore, when a client sends this header field to an
252 origin server (possibly via a proxy) from which it has never seen a 100
253 (Continue) status code, the client SHOULD NOT wait for an indefinite
254 period before sending the payload body.
255 */
256 if (req->payload_sync) {
257 i_assert(req->input_size > 0);
258 i_assert(conn->to_response == NULL);
259 conn->to_response = timeout_add(HTTP_CLIENT_CONTINUE_TIMEOUT_MSECS,
260 http_client_connection_continue_timeout, conn);
261 }
262
263 return TRUE;
264 }
265
266 static void http_client_connection_destroy(struct connection *_conn)
267 {
268 struct http_client_connection *conn =
269 (struct http_client_connection *)_conn;
270
271 conn->closing = TRUE;
272 conn->connected = FALSE;
273
274 switch (_conn->disconnect_reason) {
275 case CONNECTION_DISCONNECT_CONNECT_TIMEOUT:
276 http_client_peer_connection_failure(conn->peer);
277 break;
278 case CONNECTION_DISCONNECT_CONN_CLOSED:
279 /* retry pending requests if possible */
280 http_client_connection_retry_requests(conn,
281 HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST, "Connection lost");
282 default:
283 break;
284 }
285
286 http_client_connection_free(&conn);
287 }
288
289 static void http_client_payload_finished(struct http_client_connection *conn)
290 {
291 timeout_remove(&conn->to_input);
292 conn->conn.io = io_add(conn->conn.fd_in, IO_READ,
293 http_client_connection_input, &conn->conn);
294 }
295
296 static void http_client_payload_destroyed(struct http_client_connection *conn)
297 {
298 i_assert(conn->incoming_payload != NULL);
299 i_assert(conn->pending_request != NULL);
300 i_assert(conn->conn.io == NULL);
301
302 http_client_connection_debug(conn, "Response payload stream destroyed");
303
304 /* caller is allowed to change the socket fd to blocking while reading
305 the payload. make sure here that it's switched back. */
306 net_set_nonblock(conn->conn.fd_in, TRUE);
307
308 conn->incoming_payload = NULL;
309
310 http_client_request_finish(&conn->pending_request);
311 conn->pending_request = NULL;
312
313 if (conn->close_indicated) {
314 http_client_connection_server_close(&conn);
315 return;
316 }
317
318 /* input stream may have pending input. make sure input handler
319 gets called (but don't do it directly, since we get get here
320 somewhere from the API user's code, which we can't really know what
321 state it is in). this call also triggers sending a new request if
322 necessary. */
323 conn->to_input =
324 timeout_add_short(0, http_client_connection_input, &conn->conn);
325 }
326
327 static bool
328 http_client_connection_return_response(struct http_client_connection *conn,
329 struct http_client_request *req, struct http_response *response)
330 {
331 struct istream *payload;
332
333 i_assert(conn->incoming_payload == NULL);
334 i_assert(conn->pending_request == NULL);
335
336 req->state = HTTP_REQUEST_STATE_GOT_RESPONSE;
337
338 if (response->payload != NULL) {
339 /* wrap the stream to capture the destroy event without destroying the
340 actual payload stream. */
341 conn->incoming_payload = response->payload =
342 i_stream_create_limit(response->payload, (uoff_t)-1);
343 i_stream_set_destroy_callback(response->payload,
344 http_client_payload_destroyed,
345 conn);
346 /* the callback may add its own I/O, so we need to remove
347 our one before calling it */
348 io_remove(&conn->conn.io);
349 }
350
351 http_client_request_callback(req, response);
352
353 // FIXME: conn may be freed at this point..
354
355 if (response->payload != NULL) {
356 req->state = HTTP_REQUEST_STATE_PAYLOAD_IN;
357 payload = response->payload;
358 response->payload = NULL;
359 conn->pending_request = req;
360 i_stream_unref(&payload);
361 if (conn->to_input != NULL) {
362 /* already finished reading the payload */
363 http_client_payload_finished(conn);
364 }
365 } else {
366 http_client_request_finish(&req);
367 }
368
369 if (conn->incoming_payload == NULL) {
370 i_assert(conn->conn.io != NULL);
371 return TRUE;
372 }
373
374 return FALSE;
375 }
376
377 static void http_client_connection_input(struct connection *_conn)
378 {
379 struct http_client_connection *conn =
380 (struct http_client_connection *)_conn;
381 struct http_response *response;
382 struct http_client_request *const *req_idx;
383 struct http_client_request *req = NULL;
384 int finished = 0, ret;
385 const char *error;
386 bool no_payload = FALSE;
387
388 i_assert(conn->incoming_payload == NULL);
389
390 if (conn->to_input != NULL) {
391 /* We came here from a timeout added by
392 http_client_payload_destroyed(). The IO couldn't be added
393 back immediately in there, because the HTTP API user may
394 still have had its own IO pointed to the same fd. It should
395 be removed by now, so we can add it back. */
396 http_client_payload_finished(conn);
397 finished++;
398 }
399
400 /* get first waiting request */
401 if (array_count(&conn->request_wait_list) > 0) {
402 req_idx = array_idx(&conn->request_wait_list, 0);
403 req = req_idx[0];
404
405 /* https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21
406 Section 3.3.2:
407
408 A server MAY send a Content-Length header field in a response to a
409 HEAD request [...]
410 */
411 no_payload = (strcmp(req->method, "HEAD") == 0);
412 }
413
414 // FIXME: handle somehow if server replies before request->input is at EOF
415 while ((ret=http_response_parse_next
416 (conn->http_parser, no_payload, &response, &error)) > 0) {
417 bool aborted;
418
419 if (req == NULL) {
420 /* server sent response without any requests in the wait list */
421 http_client_connection_error(conn, "Got unexpected input from server");
422 http_client_connection_free(&conn);
423 return;
424 }
425
426 /* https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21
427 Section 7.2:
428
429 A client MUST be prepared to accept one or more 1xx status responses
430 prior to a regular response, even if the client does not expect a 100
431 (Continue) status message. Unexpected 1xx status responses MAY be
432 ignored by a user agent.
433 */
434 if (req->payload_sync && response->status == 100) {
435 conn->payload_continue = TRUE;
436 if (conn->to_response != NULL)
437 timeout_remove(&conn->to_response);
438 http_client_connection_debug(conn,
439 "Got expected 100-continue response");
440 if (http_client_request_send_more(req) < 0) {
441 http_client_connection_abort_temp_error(&conn,
442 HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST,
443 "Failed to send request");
444 }
445 return;
446 } else if (response->status / 100 == 1) {
447 /* ignore them for now */
448 http_client_connection_debug(conn,
449 "Got unexpected %u response; ignoring", response->status);
450 continue;
451 } // FIXME: handle 417 Expectation Failed
452
453 http_client_connection_debug(conn,
454 "Got %u response for request %s",
455 response->status, http_client_request_label(req));
456
457 /* remove request from queue */
458 array_delete(&conn->request_wait_list, 0, 1);
459 aborted = (req->state == HTTP_REQUEST_STATE_ABORTED);
460 http_client_request_unref(&req);
461
462 conn->close_indicated = response->connection_close;
463
464 if (!aborted) {
465 if (response->status / 100 == 3) {
466 /* redirect */
467 http_client_request_redirect(req, response->location);
468 } else {
469 /* response for application */
470 if (!http_client_connection_return_response(conn, req, response))
471 return;
472 }
473
474 finished++;
475 }
476
477 /* server closing connection? */
478 if (conn->close_indicated) {
479 http_client_connection_server_close(&conn);
480 return;
481 }
482
483 /* get next waiting request */
484 if (array_count(&conn->request_wait_list) > 0) {
485 req_idx = array_idx(&conn->request_wait_list, 0);
486 req = req_idx[0];
487 no_payload = (strcmp(req->method, "HEAD") == 0);
488 } else {
489 req = NULL;
490 no_payload = FALSE;
491 }
492 }
493
494 if (ret <= 0 &&
495 (conn->conn.input->eof || conn->conn.input->stream_errno != 0)) {
496 int stream_errno = conn->conn.input->stream_errno;
497 http_client_connection_debug(conn,
498 "Lost connection to server (error=%s)",
499 stream_errno != 0 ? strerror(stream_errno) : "EOF");
500 http_client_connection_abort_temp_error(&conn,
501 HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST, "Connection lost");
502 return;
503 }
504
505 if (ret < 0) {
506 http_client_connection_abort_error(&conn,
507 HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE, error);
508 return;
509 }
510
511 if (finished > 0) {
512 /* room for new requests */
513 http_client_peer_handle_requests(conn->peer);
514 http_client_connection_check_idle(conn);
515 }
516 }
517
518 static int http_client_connection_output(struct http_client_connection *conn)
519 {
520 struct http_client_request *const *req_idx, *req;
521 struct ostream *output = conn->conn.output;
522 int ret;
523
524 if ((ret = o_stream_flush(output)) <= 0) {
525 if (ret < 0) {
526 http_client_connection_abort_temp_error(&conn,
527 HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST, "Connection lost");
528 }
529 return ret;
530 }
531
532 if (array_count(&conn->request_wait_list) > 0 && conn->output_locked) {
533 req_idx = array_idx(&conn->request_wait_list, 0);
534 req = req_idx[0];
535
536 if (!req->payload_sync || conn->payload_continue) {
537 if (http_client_request_send_more(req) < 0) {
538 http_client_connection_abort_temp_error(&conn,
539 HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST, "Connection lost");
540 return -1;
541 }
542 if (!conn->output_locked) {
543 /* room for new requests */
544 http_client_peer_handle_requests(conn->peer);
545 http_client_connection_check_idle(conn);
546 }
547 }
548 }
549 return 1;
550 }
551
552 static void
553 http_client_connection_ready(struct http_client_connection *conn)
554 {
555 struct stat st;
556
557 conn->connected = TRUE;
558
559 if (*conn->client->set.rawlog_dir != '\0' &&
560 stat(conn->client->set.rawlog_dir, &st) == 0) {
561 iostream_rawlog_create(conn->client->set.rawlog_dir,
562 &conn->conn.input, &conn->conn.output);
563 }
564
565 conn->http_parser = http_response_parser_init(conn->conn.input);
566 o_stream_set_flush_callback(conn->conn.output,
567 http_client_connection_output, conn);
568
569 /* we never pipeline before the first response */
570 (void)http_client_connection_next_request(conn);
571 }
572
573 #ifdef HTTP_BUILD_SSL
574 static int http_client_connection_ssl_handshaked(void *context)
575 {
576 struct http_client_connection *conn = context;
577
578 if (!conn->client->set.ssl_verify) {
579 /* skip certificate checks */
580 http_client_connection_debug(conn, "SSL handshake successful");
581 return 0;
582 } else if (!ssl_iostream_has_valid_client_cert(conn->ssl_iostream)) {
583 if (!ssl_iostream_has_broken_client_cert(conn->ssl_iostream)) {
584 http_client_connection_error(conn, "SSL certificate not received");
585 } else {
586 http_client_connection_error(conn, "Received invalid SSL certificate");
587 }
588 } else {
589 const char *host = http_client_peer_get_hostname(conn->peer);
590
591 i_assert(host != NULL);
592
593 if (ssl_iostream_cert_match_name(conn->ssl_iostream, host) < 0) {
594 http_client_connection_error(conn,
595 "SSL certificate doesn't match host name");
596 } else {
597 http_client_connection_debug(conn, "SSL handshake successful");
598 return 0;
599 }
600 }
601 i_stream_close(conn->conn.input);
602 return -1;
603 }
604
605 static int
606 http_client_connection_ssl_init(struct http_client_connection *conn)
607 {
608 struct ssl_iostream_settings ssl_set;
609 const char *source;
610
611 if (conn->peer->ssl_ctx == NULL) {
612 http_client_connection_error(conn, "No SSL context");
613 return -1;
614 }
615
616 memset(&ssl_set, 0, sizeof(ssl_set));
617 if (conn->client->set.ssl_verify) {
618 ssl_set.verbose_invalid_cert = TRUE;
619 ssl_set.verify_remote_cert = TRUE;
620 ssl_set.require_valid_cert = TRUE;
621 }
622
623 if (conn->client->set.debug)
624 http_client_connection_debug(conn, "Starting SSL handshake");
625
626 source = t_strdup_printf
627 ("connection %s: ", http_client_connection_label(conn));
628 if (io_stream_create_ssl(conn->peer->ssl_ctx, source, &ssl_set,
629 &conn->conn.input, &conn->conn.output, &conn->ssl_iostream) < 0) {
630 http_client_connection_error(conn, "Couldn't initialize SSL client");
631 return -1;
632 }
633 ssl_iostream_set_handshake_callback(conn->ssl_iostream,
634 http_client_connection_ssl_handshaked, conn);
635 if (ssl_iostream_handshake(conn->ssl_iostream) < 0) {
636 http_client_connection_error(conn, "SSL handshake failed: %s",
637 ssl_iostream_get_last_error(conn->ssl_iostream));
638 return -1;
639 }
640
641 http_client_connection_ready(conn);
642 return 0;
643 }
644 #endif
645
646 static void
647 http_client_connection_connected(struct connection *_conn, bool success)
648 {
649 struct http_client_connection *conn =
650 (struct http_client_connection *)_conn;
651
652 if (!success) {
653 http_client_connection_error(conn, "Connect failed: %m");
654 http_client_peer_connection_failure(conn->peer);
655
656 } else {
657 http_client_connection_debug(conn, "Connected");
658 #ifdef HTTP_BUILD_SSL
659 if (conn->peer->addr.ssl) {
660 if (http_client_connection_ssl_init(conn) < 0)
661 http_client_peer_connection_failure(conn->peer);
662 return;
663 }
664 #endif
665 http_client_connection_ready(conn);
666 }
667 }
668
669 static const struct connection_settings http_client_connection_set = {
670 .input_max_size = (size_t)-1,
671 .output_max_size = (size_t)-1,
672 .client = TRUE
673 };
674
675 static const struct connection_vfuncs http_client_connection_vfuncs = {
676 .destroy = http_client_connection_destroy,
677 .input = http_client_connection_input,
678 .client_connected = http_client_connection_connected
679 };
680
681 struct connection_list *
682 http_client_connection_list_init(void)
683 {
684 return connection_list_init
685 (&http_client_connection_set, &http_client_connection_vfuncs);
686 }
687
688 static int http_client_connection_connect(struct http_client_connection *conn)
689 {
690 if (conn->conn.fd_in == -1) {
691 if (connection_client_connect(&conn->conn) < 0) {
692 http_client_connection_error(conn, "Could not connect");
693 return -1;
694 }
695 }
696
697 return 0;
698 }
699
700 struct http_client_connection *
701 http_client_connection_create(struct http_client_peer *peer)
702 {
703 struct http_client_connection *conn;
704 static unsigned int id = 0;
705
706 conn = i_new(struct http_client_connection, 1);
707 conn->client = peer->client;
708 conn->peer = peer;
709 i_array_init(&conn->request_wait_list, 16);
710
711 connection_init_client_ip
712 (peer->client->conn_list, &conn->conn, &peer->addr.ip, peer->addr.port);
713
714 if (http_client_connection_connect(conn) < 0) {
715 http_client_connection_free(&conn);
716 return NULL;
717 }
718
719 conn->id = id++;
720 array_append(&peer->conns, &conn, 1);
721
722 http_client_connection_debug(conn,
723 "Connection created (%d parallel connections exist)",
724 array_count(&peer->conns));
725 return conn;
726 }
727
728 void http_client_connection_free(struct http_client_connection **_conn)
729 {
730 struct http_client_connection *conn = *_conn;
731 struct http_client_connection *const *conn_idx;
732 struct http_client_peer *peer = conn->peer;
733 struct http_client_request **req;
734
735 http_client_connection_debug(conn, "Connection destroy");
736
737 conn->connected = FALSE;
738
739 #ifdef HTTP_BUILD_SSL
740 if (conn->ssl_iostream != NULL)
741 ssl_iostream_unref(&conn->ssl_iostream);
742 #endif
743
744 connection_deinit(&conn->conn);
745
746 /* abort all pending requests */
747 array_foreach_modifiable(&conn->request_wait_list, req) {
748 http_client_request_error(*req, HTTP_CLIENT_REQUEST_ERROR_ABORTED,
749 "Aborting");
750 http_client_request_unref(req);
751 }
752 array_free(&conn->request_wait_list);
753
754 if (conn->to_input != NULL)
755 timeout_remove(&conn->to_input);
756 if (conn->to_idle != NULL)
757 timeout_remove(&conn->to_idle);
758 if (conn->to_response != NULL)
759 timeout_remove(&conn->to_response);
760
761 /* remove this connection from the list */
762 array_foreach(&conn->peer->conns, conn_idx) {
763 if (*conn_idx == conn) {
764 array_delete(&conn->peer->conns,
765 array_foreach_idx(&conn->peer->conns, conn_idx), 1);
766 break;
767 }
768 }
769
770 if (conn->http_parser != NULL)
771 http_response_parser_deinit(&conn->http_parser);
772
773 i_free(conn);
774 *_conn = NULL;
775
776 http_client_peer_connection_lost(peer);
777 }
778
779 void http_client_connection_switch_ioloop(struct http_client_connection *conn)
780 {
781 if (conn->to_input != NULL)
782 conn->to_input = io_loop_move_timeout(&conn->to_input);
783 if (conn->to_idle != NULL)
784 conn->to_idle = io_loop_move_timeout(&conn->to_idle);
785 if (conn->to_response != NULL)
786 conn->to_response = io_loop_move_timeout(&conn->to_response);
787 connection_switch_ioloop(&conn->conn);
788 }