diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2013-06-03 18:34:10 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2013-06-03 18:34:10 +0000 |
commit | f143c3cab79c59dd57124f19d16ac35253843136 (patch) | |
tree | 9fa67aa3d59e9d96f5f37858e95c4ab91960ea92 /outgoing.c | |
parent | 6f61a1acd01dc2ad1d2f5c1f7458702c77c69f9c (diff) | |
download | libserf-tarball-fb6ad73dfc340d81d364f2c8bf791bcf6e84fb67.tar.gz |
serf-1.2.1HEADserf-1.2.1master
Diffstat (limited to 'outgoing.c')
-rw-r--r-- | outgoing.c | 282 |
1 files changed, 178 insertions, 104 deletions
@@ -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; +} |