Blame outgoing.c

Packit 3adb1e
/* ====================================================================
Packit 3adb1e
 *    Licensed to the Apache Software Foundation (ASF) under one
Packit 3adb1e
 *    or more contributor license agreements.  See the NOTICE file
Packit 3adb1e
 *    distributed with this work for additional information
Packit 3adb1e
 *    regarding copyright ownership.  The ASF licenses this file
Packit 3adb1e
 *    to you under the Apache License, Version 2.0 (the
Packit 3adb1e
 *    "License"); you may not use this file except in compliance
Packit 3adb1e
 *    with the License.  You may obtain a copy of the License at
Packit 3adb1e
 *
Packit 3adb1e
 *      http://www.apache.org/licenses/LICENSE-2.0
Packit 3adb1e
 *
Packit 3adb1e
 *    Unless required by applicable law or agreed to in writing,
Packit 3adb1e
 *    software distributed under the License is distributed on an
Packit 3adb1e
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
Packit 3adb1e
 *    KIND, either express or implied.  See the License for the
Packit 3adb1e
 *    specific language governing permissions and limitations
Packit 3adb1e
 *    under the License.
Packit 3adb1e
 * ====================================================================
Packit 3adb1e
 */
Packit 3adb1e
Packit 3adb1e
#include <apr_pools.h>
Packit 3adb1e
#include <apr_poll.h>
Packit 3adb1e
#include <apr_version.h>
Packit 3adb1e
#include <apr_portable.h>
Packit 3adb1e
Packit 3adb1e
#include "serf.h"
Packit 3adb1e
#include "serf_bucket_util.h"
Packit 3adb1e
Packit 3adb1e
#include "serf_private.h"
Packit 3adb1e
Packit 3adb1e
/* cleanup for sockets */
Packit 3adb1e
static apr_status_t clean_skt(void *data)
Packit 3adb1e
{
Packit 3adb1e
    serf_connection_t *conn = data;
Packit 3adb1e
    apr_status_t status = APR_SUCCESS;
Packit 3adb1e
Packit 3adb1e
    if (conn->skt) {
Packit 3adb1e
        serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt, "cleanup - ");
Packit 3adb1e
        status = apr_socket_close(conn->skt);
Packit 3adb1e
        conn->skt = NULL;
Packit 3adb1e
        serf__log_nopref(SOCK_VERBOSE, "closed socket, status %d\n", status);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t clean_resp(void *data)
Packit 3adb1e
{
Packit 3adb1e
    serf_request_t *request = data;
Packit 3adb1e
Packit 3adb1e
    /* The request's RESPOOL is being cleared.  */
Packit 3adb1e
Packit 3adb1e
    /* If the response has allocated some buckets, then destroy them (since
Packit 3adb1e
       the bucket may hold resources other than memory in RESPOOL). Also
Packit 3adb1e
       make sure to set their fields to NULL so connection closure does
Packit 3adb1e
       not attempt to free them again.  */
Packit 3adb1e
    if (request->resp_bkt) {
Packit 3adb1e
        serf_bucket_destroy(request->resp_bkt);
Packit 3adb1e
        request->resp_bkt = NULL;
Packit 3adb1e
    }
Packit 3adb1e
    if (request->req_bkt) {
Packit 3adb1e
        serf_bucket_destroy(request->req_bkt);
Packit 3adb1e
        request->req_bkt = NULL;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* ### should we worry about debug stuff, like that performed in
Packit 3adb1e
       ### destroy_request()? should we worry about calling req->handler
Packit 3adb1e
       ### to notify this "cancellation" due to pool clearing?  */
Packit 3adb1e
Packit 3adb1e
    /* This pool just got cleared/destroyed. Don't try to destroy the pool
Packit 3adb1e
       (again) when the request is canceled.  */
Packit 3adb1e
    request->respool = NULL;
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* cleanup for conns */
Packit 3adb1e
static apr_status_t clean_conn(void *data)
Packit 3adb1e
{
Packit 3adb1e
    serf_connection_t *conn = data;
Packit 3adb1e
Packit 3adb1e
    serf__log(CONN_VERBOSE, __FILE__, "cleaning up connection 0x%x\n",
Packit 3adb1e
              conn);
Packit 3adb1e
    serf_connection_close(conn);
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* Check if there is data waiting to be sent over the socket. This can happen
Packit 3adb1e
   in two situations:
Packit 3adb1e
   - The connection queue has atleast one request with unwritten data.
Packit 3adb1e
   - All requests are written and the ssl layer wrote some data while reading
Packit 3adb1e
     the response. This can happen when the server triggers a renegotiation,
Packit 3adb1e
     e.g. after the first and only request on that connection was received.
Packit 3adb1e
   Returns 1 if data is pending on CONN, NULL if not.
Packit 3adb1e
   If NEXT_REQ is not NULL, it will be filled in with the next available request
Packit 3adb1e
   with unwritten data. */
Packit 3adb1e
static int
Packit 3adb1e
request_or_data_pending(serf_request_t **next_req, serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    serf_request_t *request = conn->requests;
Packit 3adb1e
Packit 3adb1e
    while (request != NULL && request->req_bkt == NULL &&
Packit 3adb1e
           request->writing_started)
Packit 3adb1e
        request = request->next;
Packit 3adb1e
Packit 3adb1e
    if (next_req)
Packit 3adb1e
        *next_req = request;
Packit 3adb1e
Packit 3adb1e
    if (request != NULL) {
Packit 3adb1e
        return 1;
Packit 3adb1e
    } else if (conn->ostream_head) {
Packit 3adb1e
        const char *dummy;
Packit 3adb1e
        apr_size_t len;
Packit 3adb1e
        apr_status_t status;
Packit 3adb1e
Packit 3adb1e
        status = serf_bucket_peek(conn->ostream_head, &dummy,
Packit 3adb1e
                                  &len;;
Packit 3adb1e
        if (!SERF_BUCKET_READ_ERROR(status) && len) {
Packit 3adb1e
            serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt,
Packit 3adb1e
                          "All requests written but still data pending.\n");
Packit 3adb1e
            return 1;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return 0;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* Update the pollset for this connection. We tweak the pollset based on
Packit 3adb1e
 * whether we want to read and/or write, given conditions within the
Packit 3adb1e
 * connection. If the connection is not (yet) in the pollset, then it
Packit 3adb1e
 * will be added.
Packit 3adb1e
 */
Packit 3adb1e
apr_status_t serf__conn_update_pollset(serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    serf_context_t *ctx = conn->ctx;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
    apr_pollfd_t desc = { 0 };
Packit 3adb1e
Packit 3adb1e
    if (!conn->skt) {
Packit 3adb1e
        return APR_SUCCESS;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Remove the socket from the poll set. */
Packit 3adb1e
    desc.desc_type = APR_POLL_SOCKET;
Packit 3adb1e
    desc.desc.s = conn->skt;
Packit 3adb1e
    desc.reqevents = conn->reqevents;
Packit 3adb1e
Packit 3adb1e
    status = ctx->pollset_rm(ctx->pollset_baton,
Packit 3adb1e
                             &desc, &conn->baton);
Packit 3adb1e
    if (status && !APR_STATUS_IS_NOTFOUND(status))
Packit 3adb1e
        return status;
Packit 3adb1e
Packit 3adb1e
    /* Now put it back in with the correct read/write values. */
Packit 3adb1e
    desc.reqevents = APR_POLLHUP | APR_POLLERR;
Packit 3adb1e
    if (conn->requests &&
Packit 3adb1e
        conn->state != SERF_CONN_INIT) {
Packit 3adb1e
        /* If there are any outstanding events, then we want to read. */
Packit 3adb1e
        /* ### not true. we only want to read IF we have sent some data */
Packit 3adb1e
        desc.reqevents |= APR_POLLIN;
Packit 3adb1e
Packit 3adb1e
        /* Don't write if OpenSSL told us that it needs to read data first. */
Packit 3adb1e
        if (conn->stop_writing != 1) {
Packit 3adb1e
Packit 3adb1e
            /* If the connection is not closing down and
Packit 3adb1e
             *   has unwritten data or
Packit 3adb1e
             *   there are any requests that still have buckets to write out,
Packit 3adb1e
             *     then we want to write.
Packit 3adb1e
             */
Packit 3adb1e
            if (conn->vec_len &&
Packit 3adb1e
                conn->state != SERF_CONN_CLOSING)
Packit 3adb1e
                desc.reqevents |= APR_POLLOUT;
Packit 3adb1e
            else {
Packit 3adb1e
Packit 3adb1e
                if ((conn->probable_keepalive_limit &&
Packit 3adb1e
                     conn->completed_requests > conn->probable_keepalive_limit) ||
Packit 3adb1e
                    (conn->max_outstanding_requests &&
Packit 3adb1e
                     conn->completed_requests - conn->completed_responses >=
Packit 3adb1e
                     conn->max_outstanding_requests)) {
Packit 3adb1e
                        /* we wouldn't try to write any way right now. */
Packit 3adb1e
                }
Packit 3adb1e
                else if (request_or_data_pending(NULL, conn)) {
Packit 3adb1e
                    desc.reqevents |= APR_POLLOUT;
Packit 3adb1e
                }
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* If we can have async responses, always look for something to read. */
Packit 3adb1e
    if (conn->async_responses) {
Packit 3adb1e
        desc.reqevents |= APR_POLLIN;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* save our reqevents, so we can pass it in to remove later. */
Packit 3adb1e
    conn->reqevents = desc.reqevents;
Packit 3adb1e
Packit 3adb1e
    /* Note: even if we don't want to read/write this socket, we still
Packit 3adb1e
     * want to poll it for hangups and errors.
Packit 3adb1e
     */
Packit 3adb1e
    return ctx->pollset_add(ctx->pollset_baton,
Packit 3adb1e
                            &desc, &conn->baton);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
#ifdef SERF_DEBUG_BUCKET_USE
Packit 3adb1e
Packit 3adb1e
/* Make sure all response buckets were drained. */
Packit 3adb1e
static void check_buckets_drained(serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    serf_request_t *request = conn->requests;
Packit 3adb1e
Packit 3adb1e
    for ( ; request ; request = request->next ) {
Packit 3adb1e
        if (request->resp_bkt != NULL) {
Packit 3adb1e
            /* ### crap. can't do this. this allocator may have un-drained
Packit 3adb1e
             * ### REQUEST buckets.
Packit 3adb1e
             */
Packit 3adb1e
            /* serf_debug__entered_loop(request->resp_bkt->allocator); */
Packit 3adb1e
            /* ### for now, pretend we closed the conn (resets the tracking) */
Packit 3adb1e
            serf_debug__closed_conn(request->resp_bkt->allocator);
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
#endif
Packit 3adb1e
Packit 3adb1e
static void destroy_ostream(serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    if (conn->ostream_head != NULL) {
Packit 3adb1e
        serf_bucket_destroy(conn->ostream_head);
Packit 3adb1e
        conn->ostream_head = NULL;
Packit 3adb1e
        conn->ostream_tail = NULL;
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t detect_eof(void *baton, serf_bucket_t *aggregate_bucket)
Packit 3adb1e
{
Packit 3adb1e
    serf_connection_t *conn = baton;
Packit 3adb1e
    conn->hit_eof = 1;
Packit 3adb1e
    return APR_EAGAIN;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t do_conn_setup(serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
    serf_bucket_t *ostream;
Packit 3adb1e
Packit 3adb1e
    if (conn->ostream_head == NULL) {
Packit 3adb1e
        conn->ostream_head = serf_bucket_aggregate_create(conn->allocator);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (conn->ostream_tail == NULL) {
Packit 3adb1e
        conn->ostream_tail = serf__bucket_stream_create(conn->allocator,
Packit 3adb1e
                                                        detect_eof,
Packit 3adb1e
                                                        conn);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    ostream = conn->ostream_tail;
Packit 3adb1e
Packit 3adb1e
    status = (*conn->setup)(conn->skt,
Packit 3adb1e
                            &conn->stream,
Packit 3adb1e
                            &ostream,
Packit 3adb1e
                            conn->setup_baton,
Packit 3adb1e
                            conn->pool);
Packit 3adb1e
    if (status) {
Packit 3adb1e
        /* extra destroy here since it wasn't added to the head bucket yet. */
Packit 3adb1e
        serf_bucket_destroy(conn->ostream_tail);
Packit 3adb1e
        destroy_ostream(conn);
Packit 3adb1e
        return status;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    serf_bucket_aggregate_append(conn->ostream_head,
Packit 3adb1e
                                 ostream);
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* Set up the input and output stream buckets.
Packit 3adb1e
 When a tunnel over an http proxy is needed, create a socket bucket and
Packit 3adb1e
 empty aggregate bucket for sending and receiving unencrypted requests
Packit 3adb1e
 over the socket.
Packit 3adb1e
Packit 3adb1e
 After the tunnel is there, or no tunnel was needed, ask the application
Packit 3adb1e
 to create the input and output buckets, which should take care of the
Packit 3adb1e
 [en/de]cryption.
Packit 3adb1e
 */
Packit 3adb1e
Packit 3adb1e
static apr_status_t prepare_conn_streams(serf_connection_t *conn,
Packit 3adb1e
                                         serf_bucket_t **istream,
Packit 3adb1e
                                         serf_bucket_t **ostreamt,
Packit 3adb1e
                                         serf_bucket_t **ostreamh)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    if (conn->stream == NULL) {
Packit 3adb1e
        conn->latency = apr_time_now() - conn->connect_time;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Do we need a SSL tunnel first? */
Packit 3adb1e
    if (conn->state == SERF_CONN_CONNECTED) {
Packit 3adb1e
        /* If the connection does not have an associated bucket, then
Packit 3adb1e
         * call the setup callback to get one.
Packit 3adb1e
         */
Packit 3adb1e
        if (conn->stream == NULL) {
Packit 3adb1e
            status = do_conn_setup(conn);
Packit 3adb1e
            if (status) {
Packit 3adb1e
                return status;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
        *ostreamt = conn->ostream_tail;
Packit 3adb1e
        *ostreamh = conn->ostream_head;
Packit 3adb1e
        *istream = conn->stream;
Packit 3adb1e
    } else {
Packit 3adb1e
        /* SSL tunnel needed and not set up yet, get a direct unencrypted
Packit 3adb1e
         stream for this socket */
Packit 3adb1e
        if (conn->stream == NULL) {
Packit 3adb1e
            *istream = serf_bucket_socket_create(conn->skt,
Packit 3adb1e
                                                 conn->allocator);
Packit 3adb1e
        }
Packit 3adb1e
        /* Don't create the ostream bucket chain including the ssl_encrypt
Packit 3adb1e
         bucket yet. This ensure the CONNECT request is sent unencrypted
Packit 3adb1e
         to the proxy. */
Packit 3adb1e
        *ostreamt = *ostreamh = conn->ssltunnel_ostream;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* Create and connect sockets for any connections which don't have them
Packit 3adb1e
 * yet. This is the core of our lazy-connect behavior.
Packit 3adb1e
 */
Packit 3adb1e
apr_status_t serf__open_connections(serf_context_t *ctx)
Packit 3adb1e
{
Packit 3adb1e
    int i;
Packit 3adb1e
Packit 3adb1e
    for (i = ctx->conns->nelts; i--; ) {
Packit 3adb1e
        serf_connection_t *conn = GET_CONN(ctx, i);
Packit 3adb1e
        serf__authn_info_t *authn_info;
Packit 3adb1e
        apr_status_t status;
Packit 3adb1e
        apr_socket_t *skt;
Packit 3adb1e
Packit 3adb1e
        conn->seen_in_pollset = 0;
Packit 3adb1e
Packit 3adb1e
        if (conn->skt != NULL) {
Packit 3adb1e
#ifdef SERF_DEBUG_BUCKET_USE
Packit 3adb1e
            check_buckets_drained(conn);
Packit 3adb1e
#endif
Packit 3adb1e
            continue;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* Delay opening until we have something to deliver! */
Packit 3adb1e
        if (conn->requests == NULL) {
Packit 3adb1e
            continue;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        apr_pool_clear(conn->skt_pool);
Packit 3adb1e
        apr_pool_cleanup_register(conn->skt_pool, conn, clean_skt, clean_skt);
Packit 3adb1e
Packit 3adb1e
        status = apr_socket_create(&skt, conn->address->family,
Packit 3adb1e
                                   SOCK_STREAM,
Packit 3adb1e
#if APR_MAJOR_VERSION > 0
Packit 3adb1e
                                   APR_PROTO_TCP,
Packit 3adb1e
#endif
Packit 3adb1e
                                   conn->skt_pool);
Packit 3adb1e
        serf__log(SOCK_VERBOSE, __FILE__,
Packit 3adb1e
                  "created socket for conn 0x%x, status %d\n", conn, status);
Packit 3adb1e
        if (status != APR_SUCCESS)
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        /* Set the socket to be non-blocking */
Packit 3adb1e
        if ((status = apr_socket_timeout_set(skt, 0)) != APR_SUCCESS)
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        /* Disable Nagle's algorithm */
Packit 3adb1e
        if ((status = apr_socket_opt_set(skt,
Packit 3adb1e
                                         APR_TCP_NODELAY, 1)) != APR_SUCCESS)
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        /* Configured. Store it into the connection now. */
Packit 3adb1e
        conn->skt = skt;
Packit 3adb1e
Packit 3adb1e
        /* Remember time when we started connecting to server to calculate
Packit 3adb1e
           network latency. */
Packit 3adb1e
        conn->connect_time = apr_time_now();
Packit 3adb1e
Packit 3adb1e
        /* Now that the socket is set up, let's connect it. This should
Packit 3adb1e
         * return immediately.
Packit 3adb1e
         */
Packit 3adb1e
        status = apr_socket_connect(skt, conn->address);
Packit 3adb1e
        serf__log_skt(SOCK_VERBOSE, __FILE__, skt,
Packit 3adb1e
                      "connected socket for conn 0x%x, status %d\n",
Packit 3adb1e
                      conn, status);
Packit 3adb1e
        if (status != APR_SUCCESS) {
Packit 3adb1e
            if (!APR_STATUS_IS_EINPROGRESS(status))
Packit 3adb1e
                return status;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* Flag our pollset as dirty now that we have a new socket. */
Packit 3adb1e
        conn->dirty_conn = 1;
Packit 3adb1e
        ctx->dirty_pollset = 1;
Packit 3adb1e
Packit 3adb1e
        /* If the authentication was already started on another connection,
Packit 3adb1e
           prepare this connection (it might be possible to skip some
Packit 3adb1e
           part of the handshaking). */
Packit 3adb1e
        if (ctx->proxy_address) {
Packit 3adb1e
            authn_info = &ctx->proxy_authn_info;
Packit 3adb1e
            if (authn_info->scheme) {
Packit 3adb1e
                authn_info->scheme->init_conn_func(authn_info->scheme, 407,
Packit 3adb1e
                                                   conn, conn->pool);
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        authn_info = serf__get_authn_info_for_server(conn);
Packit 3adb1e
        if (authn_info->scheme) {
Packit 3adb1e
            authn_info->scheme->init_conn_func(authn_info->scheme, 401,
Packit 3adb1e
                                               conn, conn->pool);
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* Does this connection require a SSL tunnel over the proxy? */
Packit 3adb1e
        if (ctx->proxy_address && strcmp(conn->host_info.scheme, "https") == 0)
Packit 3adb1e
            serf__ssltunnel_connect(conn);
Packit 3adb1e
        else {
Packit 3adb1e
            serf_bucket_t *dummy1, *dummy2;
Packit 3adb1e
Packit 3adb1e
            conn->state = SERF_CONN_CONNECTED;
Packit 3adb1e
Packit 3adb1e
            status = prepare_conn_streams(conn, &conn->stream,
Packit 3adb1e
                                          &dummy1, &dummy2);
Packit 3adb1e
            if (status) {
Packit 3adb1e
                return status;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t no_more_writes(serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    /* Note that we should hold new requests until we open our new socket. */
Packit 3adb1e
    conn->state = SERF_CONN_CLOSING;
Packit 3adb1e
    serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt,
Packit 3adb1e
                  "stop writing on conn 0x%x\n", conn);
Packit 3adb1e
Packit 3adb1e
    /* Clear our iovec. */
Packit 3adb1e
    conn->vec_len = 0;
Packit 3adb1e
Packit 3adb1e
    /* Update the pollset to know we don't want to write on this socket any
Packit 3adb1e
     * more.
Packit 3adb1e
     */
Packit 3adb1e
    conn->dirty_conn = 1;
Packit 3adb1e
    conn->ctx->dirty_pollset = 1;
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* Read the 'Connection' header from the response. Return SERF_ERROR_CLOSING if
Packit 3adb1e
 * the header contains value 'close' indicating the server is closing the
Packit 3adb1e
 * connection right after this response.
Packit 3adb1e
 * Otherwise returns APR_SUCCESS.
Packit 3adb1e
 */
Packit 3adb1e
static apr_status_t is_conn_closing(serf_bucket_t *response)
Packit 3adb1e
{
Packit 3adb1e
    serf_bucket_t *hdrs;
Packit 3adb1e
    const char *val;
Packit 3adb1e
Packit 3adb1e
    hdrs = serf_bucket_response_get_headers(response);
Packit 3adb1e
    val = serf_bucket_headers_get(hdrs, "Connection");
Packit 3adb1e
    if (val && strcasecmp("close", val) == 0)
Packit 3adb1e
        {
Packit 3adb1e
            return SERF_ERROR_CLOSING;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static void link_requests(serf_request_t **list, serf_request_t **tail,
Packit 3adb1e
                          serf_request_t *request)
Packit 3adb1e
{
Packit 3adb1e
    if (*list == NULL) {
Packit 3adb1e
        *list = request;
Packit 3adb1e
        *tail = request;
Packit 3adb1e
    }
Packit 3adb1e
    else {
Packit 3adb1e
        (*tail)->next = request;
Packit 3adb1e
        *tail = request;
Packit 3adb1e
    }
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t destroy_request(serf_request_t *request)
Packit 3adb1e
{
Packit 3adb1e
    serf_connection_t *conn = request->conn;
Packit 3adb1e
Packit 3adb1e
    /* The request and response buckets are no longer needed,
Packit 3adb1e
       nor is the request's pool.  */
Packit 3adb1e
    if (request->resp_bkt) {
Packit 3adb1e
        serf_debug__closed_conn(request->resp_bkt->allocator);
Packit 3adb1e
        serf_bucket_destroy(request->resp_bkt);
Packit 3adb1e
        request->resp_bkt = NULL;
Packit 3adb1e
    }
Packit 3adb1e
    if (request->req_bkt) {
Packit 3adb1e
        serf_debug__closed_conn(request->req_bkt->allocator);
Packit 3adb1e
        serf_bucket_destroy(request->req_bkt);
Packit 3adb1e
        request->req_bkt = NULL;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    serf_debug__bucket_alloc_check(request->allocator);
Packit 3adb1e
    if (request->respool) {
Packit 3adb1e
        /* ### unregister the pool cleanup for self?  */
Packit 3adb1e
        apr_pool_destroy(request->respool);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    serf_bucket_mem_free(conn->allocator, request);
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t cancel_request(serf_request_t *request,
Packit 3adb1e
                                   serf_request_t **list,
Packit 3adb1e
                                   int notify_request)
Packit 3adb1e
{
Packit 3adb1e
    /* If we haven't run setup, then we won't have a handler to call. */
Packit 3adb1e
    if (request->handler && notify_request) {
Packit 3adb1e
        /* We actually don't care what the handler returns.
Packit 3adb1e
         * We have bigger matters at hand.
Packit 3adb1e
         */
Packit 3adb1e
        (*request->handler)(request, NULL, request->handler_baton,
Packit 3adb1e
                            request->respool);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (*list == request) {
Packit 3adb1e
        *list = request->next;
Packit 3adb1e
    }
Packit 3adb1e
    else {
Packit 3adb1e
        serf_request_t *scan = *list;
Packit 3adb1e
Packit 3adb1e
        while (scan->next && scan->next != request)
Packit 3adb1e
            scan = scan->next;
Packit 3adb1e
Packit 3adb1e
        if (scan->next) {
Packit 3adb1e
            scan->next = scan->next->next;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return destroy_request(request);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t remove_connection(serf_context_t *ctx,
Packit 3adb1e
                                      serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    apr_pollfd_t desc = { 0 };
Packit 3adb1e
Packit 3adb1e
    desc.desc_type = APR_POLL_SOCKET;
Packit 3adb1e
    desc.desc.s = conn->skt;
Packit 3adb1e
    desc.reqevents = conn->reqevents;
Packit 3adb1e
Packit 3adb1e
    return ctx->pollset_rm(ctx->pollset_baton,
Packit 3adb1e
                           &desc, &conn->baton);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* A socket was closed, inform the application. */
Packit 3adb1e
static void handle_conn_closed(serf_connection_t *conn, apr_status_t status)
Packit 3adb1e
{
Packit 3adb1e
    (*conn->closed)(conn, conn->closed_baton, status,
Packit 3adb1e
                    conn->pool);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t reset_connection(serf_connection_t *conn,
Packit 3adb1e
                                     int requeue_requests)
Packit 3adb1e
{
Packit 3adb1e
    serf_context_t *ctx = conn->ctx;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
    serf_request_t *old_reqs;
Packit 3adb1e
Packit 3adb1e
    conn->probable_keepalive_limit = conn->completed_responses;
Packit 3adb1e
    conn->completed_requests = 0;
Packit 3adb1e
    conn->completed_responses = 0;
Packit 3adb1e
Packit 3adb1e
    old_reqs = conn->requests;
Packit 3adb1e
Packit 3adb1e
    conn->requests = NULL;
Packit 3adb1e
    conn->requests_tail = NULL;
Packit 3adb1e
Packit 3adb1e
    /* Handle all outstanding requests. These have either not been written yet,
Packit 3adb1e
       or have been written but the expected reply wasn't received yet. */
Packit 3adb1e
    while (old_reqs) {
Packit 3adb1e
        /* If we haven't started to write the connection, bring it over
Packit 3adb1e
         * unchanged to our new socket.
Packit 3adb1e
         * Do not copy a CONNECT request to the new connection, the ssl tunnel
Packit 3adb1e
         * setup code will create a new CONNECT request already.
Packit 3adb1e
         */
Packit 3adb1e
        if (requeue_requests && !old_reqs->writing_started &&
Packit 3adb1e
            !old_reqs->ssltunnel) {
Packit 3adb1e
Packit 3adb1e
            serf_request_t *req = old_reqs;
Packit 3adb1e
            old_reqs = old_reqs->next;
Packit 3adb1e
            req->next = NULL;
Packit 3adb1e
            link_requests(&conn->requests, &conn->requests_tail, req);
Packit 3adb1e
        }
Packit 3adb1e
        else {
Packit 3adb1e
            /* Request has been consumed, or we don't want to requeue the
Packit 3adb1e
               request. Either way, inform the application that the request
Packit 3adb1e
               is cancelled. */
Packit 3adb1e
            cancel_request(old_reqs, &old_reqs, requeue_requests);
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Requests queue has been prepared for a new socket, close the old one. */
Packit 3adb1e
    if (conn->skt != NULL) {
Packit 3adb1e
        remove_connection(ctx, conn);
Packit 3adb1e
        status = apr_socket_close(conn->skt);
Packit 3adb1e
        serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt,
Packit 3adb1e
                      "closed socket, status %d\n", status);
Packit 3adb1e
        if (conn->closed != NULL) {
Packit 3adb1e
            handle_conn_closed(conn, status);
Packit 3adb1e
        }
Packit 3adb1e
        conn->skt = NULL;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (conn->stream != NULL) {
Packit 3adb1e
        serf_bucket_destroy(conn->stream);
Packit 3adb1e
        conn->stream = NULL;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    destroy_ostream(conn);
Packit 3adb1e
Packit 3adb1e
    /* Don't try to resume any writes */
Packit 3adb1e
    conn->vec_len = 0;
Packit 3adb1e
Packit 3adb1e
    conn->dirty_conn = 1;
Packit 3adb1e
    conn->ctx->dirty_pollset = 1;
Packit 3adb1e
    conn->state = SERF_CONN_INIT;
Packit 3adb1e
Packit 3adb1e
    conn->hit_eof = 0;
Packit 3adb1e
    conn->connect_time = 0;
Packit 3adb1e
    conn->latency = -1;
Packit 3adb1e
    conn->stop_writing = 0;
Packit 3adb1e
Packit 3adb1e
    serf__log(CONN_VERBOSE, __FILE__, "reset connection 0x%x\n", conn);
Packit 3adb1e
Packit 3adb1e
    conn->status = APR_SUCCESS;
Packit 3adb1e
Packit 3adb1e
    /* Let our context know that we've 'reset' the socket already. */
Packit 3adb1e
    conn->seen_in_pollset |= APR_POLLHUP;
Packit 3adb1e
Packit 3adb1e
    /* Found the connection. Closed it. All done. */
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t socket_writev(serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    apr_size_t written;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    status = apr_socket_sendv(conn->skt, conn->vec,
Packit 3adb1e
                              conn->vec_len, &written);
Packit 3adb1e
    if (status && !APR_STATUS_IS_EAGAIN(status))
Packit 3adb1e
        serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt,
Packit 3adb1e
                      "socket_sendv error %d\n", status);
Packit 3adb1e
Packit 3adb1e
    /* did we write everything? */
Packit 3adb1e
    if (written) {
Packit 3adb1e
        apr_size_t len = 0;
Packit 3adb1e
        int i;
Packit 3adb1e
Packit 3adb1e
        serf__log_skt(SOCK_MSG_VERBOSE, __FILE__, conn->skt,
Packit 3adb1e
                      "--- socket_sendv:\n");
Packit 3adb1e
Packit 3adb1e
        for (i = 0; i < conn->vec_len; i++) {
Packit 3adb1e
            len += conn->vec[i].iov_len;
Packit 3adb1e
            if (written < len) {
Packit 3adb1e
                serf__log_nopref(SOCK_MSG_VERBOSE, "%.*s",
Packit 3adb1e
                                   conn->vec[i].iov_len - (len - written),
Packit 3adb1e
                                   conn->vec[i].iov_base);
Packit 3adb1e
                if (i) {
Packit 3adb1e
                    memmove(conn->vec, &conn->vec[i],
Packit 3adb1e
                            sizeof(struct iovec) * (conn->vec_len - i));
Packit 3adb1e
                    conn->vec_len -= i;
Packit 3adb1e
                }
Packit 3adb1e
                conn->vec[0].iov_base = (char *)conn->vec[0].iov_base + (conn->vec[0].iov_len - (len - written));
Packit 3adb1e
                conn->vec[0].iov_len = len - written;
Packit 3adb1e
                break;
Packit 3adb1e
            } else {
Packit 3adb1e
                serf__log_nopref(SOCK_MSG_VERBOSE, "%.*s",
Packit 3adb1e
                                   conn->vec[i].iov_len, conn->vec[i].iov_base);
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
        if (len == written) {
Packit 3adb1e
            conn->vec_len = 0;
Packit 3adb1e
        }
Packit 3adb1e
        serf__log_nopref(SOCK_MSG_VERBOSE, "-(%d)-\n", written);
Packit 3adb1e
Packit 3adb1e
        /* Log progress information */
Packit 3adb1e
        serf__context_progress_delta(conn->ctx, 0, written);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t setup_request(serf_request_t *request)
Packit 3adb1e
{
Packit 3adb1e
    serf_connection_t *conn = request->conn;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    /* Now that we are about to serve the request, allocate a pool. */
Packit 3adb1e
    apr_pool_create(&request->respool, conn->pool);
Packit 3adb1e
    request->allocator = serf_bucket_allocator_create(request->respool,
Packit 3adb1e
                                                      NULL, NULL);
Packit 3adb1e
    apr_pool_cleanup_register(request->respool, request,
Packit 3adb1e
                              clean_resp, clean_resp);
Packit 3adb1e
Packit 3adb1e
    /* Fill in the rest of the values for the request. */
Packit 3adb1e
    status = request->setup(request, request->setup_baton,
Packit 3adb1e
                            &request->req_bkt,
Packit 3adb1e
                            &request->acceptor,
Packit 3adb1e
                            &request->acceptor_baton,
Packit 3adb1e
                            &request->handler,
Packit 3adb1e
                            &request->handler_baton,
Packit 3adb1e
                            request->respool);
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* write data out to the connection */
Packit 3adb1e
static apr_status_t write_to_connection(serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    if (conn->probable_keepalive_limit &&
Packit 3adb1e
        conn->completed_requests > conn->probable_keepalive_limit) {
Packit 3adb1e
Packit 3adb1e
        conn->dirty_conn = 1;
Packit 3adb1e
        conn->ctx->dirty_pollset = 1;
Packit 3adb1e
Packit 3adb1e
        /* backoff for now. */
Packit 3adb1e
        return APR_SUCCESS;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Keep reading and sending until we run out of stuff to read, or
Packit 3adb1e
     * writing would block.
Packit 3adb1e
     */
Packit 3adb1e
    while (1) {
Packit 3adb1e
        serf_request_t *request;
Packit 3adb1e
        int stop_reading = 0;
Packit 3adb1e
        apr_status_t status;
Packit 3adb1e
        apr_status_t read_status;
Packit 3adb1e
        serf_bucket_t *ostreamt;
Packit 3adb1e
        serf_bucket_t *ostreamh;
Packit 3adb1e
        int max_outstanding_requests = conn->max_outstanding_requests;
Packit 3adb1e
Packit 3adb1e
        /* If we're setting up an ssl tunnel, we can't send real requests
Packit 3adb1e
           at yet, as they need to be encrypted and our encrypt buckets
Packit 3adb1e
           aren't created yet as we still need to read the unencrypted
Packit 3adb1e
           response of the CONNECT request. */
Packit 3adb1e
        if (conn->state != SERF_CONN_CONNECTED)
Packit 3adb1e
            max_outstanding_requests = 1;
Packit 3adb1e
Packit 3adb1e
        if (max_outstanding_requests &&
Packit 3adb1e
            conn->completed_requests -
Packit 3adb1e
                conn->completed_responses >= max_outstanding_requests) {
Packit 3adb1e
            /* backoff for now. */
Packit 3adb1e
            return APR_SUCCESS;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* If we have unwritten data, then write what we can. */
Packit 3adb1e
        while (conn->vec_len) {
Packit 3adb1e
            status = socket_writev(conn);
Packit 3adb1e
Packit 3adb1e
            /* If the write would have blocked, then we're done. Don't try
Packit 3adb1e
             * to write anything else to the socket.
Packit 3adb1e
             */
Packit 3adb1e
            if (APR_STATUS_IS_EAGAIN(status))
Packit 3adb1e
                return APR_SUCCESS;
Packit 3adb1e
            if (APR_STATUS_IS_EPIPE(status) ||
Packit 3adb1e
                APR_STATUS_IS_ECONNRESET(status) ||
Packit 3adb1e
                APR_STATUS_IS_ECONNABORTED(status))
Packit 3adb1e
                return no_more_writes(conn);
Packit 3adb1e
            if (status)
Packit 3adb1e
                return status;
Packit 3adb1e
        }
Packit 3adb1e
        /* ### can we have a short write, yet no EAGAIN? a short write
Packit 3adb1e
           ### would imply unwritten_len > 0 ... */
Packit 3adb1e
        /* assert: unwritten_len == 0. */
Packit 3adb1e
Packit 3adb1e
        /* We may need to move forward to a request which has something
Packit 3adb1e
         * to write.
Packit 3adb1e
         */
Packit 3adb1e
        if (!request_or_data_pending(&request, conn)) {
Packit 3adb1e
            /* No more requests (with data) are registered with the
Packit 3adb1e
             * connection, and no data is pending on the outgoing stream.
Packit 3adb1e
             * Let's update the pollset so that we don't try to write to this
Packit 3adb1e
             * socket again.
Packit 3adb1e
             */
Packit 3adb1e
            conn->dirty_conn = 1;
Packit 3adb1e
            conn->ctx->dirty_pollset = 1;
Packit 3adb1e
            return APR_SUCCESS;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        status = prepare_conn_streams(conn, &conn->stream, &ostreamt, &ostreamh);
Packit 3adb1e
        if (status) {
Packit 3adb1e
            return status;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        if (request) {
Packit 3adb1e
            if (request->req_bkt == NULL) {
Packit 3adb1e
                read_status = setup_request(request);
Packit 3adb1e
                if (read_status) {
Packit 3adb1e
                    /* Something bad happened. Propagate any errors. */
Packit 3adb1e
                    return read_status;
Packit 3adb1e
                }
Packit 3adb1e
            }
Packit 3adb1e
Packit 3adb1e
            if (!request->writing_started) {
Packit 3adb1e
                request->writing_started = 1;
Packit 3adb1e
                serf_bucket_aggregate_append(ostreamt, request->req_bkt);
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* ### optimize at some point by using read_for_sendfile */
Packit 3adb1e
        /* TODO: now that read_iovec will effectively try to return as much
Packit 3adb1e
           data as available, we probably don't want to read ALL_AVAIL, but
Packit 3adb1e
           a lower number, like the size of one or a few TCP packets, the
Packit 3adb1e
           available TCP buffer size ... */
Packit 3adb1e
        read_status = serf_bucket_read_iovec(ostreamh,
Packit 3adb1e
                                             SERF_READ_ALL_AVAIL,
Packit 3adb1e
                                             IOV_MAX,
Packit 3adb1e
                                             conn->vec,
Packit 3adb1e
                                             &conn->vec_len);
Packit 3adb1e
Packit 3adb1e
        if (!conn->hit_eof) {
Packit 3adb1e
            if (APR_STATUS_IS_EAGAIN(read_status)) {
Packit 3adb1e
                /* We read some stuff, but should not try to read again. */
Packit 3adb1e
                stop_reading = 1;
Packit 3adb1e
            }
Packit 3adb1e
            else if (read_status == SERF_ERROR_WAIT_CONN) {
Packit 3adb1e
                /* The bucket told us that it can't provide more data until
Packit 3adb1e
                   more data is read from the socket. This normally happens
Packit 3adb1e
                   during a SSL handshake.
Packit 3adb1e
Packit 3adb1e
                   We should avoid looking for writability for a while so
Packit 3adb1e
                   that (hopefully) something will appear in the bucket so
Packit 3adb1e
                   we can actually write something. otherwise, we could
Packit 3adb1e
                   end up in a CPU spin: socket wants something, but we
Packit 3adb1e
                   don't have anything (and keep returning EAGAIN)
Packit 3adb1e
                 */
Packit 3adb1e
                conn->stop_writing = 1;
Packit 3adb1e
                conn->dirty_conn = 1;
Packit 3adb1e
                conn->ctx->dirty_pollset = 1;
Packit 3adb1e
            }
Packit 3adb1e
            else if (read_status && !APR_STATUS_IS_EOF(read_status)) {
Packit 3adb1e
                /* Something bad happened. Propagate any errors. */
Packit 3adb1e
                return read_status;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* If we got some data, then deliver it. */
Packit 3adb1e
        /* ### what to do if we got no data?? is that a problem? */
Packit 3adb1e
        if (conn->vec_len > 0) {
Packit 3adb1e
            status = socket_writev(conn);
Packit 3adb1e
Packit 3adb1e
            /* If we can't write any more, or an error occurred, then
Packit 3adb1e
             * we're done here.
Packit 3adb1e
             */
Packit 3adb1e
            if (APR_STATUS_IS_EAGAIN(status))
Packit 3adb1e
                return APR_SUCCESS;
Packit 3adb1e
            if (APR_STATUS_IS_EPIPE(status))
Packit 3adb1e
                return no_more_writes(conn);
Packit 3adb1e
            if (APR_STATUS_IS_ECONNRESET(status) ||
Packit 3adb1e
                APR_STATUS_IS_ECONNABORTED(status)) {
Packit 3adb1e
                return no_more_writes(conn);
Packit 3adb1e
            }
Packit 3adb1e
            if (status)
Packit 3adb1e
                return status;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        if (read_status == SERF_ERROR_WAIT_CONN) {
Packit 3adb1e
            stop_reading = 1;
Packit 3adb1e
            conn->stop_writing = 1;
Packit 3adb1e
            conn->dirty_conn = 1;
Packit 3adb1e
            conn->ctx->dirty_pollset = 1;
Packit 3adb1e
        }
Packit 3adb1e
        else if (request && read_status && conn->hit_eof &&
Packit 3adb1e
                 conn->vec_len == 0) {
Packit 3adb1e
            /* If we hit the end of the request bucket and all of its data has
Packit 3adb1e
             * been written, then clear it out to signify that we're done
Packit 3adb1e
             * sending the request. On the next iteration through this loop:
Packit 3adb1e
             * - if there are remaining bytes they will be written, and as the 
Packit 3adb1e
             * request bucket will be completely read it will be destroyed then.
Packit 3adb1e
             * - we'll see if there are other requests that need to be sent 
Packit 3adb1e
             * ("pipelining").
Packit 3adb1e
             */
Packit 3adb1e
            conn->hit_eof = 0;
Packit 3adb1e
            serf_bucket_destroy(request->req_bkt);
Packit 3adb1e
            request->req_bkt = NULL;
Packit 3adb1e
Packit 3adb1e
            /* If our connection has async responses enabled, we're not
Packit 3adb1e
             * going to get a reply back, so kill the request.
Packit 3adb1e
             */
Packit 3adb1e
            if (conn->async_responses) {
Packit 3adb1e
                conn->requests = request->next;
Packit 3adb1e
                destroy_request(request);
Packit 3adb1e
            }
Packit 3adb1e
Packit 3adb1e
            conn->completed_requests++;
Packit 3adb1e
Packit 3adb1e
            if (conn->probable_keepalive_limit &&
Packit 3adb1e
                conn->completed_requests > conn->probable_keepalive_limit) {
Packit 3adb1e
                /* backoff for now. */
Packit 3adb1e
                stop_reading = 1;
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        if (stop_reading) {
Packit 3adb1e
            return APR_SUCCESS;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
    /* NOTREACHED */
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* A response message was received from the server, so call
Packit 3adb1e
   the handler as specified on the original request. */
Packit 3adb1e
static apr_status_t handle_response(serf_request_t *request,
Packit 3adb1e
                                    apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status = APR_SUCCESS;
Packit 3adb1e
    int consumed_response = 0;
Packit 3adb1e
Packit 3adb1e
    /* Only enable the new authentication framework if the program has
Packit 3adb1e
     * registered an authentication credential callback.
Packit 3adb1e
     *
Packit 3adb1e
     * This permits older Serf apps to still handle authentication
Packit 3adb1e
     * themselves by not registering credential callbacks.
Packit 3adb1e
     */
Packit 3adb1e
    if (request->conn->ctx->cred_cb) {
Packit 3adb1e
      status = serf__handle_auth_response(&consumed_response,
Packit 3adb1e
                                          request,
Packit 3adb1e
                                          request->resp_bkt,
Packit 3adb1e
                                          request->handler_baton,
Packit 3adb1e
                                          pool);
Packit 3adb1e
Packit 3adb1e
      /* If there was an error reading the response (maybe there wasn't
Packit 3adb1e
         enough data available), don't bother passing the response to the
Packit 3adb1e
         application.
Packit 3adb1e
Packit 3adb1e
         If the authentication was tried, but failed, pass the response
Packit 3adb1e
         to the application, maybe it can do better. */
Packit 3adb1e
      if (status) {
Packit 3adb1e
          return status;
Packit 3adb1e
      }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (!consumed_response) {
Packit 3adb1e
        return (*request->handler)(request,
Packit 3adb1e
                                   request->resp_bkt,
Packit 3adb1e
                                   request->handler_baton,
Packit 3adb1e
                                   pool);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* An async response message was received from the server. */
Packit 3adb1e
static apr_status_t handle_async_response(serf_connection_t *conn,
Packit 3adb1e
                                          apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    if (conn->current_async_response == NULL) {
Packit 3adb1e
        conn->current_async_response =
Packit 3adb1e
            (*conn->async_acceptor)(NULL, conn->stream,
Packit 3adb1e
                                    conn->async_acceptor_baton, pool);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    status = (*conn->async_handler)(NULL, conn->current_async_response,
Packit 3adb1e
                                    conn->async_handler_baton, pool);
Packit 3adb1e
Packit 3adb1e
    if (APR_STATUS_IS_EOF(status)) {
Packit 3adb1e
        serf_bucket_destroy(conn->current_async_response);
Packit 3adb1e
        conn->current_async_response = NULL;
Packit 3adb1e
        status = APR_SUCCESS;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
apr_status_t
Packit 3adb1e
serf__provide_credentials(serf_context_t *ctx,
Packit 3adb1e
                          char **username,
Packit 3adb1e
                          char **password,
Packit 3adb1e
                          serf_request_t *request, void *baton,
Packit 3adb1e
                          int code, const char *authn_type,
Packit 3adb1e
                          const char *realm,
Packit 3adb1e
                          apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    serf_connection_t *conn = request->conn;
Packit 3adb1e
    serf_request_t *authn_req = request;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    if (request->ssltunnel == 1 &&
Packit 3adb1e
        conn->state == SERF_CONN_SETUP_SSLTUNNEL) {
Packit 3adb1e
        /* This is a CONNECT request to set up an SSL tunnel over a proxy.
Packit 3adb1e
           This request is created by serf, so if the proxy requires
Packit 3adb1e
           authentication, we can't ask the application for credentials with
Packit 3adb1e
           this request.
Packit 3adb1e
Packit 3adb1e
           Solution: setup the first request created by the application on
Packit 3adb1e
           this connection, and use that request and its handler_baton to
Packit 3adb1e
           call back to the application. */
Packit 3adb1e
Packit 3adb1e
        authn_req = request->next;
Packit 3adb1e
        /* assert: app_request != NULL */
Packit 3adb1e
        if (!authn_req)
Packit 3adb1e
            return APR_EGENERAL;
Packit 3adb1e
Packit 3adb1e
        if (!authn_req->req_bkt) {
Packit 3adb1e
            apr_status_t status;
Packit 3adb1e
Packit 3adb1e
            status = setup_request(authn_req);
Packit 3adb1e
            /* If we can't setup a request, don't bother setting up the
Packit 3adb1e
               ssl tunnel. */
Packit 3adb1e
            if (status)
Packit 3adb1e
                return status;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Ask the application. */
Packit 3adb1e
    status = (*ctx->cred_cb)(username, password,
Packit 3adb1e
                             authn_req, authn_req->handler_baton,
Packit 3adb1e
                             code, authn_type, realm, pool);
Packit 3adb1e
    if (status)
Packit 3adb1e
        return status;
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* read data from the connection */
Packit 3adb1e
static apr_status_t read_from_connection(serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
    apr_pool_t *tmppool;
Packit 3adb1e
    int close_connection = FALSE;
Packit 3adb1e
Packit 3adb1e
    /* Whatever is coming in on the socket corresponds to the first request
Packit 3adb1e
     * on our chain.
Packit 3adb1e
     */
Packit 3adb1e
    serf_request_t *request = conn->requests;
Packit 3adb1e
Packit 3adb1e
    /* If the stop_writing flag was set on the connection, reset it now because
Packit 3adb1e
       there is some data to read. */
Packit 3adb1e
    if (conn->stop_writing) {
Packit 3adb1e
        conn->stop_writing = 0;
Packit 3adb1e
        conn->dirty_conn = 1;
Packit 3adb1e
        conn->ctx->dirty_pollset = 1;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* assert: request != NULL */
Packit 3adb1e
Packit 3adb1e
    if ((status = apr_pool_create(&tmppool, conn->pool)) != APR_SUCCESS)
Packit 3adb1e
        goto error;
Packit 3adb1e
Packit 3adb1e
    /* Invoke response handlers until we have no more work. */
Packit 3adb1e
    while (1) {
Packit 3adb1e
        serf_bucket_t *dummy1, *dummy2;
Packit 3adb1e
Packit 3adb1e
        apr_pool_clear(tmppool);
Packit 3adb1e
Packit 3adb1e
        /* Only interested in the input stream here. */
Packit 3adb1e
        status = prepare_conn_streams(conn, &conn->stream, &dummy1, &dummy2);
Packit 3adb1e
        if (status) {
Packit 3adb1e
            goto error;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* We have a different codepath when we can have async responses. */
Packit 3adb1e
        if (conn->async_responses) {
Packit 3adb1e
            /* TODO What about socket errors? */
Packit 3adb1e
            status = handle_async_response(conn, tmppool);
Packit 3adb1e
            if (APR_STATUS_IS_EAGAIN(status)) {
Packit 3adb1e
                status = APR_SUCCESS;
Packit 3adb1e
                goto error;
Packit 3adb1e
            }
Packit 3adb1e
            if (status) {
Packit 3adb1e
                goto error;
Packit 3adb1e
            }
Packit 3adb1e
            continue;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* We are reading a response for a request we haven't
Packit 3adb1e
         * written yet!
Packit 3adb1e
         *
Packit 3adb1e
         * This shouldn't normally happen EXCEPT:
Packit 3adb1e
         *
Packit 3adb1e
         * 1) when the other end has closed the socket and we're
Packit 3adb1e
         *    pending an EOF return.
Packit 3adb1e
         * 2) Doing the initial SSL handshake - we'll get EAGAIN
Packit 3adb1e
         *    as the SSL buckets will hide the handshake from us
Packit 3adb1e
         *    but not return any data.
Packit 3adb1e
         * 3) When the server sends us an SSL alert.
Packit 3adb1e
         *
Packit 3adb1e
         * In these cases, we should not receive any actual user data.
Packit 3adb1e
         *
Packit 3adb1e
         * 4) When the server sends a error response, like 408 Request timeout.
Packit 3adb1e
         *    This response should be passed to the application.
Packit 3adb1e
         *
Packit 3adb1e
         * If we see an EOF (due to either an expired timeout or the server
Packit 3adb1e
         * sending the SSL 'close notify' shutdown alert), we'll reset the
Packit 3adb1e
         * connection and open a new one.
Packit 3adb1e
         */
Packit 3adb1e
        if (request->req_bkt || !request->writing_started) {
Packit 3adb1e
            const char *data;
Packit 3adb1e
            apr_size_t len;
Packit 3adb1e
Packit 3adb1e
            status = serf_bucket_peek(conn->stream, &data, &len;;
Packit 3adb1e
Packit 3adb1e
            if (APR_STATUS_IS_EOF(status)) {
Packit 3adb1e
                reset_connection(conn, 1);
Packit 3adb1e
                status = APR_SUCCESS;
Packit 3adb1e
                goto error;
Packit 3adb1e
            }
Packit 3adb1e
            else if (APR_STATUS_IS_EAGAIN(status) && !len) {
Packit 3adb1e
                status = APR_SUCCESS;
Packit 3adb1e
                goto error;
Packit 3adb1e
            } else if (status && !APR_STATUS_IS_EAGAIN(status)) {
Packit 3adb1e
                /* Read error */
Packit 3adb1e
                goto error;
Packit 3adb1e
            }
Packit 3adb1e
Packit 3adb1e
            /* Unexpected response from the server */
Packit 3adb1e
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* If the request doesn't have a response bucket, then call the
Packit 3adb1e
         * acceptor to get one created.
Packit 3adb1e
         */
Packit 3adb1e
        if (request->resp_bkt == NULL) {
Packit 3adb1e
            request->resp_bkt = (*request->acceptor)(request, conn->stream,
Packit 3adb1e
                                                     request->acceptor_baton,
Packit 3adb1e
                                                     tmppool);
Packit 3adb1e
            apr_pool_clear(tmppool);
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        status = handle_response(request, tmppool);
Packit 3adb1e
Packit 3adb1e
        /* Some systems will not generate a HUP poll event so we have to
Packit 3adb1e
         * handle the ECONNRESET issue and ECONNABORT here.
Packit 3adb1e
         */
Packit 3adb1e
        if (APR_STATUS_IS_ECONNRESET(status) ||
Packit 3adb1e
            APR_STATUS_IS_ECONNABORTED(status) ||
Packit 3adb1e
            status == SERF_ERROR_REQUEST_LOST) {
Packit 3adb1e
            /* If the connection had ever been good, be optimistic & try again.
Packit 3adb1e
             * If it has never tried again (incl. a retry), fail.
Packit 3adb1e
             */
Packit 3adb1e
            if (conn->completed_responses) {
Packit 3adb1e
                reset_connection(conn, 1);
Packit 3adb1e
                status = APR_SUCCESS;
Packit 3adb1e
            }
Packit 3adb1e
            else if (status == SERF_ERROR_REQUEST_LOST) {
Packit 3adb1e
                status = SERF_ERROR_ABORTED_CONNECTION;
Packit 3adb1e
            }
Packit 3adb1e
            goto error;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* If our response handler says it can't do anything more, we now
Packit 3adb1e
         * treat that as a success.
Packit 3adb1e
         */
Packit 3adb1e
        if (APR_STATUS_IS_EAGAIN(status)) {
Packit 3adb1e
            /* It is possible that while reading the response, the ssl layer
Packit 3adb1e
               has prepared some data to send. If this was the last request,
Packit 3adb1e
               serf will not check for socket writability, so force this here.
Packit 3adb1e
             */
Packit 3adb1e
            if (request_or_data_pending(&request, conn) && !request) {
Packit 3adb1e
                conn->dirty_conn = 1;
Packit 3adb1e
                conn->ctx->dirty_pollset = 1;
Packit 3adb1e
            }
Packit 3adb1e
            status = APR_SUCCESS;
Packit 3adb1e
            goto error;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* If we received APR_SUCCESS, run this loop again. */
Packit 3adb1e
        if (!status) {
Packit 3adb1e
            continue;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        close_connection = is_conn_closing(request->resp_bkt);
Packit 3adb1e
Packit 3adb1e
        if (!APR_STATUS_IS_EOF(status) &&
Packit 3adb1e
            close_connection != SERF_ERROR_CLOSING) {
Packit 3adb1e
            /* Whether success, or an error, there is no more to do unless
Packit 3adb1e
             * this request has been completed.
Packit 3adb1e
             */
Packit 3adb1e
            goto error;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* The response has been fully-read, so that means the request has
Packit 3adb1e
         * either been fully-delivered (most likely), or that we don't need to
Packit 3adb1e
         * write the rest of it anymore, e.g. when a 408 Request timeout was
Packit 3adb1e
         $ received.
Packit 3adb1e
         * Remove it from our queue and loop to read another response.
Packit 3adb1e
         */
Packit 3adb1e
        conn->requests = request->next;
Packit 3adb1e
Packit 3adb1e
        destroy_request(request);
Packit 3adb1e
Packit 3adb1e
        request = conn->requests;
Packit 3adb1e
Packit 3adb1e
        /* If we're truly empty, update our tail. */
Packit 3adb1e
        if (request == NULL) {
Packit 3adb1e
            conn->requests_tail = NULL;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        conn->completed_responses++;
Packit 3adb1e
Packit 3adb1e
        /* We've to rebuild pollset since completed_responses is changed. */
Packit 3adb1e
        conn->dirty_conn = 1;
Packit 3adb1e
        conn->ctx->dirty_pollset = 1;
Packit 3adb1e
Packit 3adb1e
        /* This means that we're being advised that the connection is done. */
Packit 3adb1e
        if (close_connection == SERF_ERROR_CLOSING) {
Packit 3adb1e
            reset_connection(conn, 1);
Packit 3adb1e
            if (APR_STATUS_IS_EOF(status))
Packit 3adb1e
                status = APR_SUCCESS;
Packit 3adb1e
            goto error;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* The server is suddenly deciding to serve more responses than we've
Packit 3adb1e
         * seen before.
Packit 3adb1e
         *
Packit 3adb1e
         * Let our requests go.
Packit 3adb1e
         */
Packit 3adb1e
        if (conn->probable_keepalive_limit &&
Packit 3adb1e
            conn->completed_responses > conn->probable_keepalive_limit) {
Packit 3adb1e
            conn->probable_keepalive_limit = 0;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* If we just ran out of requests or have unwritten requests, then
Packit 3adb1e
         * update the pollset. We don't want to read from this socket any
Packit 3adb1e
         * more. We are definitely done with this loop, too.
Packit 3adb1e
         */
Packit 3adb1e
        if (request == NULL || !request->writing_started) {
Packit 3adb1e
            conn->dirty_conn = 1;
Packit 3adb1e
            conn->ctx->dirty_pollset = 1;
Packit 3adb1e
            status = APR_SUCCESS;
Packit 3adb1e
            goto error;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
error:
Packit 3adb1e
    apr_pool_destroy(tmppool);
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
/* process all events on the connection */
Packit 3adb1e
apr_status_t serf__process_connection(serf_connection_t *conn,
Packit 3adb1e
                                      apr_int16_t events)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    /* POLLHUP/ERR should come after POLLIN so if there's an error message or
Packit 3adb1e
     * the like sitting on the connection, we give the app a chance to read
Packit 3adb1e
     * it before we trigger a reset condition.
Packit 3adb1e
     */
Packit 3adb1e
    if ((events & APR_POLLIN) != 0) {
Packit 3adb1e
        if ((status = read_from_connection(conn)) != APR_SUCCESS)
Packit 3adb1e
            return status;
Packit 3adb1e
Packit 3adb1e
        /* If we decided to reset our connection, return now as we don't
Packit 3adb1e
         * want to write.
Packit 3adb1e
         */
Packit 3adb1e
        if ((conn->seen_in_pollset & APR_POLLHUP) != 0) {
Packit 3adb1e
            return APR_SUCCESS;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
    if ((events & APR_POLLHUP) != 0) {
Packit 3adb1e
        /* The connection got reset by the server. On Windows this can happen
Packit 3adb1e
           when all data is read, so just cleanup the connection and open
Packit 3adb1e
           a new one.
Packit 3adb1e
           If we haven't had any successful responses on this connection,
Packit 3adb1e
           then error out as it is likely a server issue. */
Packit 3adb1e
        if (conn->completed_responses) {
Packit 3adb1e
            return reset_connection(conn, 1);
Packit 3adb1e
        }
Packit 3adb1e
        return SERF_ERROR_ABORTED_CONNECTION;
Packit 3adb1e
    }
Packit 3adb1e
    if ((events & APR_POLLERR) != 0) {
Packit 3adb1e
        /* We might be talking to a buggy HTTP server that doesn't
Packit 3adb1e
         * do lingering-close.  (httpd < 2.1.8 does this.)
Packit 3adb1e
         *
Packit 3adb1e
         * See:
Packit 3adb1e
         *
Packit 3adb1e
         * http://issues.apache.org/bugzilla/show_bug.cgi?id=35292
Packit 3adb1e
         */
Packit 3adb1e
        if (conn->completed_requests && !conn->probable_keepalive_limit) {
Packit 3adb1e
            return reset_connection(conn, 1);
Packit 3adb1e
        }
Packit 3adb1e
#ifdef SO_ERROR
Packit 3adb1e
        /* If possible, get the error from the platform's socket layer and
Packit 3adb1e
           convert it to an APR status code. */
Packit 3adb1e
        {
Packit 3adb1e
            apr_os_sock_t osskt;
Packit 3adb1e
            if (!apr_os_sock_get(&osskt, conn->skt)) {
Packit 3adb1e
                int error;
Packit 3adb1e
                apr_socklen_t l = sizeof(error);
Packit 3adb1e
Packit 3adb1e
                if (!getsockopt(osskt, SOL_SOCKET, SO_ERROR, (char*)&error,
Packit 3adb1e
                                &l)) {
Packit 3adb1e
                    status = APR_FROM_OS_ERROR(error);
Packit 3adb1e
Packit 3adb1e
                    /* Handle fallback for multi-homed servers.
Packit 3adb1e
                     
Packit 3adb1e
                       ### Improve algorithm to find better than just 'next'?
Packit 3adb1e
Packit 3adb1e
                       Current Windows versions already handle re-ordering for
Packit 3adb1e
                       api users by using statistics on the recently failed
Packit 3adb1e
                       connections to order the list of addresses. */
Packit 3adb1e
                    if (conn->completed_requests == 0
Packit 3adb1e
                        && conn->address->next != NULL
Packit 3adb1e
                        && (APR_STATUS_IS_ECONNREFUSED(status)
Packit 3adb1e
                            || APR_STATUS_IS_TIMEUP(status)
Packit 3adb1e
                            || APR_STATUS_IS_ENETUNREACH(status))) {
Packit 3adb1e
Packit 3adb1e
                        conn->address = conn->address->next;
Packit 3adb1e
                        return reset_connection(conn, 1);
Packit 3adb1e
                    }
Packit 3adb1e
Packit 3adb1e
                    return status;
Packit 3adb1e
                  }
Packit 3adb1e
            }
Packit 3adb1e
        }
Packit 3adb1e
#endif
Packit 3adb1e
        return APR_EGENERAL;
Packit 3adb1e
    }
Packit 3adb1e
    if ((events & APR_POLLOUT) != 0) {
Packit 3adb1e
        if ((status = write_to_connection(conn)) != APR_SUCCESS)
Packit 3adb1e
            return status;
Packit 3adb1e
    }
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
serf_connection_t *serf_connection_create(
Packit 3adb1e
    serf_context_t *ctx,
Packit 3adb1e
    apr_sockaddr_t *address,
Packit 3adb1e
    serf_connection_setup_t setup,
Packit 3adb1e
    void *setup_baton,
Packit 3adb1e
    serf_connection_closed_t closed,
Packit 3adb1e
    void *closed_baton,
Packit 3adb1e
    apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    serf_connection_t *conn = apr_pcalloc(pool, sizeof(*conn));
Packit 3adb1e
Packit 3adb1e
    conn->ctx = ctx;
Packit 3adb1e
    conn->status = APR_SUCCESS;
Packit 3adb1e
    /* Ignore server address if proxy was specified. */
Packit 3adb1e
    conn->address = ctx->proxy_address ? ctx->proxy_address : address;
Packit 3adb1e
    conn->setup = setup;
Packit 3adb1e
    conn->setup_baton = setup_baton;
Packit 3adb1e
    conn->closed = closed;
Packit 3adb1e
    conn->closed_baton = closed_baton;
Packit 3adb1e
    conn->pool = pool;
Packit 3adb1e
    conn->allocator = serf_bucket_allocator_create(pool, NULL, NULL);
Packit 3adb1e
    conn->stream = NULL;
Packit 3adb1e
    conn->ostream_head = NULL;
Packit 3adb1e
    conn->ostream_tail = NULL;
Packit 3adb1e
    conn->baton.type = SERF_IO_CONN;
Packit 3adb1e
    conn->baton.u.conn = conn;
Packit 3adb1e
    conn->hit_eof = 0;
Packit 3adb1e
    conn->state = SERF_CONN_INIT;
Packit 3adb1e
    conn->latency = -1; /* unknown */
Packit 3adb1e
Packit 3adb1e
    /* Create a subpool for our connection. */
Packit 3adb1e
    apr_pool_create(&conn->skt_pool, conn->pool);
Packit 3adb1e
Packit 3adb1e
    /* register a cleanup */
Packit 3adb1e
    apr_pool_cleanup_register(conn->pool, conn, clean_conn,
Packit 3adb1e
                              apr_pool_cleanup_null);
Packit 3adb1e
Packit 3adb1e
    /* Add the connection to the context. */
Packit 3adb1e
    *(serf_connection_t **)apr_array_push(ctx->conns) = conn;
Packit 3adb1e
Packit 3adb1e
    serf__log(CONN_VERBOSE, __FILE__, "created connection 0x%x\n",
Packit 3adb1e
              conn);
Packit 3adb1e
Packit 3adb1e
    return conn;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t serf_connection_create2(
Packit 3adb1e
    serf_connection_t **conn,
Packit 3adb1e
    serf_context_t *ctx,
Packit 3adb1e
    apr_uri_t host_info,
Packit 3adb1e
    serf_connection_setup_t setup,
Packit 3adb1e
    void *setup_baton,
Packit 3adb1e
    serf_connection_closed_t closed,
Packit 3adb1e
    void *closed_baton,
Packit 3adb1e
    apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status = APR_SUCCESS;
Packit 3adb1e
    serf_connection_t *c;
Packit 3adb1e
    apr_sockaddr_t *host_address = NULL;
Packit 3adb1e
Packit 3adb1e
    /* Set the port number explicitly, needed to create the socket later. */
Packit 3adb1e
    if (!host_info.port) {
Packit 3adb1e
        host_info.port = apr_uri_port_of_scheme(host_info.scheme);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Only lookup the address of the server if no proxy server was
Packit 3adb1e
       configured. */
Packit 3adb1e
    if (!ctx->proxy_address) {
Packit 3adb1e
        status = apr_sockaddr_info_get(&host_address,
Packit 3adb1e
                                       host_info.hostname,
Packit 3adb1e
                                       APR_UNSPEC, host_info.port, 0, pool);
Packit 3adb1e
        if (status)
Packit 3adb1e
            return status;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    c = serf_connection_create(ctx, host_address, setup, setup_baton,
Packit 3adb1e
                               closed, closed_baton, pool);
Packit 3adb1e
Packit 3adb1e
    /* We're not interested in the path following the hostname. */
Packit 3adb1e
    c->host_url = apr_uri_unparse(c->pool,
Packit 3adb1e
                                  &host_info,
Packit 3adb1e
                                  APR_URI_UNP_OMITPATHINFO |
Packit 3adb1e
                                  APR_URI_UNP_OMITUSERINFO);
Packit 3adb1e
Packit 3adb1e
    /* Store the host info without the path on the connection. */
Packit 3adb1e
    (void)apr_uri_parse(c->pool, c->host_url, &(c->host_info));
Packit 3adb1e
    if (!c->host_info.port) {
Packit 3adb1e
        c->host_info.port = apr_uri_port_of_scheme(c->host_info.scheme);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    *conn = c;
Packit 3adb1e
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t serf_connection_reset(
Packit 3adb1e
    serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    return reset_connection(conn, 0);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
apr_status_t serf_connection_close(
Packit 3adb1e
    serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    int i;
Packit 3adb1e
    serf_context_t *ctx = conn->ctx;
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
Packit 3adb1e
    for (i = ctx->conns->nelts; i--; ) {
Packit 3adb1e
        serf_connection_t *conn_seq = GET_CONN(ctx, i);
Packit 3adb1e
Packit 3adb1e
        if (conn_seq == conn) {
Packit 3adb1e
            while (conn->requests) {
Packit 3adb1e
                serf_request_cancel(conn->requests);
Packit 3adb1e
            }
Packit 3adb1e
            if (conn->skt != NULL) {
Packit 3adb1e
                remove_connection(ctx, conn);
Packit 3adb1e
                status = apr_socket_close(conn->skt);
Packit 3adb1e
                serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt,
Packit 3adb1e
                              "closed socket, status %d\n",
Packit 3adb1e
                              status);
Packit 3adb1e
                if (conn->closed != NULL) {
Packit 3adb1e
                    handle_conn_closed(conn, status);
Packit 3adb1e
                }
Packit 3adb1e
                conn->skt = NULL;
Packit 3adb1e
            }
Packit 3adb1e
            if (conn->stream != NULL) {
Packit 3adb1e
                serf_bucket_destroy(conn->stream);
Packit 3adb1e
                conn->stream = NULL;
Packit 3adb1e
            }
Packit 3adb1e
Packit 3adb1e
            destroy_ostream(conn);
Packit 3adb1e
Packit 3adb1e
            /* Remove the connection from the context. We don't want to
Packit 3adb1e
             * deal with it any more.
Packit 3adb1e
             */
Packit 3adb1e
            if (i < ctx->conns->nelts - 1) {
Packit 3adb1e
                /* move later connections over this one. */
Packit 3adb1e
                memmove(
Packit 3adb1e
                    &GET_CONN(ctx, i),
Packit 3adb1e
                    &GET_CONN(ctx, i + 1),
Packit 3adb1e
                    (ctx->conns->nelts - i - 1) * sizeof(serf_connection_t *));
Packit 3adb1e
            }
Packit 3adb1e
            --ctx->conns->nelts;
Packit 3adb1e
Packit 3adb1e
            serf__log(CONN_VERBOSE, __FILE__, "closed connection 0x%x\n",
Packit 3adb1e
                      conn);
Packit 3adb1e
Packit 3adb1e
            /* Found the connection. Closed it. All done. */
Packit 3adb1e
            return APR_SUCCESS;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* We didn't find the specified connection. */
Packit 3adb1e
    /* ### doc talks about this w.r.t poll structures. use something else? */
Packit 3adb1e
    return APR_NOTFOUND;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
void serf_connection_set_max_outstanding_requests(
Packit 3adb1e
    serf_connection_t *conn,
Packit 3adb1e
    unsigned int max_requests)
Packit 3adb1e
{
Packit 3adb1e
    if (max_requests == 0)
Packit 3adb1e
        serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt,
Packit 3adb1e
                      "Set max. nr. of outstanding requests for this "
Packit 3adb1e
                      "connection to unlimited.\n");
Packit 3adb1e
    else
Packit 3adb1e
        serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt,
Packit 3adb1e
                      "Limit max. nr. of outstanding requests for this "
Packit 3adb1e
                      "connection to %u.\n", max_requests);
Packit 3adb1e
Packit 3adb1e
    conn->max_outstanding_requests = max_requests;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
void serf_connection_set_async_responses(
Packit 3adb1e
    serf_connection_t *conn,
Packit 3adb1e
    serf_response_acceptor_t acceptor,
Packit 3adb1e
    void *acceptor_baton,
Packit 3adb1e
    serf_response_handler_t handler,
Packit 3adb1e
    void *handler_baton)
Packit 3adb1e
{
Packit 3adb1e
    conn->async_responses = 1;
Packit 3adb1e
    conn->async_acceptor = acceptor;
Packit 3adb1e
    conn->async_acceptor_baton = acceptor_baton;
Packit 3adb1e
    conn->async_handler = handler;
Packit 3adb1e
    conn->async_handler_baton = handler_baton;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static serf_request_t *
Packit 3adb1e
create_request(serf_connection_t *conn,
Packit 3adb1e
               serf_request_setup_t setup,
Packit 3adb1e
               void *setup_baton,
Packit 3adb1e
               int priority,
Packit 3adb1e
               int ssltunnel)
Packit 3adb1e
{
Packit 3adb1e
    serf_request_t *request;
Packit 3adb1e
Packit 3adb1e
    request = serf_bucket_mem_alloc(conn->allocator, sizeof(*request));
Packit 3adb1e
    request->conn = conn;
Packit 3adb1e
    request->setup = setup;
Packit 3adb1e
    request->setup_baton = setup_baton;
Packit 3adb1e
    request->handler = NULL;
Packit 3adb1e
    request->respool = NULL;
Packit 3adb1e
    request->req_bkt = NULL;
Packit 3adb1e
    request->resp_bkt = NULL;
Packit 3adb1e
    request->priority = priority;
Packit 3adb1e
    request->writing_started = 0;
Packit 3adb1e
    request->ssltunnel = ssltunnel;
Packit 3adb1e
    request->next = NULL;
Packit 3adb1e
    request->auth_baton = NULL;
Packit 3adb1e
Packit 3adb1e
    return request;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
serf_request_t *serf_connection_request_create(
Packit 3adb1e
    serf_connection_t *conn,
Packit 3adb1e
    serf_request_setup_t setup,
Packit 3adb1e
    void *setup_baton)
Packit 3adb1e
{
Packit 3adb1e
    serf_request_t *request;
Packit 3adb1e
Packit 3adb1e
    request = create_request(conn, setup, setup_baton,
Packit 3adb1e
                             0, /* priority */
Packit 3adb1e
                             0  /* ssl tunnel */);
Packit 3adb1e
Packit 3adb1e
    /* Link the request to the end of the request chain. */
Packit 3adb1e
    link_requests(&conn->requests, &conn->requests_tail, request);
Packit 3adb1e
    
Packit 3adb1e
    /* Ensure our pollset becomes writable in context run */
Packit 3adb1e
    conn->ctx->dirty_pollset = 1;
Packit 3adb1e
    conn->dirty_conn = 1;
Packit 3adb1e
Packit 3adb1e
    return request;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static serf_request_t *
Packit 3adb1e
priority_request_create(serf_connection_t *conn,
Packit 3adb1e
                        int ssltunnelreq,
Packit 3adb1e
                        serf_request_setup_t setup,
Packit 3adb1e
                        void *setup_baton)
Packit 3adb1e
{
Packit 3adb1e
    serf_request_t *request;
Packit 3adb1e
    serf_request_t *iter, *prev;
Packit 3adb1e
Packit 3adb1e
    request = create_request(conn, setup, setup_baton,
Packit 3adb1e
                             1, /* priority */
Packit 3adb1e
                             ssltunnelreq);
Packit 3adb1e
Packit 3adb1e
    /* Link the new request after the last written request. */
Packit 3adb1e
    iter = conn->requests;
Packit 3adb1e
    prev = NULL;
Packit 3adb1e
Packit 3adb1e
    /* Find a request that has data which needs to be delivered. */
Packit 3adb1e
    while (iter != NULL && iter->req_bkt == NULL && iter->writing_started) {
Packit 3adb1e
        prev = iter;
Packit 3adb1e
        iter = iter->next;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* A CONNECT request to setup an ssltunnel has absolute priority over all
Packit 3adb1e
       other requests on the connection, so:
Packit 3adb1e
       a. add it first to the queue 
Packit 3adb1e
       b. ensure that other priority requests are added after the CONNECT
Packit 3adb1e
          request */
Packit 3adb1e
    if (!request->ssltunnel) {
Packit 3adb1e
        /* Advance to next non priority request */
Packit 3adb1e
        while (iter != NULL && iter->priority) {
Packit 3adb1e
            prev = iter;
Packit 3adb1e
            iter = iter->next;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (prev) {
Packit 3adb1e
        request->next = iter;
Packit 3adb1e
        prev->next = request;
Packit 3adb1e
    } else {
Packit 3adb1e
        request->next = iter;
Packit 3adb1e
        conn->requests = request;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Ensure our pollset becomes writable in context run */
Packit 3adb1e
    conn->ctx->dirty_pollset = 1;
Packit 3adb1e
    conn->dirty_conn = 1;
Packit 3adb1e
Packit 3adb1e
    return request;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
serf_request_t *serf_connection_priority_request_create(
Packit 3adb1e
    serf_connection_t *conn,
Packit 3adb1e
    serf_request_setup_t setup,
Packit 3adb1e
    void *setup_baton)
Packit 3adb1e
{
Packit 3adb1e
    return priority_request_create(conn,
Packit 3adb1e
                                   0, /* not a ssltunnel CONNECT request */
Packit 3adb1e
                                   setup, setup_baton);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
serf_request_t *serf__ssltunnel_request_create(serf_connection_t *conn,
Packit 3adb1e
                                               serf_request_setup_t setup,
Packit 3adb1e
                                               void *setup_baton)
Packit 3adb1e
{
Packit 3adb1e
    return priority_request_create(conn,
Packit 3adb1e
                                   1, /* This is a ssltunnel CONNECT request */
Packit 3adb1e
                                   setup, setup_baton);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t serf_request_cancel(serf_request_t *request)
Packit 3adb1e
{
Packit 3adb1e
    return cancel_request(request, &request->conn->requests, 0);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t serf_request_is_written(serf_request_t *request)
Packit 3adb1e
{
Packit 3adb1e
    if (request->writing_started && !request->req_bkt)
Packit 3adb1e
        return APR_SUCCESS;
Packit 3adb1e
Packit 3adb1e
    return APR_EBUSY;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_pool_t *serf_request_get_pool(const serf_request_t *request)
Packit 3adb1e
{
Packit 3adb1e
    return request->respool;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
serf_bucket_alloc_t *serf_request_get_alloc(
Packit 3adb1e
    const serf_request_t *request)
Packit 3adb1e
{
Packit 3adb1e
    return request->allocator;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
serf_connection_t *serf_request_get_conn(
Packit 3adb1e
    const serf_request_t *request)
Packit 3adb1e
{
Packit 3adb1e
    return request->conn;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
void serf_request_set_handler(
Packit 3adb1e
    serf_request_t *request,
Packit 3adb1e
    const serf_response_handler_t handler,
Packit 3adb1e
    const void **handler_baton)
Packit 3adb1e
{
Packit 3adb1e
    request->handler = handler;
Packit 3adb1e
    request->handler_baton = handler_baton;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
serf_bucket_t *serf_request_bucket_request_create(
Packit 3adb1e
    serf_request_t *request,
Packit 3adb1e
    const char *method,
Packit 3adb1e
    const char *uri,
Packit 3adb1e
    serf_bucket_t *body,
Packit 3adb1e
    serf_bucket_alloc_t *allocator)
Packit 3adb1e
{
Packit 3adb1e
    serf_bucket_t *req_bkt, *hdrs_bkt;
Packit 3adb1e
    serf_connection_t *conn = request->conn;
Packit 3adb1e
    serf_context_t *ctx = conn->ctx;
Packit 3adb1e
    int ssltunnel;
Packit 3adb1e
Packit 3adb1e
    ssltunnel = ctx->proxy_address &&
Packit 3adb1e
                (strcmp(conn->host_info.scheme, "https") == 0);
Packit 3adb1e
Packit 3adb1e
    req_bkt = serf_bucket_request_create(method, uri, body, allocator);
Packit 3adb1e
    hdrs_bkt = serf_bucket_request_get_headers(req_bkt);
Packit 3adb1e
Packit 3adb1e
    /* Use absolute uri's in requests to a proxy. USe relative uri's in
Packit 3adb1e
       requests directly to a server or sent through an SSL tunnel. */
Packit 3adb1e
    if (ctx->proxy_address && conn->host_url &&
Packit 3adb1e
        !(ssltunnel && !request->ssltunnel)) {
Packit 3adb1e
Packit 3adb1e
        serf_bucket_request_set_root(req_bkt, conn->host_url);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if (conn->host_info.hostinfo)
Packit 3adb1e
        serf_bucket_headers_setn(hdrs_bkt, "Host",
Packit 3adb1e
                                 conn->host_info.hostinfo);
Packit 3adb1e
Packit 3adb1e
    /* Setup server authorization headers, unless this is a CONNECT request. */
Packit 3adb1e
    if (!request->ssltunnel) {
Packit 3adb1e
        serf__authn_info_t *authn_info;
Packit 3adb1e
        authn_info = serf__get_authn_info_for_server(conn);
Packit 3adb1e
        if (authn_info->scheme)
Packit 3adb1e
            authn_info->scheme->setup_request_func(HOST, 0, conn, request,
Packit 3adb1e
                                                   method, uri,
Packit 3adb1e
                                                   hdrs_bkt);
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* Setup proxy authorization headers.
Packit 3adb1e
       Don't set these headers on the requests to the server if we're using
Packit 3adb1e
       an SSL tunnel, only on the CONNECT request to setup the tunnel. */
Packit 3adb1e
    if (ctx->proxy_authn_info.scheme) {
Packit 3adb1e
        if (strcmp(conn->host_info.scheme, "https") == 0) {
Packit 3adb1e
            if (request->ssltunnel)
Packit 3adb1e
                ctx->proxy_authn_info.scheme->setup_request_func(PROXY, 0, conn,
Packit 3adb1e
                                                                 request,
Packit 3adb1e
                                                                 method, uri,
Packit 3adb1e
                                                                 hdrs_bkt);
Packit 3adb1e
        } else {
Packit 3adb1e
            ctx->proxy_authn_info.scheme->setup_request_func(PROXY, 0, conn,
Packit 3adb1e
                                                             request,
Packit 3adb1e
                                                             method, uri,
Packit 3adb1e
                                                             hdrs_bkt);
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return req_bkt;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_interval_time_t serf_connection_get_latency(serf_connection_t *conn)
Packit 3adb1e
{
Packit 3adb1e
    if (conn->ctx->proxy_address) {
Packit 3adb1e
        /* Detecting network latency for proxied connection is not implemented
Packit 3adb1e
           yet. */
Packit 3adb1e
        return -1;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return conn->latency;
Packit 3adb1e
}