Blame context.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
Packit 3adb1e
#include "serf.h"
Packit 3adb1e
#include "serf_bucket_util.h"
Packit 3adb1e
Packit 3adb1e
#include "serf_private.h"
Packit 3adb1e
Packit 3adb1e
/**
Packit 3adb1e
 * Callback function (implements serf_progress_t). Takes a number of bytes
Packit 3adb1e
 * read @a read and bytes written @a written, adds those to the total for this
Packit 3adb1e
 * context and notifies an interested party (if any).
Packit 3adb1e
 */
Packit 3adb1e
void serf__context_progress_delta(
Packit 3adb1e
    void *progress_baton,
Packit 3adb1e
    apr_off_t read,
Packit 3adb1e
    apr_off_t written)
Packit 3adb1e
{
Packit 3adb1e
    serf_context_t *ctx = progress_baton;
Packit 3adb1e
Packit 3adb1e
    ctx->progress_read += read;
Packit 3adb1e
    ctx->progress_written += written;
Packit 3adb1e
Packit 3adb1e
    if (ctx->progress_func)
Packit 3adb1e
        ctx->progress_func(ctx->progress_baton,
Packit 3adb1e
                           ctx->progress_read,
Packit 3adb1e
                           ctx->progress_written);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
/* Check for dirty connections and update their pollsets accordingly. */
Packit 3adb1e
static apr_status_t check_dirty_pollsets(serf_context_t *ctx)
Packit 3adb1e
{
Packit 3adb1e
    int i;
Packit 3adb1e
Packit 3adb1e
    /* if we're not dirty, return now. */
Packit 3adb1e
    if (!ctx->dirty_pollset) {
Packit 3adb1e
        return APR_SUCCESS;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    for (i = ctx->conns->nelts; i--; ) {
Packit 3adb1e
        serf_connection_t *conn = GET_CONN(ctx, i);
Packit 3adb1e
        apr_status_t status;
Packit 3adb1e
Packit 3adb1e
        /* if this connection isn't dirty, skip it. */
Packit 3adb1e
        if (!conn->dirty_conn) {
Packit 3adb1e
            continue;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        /* reset this connection's flag before we update. */
Packit 3adb1e
        conn->dirty_conn = 0;
Packit 3adb1e
Packit 3adb1e
        if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS)
Packit 3adb1e
            return status;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* reset our context flag now */
Packit 3adb1e
    ctx->dirty_pollset = 0;
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
static apr_status_t pollset_add(void *user_baton,
Packit 3adb1e
                                apr_pollfd_t *pfd,
Packit 3adb1e
                                void *serf_baton)
Packit 3adb1e
{
Packit 3adb1e
    serf_pollset_t *s = (serf_pollset_t*)user_baton;
Packit 3adb1e
    pfd->client_data = serf_baton;
Packit 3adb1e
    return apr_pollset_add(s->pollset, pfd);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
static apr_status_t pollset_rm(void *user_baton,
Packit 3adb1e
                               apr_pollfd_t *pfd,
Packit 3adb1e
                               void *serf_baton)
Packit 3adb1e
{
Packit 3adb1e
    serf_pollset_t *s = (serf_pollset_t*)user_baton;
Packit 3adb1e
    pfd->client_data = serf_baton;
Packit 3adb1e
    return apr_pollset_remove(s->pollset, pfd);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
void serf_config_proxy(serf_context_t *ctx,
Packit 3adb1e
                       apr_sockaddr_t *address)
Packit 3adb1e
{
Packit 3adb1e
    ctx->proxy_address = address;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
void serf_config_credentials_callback(serf_context_t *ctx,
Packit 3adb1e
                                      serf_credentials_callback_t cred_cb)
Packit 3adb1e
{
Packit 3adb1e
    ctx->cred_cb = cred_cb;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
void serf_config_authn_types(serf_context_t *ctx,
Packit 3adb1e
                             int authn_types)
Packit 3adb1e
{
Packit 3adb1e
    ctx->authn_types = authn_types;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
serf_context_t *serf_context_create_ex(
Packit 3adb1e
    void *user_baton,
Packit 3adb1e
    serf_socket_add_t addf,
Packit 3adb1e
    serf_socket_remove_t rmf,
Packit 3adb1e
    apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx));
Packit 3adb1e
Packit 3adb1e
    ctx->pool = pool;
Packit 3adb1e
Packit 3adb1e
    if (user_baton != NULL) {
Packit 3adb1e
        ctx->pollset_baton = user_baton;
Packit 3adb1e
        ctx->pollset_add = addf;
Packit 3adb1e
        ctx->pollset_rm = rmf;
Packit 3adb1e
    }
Packit 3adb1e
    else {
Packit 3adb1e
        /* build the pollset with a (default) number of connections */
Packit 3adb1e
        serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps));
Packit 3adb1e
Packit 3adb1e
        /* ### TODO: As of APR 1.4.x apr_pollset_create_ex can return a status
Packit 3adb1e
           ### other than APR_SUCCESS, so we should handle it.
Packit 3adb1e
           ### Probably move creation of the pollset to later when we have
Packit 3adb1e
           ### the possibility of returning status to the caller.
Packit 3adb1e
         */
Packit 3adb1e
#ifdef BROKEN_WSAPOLL
Packit 3adb1e
        /* APR 1.4.x switched to using WSAPoll() on Win32, but it does not
Packit 3adb1e
         * properly handle errors on a non-blocking sockets (such as
Packit 3adb1e
         * connecting to a server where no listener is active).
Packit 3adb1e
         *
Packit 3adb1e
         * So, sadly, we must force using select() on Win32.
Packit 3adb1e
         *
Packit 3adb1e
         * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E
Packit 3adb1e
         */
Packit 3adb1e
        (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0,
Packit 3adb1e
                                     APR_POLLSET_SELECT);
Packit 3adb1e
#else
Packit 3adb1e
        (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0);
Packit 3adb1e
#endif
Packit 3adb1e
        ctx->pollset_baton = ps;
Packit 3adb1e
        ctx->pollset_add = pollset_add;
Packit 3adb1e
        ctx->pollset_rm = pollset_rm;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* default to a single connection since that is the typical case */
Packit 3adb1e
    ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *));
Packit 3adb1e
Packit 3adb1e
    /* Initialize progress status */
Packit 3adb1e
    ctx->progress_read = 0;
Packit 3adb1e
    ctx->progress_written = 0;
Packit 3adb1e
Packit 3adb1e
    ctx->authn_types = SERF_AUTHN_ALL;
Packit 3adb1e
    ctx->server_authn_info = apr_hash_make(pool);
Packit 3adb1e
Packit 3adb1e
    return ctx;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
serf_context_t *serf_context_create(apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    return serf_context_create_ex(NULL, NULL, NULL, pool);
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
apr_status_t serf_context_prerun(serf_context_t *ctx)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status = APR_SUCCESS;
Packit 3adb1e
    if ((status = serf__open_connections(ctx)) != APR_SUCCESS)
Packit 3adb1e
        return status;
Packit 3adb1e
Packit 3adb1e
    if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS)
Packit 3adb1e
        return status;
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
apr_status_t serf_event_trigger(
Packit 3adb1e
    serf_context_t *s,
Packit 3adb1e
    void *serf_baton,
Packit 3adb1e
    const apr_pollfd_t *desc)
Packit 3adb1e
{
Packit 3adb1e
    apr_pollfd_t tdesc = { 0 };
Packit 3adb1e
    apr_status_t status = APR_SUCCESS;
Packit 3adb1e
    serf_io_baton_t *io = serf_baton;
Packit 3adb1e
Packit 3adb1e
    if (io->type == SERF_IO_CONN) {
Packit 3adb1e
        serf_connection_t *conn = io->u.conn;
Packit 3adb1e
        serf_context_t *ctx = conn->ctx;
Packit 3adb1e
Packit 3adb1e
        /* If this connection has already failed, return the error again, and try
Packit 3adb1e
         * to remove it from the pollset again
Packit 3adb1e
         */
Packit 3adb1e
        if (conn->status) {
Packit 3adb1e
            tdesc.desc_type = APR_POLL_SOCKET;
Packit 3adb1e
            tdesc.desc.s = conn->skt;
Packit 3adb1e
            tdesc.reqevents = conn->reqevents;
Packit 3adb1e
            ctx->pollset_rm(ctx->pollset_baton,
Packit 3adb1e
                            &tdesc, &conn->baton);
Packit 3adb1e
            return conn->status;
Packit 3adb1e
        }
Packit 3adb1e
        /* apr_pollset_poll() can return a conn multiple times... */
Packit 3adb1e
        if ((conn->seen_in_pollset & desc->rtnevents) != 0 ||
Packit 3adb1e
            (conn->seen_in_pollset & APR_POLLHUP) != 0) {
Packit 3adb1e
            return APR_SUCCESS;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        conn->seen_in_pollset |= desc->rtnevents;
Packit 3adb1e
Packit 3adb1e
        if ((conn->status = serf__process_connection(conn,
Packit 3adb1e
                                         desc->rtnevents)) != APR_SUCCESS) {
Packit 3adb1e
Packit 3adb1e
            /* it's possible that the connection was already reset and thus the
Packit 3adb1e
               socket cleaned up. */
Packit 3adb1e
            if (conn->skt) {
Packit 3adb1e
                tdesc.desc_type = APR_POLL_SOCKET;
Packit 3adb1e
                tdesc.desc.s = conn->skt;
Packit 3adb1e
                tdesc.reqevents = conn->reqevents;
Packit 3adb1e
                ctx->pollset_rm(ctx->pollset_baton,
Packit 3adb1e
                                &tdesc, &conn->baton);
Packit 3adb1e
            }
Packit 3adb1e
            return conn->status;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
    else if (io->type == SERF_IO_LISTENER) {
Packit 3adb1e
        serf_listener_t *l = io->u.listener;
Packit 3adb1e
Packit 3adb1e
        status = serf__process_listener(l);
Packit 3adb1e
Packit 3adb1e
        if (status) {
Packit 3adb1e
            return status;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
    else if (io->type == SERF_IO_CLIENT) {
Packit 3adb1e
        serf_incoming_t *c = io->u.client;
Packit 3adb1e
Packit 3adb1e
        status = serf__process_client(c, desc->rtnevents);
Packit 3adb1e
Packit 3adb1e
        if (status) {
Packit 3adb1e
            return status;
Packit 3adb1e
        }
Packit 3adb1e
    }
Packit 3adb1e
    return status;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
apr_status_t serf_context_run(
Packit 3adb1e
    serf_context_t *ctx,
Packit 3adb1e
    apr_short_interval_time_t duration,
Packit 3adb1e
    apr_pool_t *pool)
Packit 3adb1e
{
Packit 3adb1e
    apr_status_t status;
Packit 3adb1e
    apr_int32_t num;
Packit 3adb1e
    const apr_pollfd_t *desc;
Packit 3adb1e
    serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton;
Packit 3adb1e
Packit 3adb1e
    if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) {
Packit 3adb1e
        return status;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    if ((status = apr_pollset_poll(ps->pollset, duration, &num,
Packit 3adb1e
                                   &desc)) != APR_SUCCESS) {
Packit 3adb1e
        /* EINTR indicates a handled signal happened during the poll call,
Packit 3adb1e
           ignore, the application can safely retry. */
Packit 3adb1e
        if (APR_STATUS_IS_EINTR(status))
Packit 3adb1e
            return APR_SUCCESS;
Packit 3adb1e
Packit 3adb1e
        /* ### do we still need to dispatch stuff here?
Packit 3adb1e
           ### look at the potential return codes. map to our defined
Packit 3adb1e
           ### return values? ...
Packit 3adb1e
        */
Packit 3adb1e
Packit 3adb1e
        /* Use the strict documented error for poll timeouts, to allow proper
Packit 3adb1e
           handling of the other timeout types when returned from
Packit 3adb1e
           serf_event_trigger */
Packit 3adb1e
        if (APR_STATUS_IS_TIMEUP(status))
Packit 3adb1e
            return APR_TIMEUP; /* Return the documented error */
Packit 3adb1e
        return status;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    while (num--) {
Packit 3adb1e
        serf_io_baton_t *io  = desc->client_data;
Packit 3adb1e
Packit 3adb1e
        status = serf_event_trigger(ctx, io, desc);
Packit 3adb1e
        if (status) {
Packit 3adb1e
            return status;
Packit 3adb1e
        }
Packit 3adb1e
Packit 3adb1e
        desc++;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    return APR_SUCCESS;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
void serf_context_set_progress_cb(
Packit 3adb1e
    serf_context_t *ctx,
Packit 3adb1e
    const serf_progress_t progress_func,
Packit 3adb1e
    void *progress_baton)
Packit 3adb1e
{
Packit 3adb1e
    ctx->progress_func = progress_func;
Packit 3adb1e
    ctx->progress_baton = progress_baton;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
serf_bucket_t *serf_context_bucket_socket_create(
Packit 3adb1e
    serf_context_t *ctx,
Packit 3adb1e
    apr_socket_t *skt,
Packit 3adb1e
    serf_bucket_alloc_t *allocator)
Packit 3adb1e
{
Packit 3adb1e
    serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator);
Packit 3adb1e
Packit 3adb1e
    /* Use serf's default bytes read/written callback */
Packit 3adb1e
    serf_bucket_socket_set_read_progress_cb(bucket,
Packit 3adb1e
                                            serf__context_progress_delta,
Packit 3adb1e
                                            ctx);
Packit 3adb1e
Packit 3adb1e
    return bucket;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
/* ### this really ought to go somewhere else, but... meh.  */
Packit 3adb1e
void serf_lib_version(int *major, int *minor, int *patch)
Packit 3adb1e
{
Packit 3adb1e
    *major = SERF_MAJOR_VERSION;
Packit 3adb1e
    *minor = SERF_MINOR_VERSION;
Packit 3adb1e
    *patch = SERF_PATCH_VERSION;
Packit 3adb1e
}
Packit 3adb1e
Packit 3adb1e
Packit 3adb1e
const char *serf_error_string(apr_status_t errcode)
Packit 3adb1e
{
Packit 3adb1e
    switch (errcode)
Packit 3adb1e
    {
Packit 3adb1e
    case SERF_ERROR_CLOSING:
Packit 3adb1e
        return "The connection is closing";
Packit 3adb1e
    case SERF_ERROR_REQUEST_LOST:
Packit 3adb1e
        return "A request has been lost";
Packit 3adb1e
    case SERF_ERROR_WAIT_CONN:
Packit 3adb1e
        return "The connection is blocked, pending further action";
Packit 3adb1e
    case SERF_ERROR_DECOMPRESSION_FAILED:
Packit 3adb1e
        return "An error occurred during decompression";
Packit 3adb1e
    case SERF_ERROR_BAD_HTTP_RESPONSE:
Packit 3adb1e
        return "The server sent an improper HTTP response";
Packit 3adb1e
    case SERF_ERROR_TRUNCATED_HTTP_RESPONSE:
Packit 3adb1e
        return "The server sent a truncated HTTP response body.";
Packit 3adb1e
    case SERF_ERROR_ABORTED_CONNECTION:
Packit 3adb1e
        return "The server unexpectedly closed the connection.";
Packit 3adb1e
    case SERF_ERROR_SSL_COMM_FAILED:
Packit 3adb1e
        return "An error occurred during SSL communication";
Packit 3adb1e
    case SERF_ERROR_SSL_CERT_FAILED:
Packit 3adb1e
        return "An SSL certificate related error occurred ";
Packit 3adb1e
    case SERF_ERROR_AUTHN_FAILED:
Packit 3adb1e
        return "An error occurred during authentication";
Packit 3adb1e
    case SERF_ERROR_AUTHN_NOT_SUPPORTED:
Packit 3adb1e
        return "The requested authentication type(s) are not supported";
Packit 3adb1e
    case SERF_ERROR_AUTHN_MISSING_ATTRIBUTE:
Packit 3adb1e
        return "An authentication attribute is missing";
Packit 3adb1e
    case SERF_ERROR_AUTHN_INITALIZATION_FAILED:
Packit 3adb1e
        return "Initialization of an authentication type failed";
Packit 3adb1e
    case SERF_ERROR_SSLTUNNEL_SETUP_FAILED:
Packit 3adb1e
        return "The proxy server returned an error while setting up the "
Packit 3adb1e
               "SSL tunnel.";
Packit 3adb1e
    default:
Packit 3adb1e
        return NULL;
Packit 3adb1e
    }
Packit 3adb1e
Packit 3adb1e
    /* NOTREACHED  */
Packit 3adb1e
}