Author: rhuijben
Date: Mon Oct 19 14:00:40 2015
New Revision: 1709415
URL: http://svn.apache.org/viewvc?rev=1709415&view=rev
Log:
Extract the request specific functions from outgoing.c to a separate
C file. This adds a tiny bit of abstraction towards separate protocols.
This patch should have no behaviour changes, but changes function names
of several functions.
* connection_request.c
New file, copied from outgoing.c
(clean_resp,
serf__link_requests,
serf__destroy_request,
serf__cancel_request,
serf__req_list_length,
serf__setup_request,
serf__handle_response,
serf__provide_credentials,
create_request,
serf_connection_request_create,
priority_request_create,
serf__ssltunnel_request_create,
serf__request_requeue,
serf_request_cancel,
serf_request_is_written,
serf_request_get_pool,
serf_request_get_alloc,
serf_request_get_conn,
serf_request_set_handler,
serf_request_bucket_request_create): Moved here. Changed into internal API
where necessary.
* outgoing.c
(clean_resp,
link_requests,
destroy_request,
cancel_request,
req_list_length): Removed here.
(reset_connection): Update caller.
(setup_request): Removed here.
(write_to_connection): Update caller.
(handle_response,
serf__provide_credentials): Removed here.
(read_from_connection,
serf_connection_close): Update caller.
(serf_connection_request_create,
priority_request_create,
serf_connection_priority_request_create,
serf__ssltunnel_request_create,
serf__request_requeue,
serf_request_cancel,
serf_request_is_written,
serf_request_get_pool,
serf_request_get_alloc,
serf_request_get_conn,
serf_request_set_handler,
serf_request_bucket_request_create): Remove here.
* serf_private.h
(void serf__link_requests,
serf__destroy_request,
serf__cancel_request,
serf__req_list_length,
serf__setup_request,
serf__link_requests): New functions.
Added:
serf/trunk/connection_request.c
- copied, changed from r1709385, serf/trunk/outgoing.c
Modified:
serf/trunk/outgoing.c
serf/trunk/serf_private.h
Copied: serf/trunk/connection_request.c (from r1709385, serf/trunk/outgoing.c)
URL:
http://svn.apache.org/viewvc/serf/trunk/connection_request.c?p2=serf/trunk/connection_request.c&p1=serf/trunk/outgoing.c&r1=1709385&r2=1709415&rev=1709415&view=diff
==============================================================================
--- serf/trunk/outgoing.c (original)
+++ serf/trunk/connection_request.c Mon Oct 19 14:00:40 2015
@@ -29,29 +29,6 @@
#include "serf_private.h"
-/* forward definitions */
-static apr_status_t read_from_connection(serf_connection_t *conn);
-static apr_status_t write_to_connection(serf_connection_t *conn);
-static apr_status_t hangup_connection(serf_connection_t *conn);
-
-/* cleanup for sockets */
-static apr_status_t clean_skt(void *data)
-{
- serf_connection_t *conn = data;
- apr_status_t status = APR_SUCCESS;
-
- if (conn->skt) {
- status = apr_socket_close(conn->skt);
- conn->skt = NULL;
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "closed socket, status %d\n", status);
- serf_config_remove_value(conn->config, SERF_CONFIG_CONN_LOCALIP);
- serf_config_remove_value(conn->config, SERF_CONFIG_CONN_REMOTEIP);
- }
-
- return status;
-}
-
static apr_status_t clean_resp(void *data)
{
serf_request_t *request = data;
@@ -82,468 +59,8 @@ static apr_status_t clean_resp(void *dat
return APR_SUCCESS;
}
-/* cleanup for conns */
-static apr_status_t clean_conn(void *data)
-{
- serf_connection_t *conn = data;
-
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "cleaning up connection 0x%x\n", conn);
- serf_connection_close(conn);
-
- return APR_SUCCESS;
-}
-
-/* Check if there is data waiting to be sent over the socket. This can happen
- in two situations:
- - The connection queue has atleast one request with unwritten data.
- - All requests are written and the ssl layer wrote some data while reading
- the response. This can happen when the server triggers a renegotiation,
- e.g. after the first and only request on that connection was received.
- Returns 1 if data is pending on CONN, NULL if not.
- If NEXT_REQ is not NULL, it will be filled in with the next available
request
- with unwritten data. */
-static int
-request_or_data_pending(serf_request_t **next_req, serf_connection_t *conn)
-{
- int reqs_in_progress;
-
- reqs_in_progress = conn->completed_requests - conn->completed_responses;
-
- /* Prepare the next request */
- if (conn->framing_type != SERF_CONNECTION_FRAMING_TYPE_NONE
- && (conn->pipelining || (!conn->pipelining && reqs_in_progress == 0)))
- {
- /* Skip all requests that have been written completely but we're still
- waiting for a response. */
- serf_request_t *request = conn->unwritten_reqs;
-
- if (next_req)
- *next_req = request;
-
- if (request != NULL) {
- return 1;
- }
- }
-
- if (next_req)
- *next_req = NULL;
-
- if (conn->ostream_head) {
- const char *dummy;
- apr_size_t len;
- apr_status_t status;
-
- status = serf_bucket_peek(conn->ostream_head, &dummy,
- &len);
- if (!SERF_BUCKET_READ_ERROR(status) && len) {
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "Extra data to be written after sending complete
requests.\n");
- return 1;
- }
- }
-
- return 0;
-}
-
-/* Update the pollset for this connection. We tweak the pollset based on
- * whether we want to read and/or write, given conditions within the
- * connection. If the connection is not (yet) in the pollset, then it
- * will be added.
- */
-apr_status_t serf__conn_update_pollset(serf_connection_t *conn)
-{
- serf_context_t *ctx = conn->ctx;
- apr_status_t status;
- apr_pollfd_t desc = { 0 };
-
- if (!conn->skt) {
- return APR_SUCCESS;
- }
-
- /* Remove the socket from the poll set. */
- desc.desc_type = APR_POLL_SOCKET;
- desc.desc.s = conn->skt;
- desc.reqevents = conn->reqevents;
-
- status = ctx->pollset_rm(ctx->pollset_baton,
- &desc, &conn->baton);
- if (status && !APR_STATUS_IS_NOTFOUND(status))
- return status;
-
- /* Now put it back in with the correct read/write values. */
- desc.reqevents = APR_POLLHUP | APR_POLLERR;
- if ((conn->written_reqs || conn->unwritten_reqs) &&
- 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;
-
- /* Don't write if OpenSSL told us that it needs to read data first. */
- if (! conn->stop_writing) {
-
- /* 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 &&
- conn->state != SERF_CONN_CLOSING)
- desc.reqevents |= APR_POLLOUT;
- else {
-
- if ((conn->probable_keepalive_limit &&
- conn->completed_requests >
conn->probable_keepalive_limit) ||
- (conn->max_outstanding_requests &&
- conn->completed_requests - conn->completed_responses >=
- conn->max_outstanding_requests)) {
- /* we wouldn't try to write any way right now. */
- }
- else if (request_or_data_pending(NULL, conn)) {
- desc.reqevents |= APR_POLLOUT;
- }
- }
- }
- }
-
- /* If we can have async responses, always look for something to read. */
- if (conn->framing_type != SERF_CONNECTION_FRAMING_TYPE_HTTP1
- || conn->async_responses)
- {
- desc.reqevents |= APR_POLLIN;
- }
-
- /* save our reqevents, so we can pass it in to remove later. */
- conn->reqevents = desc.reqevents;
-
- /* Note: even if we don't want to read/write this socket, we still
- * want to poll it for hangups and errors.
- */
- return ctx->pollset_add(ctx->pollset_baton,
- &desc, &conn->baton);
-}
-
-#ifdef SERF_DEBUG_BUCKET_USE
-
-/* Make sure all response buckets were drained. */
-static void check_buckets_drained(serf_connection_t *conn)
-{
- serf_request_t *request = conn->requests;
-
- for ( ; request ; request = request->next ) {
- if (request->resp_bkt != NULL) {
- /* ### crap. can't do this. this allocator may have un-drained
- * ### REQUEST buckets.
- */
- /* serf_debug__entered_loop(request->resp_bkt->allocator); */
- /* ### for now, pretend we closed the conn (resets the tracking) */
- serf_debug__closed_conn(request->resp_bkt->allocator);
- }
- }
-}
-
-#endif
-
-static void destroy_ostream(serf_connection_t *conn)
-{
- if (conn->ostream_head != NULL) {
- serf_bucket_destroy(conn->ostream_head);
- conn->ostream_head = NULL;
- conn->ostream_tail = NULL;
- }
-}
-
-static apr_status_t detect_eof(void *baton, serf_bucket_t *aggregate_bucket)
-{
- serf_connection_t *conn = baton;
- conn->hit_eof = 1;
- return APR_EAGAIN;
-}
-
-static apr_status_t do_conn_setup(serf_connection_t *conn)
-{
- apr_status_t status;
- serf_bucket_t *ostream;
-
- /* ### dunno what the hell this is about. this latency stuff got
- ### added, and who knows whether it should stay... */
- conn->latency = apr_time_now() - conn->connect_time;
-
- if (conn->ostream_head == NULL) {
- conn->ostream_head = serf_bucket_aggregate_create(conn->allocator);
- }
-
- if (conn->ostream_tail == NULL) {
- conn->ostream_tail = serf__bucket_stream_create(conn->allocator,
- detect_eof,
- conn);
- }
-
- ostream = conn->ostream_tail;
-
- status = (*conn->setup)(conn->skt,
- &conn->stream,
- &ostream,
- conn->setup_baton,
- conn->pool);
- if (status) {
- /* extra destroy here since it wasn't added to the head bucket yet. */
- serf_bucket_destroy(conn->ostream_tail);
- destroy_ostream(conn);
- return status;
- }
-
- /* Share the configuration with all the buckets in the newly created output
- chain (see PLAIN or ENCRYPTED scenario's), including the request buckets
- created by the application (ostream_tail will handle this for us). */
- serf_bucket_set_config(conn->ostream_head, conn->config);
-
- /* Share the configuration with the ssl_decrypt and socket buckets. The
- response buckets wrapping the ssl_decrypt/socket buckets won't get the
- config automatically because they are upstream. */
- serf_bucket_set_config(conn->stream, conn->config);
-
- serf_bucket_aggregate_append(conn->ostream_head,
- ostream);
-
- /* We typically have one of two scenarios, based on whether the
- application decided to encrypt this connection:
-
- PLAIN:
-
- conn->stream = SOCKET(skt)
- conn->ostream_head = AGGREGATE(ostream_tail)
- conn->ostream_tail = STREAM(<detect_eof>, REQ1, REQ2, ...)
-
- ENCRYPTED:
-
- conn->stream = DECRYPT(SOCKET(skt))
- conn->ostream_head = AGGREGATE(ENCRYPT(ostream_tail))
- conn->ostream_tail = STREAM(<detect_eof>, REQ1, REQ2, ...)
-
- where STREAM is an internal variant of AGGREGATE.
- */
-
- return status;
-}
-
-/* Set up the input and output stream buckets.
- When a tunnel over an http proxy is needed, create a socket bucket and
- empty aggregate bucket for sending and receiving unencrypted requests
- over the socket.
-
- After the tunnel is there, or no tunnel was needed, ask the application
- to create the input and output buckets, which should take care of the
- [en/de]cryption.
- */
-
-static apr_status_t prepare_conn_streams(serf_connection_t *conn,
- serf_bucket_t **ostreamt,
- serf_bucket_t **ostreamh)
-{
- apr_status_t status;
-
- /* Do we need a SSL tunnel first? */
- if (conn->state == SERF_CONN_CONNECTED) {
- /* If the connection does not have an associated bucket, then
- * call the setup callback to get one.
- */
- if (conn->stream == NULL) {
- status = do_conn_setup(conn);
- if (status) {
- return status;
- }
- }
- *ostreamt = conn->ostream_tail;
- *ostreamh = conn->ostream_head;
- } else {
- /* state == SERF_CONN_SETUP_SSLTUNNEL */
-
- /* SSL tunnel needed and not set up yet, get a direct unencrypted
- stream for this socket */
- if (conn->stream == NULL) {
- conn->stream = serf_context_bucket_socket_create(conn->ctx,
- conn->skt,
- conn->allocator);
- }
-
- /* Don't create the ostream bucket chain including the ssl_encrypt
- bucket yet. This ensure the CONNECT request is sent unencrypted
- to the proxy. */
- *ostreamt = *ostreamh = conn->ssltunnel_ostream;
- }
-
- return APR_SUCCESS;
-}
-
-static void store_ipaddresses_in_config(serf_config_t *config,
- apr_socket_t *skt)
-{
- apr_sockaddr_t *sa;
-
- if (apr_socket_addr_get(&sa, APR_LOCAL, skt) == APR_SUCCESS) {
- char buf[32];
- apr_sockaddr_ip_getbuf(buf, 32, sa);
- serf_config_set_stringf(config, SERF_CONFIG_CONN_LOCALIP,
- "%s:%d", buf, sa->port);
- }
- if (apr_socket_addr_get(&sa, APR_REMOTE, skt) == APR_SUCCESS) {
- char buf[32];
- apr_sockaddr_ip_getbuf(buf, 32, sa);
- serf_config_set_stringf(config, SERF_CONFIG_CONN_REMOTEIP,
- "%s:%d", buf, sa->port);
- }
-}
-
-/* Create and connect sockets for any connections which don't have them
- * yet. This is the core of our lazy-connect behavior.
- */
-apr_status_t serf__open_connections(serf_context_t *ctx)
-{
- int i;
-
- for (i = ctx->conns->nelts; i--; ) {
- serf_connection_t *conn = GET_CONN(ctx, i);
- apr_status_t status;
- apr_socket_t *skt;
-
- conn->seen_in_pollset = 0;
-
- if (conn->skt != NULL) {
-#ifdef SERF_DEBUG_BUCKET_USE
- check_buckets_drained(conn);
-#endif
- continue;
- }
-
- /* Delay opening until we have something to deliver! */
- if (conn->unwritten_reqs == NULL) {
- continue;
- }
-
- apr_pool_clear(conn->skt_pool);
- apr_pool_cleanup_register(conn->skt_pool, conn, clean_skt,
- apr_pool_cleanup_null);
-
- status = apr_socket_create(&skt, conn->address->family,
- SOCK_STREAM,
-#if APR_MAJOR_VERSION > 0
- APR_PROTO_TCP,
-#endif
- conn->skt_pool);
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "created socket for conn 0x%x, status %d\n", conn, status);
- if (status != APR_SUCCESS)
- return status;
-
- /* Set the socket to be non-blocking */
- if ((status = apr_socket_timeout_set(skt, 0)) != APR_SUCCESS)
- return status;
-
- /* Disable Nagle's algorithm */
- if ((status = apr_socket_opt_set(skt,
- APR_TCP_NODELAY, 1)) != APR_SUCCESS)
- return status;
-
- /* 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.
- */
- status = apr_socket_connect(skt, conn->address);
- store_ipaddresses_in_config(conn->config, skt);
-
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "connected socket for conn 0x%x, status %d\n", conn, status);
- if (status != APR_SUCCESS) {
- if (!APR_STATUS_IS_EINPROGRESS(status))
- return status;
- }
-
- status = serf_config_set_string(conn->config,
- SERF_CONFIG_CONN_PIPELINING,
- (conn->max_outstanding_requests != 1 &&
- conn->pipelining == 1) ? "Y" : "N");
- if (status)
- return status;
-
- /* Flag our pollset as dirty now that we have a new socket. */
- conn->dirty_conn = 1;
- ctx->dirty_pollset = 1;
-
- /* If the authentication was already started on another connection,
- prepare this connection (it might be possible to skip some
- part of the handshaking). */
- if (ctx->proxy_address) {
- status = serf__auth_setup_connection(PROXY, conn);
- if (status){
- return status;
- }
- }
-
- status = serf__auth_setup_connection(HOST, conn);
- if (status)
- return status;
-
- /* Does this connection require a SSL tunnel over the proxy? */
- if (ctx->proxy_address && strcmp(conn->host_info.scheme, "https") == 0)
- serf__ssltunnel_connect(conn);
- else {
- conn->state = SERF_CONN_CONNECTED;
- status = do_conn_setup(conn);
- if (status)
- return status;
- }
- }
-
- return APR_SUCCESS;
-}
-
-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;
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "stop writing on conn 0x%x\n", conn);
-
- /* Clear our iovec. */
- conn->vec_len = 0;
-
- /* Update the pollset to know we don't want to write on this socket any
- * more.
- */
- conn->dirty_conn = 1;
- conn->ctx->dirty_pollset = 1;
- return APR_SUCCESS;
-}
-
-/* Read the 'Connection' header from the response. Return SERF_ERROR_CLOSING if
- * the header contains value 'close' indicating the server is closing the
- * connection right after this response.
- * Otherwise returns APR_SUCCESS.
- */
-static apr_status_t is_conn_closing(serf_bucket_t *response)
-{
- serf_bucket_t *hdrs;
- const char *val;
-
- hdrs = serf_bucket_response_get_headers(response);
- val = serf_bucket_headers_get(hdrs, "Connection");
- if (val && strcasecmp("close", val) == 0)
- {
- return SERF_ERROR_CLOSING;
- }
-
- return APR_SUCCESS;
-}
-
-static void link_requests(serf_request_t **list, serf_request_t **tail,
- serf_request_t *request)
+void serf__link_requests(serf_request_t **list, serf_request_t **tail,
+ serf_request_t *request)
{
if (*list == NULL) {
*list = request;
@@ -555,7 +72,7 @@ static void link_requests(serf_request_t
}
}
-static apr_status_t destroy_request(serf_request_t *request)
+apr_status_t serf__destroy_request(serf_request_t *request)
{
serf_connection_t *conn = request->conn;
@@ -584,9 +101,9 @@ static apr_status_t destroy_request(serf
return APR_SUCCESS;
}
-static apr_status_t cancel_request(serf_request_t *request,
- serf_request_t **list,
- int notify_request)
+apr_status_t serf__cancel_request(serf_request_t *request,
+ serf_request_t **list,
+ int notify_request)
{
/* If we haven't run setup, then we won't have a handler to call. */
if (request->handler && notify_request) {
@@ -611,11 +128,11 @@ static apr_status_t cancel_request(serf_
}
}
- return destroy_request(request);
+ return serf__destroy_request(request);
}
/* Calculate the length of a linked list of requests. */
-static unsigned int req_list_length(serf_request_t *req)
+unsigned int serf__req_list_length(serf_request_t *req)
{
unsigned int length = 0;
@@ -627,205 +144,7 @@ static unsigned int req_list_length(serf
return length;
}
-static apr_status_t remove_connection(serf_context_t *ctx,
- serf_connection_t *conn)
-{
- apr_pollfd_t desc = { 0 };
-
- desc.desc_type = APR_POLL_SOCKET;
- desc.desc.s = conn->skt;
- desc.reqevents = conn->reqevents;
-
- return ctx->pollset_rm(ctx->pollset_baton,
- &desc, &conn->baton);
-}
-
-/* A socket was closed, inform the application. */
-static void handle_conn_closed(serf_connection_t *conn, apr_status_t status)
-{
- (*conn->closed)(conn, conn->closed_baton, status,
- conn->pool);
-}
-
-static apr_status_t reset_connection(serf_connection_t *conn,
- int requeue_requests)
-{
- serf_context_t *ctx = conn->ctx;
- apr_status_t status;
- serf_request_t *old_reqs;
-
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "reset connection 0x%x\n", conn);
-
- conn->probable_keepalive_limit = conn->completed_responses;
- conn->completed_requests = 0;
- conn->completed_responses = 0;
-
- /* Clear the unwritten_reqs queue, so the application can requeue cancelled
- requests on it for the new socket. */
- old_reqs = conn->unwritten_reqs;
- conn->unwritten_reqs = NULL;
- conn->unwritten_reqs_tail = NULL;
-
- /* First, cancel all written requests for which we haven't received a
- response yet. Inform the application that the request is cancelled,
- so it can requeue them if needed. */
- while (conn->written_reqs) {
- cancel_request(conn->written_reqs, &conn->written_reqs,
- requeue_requests);
- }
- conn->written_reqs_tail = NULL;
-
- /* Handle all outstanding unwritten requests.
- TODO: what about a partially written request? */
- while (old_reqs) {
- /* If we haven't started to write the connection, bring it over
- * unchanged to our new socket.
- * Do not copy a CONNECT request to the new connection, the ssl tunnel
- * setup code will create a new CONNECT request already.
- */
- if (requeue_requests && !old_reqs->writing_started &&
- !old_reqs->ssltunnel) {
-
- serf_request_t *req = old_reqs;
- old_reqs = old_reqs->next;
- req->next = NULL;
- link_requests(&conn->unwritten_reqs, &conn->unwritten_reqs_tail,
- req);
- }
- else {
- /* We don't want to requeue the request or this request was
partially
- written. Inform the application that the request is cancelled.
*/
- cancel_request(old_reqs, &old_reqs, requeue_requests);
- }
- }
-
- /* Requests queue has been prepared for a new socket, close the old one. */
- if (conn->skt != NULL) {
- remove_connection(ctx, conn);
- status = clean_skt(conn);
- if (conn->closed != NULL) {
- handle_conn_closed(conn, status);
- }
- }
-
- if (conn->stream != NULL) {
- serf_bucket_destroy(conn->stream);
- conn->stream = NULL;
- }
-
- destroy_ostream(conn);
-
- /* Don't try to resume any writes */
- conn->vec_len = 0;
-
- conn->dirty_conn = 1;
- conn->ctx->dirty_pollset = 1;
- conn->state = SERF_CONN_INIT;
-
- conn->hit_eof = 0;
- conn->connect_time = 0;
- conn->latency = -1;
- conn->stop_writing = 0;
- conn->write_now = 0;
- /* conn->pipelining */
-
- conn->framing_type = SERF_CONNECTION_FRAMING_TYPE_HTTP1;
-
- if (conn->protocol_baton) {
- conn->perform_teardown(conn);
- conn->protocol_baton = NULL;
- }
-
- conn->perform_read = read_from_connection;
- conn->perform_write = write_to_connection;
- conn->perform_hangup = hangup_connection;
- conn->perform_teardown = NULL;
-
- conn->status = APR_SUCCESS;
-
- /* Let our context know that we've 'reset' the socket already. */
- conn->seen_in_pollset |= APR_POLLHUP;
-
- /* Recalculate the current list length */
- conn->nr_of_written_reqs = 0;
- conn->nr_of_unwritten_reqs = req_list_length(conn->unwritten_reqs);
-
- /* Found the connection. Closed it. All done. */
- return APR_SUCCESS;
-}
-
-static apr_status_t socket_writev(serf_connection_t *conn)
-{
- apr_size_t written;
- apr_status_t status;
-
- status = apr_socket_sendv(conn->skt, conn->vec,
- conn->vec_len, &written);
- if (status && !APR_STATUS_IS_EAGAIN(status))
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "socket_sendv error %d\n", status);
-
- /* did we write everything? */
- if (written) {
- apr_size_t len = 0;
- int i;
-
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "--- socket_sendv: %d bytes. --\n", written);
-
- for (i = 0; i < conn->vec_len; i++) {
- len += conn->vec[i].iov_len;
- if (written < len) {
- serf__log_nopref(LOGLVL_DEBUG, LOGCOMP_RAWMSG, conn->config,
- "%.*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));
- conn->vec_len -= i;
- }
- 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(LOGLVL_DEBUG, LOGCOMP_RAWMSG, conn->config,
- "%.*s",
- conn->vec[i].iov_len, conn->vec[i].iov_base);
- }
- }
- if (len == written) {
- conn->vec_len = 0;
- }
- serf__log_nopref(LOGLVL_DEBUG, LOGCOMP_RAWMSG, conn->config, "\n");
-
- /* Log progress information */
- serf__context_progress_delta(conn->ctx, 0, written);
- }
-
- return status;
-}
-
-apr_status_t serf__connection_flush(serf_connection_t *conn)
-{
- apr_status_t status = APR_SUCCESS;
-
- while (conn->vec_len && !status) {
- status = socket_writev(conn);
-
- /* If the write would have blocked, then we're done. Don't try
- * to write anything else to the socket.
- */
- if (APR_STATUS_IS_EPIPE(status)
- || APR_STATUS_IS_ECONNRESET(status)
- || APR_STATUS_IS_ECONNABORTED(status))
- return no_more_writes(conn);
-
- }
- return status;
-}
-
-static apr_status_t setup_request(serf_request_t *request)
+apr_status_t serf__setup_request(serf_request_t *request)
{
serf_connection_t *conn = request->conn;
apr_status_t status;
@@ -848,198 +167,10 @@ static apr_status_t setup_request(serf_r
return status;
}
-/* write data out to the connection */
-static apr_status_t write_to_connection(serf_connection_t *conn)
-{
- if (conn->probable_keepalive_limit &&
- conn->completed_requests > conn->probable_keepalive_limit) {
-
- conn->dirty_conn = 1;
- conn->ctx->dirty_pollset = 1;
-
- /* backoff for now. */
- return APR_SUCCESS;
- }
-
- /* Keep reading and sending until we run out of stuff to read, or
- * writing would block.
- */
- while (1) {
- serf_request_t *request;
- int stop_reading = 0;
- apr_status_t status;
- apr_status_t read_status;
- serf_bucket_t *ostreamt;
- serf_bucket_t *ostreamh;
- int reqs_in_progress;
-
- reqs_in_progress = conn->completed_requests -
conn->completed_responses;
-
- /* If we're setting up an ssl tunnel, we can't send real requests
- at yet, as they need to be encrypted and our encrypt buckets
- aren't created yet as we still need to read the unencrypted
- response of the CONNECT request. */
- if (conn->state == SERF_CONN_SETUP_SSLTUNNEL && reqs_in_progress > 0)
- {
- return APR_SUCCESS;
- }
-
- /* We try to limit the number of in-flight requests so that we
- don't have to repeat too many if the connection drops. */
- if (conn->max_outstanding_requests
- && (reqs_in_progress >= conn->max_outstanding_requests))
- {
- /* backoff for now. */
- return APR_SUCCESS;
- }
-
- /* If we have unwritten data, then write what we can. */
- status = serf__connection_flush(conn);
- if (APR_STATUS_IS_EAGAIN(status))
- return APR_SUCCESS;
- else if (status)
- return status;
-
- /* ### can we have a short write, yet no EAGAIN? a short write
- ### would imply unwritten_len > 0 ... */
- /* assert: unwritten_len == 0. */
-
- /* We may need to move forward to a request which has something
- * to write.
- */
- if (!request_or_data_pending(&request, conn)) {
- /* No more requests (with data) are registered with the
- * connection, and no data is pending on the outgoing stream.
- * Let's update the pollset so that we don't try to write to this
- * socket again.
- */
- conn->dirty_conn = 1;
- conn->ctx->dirty_pollset = 1;
- return APR_SUCCESS;
- }
-
- status = prepare_conn_streams(conn, &ostreamt, &ostreamh);
- if (status) {
- return status;
- }
-
- if (request) {
- if (request->req_bkt == NULL) {
- read_status = setup_request(request);
- if (read_status) {
- /* Something bad happened. Propagate any errors. */
- return read_status;
- }
- }
-
- if (!request->writing_started) {
- request->writing_started = 1;
- serf_bucket_aggregate_append(ostreamt, request->req_bkt);
- }
- }
-
- /* ### optimize at some point by using read_for_sendfile */
- /* TODO: now that read_iovec will effectively try to return as much
- data as available, we probably don't want to read ALL_AVAIL, but
- a lower number, like the size of one or a few TCP packets, the
- available TCP buffer size ... */
- read_status = serf_bucket_read_iovec(ostreamh,
- SERF_READ_ALL_AVAIL,
- IOV_MAX,
- conn->vec,
- &conn->vec_len);
-
- if (!conn->hit_eof) {
- if (APR_STATUS_IS_EAGAIN(read_status)) {
- /* We read some stuff, but should not try to read again. */
- stop_reading = 1;
- }
- else if (read_status == SERF_ERROR_WAIT_CONN) {
- /* The bucket told us that it can't provide more data until
- more data is read from the socket. This normally happens
- during a SSL handshake.
-
- We should avoid looking for writability for a while so
- that (hopefully) something will appear in the bucket so
- we can actually write something. otherwise, we could
- end up in a CPU spin: socket wants something, but we
- don't have anything (and keep returning EAGAIN)
- */
- conn->stop_writing = 1;
- conn->dirty_conn = 1;
- conn->ctx->dirty_pollset = 1;
- }
- else if (read_status && !APR_STATUS_IS_EOF(read_status)) {
- /* Something bad happened. Propagate any errors. */
- return read_status;
- }
- }
-
- /* If we got some data, then deliver it. */
- /* ### what to do if we got no data?? is that a problem? */
- status = serf__connection_flush(conn);
- if (APR_STATUS_IS_EAGAIN(status))
- return APR_SUCCESS;
- else if (status)
- return status;
-
- if (read_status == SERF_ERROR_WAIT_CONN) {
- stop_reading = 1;
- conn->stop_writing = 1;
- conn->dirty_conn = 1;
- conn->ctx->dirty_pollset = 1;
- }
- else if (request && read_status && conn->hit_eof &&
- conn->vec_len == 0) {
- /* If we hit the end of the request bucket and all of its data has
- * been written, then clear it out to signify that we're done
- * sending the request. On the next iteration through this loop:
- * - if there are remaining bytes they will be written, and as the
- * request bucket will be completely read it will be destroyed
then.
- * - we'll see if there are other requests that need to be sent
- * ("pipelining").
- */
- conn->hit_eof = 0;
- serf_bucket_destroy(request->req_bkt);
- request->req_bkt = NULL;
-
- /* Move the request to the written queue */
- link_requests(&conn->written_reqs, &conn->written_reqs_tail,
- request);
- conn->nr_of_written_reqs++;
- conn->unwritten_reqs = conn->unwritten_reqs->next;
- conn->nr_of_unwritten_reqs--;
- request->next = NULL;
-
- /* If our connection has async responses enabled, we're not
- * going to get a reply back, so kill the request.
- */
- if (conn->async_responses) {
- conn->unwritten_reqs = request->next;
- conn->nr_of_unwritten_reqs--;
- destroy_request(request);
- }
-
- conn->completed_requests++;
-
- if (conn->probable_keepalive_limit &&
- conn->completed_requests > conn->probable_keepalive_limit) {
- /* backoff for now. */
- stop_reading = 1;
- }
- }
-
- if (stop_reading) {
- return APR_SUCCESS;
- }
- }
- /* NOTREACHED */
-}
-
/* A response message was received from the server, so call
the handler as specified on the original request. */
-static apr_status_t handle_response(serf_request_t *request,
- apr_pool_t *pool)
+apr_status_t serf__handle_response(serf_request_t *request,
+ apr_pool_t *pool)
{
int consumed_response = 0;
@@ -1120,31 +251,6 @@ static apr_status_t handle_response(serf
return APR_SUCCESS;
}
-/* An async response message was received from the server. */
-static apr_status_t handle_async_response(serf_connection_t *conn,
- apr_pool_t *pool)
-{
- apr_status_t status;
-
- if (conn->current_async_response == NULL) {
- conn->current_async_response =
- (*conn->async_acceptor)(NULL, conn->stream,
- conn->async_acceptor_baton, pool);
- }
-
- status = (*conn->async_handler)(NULL, conn->current_async_response,
- conn->async_handler_baton, pool);
-
- if (APR_STATUS_IS_EOF(status)) {
- serf_bucket_destroy(conn->current_async_response);
- conn->current_async_response = NULL;
- status = APR_SUCCESS;
- }
-
- return status;
-}
-
-
apr_status_t
serf__provide_credentials(serf_context_t *ctx,
char **username,
@@ -1181,7 +287,7 @@ serf__provide_credentials(serf_context_t
if (!authn_req->req_bkt) {
apr_status_t status;
- status = setup_request(authn_req);
+ status = serf__setup_request(authn_req);
/* If we can't setup a request, don't bother setting up the
ssl tunnel. */
if (status)
@@ -1199,627 +305,6 @@ serf__provide_credentials(serf_context_t
return APR_SUCCESS;
}
-/* read data from the connection */
-static apr_status_t read_from_connection(serf_connection_t *conn)
-{
- apr_status_t status;
- apr_pool_t *tmppool;
- apr_status_t close_connection = APR_SUCCESS;
-
- /* Whatever is coming in on the socket corresponds to the first request
- * on our chain.
- */
- serf_request_t *request = conn->written_reqs;
- if (!request) {
- /* Request wasn't completely written yet! */
- request = conn->unwritten_reqs;
- }
-
- /* If the stop_writing flag was set on the connection, reset it now because
- there is some data to read. */
- if (conn->stop_writing) {
- conn->stop_writing = 0;
- conn->dirty_conn = 1;
- conn->ctx->dirty_pollset = 1;
- }
-
- /* assert: request != NULL */
-
- if ((status = apr_pool_create(&tmppool, conn->pool)) != APR_SUCCESS)
- return status;
-
- /* Invoke response handlers until we have no more work. */
- while (1) {
- serf_bucket_t *dummy1, *dummy2;
-
- apr_pool_clear(tmppool);
-
- /* Only interested in the input stream here. */
- status = prepare_conn_streams(conn, &dummy1, &dummy2);
- if (status) {
- goto error;
- }
-
- /* We have a different codepath when we can have async responses. */
- if (conn->async_responses) {
- /* TODO What about socket errors? */
- status = handle_async_response(conn, tmppool);
- if (APR_STATUS_IS_EAGAIN(status)) {
- status = APR_SUCCESS;
- goto error;
- }
- if (status) {
- goto error;
- }
- continue;
- }
-
- /* We are reading a response for a request we haven't
- * written yet!
- *
- * This shouldn't normally happen EXCEPT:
- *
- * 1) when the other end has closed the socket and we're
- * pending an EOF return.
- * 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.
- *
- * 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->writing_started) {
- const char *data;
- apr_size_t len;
-
- status = serf_bucket_peek(conn->stream, &data, &len);
-
- if (APR_STATUS_IS_EOF(status)) {
- reset_connection(conn, 1);
- status = APR_SUCCESS;
- goto error;
- }
- 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;
- }
-
- /* Unexpected response from the server */
- if (conn->write_now) {
- status = write_to_connection(conn);
-
- if (!SERF_BUCKET_READ_ERROR(status))
- status = APR_SUCCESS;
- }
- }
-
- if (conn->framing_type == SERF_CONNECTION_FRAMING_TYPE_NONE)
- break;
-
- /* If the request doesn't have a response bucket, then call the
- * acceptor to get one created.
- */
- if (request->resp_bkt == NULL) {
- if (! request->acceptor) {
- /* Request wasn't even setup.
- Server replying before it received anything? */
- return SERF_ERROR_BAD_HTTP_RESPONSE;
- }
-
- request->resp_bkt = (*request->acceptor)(request, conn->stream,
- request->acceptor_baton,
- tmppool);
- apr_pool_clear(tmppool);
-
- /* Share the configuration with the response bucket(s) */
- serf_bucket_set_config(request->resp_bkt, conn->config);
- }
-
- status = handle_response(request, tmppool);
-
- /* If we received APR_SUCCESS, run this loop again. */
- if (!status) {
- continue;
- }
-
- /* Some systems will not generate a HUP poll event so we have to
- * handle the ECONNRESET issue and ECONNABORT here.
- */
- if (APR_STATUS_IS_ECONNRESET(status) ||
- APR_STATUS_IS_ECONNABORTED(status) ||
- status == SERF_ERROR_REQUEST_LOST) {
- /* 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;
- }
-
- /* This connection uses HTTP pipelining and the server asked for a
- renegotiation (e.g. to access the requested resource a specific
- client certificate is required).
- Because of a known problem in OpenSSL this won't work most of the
- time, so as a workaround, when the server asks for a renegotiation
- on a connection using HTTP pipelining, we reset the connection,
- disable pipelining and reconnect to the server. */
- if (status == SERF_ERROR_SSL_NEGOTIATE_IN_PROGRESS) {
- serf__log(LOGLVL_WARNING, LOGCOMP_CONN, __FILE__, conn->config,
- "The server requested renegotiation. Disable HTTP "
- "pipelining and reset the connection.\n", conn);
-
- serf__connection_set_pipelining(conn, 0);
- reset_connection(conn, 1);
- status = APR_SUCCESS;
- goto error;
- }
-
- /* If our response handler says it can't do anything more, we now
- * treat that as a success.
- */
- if (APR_STATUS_IS_EAGAIN(status)) {
- /* It is possible that while reading the response, the ssl layer
- has prepared some data to send. If this was the last request,
- serf will not check for socket writability, so force this here.
- */
- if (request_or_data_pending(&request, conn) && !request) {
- conn->dirty_conn = 1;
- conn->ctx->dirty_pollset = 1;
- }
- status = APR_SUCCESS;
- goto error;
- }
-
- close_connection = is_conn_closing(request->resp_bkt);
-
- if (!APR_STATUS_IS_EOF(status) &&
- close_connection != SERF_ERROR_CLOSING) {
- /* Whether success, or an error, there is no more to do unless
- * this request has been completed.
- */
- goto error;
- }
-
- /* 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.
- */
- if (request == conn->written_reqs) {
- conn->written_reqs = request->next;
- conn->nr_of_written_reqs--;
- } else {
- conn->unwritten_reqs = request->next;
- conn->nr_of_unwritten_reqs--;
- }
-
- destroy_request(request);
-
- request = conn->written_reqs;
- if (!request) {
- /* Received responses for all written requests */
- conn->written_reqs_tail = NULL;
- /* Request wasn't completely written yet! */
- request = conn->unwritten_reqs;
- if (!request)
- conn->unwritten_reqs_tail = NULL;
- }
-
- conn->completed_responses++;
-
- /* We have received a response. If there are no more outstanding
- requests on this connection, we should stop polling for READ events
- for now. */
- if (!conn->written_reqs && !conn->unwritten_reqs) {
- conn->dirty_conn = 1;
- conn->ctx->dirty_pollset = 1;
- }
-
- /* This means that we're being advised that the connection is done. */
- if (close_connection == SERF_ERROR_CLOSING) {
- reset_connection(conn, 1);
- if (APR_STATUS_IS_EOF(status))
- status = APR_SUCCESS;
- goto error;
- }
-
- /* The server is suddenly deciding to serve more responses than we've
- * seen before.
- *
- * Let our requests go.
- */
- if (conn->probable_keepalive_limit &&
- conn->completed_responses > conn->probable_keepalive_limit) {
- conn->probable_keepalive_limit = 0;
- }
-
- /* If we just ran out of requests or have unwritten requests, then
- * update the pollset. We don't want to read from this socket any
- * more. We are definitely done with this loop, too.
- */
- if (request == NULL || !request->writing_started) {
- conn->dirty_conn = 1;
- conn->ctx->dirty_pollset = 1;
- status = APR_SUCCESS;
- goto error;
- }
- }
-
-error:
- apr_pool_destroy(tmppool);
- return status;
-}
-
-/* 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.
-
- If we haven't had any successful responses on this connection,
- then error out as it is likely a server issue. */
-static apr_status_t hangup_connection(serf_connection_t *conn)
-{
- if (conn->completed_responses) {
- return reset_connection(conn, 1);
- }
-
- return SERF_ERROR_ABORTED_CONNECTION;
-}
-
-/* process all events on the connection */
-apr_status_t serf__process_connection(serf_connection_t *conn,
- apr_int16_t events)
-{
- apr_status_t status;
-
- /* POLLHUP/ERR should come after POLLIN so if there's an error message or
- * the like sitting on the connection, we give the app a chance to read
- * it before we trigger a reset condition.
- */
- if ((events & APR_POLLIN) != 0) {
- if ((status = conn->perform_read(conn)) != APR_SUCCESS)
- return status;
-
- /* If we decided to reset our connection, return now as we don't
- * want to write.
- */
- if ((conn->seen_in_pollset & APR_POLLHUP) != 0) {
- return APR_SUCCESS;
- }
- }
- if ((events & APR_POLLHUP) != 0) {
- /* The connection got reset by the server. */
- return conn->perform_hangup(conn);
- }
- if ((events & APR_POLLERR) != 0) {
- /* We might be talking to a buggy HTTP server that doesn't
- * do lingering-close. (httpd < 2.1.8 does this.)
- *
- * See:
- *
- * http://issues.apache.org/bugzilla/show_bug.cgi?id=35292
- */
- if (conn->completed_requests && !conn->probable_keepalive_limit) {
- return reset_connection(conn, 1);
- }
-#ifdef SO_ERROR
- /* If possible, get the error from the platform's socket layer and
- convert it to an APR status code. */
- {
- apr_os_sock_t osskt;
- if (!apr_os_sock_get(&osskt, conn->skt)) {
- int error;
- apr_socklen_t l = sizeof(error);
-
- if (!getsockopt(osskt, SOL_SOCKET, SO_ERROR, (char*)&error,
- &l)) {
- status = APR_FROM_OS_ERROR(error);
-
- /* Handle fallback for multi-homed servers.
-
- ### Improve algorithm to find better than just 'next'?
-
- Current Windows versions already handle re-ordering for
- api users by using statistics on the recently failed
- connections to order the list of addresses. */
- if (conn->completed_requests == 0
- && conn->address->next != NULL
- && (APR_STATUS_IS_ECONNREFUSED(status)
- || APR_STATUS_IS_TIMEUP(status)
- || APR_STATUS_IS_ENETUNREACH(status))) {
-
- conn->address = conn->address->next;
- return reset_connection(conn, 1);
- }
-
- return status;
- }
- }
- }
-#endif
- return APR_EGENERAL;
- }
- if ((events & APR_POLLOUT) != 0) {
- if ((status = conn->perform_write(conn)) != APR_SUCCESS)
- return status;
- }
- return APR_SUCCESS;
-}
-
-serf_connection_t *serf_connection_create(
- serf_context_t *ctx,
- apr_sockaddr_t *address,
- serf_connection_setup_t setup,
- void *setup_baton,
- serf_connection_closed_t closed,
- void *closed_baton,
- apr_pool_t *pool)
-{
- serf_connection_t *conn = apr_pcalloc(pool, sizeof(*conn));
-
- conn->ctx = ctx;
- conn->status = APR_SUCCESS;
- /* 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;
- conn->closed_baton = closed_baton;
- conn->pool = pool;
- conn->allocator = serf_bucket_allocator_create(pool, NULL, NULL);
- conn->stream = NULL;
- conn->ostream_head = NULL;
- conn->ostream_tail = NULL;
- conn->baton.type = SERF_IO_CONN;
- conn->baton.u.conn = conn;
- conn->hit_eof = 0;
- conn->state = SERF_CONN_INIT;
- conn->latency = -1; /* unknown */
- conn->stop_writing = 0;
- conn->write_now = 0;
- conn->pipelining = 1;
- conn->framing_type = SERF_CONNECTION_FRAMING_TYPE_HTTP1;
- conn->perform_read = read_from_connection;
- conn->perform_write = write_to_connection;
- conn->perform_hangup = hangup_connection;
- conn->perform_teardown = NULL;
- conn->protocol_baton = NULL;
-
- /* Create a subpool for our connection. */
- apr_pool_create(&conn->skt_pool, conn->pool);
-
- /* register a cleanup */
- apr_pool_cleanup_register(conn->pool, conn, clean_conn,
- apr_pool_cleanup_null);
-
- /* Add the connection to the context. */
- *(serf_connection_t **)apr_array_push(ctx->conns) = conn;
-
- return conn;
-}
-
-apr_status_t serf_connection_create2(
- serf_connection_t **conn,
- serf_context_t *ctx,
- apr_uri_t host_info,
- serf_connection_setup_t setup,
- void *setup_baton,
- serf_connection_closed_t closed,
- void *closed_baton,
- apr_pool_t *pool)
-{
- apr_status_t status = APR_SUCCESS;
- serf_config_t *config;
- serf_connection_t *c;
- apr_sockaddr_t *host_address = NULL;
-
- /* 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);
-
- /* We're not interested in the path following the hostname. */
- c->host_url = apr_uri_unparse(c->pool,
- &host_info,
- APR_URI_UNP_OMITPATHINFO |
- APR_URI_UNP_OMITUSERINFO);
-
- /* Store the host info without the path on the connection. */
- (void)apr_uri_parse(c->pool, c->host_url, &(c->host_info));
- if (!c->host_info.port) {
- c->host_info.port = apr_uri_port_of_scheme(c->host_info.scheme);
- }
-
- /* Store the connection specific info in the configuration store */
- status = serf__config_store_get_config(ctx, c, &config, pool);
- if (status)
- return status;
- c->config = config;
- serf_config_set_stringc(config, SERF_CONFIG_HOST_NAME,
- c->host_info.hostname);
- serf_config_set_stringc(config, SERF_CONFIG_HOST_PORT,
- apr_itoa(ctx->pool, c->host_info.port));
-
- *conn = c;
-
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, c->config,
- "created connection 0x%x\n", c);
-
- return status;
-}
-
-apr_status_t serf_connection_reset(
- serf_connection_t *conn)
-{
- return reset_connection(conn, 0);
-}
-
-
-apr_status_t serf_connection_close(
- serf_connection_t *conn)
-{
- int i;
- serf_context_t *ctx = conn->ctx;
- apr_status_t status;
-
- for (i = ctx->conns->nelts; i--; ) {
- serf_connection_t *conn_seq = GET_CONN(ctx, i);
-
- if (conn_seq == conn) {
- /* The application asked to close the connection, no need to notify
- it for each cancelled request. */
- while (conn->written_reqs) {
- cancel_request(conn->written_reqs, &conn->written_reqs, 0);
- }
- while (conn->unwritten_reqs) {
- cancel_request(conn->unwritten_reqs, &conn->unwritten_reqs, 0);
- }
- if (conn->skt != NULL) {
- remove_connection(ctx, conn);
- status = clean_skt(conn);
- if (conn->closed != NULL) {
- handle_conn_closed(conn, status);
- }
- }
- if (conn->stream != NULL) {
- serf_bucket_destroy(conn->stream);
- conn->stream = NULL;
- }
-
- destroy_ostream(conn);
-
- if (conn->protocol_baton) {
- conn->perform_teardown(conn);
- conn->protocol_baton = NULL;
- }
-
- /* Remove the connection from the context. We don't want to
- * deal with it any more.
- */
- if (i < ctx->conns->nelts - 1) {
- /* move later connections over this one. */
- memmove(
- &GET_CONN(ctx, i),
- &GET_CONN(ctx, i + 1),
- (ctx->conns->nelts - i - 1) * sizeof(serf_connection_t *));
- }
- --ctx->conns->nelts;
-
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "closed connection 0x%x\n", conn);
-
- /* Found the connection. Closed it. All done. */
- return APR_SUCCESS;
- }
- }
-
- /* We didn't find the specified connection. */
- /* ### doc talks about this w.r.t poll structures. use something else? */
- return APR_NOTFOUND;
-}
-
-
-void serf_connection_set_max_outstanding_requests(
- serf_connection_t *conn,
- unsigned int max_requests)
-{
- if (max_requests == 0)
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "Set max. nr. of outstanding requests for this "
- "connection to unlimited.\n");
- else
- serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, conn->config,
- "Limit max. nr. of outstanding requests for this "
- "connection to %u.\n", max_requests);
-
- conn->max_outstanding_requests = max_requests;
-}
-
-/* Disable HTTP pipelining, ensure that only one request is outstanding at a
- time. This is an internal method, an application that wants to disable
- HTTP pipelining can achieve this by calling:
- serf_connection_set_max_outstanding_requests(conn, 1) .
- */
-void serf__connection_set_pipelining(serf_connection_t *conn, int enabled)
-{
- conn->pipelining = enabled;
-}
-
-void serf_connection_set_async_responses(
- serf_connection_t *conn,
- serf_response_acceptor_t acceptor,
- void *acceptor_baton,
- serf_response_handler_t handler,
- void *handler_baton)
-{
- conn->async_responses = 1;
- conn->async_acceptor = acceptor;
- conn->async_acceptor_baton = acceptor_baton;
- conn->async_handler = handler;
- conn->async_handler_baton = handler_baton;
-}
-
-void serf_connection_set_framing_type(
- serf_connection_t *conn,
- serf_connection_framing_type_t framing_type)
-{
- conn->framing_type = framing_type;
-
- if (conn->skt) {
- conn->dirty_conn = 1;
- conn->ctx->dirty_pollset = 1;
- conn->stop_writing = 0;
- conn->write_now = 1;
-
- /* Close down existing protocol */
- if (conn->protocol_baton) {
- conn->perform_teardown(conn);
- conn->protocol_baton = NULL;
- }
-
- /* Reset to default */
- conn->perform_read = read_from_connection;
- conn->perform_write = write_to_connection;
- conn->perform_hangup = hangup_connection;
- conn->perform_teardown = NULL;
-
- switch (framing_type) {
- case SERF_CONNECTION_FRAMING_TYPE_HTTP2:
- serf__http2_protocol_init(conn);
- break;
- default:
- break;
- }
- }
-}
-
static serf_request_t *
create_request(serf_connection_t *conn,
serf_request_setup_t setup,
@@ -1858,7 +343,7 @@ serf_request_t *serf_connection_request_
0 /* ssl tunnel */);
/* Link the request to the end of the request chain. */
- link_requests(&conn->unwritten_reqs, &conn->unwritten_reqs_tail, request);
+ serf__link_requests(&conn->unwritten_reqs, &conn->unwritten_reqs_tail,
request);
conn->nr_of_unwritten_reqs++;
/* Ensure our pollset becomes writable in context run */
@@ -1961,9 +446,9 @@ apr_status_t serf_request_cancel(serf_re
tmp = tmp->next;
if (tmp)
- return cancel_request(request, &conn->unwritten_reqs, 0);
+ return serf__cancel_request(request, &conn->unwritten_reqs, 0);
else
- return cancel_request(request, &conn->written_reqs, 0);
+ return serf__cancel_request(request, &conn->written_reqs, 0);
}
@@ -2045,24 +530,3 @@ serf_bucket_t *serf_request_bucket_reque
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;
-}
-
-unsigned int serf_connection_queued_requests(serf_connection_t *conn)
-{
- return conn->nr_of_unwritten_reqs;
-}
-
-unsigned int serf_connection_pending_requests(serf_connection_t *conn)
-{
- return conn->nr_of_unwritten_reqs + conn->nr_of_written_reqs;
-}
Modified: serf/trunk/outgoing.c
URL:
http://svn.apache.org/viewvc/serf/trunk/outgoing.c?rev=1709415&r1=1709414&r2=1709415&view=diff
==============================================================================
--- serf/trunk/outgoing.c (original)
+++ serf/trunk/outgoing.c Mon Oct 19 14:00:40 2015
@@ -52,36 +52,6 @@ static apr_status_t clean_skt(void *data
return status;
}
-static apr_status_t clean_resp(void *data)
-{
- serf_request_t *request = data;
-
- /* The request's RESPOOL is being cleared. */
-
- /* If the response has allocated some buckets, then destroy them (since
- the bucket may hold resources other than memory in RESPOOL). Also
- make sure to set their fields to NULL so connection closure does
- not attempt to free them again. */
- if (request->resp_bkt) {
- serf_bucket_destroy(request->resp_bkt);
- request->resp_bkt = NULL;
- }
- if (request->req_bkt) {
- serf_bucket_destroy(request->req_bkt);
- request->req_bkt = NULL;
- }
-
- /* ### should we worry about debug stuff, like that performed in
- ### destroy_request()? should we worry about calling req->handler
- ### to notify this "cancellation" due to pool clearing? */
-
- /* This pool just got cleared/destroyed. Don't try to destroy the pool
- (again) when the request is canceled. */
- request->respool = NULL;
-
- return APR_SUCCESS;
-}
-
/* cleanup for conns */
static apr_status_t clean_conn(void *data)
{
@@ -542,91 +512,6 @@ static apr_status_t is_conn_closing(serf
return APR_SUCCESS;
}
-static void link_requests(serf_request_t **list, serf_request_t **tail,
- serf_request_t *request)
-{
- if (*list == NULL) {
- *list = request;
- *tail = request;
- }
- else {
- (*tail)->next = request;
- *tail = request;
- }
-}
-
-static apr_status_t destroy_request(serf_request_t *request)
-{
- serf_connection_t *conn = request->conn;
-
- /* The request and response buckets are no longer needed,
- nor is the request's pool. */
- if (request->resp_bkt) {
- serf_debug__closed_conn(request->resp_bkt->allocator);
- serf_bucket_destroy(request->resp_bkt);
- request->resp_bkt = NULL;
- }
- if (request->req_bkt) {
- serf_debug__closed_conn(request->req_bkt->allocator);
- serf_bucket_destroy(request->req_bkt);
- request->req_bkt = NULL;
- }
-
- if (request->respool) {
- serf_debug__bucket_alloc_check(request->allocator);
-
- /* ### unregister the pool cleanup for self? */
- apr_pool_destroy(request->respool);
- }
-
- serf_bucket_mem_free(conn->allocator, request);
-
- return APR_SUCCESS;
-}
-
-static apr_status_t cancel_request(serf_request_t *request,
- serf_request_t **list,
- int notify_request)
-{
- /* If we haven't run setup, then we won't have a handler to call. */
- if (request->handler && notify_request) {
- /* We actually don't care what the handler returns.
- * We have bigger matters at hand.
- */
- (*request->handler)(request, NULL, request->handler_baton,
- request->respool);
- }
-
- if (*list == request) {
- *list = request->next;
- }
- else {
- serf_request_t *scan = *list;
-
- while (scan->next && scan->next != request)
- scan = scan->next;
-
- if (scan->next) {
- scan->next = scan->next->next;
- }
- }
-
- return destroy_request(request);
-}
-
-/* Calculate the length of a linked list of requests. */
-static unsigned int req_list_length(serf_request_t *req)
-{
- unsigned int length = 0;
-
- while (req) {
- length++;
- req = req->next;
- }
-
- return length;
-}
-
static apr_status_t remove_connection(serf_context_t *ctx,
serf_connection_t *conn)
{
@@ -671,8 +556,8 @@ static apr_status_t reset_connection(ser
response yet. Inform the application that the request is cancelled,
so it can requeue them if needed. */
while (conn->written_reqs) {
- cancel_request(conn->written_reqs, &conn->written_reqs,
- requeue_requests);
+ serf__cancel_request(conn->written_reqs, &conn->written_reqs,
+ requeue_requests);
}
conn->written_reqs_tail = NULL;
@@ -690,13 +575,14 @@ static apr_status_t reset_connection(ser
serf_request_t *req = old_reqs;
old_reqs = old_reqs->next;
req->next = NULL;
- link_requests(&conn->unwritten_reqs, &conn->unwritten_reqs_tail,
- req);
+ serf__link_requests(&conn->unwritten_reqs,
+ &conn->unwritten_reqs_tail,
+ req);
}
else {
/* We don't want to requeue the request or this request was
partially
written. Inform the application that the request is cancelled.
*/
- cancel_request(old_reqs, &old_reqs, requeue_requests);
+ serf__cancel_request(old_reqs, &old_reqs, requeue_requests);
}
}
@@ -749,7 +635,7 @@ static apr_status_t reset_connection(ser
/* Recalculate the current list length */
conn->nr_of_written_reqs = 0;
- conn->nr_of_unwritten_reqs = req_list_length(conn->unwritten_reqs);
+ conn->nr_of_unwritten_reqs = serf__req_list_length(conn->unwritten_reqs);
/* Found the connection. Closed it. All done. */
return APR_SUCCESS;
@@ -825,29 +711,6 @@ apr_status_t serf__connection_flush(serf
return status;
}
-static apr_status_t setup_request(serf_request_t *request)
-{
- serf_connection_t *conn = request->conn;
- apr_status_t status;
-
- /* Now that we are about to serve the request, allocate a pool. */
- apr_pool_create(&request->respool, conn->pool);
- request->allocator = serf_bucket_allocator_create(request->respool,
- NULL, NULL);
- apr_pool_cleanup_register(request->respool, request,
- clean_resp, apr_pool_cleanup_null);
-
- /* Fill in the rest of the values for the request. */
- status = request->setup(request, request->setup_baton,
- &request->req_bkt,
- &request->acceptor,
- &request->acceptor_baton,
- &request->handler,
- &request->handler_baton,
- request->respool);
- return status;
-}
-
/* write data out to the connection */
static apr_status_t write_to_connection(serf_connection_t *conn)
{
@@ -925,7 +788,7 @@ static apr_status_t write_to_connection(
if (request) {
if (request->req_bkt == NULL) {
- read_status = setup_request(request);
+ read_status = serf__setup_request(request);
if (read_status) {
/* Something bad happened. Propagate any errors. */
return read_status;
@@ -1004,8 +867,8 @@ static apr_status_t write_to_connection(
request->req_bkt = NULL;
/* Move the request to the written queue */
- link_requests(&conn->written_reqs, &conn->written_reqs_tail,
- request);
+ serf__link_requests(&conn->written_reqs, &conn->written_reqs_tail,
+ request);
conn->nr_of_written_reqs++;
conn->unwritten_reqs = conn->unwritten_reqs->next;
conn->nr_of_unwritten_reqs--;
@@ -1017,7 +880,7 @@ static apr_status_t write_to_connection(
if (conn->async_responses) {
conn->unwritten_reqs = request->next;
conn->nr_of_unwritten_reqs--;
- destroy_request(request);
+ serf__destroy_request(request);
}
conn->completed_requests++;
@@ -1036,89 +899,7 @@ static apr_status_t write_to_connection(
/* NOTREACHED */
}
-/* A response message was received from the server, so call
- the handler as specified on the original request. */
-static apr_status_t handle_response(serf_request_t *request,
- apr_pool_t *pool)
-{
- int consumed_response = 0;
-
- /* Only enable the new authentication framework if the program has
- * registered an authentication credential callback.
- *
- * This permits older Serf apps to still handle authentication
- * themselves by not registering credential callbacks.
- */
- if (request->conn->ctx->cred_cb) {
- apr_status_t status;
-
- status = serf__handle_auth_response(&consumed_response,
- request,
- request->resp_bkt,
- pool);
-
- if (SERF_BUCKET_READ_ERROR(status)) {
-
- /* There was an error while checking the authentication headers of
- the response. We need to inform the application - which
- hasn't seen this response yet - of the error.
-
- These are the possible causes of the error:
-
- 1. A communication error while reading the response status line,
- headers or while discarding the response body: pass the
- response unchanged to the application, it will see the same
- error as serf did.
-
- 2. A 401/407 response status for a supported authn scheme that
- resulted in authn failure:
- Pass the response as received to the application, the
response
- body can contain an error description. Terminate the response
- body with the AUTHN error instead of APR_EOF.
-
- 3. A 401/407 response status for a supported authn scheme that
- resulted in an unknown error returned by the application in
- the credentials callback (Basic/Digest):
- Handle the same as 2.
-
- 4. A 2xx response status for a supported authn scheme that
- resulted in authn failure:
- Pass the response headers to the application. The response
- body is untrusted, so we should drop it and return the AUTHN
- error instead of APR_EOF.
-
- serf__handle_auth_response will already discard the response
- body, so we can handle this case the same as 2.
-
- In summary, all these cases can be handled in the same way: call
- the application's response handler with the response bucket, but
- make sure that the application sees error code STATUS instead of
- APR_EOF after reading the response body.
- */
-
- serf__bucket_response_set_error_on_eof(request->resp_bkt, status);
-
- /* Ignore the application's status code here, use the error status
- from serf__handle_auth_response. */
- (void)(*request->handler)(request,
- request->resp_bkt,
- request->handler_baton,
- pool);
- }
-
- if (status)
- return status;
- }
-
- if (!consumed_response) {
- return (*request->handler)(request,
- request->resp_bkt,
- request->handler_baton,
- pool);
- }
- return APR_SUCCESS;
-}
/* An async response message was received from the server. */
static apr_status_t handle_async_response(serf_connection_t *conn,
@@ -1144,61 +925,6 @@ static apr_status_t handle_async_respons
return status;
}
-
-apr_status_t
-serf__provide_credentials(serf_context_t *ctx,
- char **username,
- char **password,
- serf_request_t *request,
- int code, const char *authn_type,
- const char *realm,
- apr_pool_t *pool)
-{
- serf_connection_t *conn = request->conn;
- serf_request_t *authn_req = request;
- apr_status_t status;
-
- if (request->ssltunnel == 1 &&
- conn->state == SERF_CONN_SETUP_SSLTUNNEL) {
- /* This is a CONNECT request to set up an SSL tunnel over a proxy.
- This request is created by serf, so if the proxy requires
- authentication, we can't ask the application for credentials with
- this request.
-
- Solution: setup the first request created by the application on
- this connection, and use that request and its handler_baton to
- call back to the application. */
-
- /* request->next will be NULL if this was the last request written */
- authn_req = request->next;
- if (!authn_req)
- authn_req = conn->unwritten_reqs;
-
- /* assert: app_request != NULL */
- if (!authn_req)
- return APR_EGENERAL;
-
- if (!authn_req->req_bkt) {
- apr_status_t status;
-
- status = setup_request(authn_req);
- /* If we can't setup a request, don't bother setting up the
- ssl tunnel. */
- if (status)
- return status;
- }
- }
-
- /* Ask the application. */
- status = (*ctx->cred_cb)(username, password,
- authn_req, authn_req->handler_baton,
- code, authn_type, realm, pool);
- if (status)
- return status;
-
- return APR_SUCCESS;
-}
-
/* read data from the connection */
static apr_status_t read_from_connection(serf_connection_t *conn)
{
@@ -1325,7 +1051,7 @@ static apr_status_t read_from_connection
serf_bucket_set_config(request->resp_bkt, conn->config);
}
- status = handle_response(request, tmppool);
+ status = serf__handle_response(request, tmppool);
/* If we received APR_SUCCESS, run this loop again. */
if (!status) {
@@ -1409,7 +1135,7 @@ static apr_status_t read_from_connection
conn->nr_of_unwritten_reqs--;
}
- destroy_request(request);
+ serf__destroy_request(request);
request = conn->written_reqs;
if (!request) {
@@ -1696,10 +1422,10 @@ apr_status_t serf_connection_close(
/* The application asked to close the connection, no need to notify
it for each cancelled request. */
while (conn->written_reqs) {
- cancel_request(conn->written_reqs, &conn->written_reqs, 0);
+ serf__cancel_request(conn->written_reqs, &conn->written_reqs,
0);
}
while (conn->unwritten_reqs) {
- cancel_request(conn->unwritten_reqs, &conn->unwritten_reqs, 0);
+ serf__cancel_request(conn->unwritten_reqs,
&conn->unwritten_reqs, 0);
}
if (conn->skt != NULL) {
remove_connection(ctx, conn);
@@ -1846,205 +1572,6 @@ create_request(serf_connection_t *conn,
return request;
}
-serf_request_t *serf_connection_request_create(
- serf_connection_t *conn,
- serf_request_setup_t setup,
- void *setup_baton)
-{
- serf_request_t *request;
-
- request = create_request(conn, setup, setup_baton,
- 0, /* priority */
- 0 /* ssl tunnel */);
-
- /* Link the request to the end of the request chain. */
- link_requests(&conn->unwritten_reqs, &conn->unwritten_reqs_tail, request);
- conn->nr_of_unwritten_reqs++;
-
- /* Ensure our pollset becomes writable in context run */
- conn->ctx->dirty_pollset = 1;
- conn->dirty_conn = 1;
-
- return request;
-}
-
-static serf_request_t *
-priority_request_create(serf_connection_t *conn,
- int ssltunnelreq,
- serf_request_setup_t setup,
- void *setup_baton)
-{
- serf_request_t *request;
- serf_request_t *iter, *prev;
-
- request = create_request(conn, setup, setup_baton,
- 1, /* priority */
- ssltunnelreq);
-
- /* Link the new request after the last written request. */
- iter = conn->unwritten_reqs;
- prev = NULL;
-
- /* TODO: what if a request is partially written? */
- /* Find a request that has data which needs to be delivered. */
- while (iter != NULL && iter->req_bkt == NULL && iter->writing_started) {
- prev = iter;
- iter = iter->next;
- }
-
- /* A CONNECT request to setup an ssltunnel has absolute priority over all
- other requests on the connection, so:
- a. add it first to the queue
- b. ensure that other priority requests are added after the CONNECT
- request */
- if (!request->ssltunnel) {
- /* Advance to next non priority request */
- while (iter != NULL && iter->priority) {
- prev = iter;
- iter = iter->next;
- }
- }
-
- if (prev) {
- request->next = iter;
- prev->next = request;
- } else {
- request->next = iter;
- conn->unwritten_reqs = request;
- }
- conn->nr_of_unwritten_reqs++;
-
- /* Ensure our pollset becomes writable in context run */
- conn->ctx->dirty_pollset = 1;
- conn->dirty_conn = 1;
-
- return request;
-}
-
-serf_request_t *serf_connection_priority_request_create(
- serf_connection_t *conn,
- serf_request_setup_t setup,
- void *setup_baton)
-{
- return priority_request_create(conn,
- 0, /* not a ssltunnel CONNECT request */
- setup, setup_baton);
-}
-
-serf_request_t *serf__ssltunnel_request_create(serf_connection_t *conn,
- serf_request_setup_t setup,
- void *setup_baton)
-{
- return priority_request_create(conn,
- 1, /* This is a ssltunnel CONNECT request */
- setup, setup_baton);
-}
-
-
-serf_request_t *serf__request_requeue(const serf_request_t *request)
-{
- /* ### in the future, maybe we could reset REQUEST and try again? */
- return priority_request_create(request->conn,
- request->ssltunnel,
- request->setup,
- request->setup_baton);
-}
-
-
-apr_status_t serf_request_cancel(serf_request_t *request)
-{
- serf_connection_t *conn = request->conn;
- serf_request_t *tmp = conn->unwritten_reqs;
-
- /* Find out which queue holds the request */
- while (tmp != NULL && tmp != request)
- tmp = tmp->next;
-
- if (tmp)
- return cancel_request(request, &conn->unwritten_reqs, 0);
- else
- return cancel_request(request, &conn->written_reqs, 0);
-
-}
-
-apr_status_t serf_request_is_written(serf_request_t *request)
-{
- if (request->writing_started && !request->req_bkt)
- return APR_SUCCESS;
-
- return APR_EBUSY;
-}
-
-apr_pool_t *serf_request_get_pool(const serf_request_t *request)
-{
- return request->respool;
-}
-
-
-serf_bucket_alloc_t *serf_request_get_alloc(
- const serf_request_t *request)
-{
- return request->allocator;
-}
-
-
-serf_connection_t *serf_request_get_conn(
- const serf_request_t *request)
-{
- return request->conn;
-}
-
-
-void serf_request_set_handler(
- serf_request_t *request,
- const serf_response_handler_t handler,
- const void **handler_baton)
-{
- request->handler = handler;
- request->handler_baton = handler_baton;
-}
-
-
-serf_bucket_t *serf_request_bucket_request_create(
- serf_request_t *request,
- const char *method,
- const char *uri,
- serf_bucket_t *body,
- serf_bucket_alloc_t *allocator)
-{
- serf_bucket_t *req_bkt;
- serf_bucket_t *hdrs_bkt;
- serf_connection_t *conn = request->conn;
- serf_context_t *ctx = conn->ctx;
- int tunneled;
-
- tunneled = ctx->proxy_address
- && (strcmp(conn->host_info.scheme, "https") == 0);
-
- req_bkt = serf_bucket_request_create(method, uri, body, allocator);
- hdrs_bkt = serf_bucket_request_get_headers(req_bkt);
-
- /* Use absolute uri's in requests to a proxy. USe relative uri's in
- requests directly to a server or sent through an SSL tunnel. */
- if (ctx->proxy_address && conn->host_url && !tunneled)
- {
- serf_bucket_request_set_root(req_bkt, conn->host_url);
- }
-
- if (conn->host_info.hostinfo)
- {
- serf_bucket_headers_setn(hdrs_bkt, "Host", conn->host_info.hostinfo);
- }
-
- /* Setup server authentication headers. */
- serf__auth_setup_request(HOST, request, method, uri, hdrs_bkt);
-
- /* Setup proxy authentication headers, unless we're tunneling. */
- if (!tunneled)
- serf__auth_setup_request(PROXY, request, method, uri, hdrs_bkt);
-
- return req_bkt;
-}
apr_interval_time_t serf_connection_get_latency(serf_connection_t *conn)
{
Modified: serf/trunk/serf_private.h
URL:
http://svn.apache.org/viewvc/serf/trunk/serf_private.h?rev=1709415&r1=1709414&r2=1709415&view=diff
==============================================================================
--- serf/trunk/serf_private.h (original)
+++ serf/trunk/serf_private.h Mon Oct 19 14:00:40 2015
@@ -536,6 +536,17 @@ apr_status_t serf__hpack_huffman_encode(
unsigned char *encoded,
apr_size_t *encoded_len);
+/* From connection_request.c */
+void serf__link_requests(serf_request_t **list, serf_request_t **tail,
+ serf_request_t *request);
+apr_status_t serf__destroy_request(serf_request_t *request);
+apr_status_t serf__cancel_request(serf_request_t *request,
+ serf_request_t **list,
+ int notify_request);
+unsigned int serf__req_list_length(serf_request_t *req);
+apr_status_t serf__setup_request(serf_request_t *request);
+void serf__link_requests(serf_request_t **list, serf_request_t **tail,
+ serf_request_t *request);
/** Logging functions. **/