summaryrefslogtreecommitdiff
path: root/outgoing.c
diff options
context:
space:
mode:
Diffstat (limited to 'outgoing.c')
-rw-r--r--outgoing.c282
1 files changed, 178 insertions, 104 deletions
diff --git a/outgoing.c b/outgoing.c
index 53fac0a..2c63e96 100644
--- a/outgoing.c
+++ b/outgoing.c
@@ -29,8 +29,10 @@ static apr_status_t clean_skt(void *data)
apr_status_t status = APR_SUCCESS;
if (conn->skt) {
+ serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt, "cleanup - ");
status = apr_socket_close(conn->skt);
conn->skt = NULL;
+ serf__log_nopref(SOCK_VERBOSE, "closed socket, status %d\n", status);
}
return status;
@@ -71,6 +73,8 @@ static apr_status_t clean_conn(void *data)
{
serf_connection_t *conn = data;
+ serf__log(CONN_VERBOSE, __FILE__, "cleaning up connection 0x%x\n",
+ conn);
serf_connection_close(conn);
return APR_SUCCESS;
@@ -103,15 +107,19 @@ apr_status_t serf__conn_update_pollset(serf_connection_t *conn)
/* Now put it back in with the correct read/write values. */
desc.reqevents = APR_POLLHUP | APR_POLLERR;
- if (conn->requests) {
+ if (conn->requests &&
+ conn->state != SERF_CONN_INIT) {
/* If there are any outstanding events, then we want to read. */
/* ### not true. we only want to read IF we have sent some data */
desc.reqevents |= APR_POLLIN;
- /* If the connection has unwritten data, or there are any requests
- * that still have buckets to write out, then we want to write.
+ /* If the connection is not closing down and
+ * has unwritten data or
+ * there are any requests that still have buckets to write out,
+ * then we want to write.
*/
- if (conn->vec_len)
+ if (conn->vec_len &&
+ conn->state != SERF_CONN_CLOSING)
desc.reqevents |= APR_POLLOUT;
else {
serf_request_t *request = conn->requests;
@@ -180,7 +188,6 @@ apr_status_t serf__open_connections(serf_context_t *ctx)
serf_connection_t *conn = GET_CONN(ctx, i);
apr_status_t status;
apr_socket_t *skt;
- apr_sockaddr_t *serv_addr;
conn->seen_in_pollset = 0;
@@ -199,18 +206,15 @@ apr_status_t serf__open_connections(serf_context_t *ctx)
apr_pool_clear(conn->skt_pool);
apr_pool_cleanup_register(conn->skt_pool, conn, clean_skt, clean_skt);
- /* Do we have to connect to a proxy server? */
- if (ctx->proxy_address)
- serv_addr = ctx->proxy_address;
- else
- serv_addr = conn->address;
-
- if ((status = apr_socket_create(&skt, serv_addr->family,
- SOCK_STREAM,
+ status = apr_socket_create(&skt, conn->address->family,
+ SOCK_STREAM,
#if APR_MAJOR_VERSION > 0
- APR_PROTO_TCP,
+ APR_PROTO_TCP,
#endif
- conn->skt_pool)) != APR_SUCCESS)
+ conn->skt_pool);
+ serf__log(SOCK_VERBOSE, __FILE__,
+ "created socket for conn 0x%x, status %d\n", conn, status);
+ if (status != APR_SUCCESS)
return status;
/* Set the socket to be non-blocking */
@@ -225,11 +229,18 @@ apr_status_t serf__open_connections(serf_context_t *ctx)
/* Configured. Store it into the connection now. */
conn->skt = skt;
+ /* Remember time when we started connecting to server to calculate
+ network latency. */
+ conn->connect_time = apr_time_now();
+
/* Now that the socket is set up, let's connect it. This should
* return immediately.
*/
- if ((status = apr_socket_connect(skt,
- serv_addr)) != APR_SUCCESS) {
+ status = apr_socket_connect(skt, conn->address);
+ serf__log_skt(SOCK_VERBOSE, __FILE__, skt,
+ "connected socket for conn 0x%x, status %d\n",
+ conn, status);
+ if (status != APR_SUCCESS) {
if (!APR_STATUS_IS_EINPROGRESS(status))
return status;
}
@@ -256,6 +267,7 @@ apr_status_t serf__open_connections(serf_context_t *ctx)
serf__ssltunnel_connect(conn);
else
conn->state = SERF_CONN_CONNECTED;
+
}
return APR_SUCCESS;
@@ -266,14 +278,8 @@ static apr_status_t no_more_writes(serf_connection_t *conn,
{
/* Note that we should hold new requests until we open our new socket. */
conn->state = SERF_CONN_CLOSING;
-
- /* We can take the *next* request in our list and assume it hasn't
- * been written yet and 'save' it for the new socket.
- */
- conn->hold_requests = request->next;
- conn->hold_requests_tail = conn->requests_tail;
- request->next = NULL;
- conn->requests_tail = request;
+ serf__log(CONN_VERBOSE, __FILE__, "stop writing on conn 0x%x\n",
+ conn);
/* Clear our iovec. */
conn->vec_len = 0;
@@ -411,27 +417,22 @@ static apr_status_t reset_connection(serf_connection_t *conn,
{
serf_context_t *ctx = conn->ctx;
apr_status_t status;
- serf_request_t *old_reqs, *held_reqs, *held_reqs_tail;
+ serf_request_t *old_reqs;
conn->probable_keepalive_limit = conn->completed_responses;
conn->completed_requests = 0;
conn->completed_responses = 0;
old_reqs = conn->requests;
- held_reqs = conn->hold_requests;
- held_reqs_tail = conn->hold_requests_tail;
-
- if (conn->state == SERF_CONN_CLOSING) {
- conn->hold_requests = NULL;
- conn->hold_requests_tail = NULL;
- }
conn->requests = NULL;
conn->requests_tail = NULL;
+ /* Handle all outstanding requests. These have either not been written yet,
+ or have been written but the expected reply wasn't received yet. */
while (old_reqs) {
/* If we haven't started to write the connection, bring it over
- * unchanged to our new socket. Otherwise, call the cancel function.
+ * unchanged to our new socket.
*/
if (requeue_requests && !old_reqs->written) {
serf_request_t *req = old_reqs;
@@ -440,23 +441,19 @@ static apr_status_t reset_connection(serf_connection_t *conn,
link_requests(&conn->requests, &conn->requests_tail, req);
}
else {
+ /* Request has been consumed, or we don't want to requeue the
+ request. Either way, inform the application that the request
+ is cancelled. */
cancel_request(old_reqs, &old_reqs, requeue_requests);
}
}
- if (conn->requests_tail) {
- conn->requests_tail->next = held_reqs;
- }
- else {
- conn->requests = held_reqs;
- }
- if (held_reqs_tail) {
- conn->requests_tail = held_reqs_tail;
- }
-
+ /* Requests queue has been prepared for a new socket, close the old one. */
if (conn->skt != NULL) {
remove_connection(ctx, conn);
status = apr_socket_close(conn->skt);
+ serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt,
+ "closed socket, status %d\n", status);
if (conn->closed != NULL) {
handle_conn_closed(conn, status);
}
@@ -477,6 +474,8 @@ static apr_status_t reset_connection(serf_connection_t *conn,
conn->ctx->dirty_pollset = 1;
conn->state = SERF_CONN_INIT;
+ serf__log(CONN_VERBOSE, __FILE__, "reset connection 0x%x\n", conn);
+
conn->status = APR_SUCCESS;
/* Let our context know that we've 'reset' the socket already. */
@@ -493,15 +492,24 @@ static apr_status_t socket_writev(serf_connection_t *conn)
status = apr_socket_sendv(conn->skt, conn->vec,
conn->vec_len, &written);
+ if (status && !APR_STATUS_IS_EAGAIN(status))
+ serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt,
+ "socket_sendv error %d\n", status);
/* did we write everything? */
if (written) {
apr_size_t len = 0;
int i;
+ serf__log_skt(SOCK_MSG_VERBOSE, __FILE__, conn->skt,
+ "--- socket_sendv:\n");
+
for (i = 0; i < conn->vec_len; i++) {
len += conn->vec[i].iov_len;
if (written < len) {
+ serf__log_nopref(SOCK_MSG_VERBOSE, "%.*s",
+ conn->vec[i].iov_len - (len - written),
+ conn->vec[i].iov_base);
if (i) {
memmove(conn->vec, &conn->vec[i],
sizeof(struct iovec) * (conn->vec_len - i));
@@ -510,11 +518,15 @@ static apr_status_t socket_writev(serf_connection_t *conn)
conn->vec[0].iov_base = (char *)conn->vec[0].iov_base + (conn->vec[0].iov_len - (len - written));
conn->vec[0].iov_len = len - written;
break;
+ } else {
+ serf__log_nopref(SOCK_MSG_VERBOSE, "%.*s",
+ conn->vec[i].iov_len, conn->vec[i].iov_base);
}
}
if (len == written) {
conn->vec_len = 0;
}
+ serf__log_nopref(SOCK_MSG_VERBOSE, "-(%d)-\n", written);
/* Log progress information */
serf__context_progress_delta(conn->ctx, 0, written);
@@ -582,6 +594,10 @@ static apr_status_t prepare_conn_streams(serf_connection_t *conn,
{
apr_status_t status;
+ if (conn->stream == NULL) {
+ conn->latency = apr_time_now() - conn->connect_time;
+ }
+
/* Do we need a SSL tunnel first? */
if (conn->state == SERF_CONN_CONNECTED) {
/* If the connection does not have an associated bucket, then
@@ -667,7 +683,9 @@ static apr_status_t write_to_connection(serf_connection_t *conn)
*/
if (APR_STATUS_IS_EAGAIN(status))
return APR_SUCCESS;
- if (APR_STATUS_IS_EPIPE(status))
+ if (APR_STATUS_IS_EPIPE(status) ||
+ APR_STATUS_IS_ECONNRESET(status) ||
+ APR_STATUS_IS_ECONNABORTED(status))
return no_more_writes(conn, request);
if (status)
return status;
@@ -762,7 +780,8 @@ static apr_status_t write_to_connection(serf_connection_t *conn)
return APR_SUCCESS;
if (APR_STATUS_IS_EPIPE(status))
return no_more_writes(conn, request);
- if (APR_STATUS_IS_ECONNRESET(status)) {
+ if (APR_STATUS_IS_ECONNRESET(status) ||
+ APR_STATUS_IS_ECONNABORTED(status)) {
return no_more_writes(conn, request);
}
if (status)
@@ -929,31 +948,38 @@ static apr_status_t read_from_connection(serf_connection_t *conn)
* 2) Doing the initial SSL handshake - we'll get EAGAIN
* as the SSL buckets will hide the handshake from us
* but not return any data.
+ * 3) When the server sends us an SSL alert.
*
* In these cases, we should not receive any actual user data.
*
- * If we see an EOF (due to an expired timeout), we'll reset the
+ * 4) When the server sends a error response, like 408 Request timeout.
+ * This response should be passed to the application.
+ *
+ * If we see an EOF (due to either an expired timeout or the server
+ * sending the SSL 'close notify' shutdown alert), we'll reset the
* connection and open a new one.
*/
if (request->req_bkt || !request->written) {
const char *data;
apr_size_t len;
- status = serf_bucket_read(conn->stream, SERF_READ_ALL_AVAIL,
- &data, &len);
+ status = serf_bucket_peek(conn->stream, &data, &len);
- if (!status && len) {
- status = APR_EGENERAL;
- }
- else if (APR_STATUS_IS_EOF(status)) {
+ if (APR_STATUS_IS_EOF(status)) {
reset_connection(conn, 1);
status = APR_SUCCESS;
+ goto error;
}
- else if (APR_STATUS_IS_EAGAIN(status)) {
+ else if (APR_STATUS_IS_EAGAIN(status) && !len) {
status = APR_SUCCESS;
+ goto error;
+ } else if (status && !APR_STATUS_IS_EAGAIN(status)) {
+ /* Read error */
+ goto error;
}
- goto error;
+ /* Unexpected response from the server */
+
}
/* If the request doesn't have a response bucket, then call the
@@ -969,12 +995,21 @@ static apr_status_t read_from_connection(serf_connection_t *conn)
status = handle_response(request, tmppool);
/* Some systems will not generate a HUP poll event so we have to
- * handle the ECONNRESET issue here.
+ * handle the ECONNRESET issue and ECONNABORT here.
*/
if (APR_STATUS_IS_ECONNRESET(status) ||
+ APR_STATUS_IS_ECONNABORTED(status) ||
status == SERF_ERROR_REQUEST_LOST) {
- reset_connection(conn, 1);
- status = APR_SUCCESS;
+ /* If the connection had ever been good, be optimistic & try again.
+ * If it has never tried again (incl. a retry), fail.
+ */
+ if (conn->completed_responses) {
+ reset_connection(conn, 1);
+ status = APR_SUCCESS;
+ }
+ else if (status == SERF_ERROR_REQUEST_LOST) {
+ status = SERF_ERROR_ABORTED_CONNECTION;
+ }
goto error;
}
@@ -1001,9 +1036,11 @@ static apr_status_t read_from_connection(serf_connection_t *conn)
goto error;
}
- /* The request has been fully-delivered, and the response has
- * been fully-read. Remove it from our queue and loop to read
- * another response.
+ /* The response has been fully-read, so that means the request has
+ * either been fully-delivered (most likely), or that we don't need to
+ * write the rest of it anymore, e.g. when a 408 Request timeout was
+ $ received.
+ * Remove it from our queue and loop to read another response.
*/
conn->requests = request->next;
@@ -1081,8 +1118,13 @@ apr_status_t serf__process_connection(serf_connection_t *conn,
if ((events & APR_POLLHUP) != 0) {
/* The connection got reset by the server. On Windows this can happen
when all data is read, so just cleanup the connection and open
- a new one. */
- return reset_connection(conn, 1);
+ a new one.
+ If we haven't had any successful responses on this connection,
+ then error out as it is likely a server issue. */
+ if (conn->completed_responses) {
+ return reset_connection(conn, 1);
+ }
+ return SERF_ERROR_ABORTED_CONNECTION;
}
if ((events & APR_POLLERR) != 0) {
/* We might be talking to a buggy HTTP server that doesn't
@@ -1117,7 +1159,8 @@ serf_connection_t *serf_connection_create(
conn->ctx = ctx;
conn->status = APR_SUCCESS;
- conn->address = address;
+ /* Ignore server address if proxy was specified. */
+ conn->address = ctx->proxy_address ? ctx->proxy_address : address;
conn->setup = setup;
conn->setup_baton = setup_baton;
conn->closed = closed;
@@ -1131,6 +1174,7 @@ serf_connection_t *serf_connection_create(
conn->baton.u.conn = conn;
conn->hit_eof = 0;
conn->state = SERF_CONN_INIT;
+ conn->latency = -1; /* unknown */
/* Create a subpool for our connection. */
apr_pool_create(&conn->skt_pool, conn->pool);
@@ -1141,6 +1185,9 @@ serf_connection_t *serf_connection_create(
/* Add the connection to the context. */
*(serf_connection_t **)apr_array_push(ctx->conns) = conn;
+ serf__log(CONN_VERBOSE, __FILE__, "created connection 0x%x\n",
+ conn);
+
return conn;
}
@@ -1154,16 +1201,24 @@ apr_status_t serf_connection_create2(
void *closed_baton,
apr_pool_t *pool)
{
- apr_status_t status;
+ apr_status_t status = APR_SUCCESS;
serf_connection_t *c;
- apr_sockaddr_t *host_address;
+ apr_sockaddr_t *host_address = NULL;
- /* Parse the url, store the address of the server. */
- status = apr_sockaddr_info_get(&host_address,
- host_info.hostname,
- APR_UNSPEC, host_info.port, 0, pool);
- if (status)
- return status;
+ /* Set the port number explicitly, needed to create the socket later. */
+ if (!host_info.port) {
+ host_info.port = apr_uri_port_of_scheme(host_info.scheme);
+ }
+
+ /* Only lookup the address of the server if no proxy server was
+ configured. */
+ if (!ctx->proxy_address) {
+ status = apr_sockaddr_info_get(&host_address,
+ host_info.hostname,
+ APR_UNSPEC, host_info.port, 0, pool);
+ if (status)
+ return status;
+ }
c = serf_connection_create(ctx, host_address, setup, setup_baton,
closed, closed_baton, pool);
@@ -1203,6 +1258,9 @@ apr_status_t serf_connection_close(
if (conn->skt != NULL) {
remove_connection(ctx, conn);
status = apr_socket_close(conn->skt);
+ serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt,
+ "closed socket, status %d\n",
+ status);
if (conn->closed != NULL) {
handle_conn_closed(conn, status);
}
@@ -1213,6 +1271,8 @@ apr_status_t serf_connection_close(
conn->stream = NULL;
}
+ destroy_ostream(conn);
+
/* Remove the connection from the context. We don't want to
* deal with it any more.
*/
@@ -1225,6 +1285,9 @@ apr_status_t serf_connection_close(
}
--ctx->conns->nelts;
+ serf__log(CONN_VERBOSE, __FILE__, "closed connection 0x%x\n",
+ conn);
+
/* Found the connection. Closed it. All done. */
return APR_SUCCESS;
}
@@ -1240,6 +1303,15 @@ void serf_connection_set_max_outstanding_requests(
serf_connection_t *conn,
unsigned int max_requests)
{
+ if (max_requests == 0)
+ serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt,
+ "Set max. nr. of outstanding requests for this "
+ "connection to unlimited.\n");
+ else
+ serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt,
+ "Limit max. nr. of outstanding requests for this "
+ "connection to %u.\n", max_requests);
+
conn->max_outstanding_requests = max_requests;
}
@@ -1279,16 +1351,11 @@ serf_request_t *serf_connection_request_create(
request->next = NULL;
/* Link the request to the end of the request chain. */
- if (conn->state == SERF_CONN_CLOSING) {
- link_requests(&conn->hold_requests, &conn->hold_requests_tail, request);
- }
- else {
- link_requests(&conn->requests, &conn->requests_tail, request);
-
- /* Ensure our pollset becomes writable in context run */
- conn->ctx->dirty_pollset = 1;
- conn->dirty_conn = 1;
- }
+ link_requests(&conn->requests, &conn->requests_tail, request);
+
+ /* Ensure our pollset becomes writable in context run */
+ conn->ctx->dirty_pollset = 1;
+ conn->dirty_conn = 1;
return request;
}
@@ -1314,14 +1381,8 @@ serf_request_t *serf_connection_priority_request_create(
request->written = 0;
request->next = NULL;
- /* Link the new request after the last written request, but before all
- upcoming requests. */
- if (conn->state == SERF_CONN_CLOSING) {
- iter = conn->hold_requests;
- }
- else {
- iter = conn->requests;
- }
+ /* Link the new request after the last written request. */
+ iter = conn->requests;
prev = NULL;
/* Find a request that has data which needs to be delivered. */
@@ -1341,19 +1402,12 @@ serf_request_t *serf_connection_priority_request_create(
prev->next = request;
} else {
request->next = iter;
- if (conn->state == SERF_CONN_CLOSING) {
- conn->hold_requests = request;
- }
- else {
- conn->requests = request;
- }
+ conn->requests = request;
}
- if (conn->state != SERF_CONN_CLOSING) {
- /* Ensure our pollset becomes writable in context run */
- conn->ctx->dirty_pollset = 1;
- conn->dirty_conn = 1;
- }
+ /* Ensure our pollset becomes writable in context run */
+ conn->ctx->dirty_pollset = 1;
+ conn->dirty_conn = 1;
return request;
}
@@ -1364,6 +1418,13 @@ apr_status_t serf_request_cancel(serf_request_t *request)
return cancel_request(request, &request->conn->requests, 0);
}
+apr_status_t serf_request_is_written(serf_request_t *request)
+{
+ if (request->written && !request->req_bkt)
+ return APR_SUCCESS;
+
+ return APR_EBUSY;
+}
apr_pool_t *serf_request_get_pool(const serf_request_t *request)
{
@@ -1419,13 +1480,26 @@ serf_bucket_t *serf_request_bucket_request_create(
/* Setup server authorization headers */
if (ctx->authn_info.scheme)
- ctx->authn_info.scheme->setup_request_func(401, conn, method, uri,
+ ctx->authn_info.scheme->setup_request_func(HOST, 0, conn, request,
+ method, uri,
hdrs_bkt);
/* Setup proxy authorization headers */
if (ctx->proxy_authn_info.scheme)
- ctx->proxy_authn_info.scheme->setup_request_func(407, conn, method,
- uri, hdrs_bkt);
+ ctx->proxy_authn_info.scheme->setup_request_func(PROXY, 0, conn,
+ request,
+ method, uri, hdrs_bkt);
return req_bkt;
}
+
+apr_interval_time_t serf_connection_get_latency(serf_connection_t *conn)
+{
+ if (conn->ctx->proxy_address) {
+ /* Detecting network latency for proxied connection is not implemented
+ yet. */
+ return -1;
+ }
+
+ return conn->latency;
+}