diff options
Diffstat (limited to 'context.c')
-rw-r--r-- | context.c | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/context.c b/context.c new file mode 100644 index 0000000..d4aa220 --- /dev/null +++ b/context.c @@ -0,0 +1,371 @@ +/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <apr_pools.h> +#include <apr_poll.h> +#include <apr_version.h> + +#include "serf.h" +#include "serf_bucket_util.h" + +#include "serf_private.h" + +/* Older versions of APR do not have the APR_VERSION_AT_LEAST macro. Those + implementations are safe. + + If the macro *is* defined, and we're on WIN32, and APR is version 1.4.0, + then we have a broken WSAPoll() implementation. + + See serf_context_create_ex() below. */ +#if defined(APR_VERSION_AT_LEAST) && defined(WIN32) +#if APR_VERSION_AT_LEAST(1,4,0) +#define BROKEN_WSAPOLL +#endif +#endif + + +/** + * Callback function (implements serf_progress_t). Takes a number of bytes + * read @a read and bytes written @a written, adds those to the total for this + * context and notifies an interested party (if any). + */ +void serf__context_progress_delta( + void *progress_baton, + apr_off_t read, + apr_off_t written) +{ + serf_context_t *ctx = progress_baton; + + ctx->progress_read += read; + ctx->progress_written += written; + + if (ctx->progress_func) + ctx->progress_func(ctx->progress_baton, + ctx->progress_read, + ctx->progress_written); +} + + +/* Check for dirty connections and update their pollsets accordingly. */ +static apr_status_t check_dirty_pollsets(serf_context_t *ctx) +{ + int i; + + /* if we're not dirty, return now. */ + if (!ctx->dirty_pollset) { + return APR_SUCCESS; + } + + for (i = ctx->conns->nelts; i--; ) { + serf_connection_t *conn = GET_CONN(ctx, i); + apr_status_t status; + + /* if this connection isn't dirty, skip it. */ + if (!conn->dirty_conn) { + continue; + } + + /* reset this connection's flag before we update. */ + conn->dirty_conn = 0; + + if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS) + return status; + } + + /* reset our context flag now */ + ctx->dirty_pollset = 0; + + return APR_SUCCESS; +} + + +static apr_status_t pollset_add(void *user_baton, + apr_pollfd_t *pfd, + void *serf_baton) +{ + serf_pollset_t *s = (serf_pollset_t*)user_baton; + pfd->client_data = serf_baton; + return apr_pollset_add(s->pollset, pfd); +} + +static apr_status_t pollset_rm(void *user_baton, + apr_pollfd_t *pfd, + void *serf_baton) +{ + serf_pollset_t *s = (serf_pollset_t*)user_baton; + pfd->client_data = serf_baton; + return apr_pollset_remove(s->pollset, pfd); +} + + +void serf_config_proxy(serf_context_t *ctx, + apr_sockaddr_t *address) +{ + ctx->proxy_address = address; +} + + +void serf_config_credentials_callback(serf_context_t *ctx, + serf_credentials_callback_t cred_cb) +{ + ctx->cred_cb = cred_cb; +} + + +void serf_config_authn_types(serf_context_t *ctx, + int authn_types) +{ + ctx->authn_types = authn_types; +} + + +serf_context_t *serf_context_create_ex( + void *user_baton, + serf_socket_add_t addf, + serf_socket_remove_t rmf, + apr_pool_t *pool) +{ + serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx)); + + ctx->pool = pool; + + if (user_baton != NULL) { + ctx->pollset_baton = user_baton; + ctx->pollset_add = addf; + ctx->pollset_rm = rmf; + } + else { + /* build the pollset with a (default) number of connections */ + serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps)); +#ifdef BROKEN_WSAPOLL + /* APR 1.4.x switched to using WSAPoll() on Win32, but it does not + * properly handle errors on a non-blocking sockets (such as + * connecting to a server where no listener is active). + * + * So, sadly, we must force using select() on Win32. + * + * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E + */ + (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0, + APR_POLLSET_SELECT); +#else + (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0); +#endif + ctx->pollset_baton = ps; + ctx->pollset_add = pollset_add; + ctx->pollset_rm = pollset_rm; + } + + /* default to a single connection since that is the typical case */ + ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *)); + + /* Initialize progress status */ + ctx->progress_read = 0; + ctx->progress_written = 0; + + ctx->authn_types = SERF_AUTHN_ALL; + + return ctx; +} + + +serf_context_t *serf_context_create(apr_pool_t *pool) +{ + return serf_context_create_ex(NULL, NULL, NULL, pool); +} + +apr_status_t serf_context_prerun(serf_context_t *ctx) +{ + apr_status_t status = APR_SUCCESS; + if ((status = serf__open_connections(ctx)) != APR_SUCCESS) + return status; + + if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS) + return status; + return status; +} + + +apr_status_t serf_event_trigger( + serf_context_t *s, + void *serf_baton, + const apr_pollfd_t *desc) +{ + apr_pollfd_t tdesc = { 0 }; + apr_status_t status = APR_SUCCESS; + serf_io_baton_t *io = serf_baton; + + if (io->type == SERF_IO_CONN) { + serf_connection_t *conn = io->u.conn; + serf_context_t *ctx = conn->ctx; + + /* If this connection has already failed, return the error again, and try + * to remove it from the pollset again + */ + if (conn->status) { + tdesc.desc_type = APR_POLL_SOCKET; + tdesc.desc.s = conn->skt; + tdesc.reqevents = conn->reqevents; + ctx->pollset_rm(ctx->pollset_baton, + &tdesc, conn); + return conn->status; + } + /* apr_pollset_poll() can return a conn multiple times... */ + if ((conn->seen_in_pollset & desc->rtnevents) != 0 || + (conn->seen_in_pollset & APR_POLLHUP) != 0) { + return APR_SUCCESS; + } + + conn->seen_in_pollset |= desc->rtnevents; + + if ((conn->status = serf__process_connection(conn, + desc->rtnevents)) != APR_SUCCESS) { + + /* it's possible that the connection was already reset and thus the + socket cleaned up. */ + if (conn->skt) { + tdesc.desc_type = APR_POLL_SOCKET; + tdesc.desc.s = conn->skt; + tdesc.reqevents = conn->reqevents; + ctx->pollset_rm(ctx->pollset_baton, + &tdesc, conn); + } + return conn->status; + } + } + else if (io->type == SERF_IO_LISTENER) { + serf_listener_t *l = io->u.listener; + + status = serf__process_listener(l); + + if (status) { + return status; + } + } + else if (io->type == SERF_IO_CLIENT) { + serf_incoming_t *c = io->u.client; + + status = serf__process_client(c, desc->rtnevents); + + if (status) { + return status; + } + } + return status; +} + + +apr_status_t serf_context_run( + serf_context_t *ctx, + apr_short_interval_time_t duration, + apr_pool_t *pool) +{ + apr_status_t status; + apr_int32_t num; + const apr_pollfd_t *desc; + serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton; + + if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) { + return status; + } + + if ((status = apr_pollset_poll(ps->pollset, duration, &num, + &desc)) != APR_SUCCESS) { + /* ### do we still need to dispatch stuff here? + ### look at the potential return codes. map to our defined + ### return values? ... + */ + return status; + } + + while (num--) { + serf_connection_t *conn = desc->client_data; + + status = serf_event_trigger(ctx, conn, desc); + if (status) { + return status; + } + + desc++; + } + + return APR_SUCCESS; +} + + +void serf_context_set_progress_cb( + serf_context_t *ctx, + const serf_progress_t progress_func, + void *progress_baton) +{ + ctx->progress_func = progress_func; + ctx->progress_baton = progress_baton; +} + + +serf_bucket_t *serf_context_bucket_socket_create( + serf_context_t *ctx, + apr_socket_t *skt, + serf_bucket_alloc_t *allocator) +{ + serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator); + + /* Use serf's default bytes read/written callback */ + serf_bucket_socket_set_read_progress_cb(bucket, + serf__context_progress_delta, + ctx); + + return bucket; +} + + +/* ### this really ought to go somewhere else, but... meh. */ +void serf_lib_version(int *major, int *minor, int *patch) +{ + *major = SERF_MAJOR_VERSION; + *minor = SERF_MINOR_VERSION; + *patch = SERF_PATCH_VERSION; +} + + +const char *serf_error_string(apr_status_t errcode) +{ + switch (errcode) + { + case SERF_ERROR_CLOSING: + return "The connection is closing"; + case SERF_ERROR_REQUEST_LOST: + return "A request has been lost"; + case SERF_ERROR_WAIT_CONN: + return "The connection is blocked, pending further action"; + case SERF_ERROR_DECOMPRESSION_FAILED: + return "An error occurred during decompression"; + case SERF_ERROR_BAD_HTTP_RESPONSE: + return "The server sent an improper HTTP response"; + case SERF_ERROR_AUTHN_FAILED: + return "An error occurred during authentication"; + case SERF_ERROR_AUTHN_NOT_SUPPORTED: + return "The requested authentication type(s) are not supported"; + case SERF_ERROR_AUTHN_MISSING_ATTRIBUTE: + return "An authentication attribute is missing"; + case SERF_ERROR_AUTHN_INITALIZATION_FAILED: + return "Initialization of an authentication type failed"; + + default: + return NULL; + } + + /* NOTREACHED */ +} |