http-client-connection.c revision 744c06ab0d4aebf0e35665740ea7b13b57fd2f59
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainenstatic inline void
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainenhttp_client_connection_debug(struct http_client_connection *conn,
8952d797eca36f997ec36569e783871b597a9216Timo Sirainenstatic inline void
8952d797eca36f997ec36569e783871b597a9216Timo Sirainenhttp_client_connection_debug(struct http_client_connection *conn,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen const char *format, ...)
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainenstatic void http_client_connection_ready(struct http_client_connection *conn);
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainenstatic void http_client_connection_input(struct connection *_conn);
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainenhttp_client_connection_count_pending(struct http_client_connection *conn)
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen unsigned int pending_count = array_count(&conn->request_wait_list);
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen if (conn->in_req_callback || conn->pending_request != NULL)
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainenbool http_client_connection_is_idle(struct http_client_connection *conn)
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainenhttp_client_connection_retry_requests(struct http_client_connection *conn,
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainen const struct http_client_settings *set = &conn->client->set;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (!array_is_created(&conn->request_wait_list))
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "Aborting pending requests with error");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "Retrying pending requests");
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen array_foreach_modifiable(&conn->request_wait_list, req_idx) {
14189e0d0af45ddcb888d026bd8d7e4609912ec5Timo Sirainen /* drop reference from connection */
7cd055a212d44067e2d94452c05691d696c9f699Timo Sirainen /* retry the request, which may drop it */
8952d797eca36f997ec36569e783871b597a9216Timo Sirainen if (req->state < HTTP_REQUEST_STATE_FINISHED) {
e8434aad92ea6ff1c915b708294dbd0c7ff5908dMichael M Slusarz http_client_request_retry(req, status, error);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenhttp_client_connection_server_close(struct http_client_connection **_conn)
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen "Server explicitly closed connection");
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen array_foreach_modifiable(&conn->request_wait_list, req_idx) {
2a24f3565c61cb429d1e428601f153ce53b8bae3Timo Sirainen /* drop reference from connection */
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen /* resubmit the request, which may drop it */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenhttp_client_connection_abort_error(struct http_client_connection **_conn,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_debug(conn, "Aborting connection: %s", error);
61f39b0358a72ebc693d84ba5bac74489ee7df41Timo Sirainen array_foreach_modifiable(&conn->request_wait_list, req_idx) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* drop reference from connection */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen /* drop request if not already aborted */
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen http_client_request_error(&req, status, error);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenhttp_client_connection_abort_any_requests(struct http_client_connection *conn)
92a7f5f9bf20c0bd1b1ac309d100f9c144e2b127Timo Sirainen if (array_is_created(&conn->request_wait_list)) {
e22ec7998afd426c53c658483ce66b6e404e27c6Timo Sirainen array_foreach_modifiable(&conn->request_wait_list, req_idx) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen /* drop reference from connection */
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen /* drop request if not already aborted */
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen /* drop reference from connection */
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen if (http_client_request_unref(&conn->pending_request)) {
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen /* drop request if not already aborted */
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainenstatic const char *
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainenhttp_client_connection_get_timing_info(struct http_client_connection *conn)
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen unsigned int sent_msecs, total_msecs, connected_msecs;
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen if (array_count(&conn->request_wait_list) > 0) {
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen requestp = array_idx(&conn->request_wait_list, 0);
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen sent_msecs = timeval_diff_msecs(&ioloop_timeval, &(*requestp)->sent_time);
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen total_msecs = timeval_diff_msecs(&ioloop_timeval, &(*requestp)->submit_time);
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen str_printfa(str, "Request sent %u.%03u secs ago",
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen str_printfa(str, ", %u attempts in %u.%03u secs",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen connected_msecs = timeval_diff_msecs(&ioloop_timeval, &conn->connected_timestamp);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen str_printfa(str, ", connected %u.%03u secs ago",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenhttp_client_connection_abort_temp_error(struct http_client_connection **_conn,
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen if (status == HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST &&
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen sslerr = ssl_iostream_get_last_error(conn->ssl_iostream);
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen error = t_strdup_printf("%s (last SSL error: %s)",
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen if (ssl_iostream_has_handshake_failed(conn->ssl_iostream)) {
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen /* this isn't really a "connection lost", but that we
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen don't trust the remote's SSL certificate. don't
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE, error);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_get_timing_info(conn));
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "Aborting connection with temporary error: %s", error);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_retry_requests(conn, status, error);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenint http_client_connection_check_ready(struct http_client_connection *conn)
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen /* this can happen when a nested ioloop is created inside request
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen callback. we currently don't reuse connections that are occupied
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen this way, but theoretically we could, although that would add
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen quite a bit of complexity.
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen if (!conn->connected || conn->output_locked || conn->output_broken ||
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (conn->last_ioloop != NULL && conn->last_ioloop != current_ioloop) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* Active ioloop is different from what we saw earlier;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen we may have missed a disconnection event on this connection.
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen Verify status by reading from connection. */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if ((ret=i_stream_read(conn->conn.input)) == -1) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen int stream_errno = conn->conn.input->stream_errno;
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen i_assert(conn->conn.input->stream_errno != 0 || conn->conn.input->eof);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_abort_temp_error(&conn,
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen t_strdup_printf("Connection lost: read(%s) failed: %s",
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainenhttp_client_connection_idle_timeout(struct http_client_connection *conn)
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen http_client_connection_debug(conn, "Idle connection timed out");
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen /* cannot get here unless connection was established at some point */
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainenvoid http_client_connection_check_idle(struct http_client_connection *conn)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* timeout already set */
7c04ede0da5749691624a1fb962ac29cd0167050Timo Sirainen /* set timeout for this connection */
7c04ede0da5749691624a1fb962ac29cd0167050Timo Sirainen if (count > conn->client->set.max_parallel_connections) {
7c04ede0da5749691624a1fb962ac29cd0167050Timo Sirainen /* instant death for (urgent) connections above limit */
7c04ede0da5749691624a1fb962ac29cd0167050Timo Sirainen unsigned int idle_count = http_client_peer_idle_connections(conn->peer);
7c04ede0da5749691624a1fb962ac29cd0167050Timo Sirainen /* kill duplicate connections quicker;
7c04ede0da5749691624a1fb962ac29cd0167050Timo Sirainen linearly based on the number of connections */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen timeout = (conn->client->set.max_parallel_connections - idle_count) *
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "No more requests queued; going idle (timeout = %u msecs)",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen timeout_add(timeout, http_client_connection_idle_timeout, conn);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* there should be no idle timeout */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenhttp_client_connection_request_timeout(struct http_client_connection *conn)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_abort_temp_error(&conn,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT, "Request timed out");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenvoid http_client_connection_start_request_timeout(
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen unsigned int timeout_msecs = conn->client->set.request_timeout_msecs;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen conn->to_requests = timeout_add(timeout_msecs,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_request_timeout, conn);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenvoid http_client_connection_reset_request_timeout(
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenvoid http_client_connection_stop_request_timeout(
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenhttp_client_connection_continue_timeout(struct http_client_connection *conn)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "Expected 100-continue response timed out; sending payload anyway");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen wait_reqs = array_get(&conn->request_wait_list, &wait_count);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (http_client_request_send_more(req, FALSE, &error) < 0) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_abort_temp_error(&conn,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen t_strdup_printf("Failed to send request: %s", error));
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenint http_client_connection_next_request(struct http_client_connection *conn)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if ((ret=http_client_connection_check_ready(conn)) <= 0) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "Not ready for next request");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* claim request, but no urgent request can be second in line */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen pipelined = array_count(&conn->request_wait_list) > 0 ||
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen req = http_client_peer_claim_request(conn->peer, pipelined);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen i_assert(req->state == HTTP_REQUEST_STATE_QUEUED);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* add request to wait list and add a reference */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen array_append(&conn->request_wait_list, &req, 1);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_debug(conn, "Claimed request %s",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (http_client_request_send(req, pipelined, &error) < 0) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_abort_temp_error(&conn,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen t_strdup_printf("Failed to send request: %s", error));
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* RFC 7231, Section 5.1.1: Expect
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen o A client that sends a 100-continue expectation is not required to
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen wait for any specific length of time; such a client MAY proceed to
f7d018e7e0980044e3d537958126e44ef4c45056Timo Sirainen send the message body even if it has not yet received a response.
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen Furthermore, since 100 (Continue) responses cannot be sent through
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen an HTTP/1.0 intermediary, such a client SHOULD NOT wait for an
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen indefinite period before sending the message body.
6ea145a99eeee923602f04d3c9183bbdba6cd190Timo Sirainen if (req->payload_sync && !conn->peer->seen_100_response) {
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainen i_assert(req->payload_chunked || req->payload_size > 0);
7cd055a212d44067e2d94452c05691d696c9f699Timo Sirainen conn->to_response = timeout_add(HTTP_CLIENT_CONTINUE_TIMEOUT_MSECS,
8952d797eca36f997ec36569e783871b597a9216Timo Sirainen http_client_connection_continue_timeout, conn);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenstatic void http_client_connection_destroy(struct connection *_conn)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen unsigned int msecs;
e8434aad92ea6ff1c915b708294dbd0c7ff5908dMichael M Slusarz if (conn->connected_timestamp.tv_sec == 0) {
e8434aad92ea6ff1c915b708294dbd0c7ff5908dMichael M Slusarz msecs = timeval_diff_msecs(&ioloop_timeval,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "connect(%s) failed: Connection timed out in %u.%03u secs",
c72cfe4a2bda39fff3b8a8bd64b31a7cc14d7d11Timo Sirainen "SSL handshaking with %s failed: Connection timed out in %u.%03u secs",
ce74395e2a932342e04fb682395bcce111574969Timo Sirainen http_client_connection_debug(conn, "%s", error);
ce74395e2a932342e04fb682395bcce111574969Timo Sirainen http_client_peer_connection_failure(conn->peer, error);
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen /* retry pending requests if possible */
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen error = _conn->input == NULL ? "Connection lost" :
ce74395e2a932342e04fb682395bcce111574969Timo Sirainen http_client_connection_debug(conn, "%s", error);
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST, error);
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainenstatic void http_client_payload_finished(struct http_client_connection *conn)
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen conn->conn.io = io_add_istream(conn->conn.input,
a1044a46a8f3512173f4ea2684ef1fc3e61645c7Timo Sirainen if (array_count(&conn->request_wait_list) > 0)
14189e0d0af45ddcb888d026bd8d7e4609912ec5Timo Sirainen http_client_connection_start_request_timeout(conn);
f9eee365367f37b1692c07db6c23d30243844aaaTimo Sirainenhttp_client_payload_destroyed_timeout(struct http_client_connection *conn)
2599a77a28bde0653fa090802424469904d518eeTimo Sirainenstatic void http_client_payload_destroyed(struct http_client_request *req)
2599a77a28bde0653fa090802424469904d518eeTimo Sirainen struct http_client_connection *conn = req->conn;
8952d797eca36f997ec36569e783871b597a9216Timo Sirainen "Response payload stream destroyed (%u ms after initial response)",
8952d797eca36f997ec36569e783871b597a9216Timo Sirainen timeval_diff_msecs(&ioloop_timeval, &req->response_time));
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen /* caller is allowed to change the socket fd to blocking while reading
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen the payload. make sure here that it's switched back. */
07038d3a12a915e98f794566f56a0ed12e0653ebAki Tuomi /* drop reference from connection */
07038d3a12a915e98f794566f56a0ed12e0653ebAki Tuomi if (http_client_request_unref(&conn->pending_request)) {
07038d3a12a915e98f794566f56a0ed12e0653ebAki Tuomi /* finish request if not already aborted */
07038d3a12a915e98f794566f56a0ed12e0653ebAki Tuomi /* input stream may have pending input. make sure input handler
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen gets called (but don't do it directly, since we get get here
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen somewhere from the API user's code, which we can't really know what
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen state it is in). this call also triggers sending a new request if
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen necessary. */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen (0, http_client_payload_destroyed_timeout, conn);
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen /* room for new requests */
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen if (http_client_connection_check_ready(conn) > 0)
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen http_client_peer_trigger_request_handler(conn->peer);
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen /* wrap the stream to capture the destroy event without destroying the
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen actual payload stream. */
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen i_stream_add_destroy_callback(response->payload,
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen /* the callback may add its own I/O, so we need to remove
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen our one before calling it */
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen /* we've received the request itself, and we can't reset the
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen timeout during the payload reading. */
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen http_client_connection_stop_request_timeout(conn);
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen retrying = !http_client_request_callback(req, response);
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen if (!http_client_connection_unref(&tmp_conn) ||
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen /* the callback managed to get this connection destroyed */
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen /* retrying, don't destroy the request */
47a5a7e8296f3b8f2fac9a0659d4de3f2723ba4aMartti Rannanjärvi i_stream_remove_destroy_callback(conn->incoming_payload,
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen conn->conn.io = io_add_istream(conn->conn.input,
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen /* maintain request reference while payload is pending */
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen /* request is dereferenced in payload destroy callback */
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen if (conn->to_input != NULL && conn->conn.input != NULL) {
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen /* already finished reading the payload */
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen if (conn->incoming_payload == NULL && conn->conn.input != NULL) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen conn->peer->addr.type == HTTP_CLIENT_PEER_ADDR_RAW);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenstatic void http_client_connection_input(struct connection *_conn)
4d8f538565145fd90eae48df5c4f2ed76e51ca78Timo Sirainen struct http_client_request *req = NULL, *req_ref;
4d8f538565145fd90eae48df5c4f2ed76e51ca78Timo Sirainen unsigned int count;
c72cfe4a2bda39fff3b8a8bd64b31a7cc14d7d11Timo Sirainen !ssl_iostream_is_handshaked(conn->ssl_iostream)) {
e8434aad92ea6ff1c915b708294dbd0c7ff5908dMichael M Slusarz /* finish SSL negotiation by reading from input stream */
e8434aad92ea6ff1c915b708294dbd0c7ff5908dMichael M Slusarz while ((ret=i_stream_read(conn->conn.input)) > 0) {
f9eee365367f37b1692c07db6c23d30243844aaaTimo Sirainen if (ssl_iostream_is_handshaked(conn->ssl_iostream))
e564ff0581fc44b78badf8da36e68f9f7a27807eTimo Sirainen int stream_errno = conn->conn.input->stream_errno;
2599a77a28bde0653fa090802424469904d518eeTimo Sirainen /* failed somehow */
2599a77a28bde0653fa090802424469904d518eeTimo Sirainen "SSL handshaking with %s failed: "
07038d3a12a915e98f794566f56a0ed12e0653ebAki Tuomi "read(%s) failed: %s",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen i_stream_get_error(conn->conn.input) : "EOF");
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen http_client_peer_connection_failure(conn->peer, error);
ba4626cd5be3d225a7a89aa338d92b8fb411fd1cTimo Sirainen http_client_connection_debug(conn, "%s", error);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (!ssl_iostream_is_handshaked(conn->ssl_iostream)) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* not finished */
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen /* ready for first request */
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen /* We came here from a timeout added by
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen http_client_payload_destroyed(). The IO couldn't be added
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen back immediately in there, because the HTTP API user may
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen still have had its own IO pointed to the same fd. It should
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen be removed by now, so we can add it back. */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* we've seen activity from the server; reset request timeout */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_reset_request_timeout(conn);
c72cfe4a2bda39fff3b8a8bd64b31a7cc14d7d11Timo Sirainen /* get first waiting request */
c72cfe4a2bda39fff3b8a8bd64b31a7cc14d7d11Timo Sirainen reqs = array_get(&conn->request_wait_list, &count);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* determine whether to expect a response payload */
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen payload_type = http_client_request_get_payload_type(req);
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED;
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen /* drop connection with broken output if last possible input was
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen (count == 1 && req->state == HTTP_REQUEST_STATE_ABORTED))) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen (conn->http_parser, payload_type, &response, &error)) > 0) {
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainen /* server sent response without any requests in the wait list */
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainen "Server explicitly closed connection: 408 %s",
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainen "Got unexpected input from server: %u %s",
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen /* Got some response; cancel response timeout */
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen /* RFC 7231, Section 6.2:
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen A client MUST be able to parse one or more 1xx responses received
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen prior to a final response, even if the client does not expect one. A
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen user agent MAY ignore unexpected 1xx responses.
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainen if (req->payload_sync && response.status == 100) {
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen "Got 100-continue response after timeout");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "Got expected 100-continue response");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (req->state == HTTP_REQUEST_STATE_ABORTED) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "Request aborted before sending payload was complete.");
8952d797eca36f997ec36569e783871b597a9216Timo Sirainen if (http_client_request_send_more(req, FALSE, &error) < 0) {
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen http_client_connection_abort_temp_error(&conn,
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen t_strdup_printf("Failed to send request: %s", error));
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen /* ignore other 1xx for now */
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen "Got unexpected %u response; ignoring", response.status);
f0e811f0e306bb20d3da9c26353bdd5669132f29Timo Sirainen req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT) {
f0e811f0e306bb20d3da9c26353bdd5669132f29Timo Sirainen /* got early response from server while we're still sending request
f0e811f0e306bb20d3da9c26353bdd5669132f29Timo Sirainen payload. we cannot recover from this reliably, so we stop sending
3e8842470a4a17017529d43b39c40a7549c2ecf2Timo Sirainen payload and close the connection once the response is processed */
c69a177207ed18d0f0210347430a60957136bd6cJosef 'Jeff' Sipek "Got early input from server; "
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "request payload not completely sent (will close connection)");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen o_stream_unset_flush_callback(conn->conn.output);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "Got %u response for request %s (took %u ms + %u ms in queue)",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen response.status, http_client_request_label(req),
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen timeval_diff_msecs(&req->response_time, &req->sent_time),
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen timeval_diff_msecs(&req->sent_time, &req->submit_time));
2ccb478c35972517721ce415d81fcbd11a73fad3Timo Sirainen /* make sure connection output is unlocked if 100-continue failed */
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen if (req->payload_sync && !req->payload_sync_continue) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_debug(conn, "Unlocked output");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* remove request from queue */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen aborted = (req->state == HTTP_REQUEST_STATE_ABORTED);
1856c361aad526948d56d8aafd576bca94516b92Timo Sirainen conn->close_indicated = response.connection_close;
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen /* response cannot be 2xx if request payload was not completely sent
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen "Server responded with success response "
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen "before all payload was sent");
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen /* don't redirect/retry if we're sending data in small
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen blocks via http_client_request_send_payload()
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen and we're not waiting for 100 continue */
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen (req->payload_sync && !req->payload_sync_continue)) {
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen /* failed Expect: */
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen if (response.status == 417 && req->payload_sync) {
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen /* drop Expect: continue */
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen /* redirection */
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen } else if (!req->client->set.no_auto_redirect &&
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen response.status / 100 == 3 && response.status != 304 &&
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen /* redirect (possibly after delay) */
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen if (http_client_request_delay_from_response(req, &response) >= 0) {
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen /* service unavailable */
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen /* automatically retry after delay if indicated */
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen http_client_request_delay_from_response(req, &response) > 0 &&
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen /* request timeout (by server) */
f9cf9852b0338910f1a710297374943d66fea480Timo Sirainen /* automatically retry */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* connection close is implicit, although server should indicate
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen that explicitly */
e401fa68eb1e7761ffd0b747919d44568555efeeTimo Sirainen /* response for application */
0098ac3b6dcd8ef6ac20f87a8285da201db75a01Timo Sirainen /* server closing connection? */
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen /* get next waiting request */
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen reqs = array_get(&conn->request_wait_list, &count);
0098ac3b6dcd8ef6ac20f87a8285da201db75a01Timo Sirainen /* determine whether to expect a response payload */
0098ac3b6dcd8ef6ac20f87a8285da201db75a01Timo Sirainen payload_type = http_client_request_get_payload_type(req);
b394d41ad4da0e2e7b8bfafccf3b4f3e9ac26ad1Timo Sirainen /* no more requests waiting for the connection */
b394d41ad4da0e2e7b8bfafccf3b4f3e9ac26ad1Timo Sirainen payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED;
2522acb523343dd37bf788747d86d9470fc08025Timo Sirainen /* drop connection with broken output if last possible input was
2522acb523343dd37bf788747d86d9470fc08025Timo Sirainen (count == 1 && req->state == HTTP_REQUEST_STATE_ABORTED))) {
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen (conn->conn.input->eof || conn->conn.input->stream_errno != 0)) {
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen int stream_errno = conn->conn.input->stream_errno;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_abort_temp_error(&conn,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen t_strdup_printf("Connection lost: read(%s) failed: %s",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen HTTP_CLIENT_REQUEST_ERROR_BAD_RESPONSE, error);
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen /* connection still alive after (at least one) request;
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen we can pipeline -> mark for subsequent connections */
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen /* room for new requests */
07038d3a12a915e98f794566f56a0ed12e0653ebAki Tuomi http_client_peer_trigger_request_handler(conn->peer);
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainenint http_client_connection_output(struct http_client_connection *conn)
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainen unsigned int count;
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen /* we've seen activity from the server; reset request timeout */
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainen http_client_connection_reset_request_timeout(conn);
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen http_client_connection_abort_temp_error(&conn,
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen t_strdup_printf("Connection lost: write(%s) failed: %s",
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen !ssl_iostream_is_handshaked(conn->ssl_iostream))
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen reqs = array_get(&conn->request_wait_list, &count);
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen struct http_client_request *req = reqs[count-1];
a8a5f2f8ccc134dcc7b61ea48664c3381db4a1aaTimo Sirainen bool pipelined = (count > 1 || conn->pending_request != NULL);
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen if (req->state == HTTP_REQUEST_STATE_ABORTED) {
2a24f3565c61cb429d1e428601f153ce53b8bae3Timo Sirainen "Request aborted before sending payload was complete.");
2a24f3565c61cb429d1e428601f153ce53b8bae3Timo Sirainen if (!req->payload_sync || req->payload_sync_continue) {
2a24f3565c61cb429d1e428601f153ce53b8bae3Timo Sirainen if (http_client_request_send_more(req, pipelined, &error) < 0) {
2a24f3565c61cb429d1e428601f153ce53b8bae3Timo Sirainen http_client_connection_abort_temp_error(&conn,
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen t_strdup_printf("Connection lost: %s", error));
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* room for new requests */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (http_client_connection_check_ready(conn) > 0)
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen http_client_peer_trigger_request_handler(conn->peer);
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainenhttp_client_connection_start_tunnel(struct http_client_connection **_conn,
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainen /* claim connection streams */
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen /* detach from connection */
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainenhttp_client_connection_ready(struct http_client_connection *conn)
1128c114416bdc4df0b41d3e15429a1522e5cfe4Timo Sirainen http_client_connection_debug(conn, "Ready for requests");
2a24f3565c61cb429d1e428601f153ce53b8bae3Timo Sirainen /* connected */
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen /* indicate connection success */
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen http_client_peer_connection_success(conn->peer);
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen /* start raw log */
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen iostream_rawlog_create(conn->client->set.rawlog_dir,
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen /* direct tunneling connections handle connect requests just by providing a
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen raw connection */
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen if (conn->peer->addr.type == HTTP_CLIENT_PEER_ADDR_RAW) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen req = http_client_peer_claim_request(conn->peer, FALSE);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen (void)http_client_connection_return_response(conn, req, &response);
ce74395e2a932342e04fb682395bcce111574969Timo Sirainen "No raw connect requests pending; closing useless connection");
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen /* start protocol I/O */
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainen (conn->conn.input, &conn->client->set.response_hdr_limits);
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainen o_stream_set_flush_callback(conn->conn.output,
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainenhttp_client_connection_ssl_handshaked(const char **error_r, void *context)
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainen struct http_client_connection *conn = context;
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen const char *error, *host = conn->peer->addr.a.tcp.https_name;
a5f2707224b10f26e3d478a2b11e8d01f1b8f609Timo Sirainen if (ssl_iostream_check_cert_validity(conn->ssl_iostream, host, &error) == 0)
a2c4998f6e1fe5ea9a2c9bafd678cd4b6b064a0bTimo Sirainen http_client_connection_debug(conn, "SSL handshake successful");
1fb5e50695bbbc0da082e5a6f19f29d2bb2f6531Timo Sirainen else if (conn->client->set.ssl->allow_invalid_cert) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_debug(conn, "SSL handshake successful, "
4db61af2cfe2b206113bcc4b6153521679702bb4Timo Sirainenhttp_client_connection_ssl_init(struct http_client_connection *conn,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen const char **error_r)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (!conn->client->set.ssl->allow_invalid_cert) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_debug(conn, "Starting SSL handshake");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (io_stream_create_ssl_client(conn->client->ssl_ctx,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "Couldn't initialize SSL client for %s: %s",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen ssl_iostream_set_handshake_callback(conn->ssl_iostream,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (ssl_iostream_handshake(conn->ssl_iostream) < 0) {
e07677bb15404a3c18ad205efae86d6db31c3150Timo Sirainen *error_r = t_strdup_printf("SSL handshake to %s failed: %s",
e07677bb15404a3c18ad205efae86d6db31c3150Timo Sirainen conn->conn.name, ssl_iostream_get_last_error(conn->ssl_iostream));
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (ssl_iostream_is_handshaked(conn->ssl_iostream)) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* wait for handshake to complete; connection input handler does the rest
8759c5d294e762fe9c5b7b19f3842b23aaaaf4ebTimo Sirainen by reading from the input stream */
8759c5d294e762fe9c5b7b19f3842b23aaaaf4ebTimo Sirainen o_stream_set_flush_callback(conn->conn.output,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenhttp_client_connection_connected(struct connection *_conn, bool success)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen const struct http_client_settings *set = &conn->client->set;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_peer_connection_failure(conn->peer, t_strdup_printf(
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_debug(conn, "Connected");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen (void)net_set_tcp_nodelay(_conn->fd_out, TRUE);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen i_error("net_set_send_buffer_size(%"PRIuSIZE_T") failed: %m",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen i_error("net_set_recv_buffer_size(%"PRIuSIZE_T") failed: %m",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (http_client_peer_addr_is_https(&conn->peer->addr)) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen if (http_client_connection_ssl_init(conn, &error) < 0) {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_peer_connection_failure(conn->peer, error);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_debug(conn, "%s", error);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenstatic const struct connection_settings http_client_connection_set = {
61f39b0358a72ebc693d84ba5bac74489ee7df41Timo Sirainen .delayed_unix_client_connected_callback = TRUE
61f39b0358a72ebc693d84ba5bac74489ee7df41Timo Sirainenstatic const struct connection_vfuncs http_client_connection_vfuncs = {
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen .client_connected = http_client_connection_connected
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen (&http_client_connection_set, &http_client_connection_vfuncs);
b096ecf3188cdb9162460ed7ae885c03f3161462Timo Sirainenhttp_client_connection_delayed_connect_error(struct http_client_connection *conn)
b096ecf3188cdb9162460ed7ae885c03f3161462Timo Sirainen http_client_connection_connected(&conn->conn, FALSE);
b096ecf3188cdb9162460ed7ae885c03f3161462Timo Sirainenstatic void http_client_connect_timeout(struct http_client_connection *conn)
b096ecf3188cdb9162460ed7ae885c03f3161462Timo Sirainen conn->conn.disconnect_reason = CONNECTION_DISCONNECT_CONNECT_TIMEOUT;
769cbb608e9ed620063708aff49fc1b6e924394aTimo Sirainenhttp_client_connection_connect(struct http_client_connection *conn)
769cbb608e9ed620063708aff49fc1b6e924394aTimo Sirainen unsigned int msecs;
769cbb608e9ed620063708aff49fc1b6e924394aTimo Sirainen conn->connect_start_timestamp = ioloop_timeval;
769cbb608e9ed620063708aff49fc1b6e924394aTimo Sirainen if (connection_client_connect(&conn->conn) < 0) {
769cbb608e9ed620063708aff49fc1b6e924394aTimo Sirainen http_client_connection_debug(conn, "Connect failed: %m");
b096ecf3188cdb9162460ed7ae885c03f3161462Timo Sirainen http_client_connection_delayed_connect_error, conn);
b096ecf3188cdb9162460ed7ae885c03f3161462Timo Sirainen /* don't use connection.h timeout because we want this timeout
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen to include also the SSL handshake */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen msecs = conn->client->set.connect_timeout_msecs;
b096ecf3188cdb9162460ed7ae885c03f3161462Timo Sirainen msecs = conn->client->set.request_timeout_msecs;
61f39b0358a72ebc693d84ba5bac74489ee7df41Timo Sirainen timeout_add(msecs, http_client_connect_timeout, conn);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenhttp_client_connect_tunnel_timeout(struct http_client_connection *conn)
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen const char *error, *name = http_client_peer_addr2str(&conn->peer->addr);
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen unsigned int msecs;
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen "Tunnel connect(%s) failed: "
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen "Connection timed out in %u.%03u secs",
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen http_client_connection_debug(conn, "%s", error);
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen http_client_peer_connection_failure(conn->peer, error);
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainenhttp_client_connection_tunnel_response(const struct http_response *response,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen const char *name = http_client_peer_addr2str(&conn->peer->addr);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_peer_connection_failure(conn->peer, t_strdup_printf(
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_request_start_tunnel(conn->connect_request, &tunnel);
caf029d36a826106e48b8682f15ea0fc01fdd8f4Timo Sirainen (conn->client->conn_list, &conn->conn, name, tunnel.input, tunnel.output);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenhttp_client_connection_connect_tunnel(struct http_client_connection *conn,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen unsigned int msecs;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen conn->connect_start_timestamp = ioloop_timeval;
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen conn->connect_request = http_client_request_connect_ip
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen (conn->client, ip, port, http_client_connection_tunnel_response, conn);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_request_set_urgent(conn->connect_request);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_request_submit(conn->connect_request);
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen /* don't use connection.h timeout because we want this timeout
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen to include also the SSL handshake */
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen msecs = conn->client->set.connect_timeout_msecs;
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen msecs = conn->client->set.request_timeout_msecs;
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen timeout_add(msecs, http_client_connect_tunnel_timeout, conn);
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainenhttp_client_connection_create(struct http_client_peer *peer)
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen static unsigned int id = 0;
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen const struct http_client_peer_addr *addr = &peer->addr;
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen conn = i_new(struct http_client_connection, 1);
dd3d20d9b5821077164183a260af9bde0db3ff3fTimo Sirainen if (peer->addr.type != HTTP_CLIENT_PEER_ADDR_RAW)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen connection_init_client_unix(peer->client->conn_list, &conn->conn,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen connection_init_client_ip(peer->client->conn_list, &conn->conn,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen "%s connection created (%d parallel connections exist)%s",
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenvoid http_client_connection_ref(struct http_client_connection *conn)
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenhttp_client_connection_disconnect(struct http_client_connection *conn)
61f39b0358a72ebc693d84ba5bac74489ee7df41Timo Sirainen struct http_client_connection *const *conn_idx;
61f39b0358a72ebc693d84ba5bac74489ee7df41Timo Sirainen http_client_connection_debug(conn, "Connection disconnect");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_request_abort(&conn->connect_request);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* the stream is still accessed by lib-http caller. */
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen i_stream_remove_destroy_callback(conn->incoming_payload,
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_client_connection_abort_any_requests(conn);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen http_response_parser_deinit(&conn->http_parser);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen /* remove this connection from the list */
6b4b3e5fe8d9e84f4b1356ee898ca76996a11fe1Timo Sirainen array_delete(conn_arr, array_foreach_idx(conn_arr, conn_idx), 1);
92a7f5f9bf20c0bd1b1ac309d100f9c144e2b127Timo Sirainenbool http_client_connection_unref(struct http_client_connection **_conn)
e22ec7998afd426c53c658483ce66b6e404e27c6Timo Sirainen http_client_connection_debug(conn, "Connection destroy");
bb2b3656ef7635acc374f7fc19b25aeeb454ae95Timo Sirainen if (array_is_created(&conn->request_wait_list))
ce74395e2a932342e04fb682395bcce111574969Timo Sirainenvoid http_client_connection_close(struct http_client_connection **_conn)
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen http_client_connection_debug(conn, "Connection close");
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainenvoid http_client_connection_peer_closed(struct http_client_connection **_conn)
bb2b3656ef7635acc374f7fc19b25aeeb454ae95Timo Sirainen http_client_connection_debug(conn, "Peer closed");
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainenvoid http_client_connection_switch_ioloop(struct http_client_connection *conn)
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen conn->io_req_payload = io_loop_move_io(&conn->io_req_payload);
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen conn->to_requests = io_loop_move_timeout(&conn->to_requests);
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen conn->to_connect = io_loop_move_timeout(&conn->to_connect);
b772ddf3cfb606dddaa465b317a0dc01bf06c6e4Timo Sirainen conn->to_input = io_loop_move_timeout(&conn->to_input);
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen conn->to_idle = io_loop_move_timeout(&conn->to_idle);
709ee5a909d482f31611f9e6cc10d893a272e061Timo Sirainen conn->to_response = io_loop_move_timeout(&conn->to_response);