context.c revision 262324
119370Spst/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
219370Spst *
398944Sobrien * Licensed under the Apache License, Version 2.0 (the "License");
4130803Smarcel * you may not use this file except in compliance with the License.
5130803Smarcel * You may obtain a copy of the License at
619370Spst *
798944Sobrien *     http://www.apache.org/licenses/LICENSE-2.0
819370Spst *
998944Sobrien * Unless required by applicable law or agreed to in writing, software
1098944Sobrien * distributed under the License is distributed on an "AS IS" BASIS,
1198944Sobrien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1298944Sobrien * See the License for the specific language governing permissions and
1319370Spst * limitations under the License.
1498944Sobrien */
1598944Sobrien
1698944Sobrien#include <apr_pools.h>
1798944Sobrien#include <apr_poll.h>
1819370Spst#include <apr_version.h>
1998944Sobrien
2098944Sobrien#include "serf.h"
2198944Sobrien#include "serf_bucket_util.h"
2298944Sobrien
2398944Sobrien#include "serf_private.h"
2446283Sdfr
2519370Spst/**
2619370Spst * Callback function (implements serf_progress_t). Takes a number of bytes
2719370Spst * read @a read and bytes written @a written, adds those to the total for this
2819370Spst * context and notifies an interested party (if any).
2919370Spst */
3019370Spstvoid serf__context_progress_delta(
3119370Spst    void *progress_baton,
3219370Spst    apr_off_t read,
3319370Spst    apr_off_t written)
3419370Spst{
3519370Spst    serf_context_t *ctx = progress_baton;
36130803Smarcel
3719370Spst    ctx->progress_read += read;
3819370Spst    ctx->progress_written += written;
3919370Spst
4019370Spst    if (ctx->progress_func)
4198944Sobrien        ctx->progress_func(ctx->progress_baton,
42130803Smarcel                           ctx->progress_read,
43130803Smarcel                           ctx->progress_written);
44130803Smarcel}
45130803Smarcel
46130803Smarcel
47130803Smarcel/* Check for dirty connections and update their pollsets accordingly. */
4819370Spststatic apr_status_t check_dirty_pollsets(serf_context_t *ctx)
4946283Sdfr{
5046283Sdfr    int i;
5198944Sobrien
5246283Sdfr    /* if we're not dirty, return now. */
5398944Sobrien    if (!ctx->dirty_pollset) {
5446283Sdfr        return APR_SUCCESS;
5598944Sobrien    }
5646283Sdfr
5798944Sobrien    for (i = ctx->conns->nelts; i--; ) {
5846283Sdfr        serf_connection_t *conn = GET_CONN(ctx, i);
5946283Sdfr        apr_status_t status;
6046283Sdfr
6198944Sobrien        /* if this connection isn't dirty, skip it. */
6219370Spst        if (!conn->dirty_conn) {
6398944Sobrien            continue;
6446283Sdfr        }
6598944Sobrien
6619370Spst        /* reset this connection's flag before we update. */
6798944Sobrien        conn->dirty_conn = 0;
6819370Spst
6998944Sobrien        if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS)
7046283Sdfr            return status;
7198944Sobrien    }
7219370Spst
7398944Sobrien    /* reset our context flag now */
7419370Spst    ctx->dirty_pollset = 0;
7598944Sobrien
7619370Spst    return APR_SUCCESS;
7798944Sobrien}
7819370Spst
7998944Sobrien
8019370Spststatic apr_status_t pollset_add(void *user_baton,
8198944Sobrien                                apr_pollfd_t *pfd,
8219370Spst                                void *serf_baton)
8398944Sobrien{
8498944Sobrien    serf_pollset_t *s = (serf_pollset_t*)user_baton;
8519370Spst    pfd->client_data = serf_baton;
8698944Sobrien    return apr_pollset_add(s->pollset, pfd);
8798944Sobrien}
8819370Spst
8998944Sobrienstatic apr_status_t pollset_rm(void *user_baton,
9098944Sobrien                               apr_pollfd_t *pfd,
9119370Spst                               void *serf_baton)
9298944Sobrien{
9398944Sobrien    serf_pollset_t *s = (serf_pollset_t*)user_baton;
9498944Sobrien    pfd->client_data = serf_baton;
9598944Sobrien    return apr_pollset_remove(s->pollset, pfd);
9646283Sdfr}
9798944Sobrien
9898944Sobrien
9998944Sobrienvoid serf_config_proxy(serf_context_t *ctx,
10098944Sobrien                       apr_sockaddr_t *address)
10198944Sobrien{
10219370Spst    ctx->proxy_address = address;
10398944Sobrien}
10498944Sobrien
10598944Sobrien
10698944Sobrienvoid serf_config_credentials_callback(serf_context_t *ctx,
10798944Sobrien                                      serf_credentials_callback_t cred_cb)
10898944Sobrien{
10919370Spst    ctx->cred_cb = cred_cb;
11019370Spst}
11119370Spst
11219370Spst
11319370Spstvoid serf_config_authn_types(serf_context_t *ctx,
11419370Spst                             int authn_types)
11519370Spst{
11619370Spst    ctx->authn_types = authn_types;
11719370Spst}
11819370Spst
11919370Spst
12098944Sobrienserf_context_t *serf_context_create_ex(
12198944Sobrien    void *user_baton,
12298944Sobrien    serf_socket_add_t addf,
12398944Sobrien    serf_socket_remove_t rmf,
12498944Sobrien    apr_pool_t *pool)
12598944Sobrien{
12698944Sobrien    serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx));
12719370Spst
12846283Sdfr    ctx->pool = pool;
12946283Sdfr
13098944Sobrien    if (user_baton != NULL) {
13146283Sdfr        ctx->pollset_baton = user_baton;
13298944Sobrien        ctx->pollset_add = addf;
13346283Sdfr        ctx->pollset_rm = rmf;
13498944Sobrien    }
13546283Sdfr    else {
136130803Smarcel        /* build the pollset with a (default) number of connections */
13746283Sdfr        serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps));
13846283Sdfr
13946283Sdfr        /* ### TODO: As of APR 1.4.x apr_pollset_create_ex can return a status
140130803Smarcel           ### other than APR_SUCCESS, so we should handle it.
14119370Spst           ### Probably move creation of the pollset to later when we have
14219370Spst           ### the possibility of returning status to the caller.
14319370Spst         */
14419370Spst#ifdef BROKEN_WSAPOLL
14519370Spst        /* APR 1.4.x switched to using WSAPoll() on Win32, but it does not
14619370Spst         * properly handle errors on a non-blocking sockets (such as
14719370Spst         * connecting to a server where no listener is active).
14819370Spst         *
14919370Spst         * So, sadly, we must force using select() on Win32.
150130803Smarcel         *
151130803Smarcel         * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E
15246283Sdfr         */
15346283Sdfr        (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0,
15446283Sdfr                                     APR_POLLSET_SELECT);
15546283Sdfr#else
15646283Sdfr        (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0);
15746283Sdfr#endif
15846283Sdfr        ctx->pollset_baton = ps;
15946283Sdfr        ctx->pollset_add = pollset_add;
160130803Smarcel        ctx->pollset_rm = pollset_rm;
161130803Smarcel    }
162130803Smarcel
163130803Smarcel    /* default to a single connection since that is the typical case */
164130803Smarcel    ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *));
165130803Smarcel
166130803Smarcel    /* Initialize progress status */
167130803Smarcel    ctx->progress_read = 0;
168130803Smarcel    ctx->progress_written = 0;
169130803Smarcel
170130803Smarcel    ctx->authn_types = SERF_AUTHN_ALL;
171130803Smarcel    ctx->server_authn_info = apr_hash_make(pool);
172130803Smarcel
173130803Smarcel    return ctx;
174130803Smarcel}
175130803Smarcel
176130803Smarcel
177130803Smarcelserf_context_t *serf_context_create(apr_pool_t *pool)
178130803Smarcel{
179130803Smarcel    return serf_context_create_ex(NULL, NULL, NULL, pool);
180130803Smarcel}
181130803Smarcel
182130803Smarcelapr_status_t serf_context_prerun(serf_context_t *ctx)
183130803Smarcel{
184130803Smarcel    apr_status_t status = APR_SUCCESS;
185130803Smarcel    if ((status = serf__open_connections(ctx)) != APR_SUCCESS)
186130803Smarcel        return status;
187130803Smarcel
188130803Smarcel    if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS)
189130803Smarcel        return status;
190130803Smarcel    return status;
191130803Smarcel}
192130803Smarcel
193130803Smarcel
194130803Smarcelapr_status_t serf_event_trigger(
195130803Smarcel    serf_context_t *s,
196130803Smarcel    void *serf_baton,
197130803Smarcel    const apr_pollfd_t *desc)
198130803Smarcel{
19946283Sdfr    apr_pollfd_t tdesc = { 0 };
20046283Sdfr    apr_status_t status = APR_SUCCESS;
201130803Smarcel    serf_io_baton_t *io = serf_baton;
202130803Smarcel
203130803Smarcel    if (io->type == SERF_IO_CONN) {
20446283Sdfr        serf_connection_t *conn = io->u.conn;
205130803Smarcel        serf_context_t *ctx = conn->ctx;
206130803Smarcel
207130803Smarcel        /* If this connection has already failed, return the error again, and try
20846283Sdfr         * to remove it from the pollset again
209130803Smarcel         */
210130803Smarcel        if (conn->status) {
211130803Smarcel            tdesc.desc_type = APR_POLL_SOCKET;
21246283Sdfr            tdesc.desc.s = conn->skt;
213130803Smarcel            tdesc.reqevents = conn->reqevents;
214130803Smarcel            ctx->pollset_rm(ctx->pollset_baton,
215130803Smarcel                            &tdesc, conn);
216130803Smarcel            return conn->status;
217130803Smarcel        }
218130803Smarcel        /* apr_pollset_poll() can return a conn multiple times... */
219130803Smarcel        if ((conn->seen_in_pollset & desc->rtnevents) != 0 ||
220130803Smarcel            (conn->seen_in_pollset & APR_POLLHUP) != 0) {
221130803Smarcel            return APR_SUCCESS;
222130803Smarcel        }
223130803Smarcel
224130803Smarcel        conn->seen_in_pollset |= desc->rtnevents;
225130803Smarcel
226130803Smarcel        if ((conn->status = serf__process_connection(conn,
22746283Sdfr                                         desc->rtnevents)) != APR_SUCCESS) {
228130803Smarcel
229130803Smarcel            /* it's possible that the connection was already reset and thus the
23046283Sdfr               socket cleaned up. */
231130803Smarcel            if (conn->skt) {
232130803Smarcel                tdesc.desc_type = APR_POLL_SOCKET;
233130803Smarcel                tdesc.desc.s = conn->skt;
23446283Sdfr                tdesc.reqevents = conn->reqevents;
235130803Smarcel                ctx->pollset_rm(ctx->pollset_baton,
236130803Smarcel                                &tdesc, conn);
237130803Smarcel            }
23846283Sdfr            return conn->status;
239130803Smarcel        }
240130803Smarcel    }
24146283Sdfr    else if (io->type == SERF_IO_LISTENER) {
242130803Smarcel        serf_listener_t *l = io->u.listener;
243130803Smarcel
244130803Smarcel        status = serf__process_listener(l);
245130803Smarcel
246130803Smarcel        if (status) {
247130803Smarcel            return status;
248130803Smarcel        }
24946283Sdfr    }
250130803Smarcel    else if (io->type == SERF_IO_CLIENT) {
251130803Smarcel        serf_incoming_t *c = io->u.client;
252130803Smarcel
253130803Smarcel        status = serf__process_client(c, desc->rtnevents);
254130803Smarcel
25519370Spst        if (status) {
256130803Smarcel            return status;
257130803Smarcel        }
258130803Smarcel    }
259130803Smarcel    return status;
26019370Spst}
261130803Smarcel
262130803Smarcel
263130803Smarcelapr_status_t serf_context_run(
26419370Spst    serf_context_t *ctx,
265130803Smarcel    apr_short_interval_time_t duration,
266130803Smarcel    apr_pool_t *pool)
267130803Smarcel{
268130803Smarcel    apr_status_t status;
269130803Smarcel    apr_int32_t num;
270130803Smarcel    const apr_pollfd_t *desc;
271130803Smarcel    serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton;
272130803Smarcel
27346283Sdfr    if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) {
274130803Smarcel        return status;
275130803Smarcel    }
276130803Smarcel
277130803Smarcel    if ((status = apr_pollset_poll(ps->pollset, duration, &num,
27846283Sdfr                                   &desc)) != APR_SUCCESS) {
279130803Smarcel        /* EINTR indicates a handled signal happened during the poll call,
280130803Smarcel           ignore, the application can safely retry. */
281130803Smarcel        if (APR_STATUS_IS_EINTR(status))
282130803Smarcel            return APR_SUCCESS;
283130803Smarcel
284130803Smarcel        /* ### do we still need to dispatch stuff here?
285130803Smarcel           ### look at the potential return codes. map to our defined
286130803Smarcel           ### return values? ...
287130803Smarcel        */
288130803Smarcel
289130803Smarcel        /* Use the strict documented error for poll timeouts, to allow proper
290130803Smarcel           handling of the other timeout types when returned from
29146283Sdfr           serf_event_trigger */
292130803Smarcel        if (APR_STATUS_IS_TIMEUP(status))
293130803Smarcel            return APR_TIMEUP; /* Return the documented error */
294130803Smarcel        return status;
295130803Smarcel    }
296130803Smarcel
297130803Smarcel    while (num--) {
298130803Smarcel        serf_connection_t *conn = desc->client_data;
299130803Smarcel
300130803Smarcel        status = serf_event_trigger(ctx, conn, desc);
301130803Smarcel        if (status) {
302130803Smarcel            return status;
303130803Smarcel        }
30446283Sdfr
305130803Smarcel        desc++;
306130803Smarcel    }
307130803Smarcel
308130803Smarcel    return APR_SUCCESS;
309130803Smarcel}
310130803Smarcel
31146283Sdfr
312130803Smarcelvoid serf_context_set_progress_cb(
313130803Smarcel    serf_context_t *ctx,
314130803Smarcel    const serf_progress_t progress_func,
315130803Smarcel    void *progress_baton)
316130803Smarcel{
317130803Smarcel    ctx->progress_func = progress_func;
31819370Spst    ctx->progress_baton = progress_baton;
319130803Smarcel}
320130803Smarcel
321130803Smarcel
322130803Smarcelserf_bucket_t *serf_context_bucket_socket_create(
323130803Smarcel    serf_context_t *ctx,
32419370Spst    apr_socket_t *skt,
325130803Smarcel    serf_bucket_alloc_t *allocator)
326130803Smarcel{
327130803Smarcel    serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator);
328130803Smarcel
329130803Smarcel    /* Use serf's default bytes read/written callback */
330130803Smarcel    serf_bucket_socket_set_read_progress_cb(bucket,
331130803Smarcel                                            serf__context_progress_delta,
332130803Smarcel                                            ctx);
333130803Smarcel
334130803Smarcel    return bucket;
335130803Smarcel}
336130803Smarcel
337130803Smarcel
338130803Smarcel/* ### this really ought to go somewhere else, but... meh.  */
339130803Smarcelvoid serf_lib_version(int *major, int *minor, int *patch)
340130803Smarcel{
341130803Smarcel    *major = SERF_MAJOR_VERSION;
342130803Smarcel    *minor = SERF_MINOR_VERSION;
343130803Smarcel    *patch = SERF_PATCH_VERSION;
344130803Smarcel}
345130803Smarcel
346130803Smarcel
347130803Smarcelconst char *serf_error_string(apr_status_t errcode)
348130803Smarcel{
349130803Smarcel    switch (errcode)
350130803Smarcel    {
351130803Smarcel    case SERF_ERROR_CLOSING:
352130803Smarcel        return "The connection is closing";
353130803Smarcel    case SERF_ERROR_REQUEST_LOST:
354130803Smarcel        return "A request has been lost";
355130803Smarcel    case SERF_ERROR_WAIT_CONN:
356130803Smarcel        return "The connection is blocked, pending further action";
357242936Semaste    case SERF_ERROR_DECOMPRESSION_FAILED:
358130803Smarcel        return "An error occurred during decompression";
359130803Smarcel    case SERF_ERROR_BAD_HTTP_RESPONSE:
360130803Smarcel        return "The server sent an improper HTTP response";
361130803Smarcel    case SERF_ERROR_TRUNCATED_HTTP_RESPONSE:
362130803Smarcel        return "The server sent a truncated HTTP response body.";
363130803Smarcel    case SERF_ERROR_ABORTED_CONNECTION:
364130803Smarcel        return "The server unexpectedly closed the connection.";
365130803Smarcel    case SERF_ERROR_SSL_COMM_FAILED:
366130803Smarcel        return "An error occurred during SSL communication";
367130803Smarcel    case SERF_ERROR_SSL_CERT_FAILED:
368130803Smarcel        return "An SSL certificate related error occurred ";
369130803Smarcel    case SERF_ERROR_AUTHN_FAILED:
370130803Smarcel        return "An error occurred during authentication";
371130803Smarcel    case SERF_ERROR_AUTHN_NOT_SUPPORTED:
372130803Smarcel        return "The requested authentication type(s) are not supported";
373130803Smarcel    case SERF_ERROR_AUTHN_MISSING_ATTRIBUTE:
374130803Smarcel        return "An authentication attribute is missing";
375130803Smarcel    case SERF_ERROR_AUTHN_INITALIZATION_FAILED:
376130803Smarcel        return "Initialization of an authentication type failed";
377130803Smarcel    case SERF_ERROR_SSLTUNNEL_SETUP_FAILED:
378130803Smarcel        return "The proxy server returned an error while setting up the "
379130803Smarcel               "SSL tunnel.";
380130803Smarcel    default:
381130803Smarcel        return NULL;
382130803Smarcel    }
383130803Smarcel
384130803Smarcel    /* NOTREACHED  */
385130803Smarcel}
386130803Smarcel