context.c revision 251877
1251877Speter/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
2251877Speter *
3251877Speter * Licensed under the Apache License, Version 2.0 (the "License");
4251877Speter * you may not use this file except in compliance with the License.
5251877Speter * You may obtain a copy of the License at
6251877Speter *
7251877Speter *     http://www.apache.org/licenses/LICENSE-2.0
8251877Speter *
9251877Speter * Unless required by applicable law or agreed to in writing, software
10251877Speter * distributed under the License is distributed on an "AS IS" BASIS,
11251877Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12251877Speter * See the License for the specific language governing permissions and
13251877Speter * limitations under the License.
14251877Speter */
15251877Speter
16251877Speter#include <apr_pools.h>
17251877Speter#include <apr_poll.h>
18251877Speter#include <apr_version.h>
19251877Speter
20251877Speter#include "serf.h"
21251877Speter#include "serf_bucket_util.h"
22251877Speter
23251877Speter#include "serf_private.h"
24251877Speter
25251877Speter/* Older versions of APR do not have the APR_VERSION_AT_LEAST macro. Those
26251877Speter   implementations are safe.
27251877Speter
28251877Speter   If the macro *is* defined, and we're on WIN32, and APR is version 1.4.0,
29251877Speter   then we have a broken WSAPoll() implementation.
30251877Speter
31251877Speter   See serf_context_create_ex() below.  */
32251877Speter#if defined(APR_VERSION_AT_LEAST) && defined(WIN32)
33251877Speter#if APR_VERSION_AT_LEAST(1,4,0)
34251877Speter#define BROKEN_WSAPOLL
35251877Speter#endif
36251877Speter#endif
37251877Speter
38251877Speter
39251877Speter/**
40251877Speter * Callback function (implements serf_progress_t). Takes a number of bytes
41251877Speter * read @a read and bytes written @a written, adds those to the total for this
42251877Speter * context and notifies an interested party (if any).
43251877Speter */
44251877Spetervoid serf__context_progress_delta(
45251877Speter    void *progress_baton,
46251877Speter    apr_off_t read,
47251877Speter    apr_off_t written)
48251877Speter{
49251877Speter    serf_context_t *ctx = progress_baton;
50251877Speter
51251877Speter    ctx->progress_read += read;
52251877Speter    ctx->progress_written += written;
53251877Speter
54251877Speter    if (ctx->progress_func)
55251877Speter        ctx->progress_func(ctx->progress_baton,
56251877Speter                           ctx->progress_read,
57251877Speter                           ctx->progress_written);
58251877Speter}
59251877Speter
60251877Speter
61251877Speter/* Check for dirty connections and update their pollsets accordingly. */
62251877Speterstatic apr_status_t check_dirty_pollsets(serf_context_t *ctx)
63251877Speter{
64251877Speter    int i;
65251877Speter
66251877Speter    /* if we're not dirty, return now. */
67251877Speter    if (!ctx->dirty_pollset) {
68251877Speter        return APR_SUCCESS;
69251877Speter    }
70251877Speter
71251877Speter    for (i = ctx->conns->nelts; i--; ) {
72251877Speter        serf_connection_t *conn = GET_CONN(ctx, i);
73251877Speter        apr_status_t status;
74251877Speter
75251877Speter        /* if this connection isn't dirty, skip it. */
76251877Speter        if (!conn->dirty_conn) {
77251877Speter            continue;
78251877Speter        }
79251877Speter
80251877Speter        /* reset this connection's flag before we update. */
81251877Speter        conn->dirty_conn = 0;
82251877Speter
83251877Speter        if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS)
84251877Speter            return status;
85251877Speter    }
86251877Speter
87251877Speter    /* reset our context flag now */
88251877Speter    ctx->dirty_pollset = 0;
89251877Speter
90251877Speter    return APR_SUCCESS;
91251877Speter}
92251877Speter
93251877Speter
94251877Speterstatic apr_status_t pollset_add(void *user_baton,
95251877Speter                                apr_pollfd_t *pfd,
96251877Speter                                void *serf_baton)
97251877Speter{
98251877Speter    serf_pollset_t *s = (serf_pollset_t*)user_baton;
99251877Speter    pfd->client_data = serf_baton;
100251877Speter    return apr_pollset_add(s->pollset, pfd);
101251877Speter}
102251877Speter
103251877Speterstatic apr_status_t pollset_rm(void *user_baton,
104251877Speter                               apr_pollfd_t *pfd,
105251877Speter                               void *serf_baton)
106251877Speter{
107251877Speter    serf_pollset_t *s = (serf_pollset_t*)user_baton;
108251877Speter    pfd->client_data = serf_baton;
109251877Speter    return apr_pollset_remove(s->pollset, pfd);
110251877Speter}
111251877Speter
112251877Speter
113251877Spetervoid serf_config_proxy(serf_context_t *ctx,
114251877Speter                       apr_sockaddr_t *address)
115251877Speter{
116251877Speter    ctx->proxy_address = address;
117251877Speter}
118251877Speter
119251877Speter
120251877Spetervoid serf_config_credentials_callback(serf_context_t *ctx,
121251877Speter                                      serf_credentials_callback_t cred_cb)
122251877Speter{
123251877Speter    ctx->cred_cb = cred_cb;
124251877Speter}
125251877Speter
126251877Speter
127251877Spetervoid serf_config_authn_types(serf_context_t *ctx,
128251877Speter                             int authn_types)
129251877Speter{
130251877Speter    ctx->authn_types = authn_types;
131251877Speter}
132251877Speter
133251877Speter
134251877Speterserf_context_t *serf_context_create_ex(
135251877Speter    void *user_baton,
136251877Speter    serf_socket_add_t addf,
137251877Speter    serf_socket_remove_t rmf,
138251877Speter    apr_pool_t *pool)
139251877Speter{
140251877Speter    serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx));
141251877Speter
142251877Speter    ctx->pool = pool;
143251877Speter
144251877Speter    if (user_baton != NULL) {
145251877Speter        ctx->pollset_baton = user_baton;
146251877Speter        ctx->pollset_add = addf;
147251877Speter        ctx->pollset_rm = rmf;
148251877Speter    }
149251877Speter    else {
150251877Speter        /* build the pollset with a (default) number of connections */
151251877Speter        serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps));
152251877Speter
153251877Speter        /* ### TODO: As of APR 1.4.x apr_pollset_create_ex can return a status
154251877Speter           ### other than APR_SUCCESS, so we should handle it.
155251877Speter           ### Probably move creation of the pollset to later when we have
156251877Speter           ### the possibility of returning status to the caller.
157251877Speter         */
158251877Speter#ifdef BROKEN_WSAPOLL
159251877Speter        /* APR 1.4.x switched to using WSAPoll() on Win32, but it does not
160251877Speter         * properly handle errors on a non-blocking sockets (such as
161251877Speter         * connecting to a server where no listener is active).
162251877Speter         *
163251877Speter         * So, sadly, we must force using select() on Win32.
164251877Speter         *
165251877Speter         * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E
166251877Speter         */
167251877Speter        (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0,
168251877Speter                                     APR_POLLSET_SELECT);
169251877Speter#else
170251877Speter        (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0);
171251877Speter#endif
172251877Speter        ctx->pollset_baton = ps;
173251877Speter        ctx->pollset_add = pollset_add;
174251877Speter        ctx->pollset_rm = pollset_rm;
175251877Speter    }
176251877Speter
177251877Speter    /* default to a single connection since that is the typical case */
178251877Speter    ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *));
179251877Speter
180251877Speter    /* Initialize progress status */
181251877Speter    ctx->progress_read = 0;
182251877Speter    ctx->progress_written = 0;
183251877Speter
184251877Speter    ctx->authn_types = SERF_AUTHN_ALL;
185251877Speter
186251877Speter    return ctx;
187251877Speter}
188251877Speter
189251877Speter
190251877Speterserf_context_t *serf_context_create(apr_pool_t *pool)
191251877Speter{
192251877Speter    return serf_context_create_ex(NULL, NULL, NULL, pool);
193251877Speter}
194251877Speter
195251877Speterapr_status_t serf_context_prerun(serf_context_t *ctx)
196251877Speter{
197251877Speter    apr_status_t status = APR_SUCCESS;
198251877Speter    if ((status = serf__open_connections(ctx)) != APR_SUCCESS)
199251877Speter        return status;
200251877Speter
201251877Speter    if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS)
202251877Speter        return status;
203251877Speter    return status;
204251877Speter}
205251877Speter
206251877Speter
207251877Speterapr_status_t serf_event_trigger(
208251877Speter    serf_context_t *s,
209251877Speter    void *serf_baton,
210251877Speter    const apr_pollfd_t *desc)
211251877Speter{
212251877Speter    apr_pollfd_t tdesc = { 0 };
213251877Speter    apr_status_t status = APR_SUCCESS;
214251877Speter    serf_io_baton_t *io = serf_baton;
215251877Speter
216251877Speter    if (io->type == SERF_IO_CONN) {
217251877Speter        serf_connection_t *conn = io->u.conn;
218251877Speter        serf_context_t *ctx = conn->ctx;
219251877Speter
220251877Speter        /* If this connection has already failed, return the error again, and try
221251877Speter         * to remove it from the pollset again
222251877Speter         */
223251877Speter        if (conn->status) {
224251877Speter            tdesc.desc_type = APR_POLL_SOCKET;
225251877Speter            tdesc.desc.s = conn->skt;
226251877Speter            tdesc.reqevents = conn->reqevents;
227251877Speter            ctx->pollset_rm(ctx->pollset_baton,
228251877Speter                            &tdesc, conn);
229251877Speter            return conn->status;
230251877Speter        }
231251877Speter        /* apr_pollset_poll() can return a conn multiple times... */
232251877Speter        if ((conn->seen_in_pollset & desc->rtnevents) != 0 ||
233251877Speter            (conn->seen_in_pollset & APR_POLLHUP) != 0) {
234251877Speter            return APR_SUCCESS;
235251877Speter        }
236251877Speter
237251877Speter        conn->seen_in_pollset |= desc->rtnevents;
238251877Speter
239251877Speter        if ((conn->status = serf__process_connection(conn,
240251877Speter                                         desc->rtnevents)) != APR_SUCCESS) {
241251877Speter
242251877Speter            /* it's possible that the connection was already reset and thus the
243251877Speter               socket cleaned up. */
244251877Speter            if (conn->skt) {
245251877Speter                tdesc.desc_type = APR_POLL_SOCKET;
246251877Speter                tdesc.desc.s = conn->skt;
247251877Speter                tdesc.reqevents = conn->reqevents;
248251877Speter                ctx->pollset_rm(ctx->pollset_baton,
249251877Speter                                &tdesc, conn);
250251877Speter            }
251251877Speter            return conn->status;
252251877Speter        }
253251877Speter    }
254251877Speter    else if (io->type == SERF_IO_LISTENER) {
255251877Speter        serf_listener_t *l = io->u.listener;
256251877Speter
257251877Speter        status = serf__process_listener(l);
258251877Speter
259251877Speter        if (status) {
260251877Speter            return status;
261251877Speter        }
262251877Speter    }
263251877Speter    else if (io->type == SERF_IO_CLIENT) {
264251877Speter        serf_incoming_t *c = io->u.client;
265251877Speter
266251877Speter        status = serf__process_client(c, desc->rtnevents);
267251877Speter
268251877Speter        if (status) {
269251877Speter            return status;
270251877Speter        }
271251877Speter    }
272251877Speter    return status;
273251877Speter}
274251877Speter
275251877Speter
276251877Speterapr_status_t serf_context_run(
277251877Speter    serf_context_t *ctx,
278251877Speter    apr_short_interval_time_t duration,
279251877Speter    apr_pool_t *pool)
280251877Speter{
281251877Speter    apr_status_t status;
282251877Speter    apr_int32_t num;
283251877Speter    const apr_pollfd_t *desc;
284251877Speter    serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton;
285251877Speter
286251877Speter    if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) {
287251877Speter        return status;
288251877Speter    }
289251877Speter
290251877Speter    if ((status = apr_pollset_poll(ps->pollset, duration, &num,
291251877Speter                                   &desc)) != APR_SUCCESS) {
292251877Speter        /* EINTR indicates a handled signal happened during the poll call,
293251877Speter           ignore, the application can safely retry. */
294251877Speter        if (APR_STATUS_IS_EINTR(status))
295251877Speter            return APR_SUCCESS;
296251877Speter
297251877Speter        /* ### do we still need to dispatch stuff here?
298251877Speter           ### look at the potential return codes. map to our defined
299251877Speter           ### return values? ...
300251877Speter        */
301251877Speter        return status;
302251877Speter    }
303251877Speter
304251877Speter    while (num--) {
305251877Speter        serf_connection_t *conn = desc->client_data;
306251877Speter
307251877Speter        status = serf_event_trigger(ctx, conn, desc);
308251877Speter        if (status) {
309251877Speter            return status;
310251877Speter        }
311251877Speter
312251877Speter        desc++;
313251877Speter    }
314251877Speter
315251877Speter    return APR_SUCCESS;
316251877Speter}
317251877Speter
318251877Speter
319251877Spetervoid serf_context_set_progress_cb(
320251877Speter    serf_context_t *ctx,
321251877Speter    const serf_progress_t progress_func,
322251877Speter    void *progress_baton)
323251877Speter{
324251877Speter    ctx->progress_func = progress_func;
325251877Speter    ctx->progress_baton = progress_baton;
326251877Speter}
327251877Speter
328251877Speter
329251877Speterserf_bucket_t *serf_context_bucket_socket_create(
330251877Speter    serf_context_t *ctx,
331251877Speter    apr_socket_t *skt,
332251877Speter    serf_bucket_alloc_t *allocator)
333251877Speter{
334251877Speter    serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator);
335251877Speter
336251877Speter    /* Use serf's default bytes read/written callback */
337251877Speter    serf_bucket_socket_set_read_progress_cb(bucket,
338251877Speter                                            serf__context_progress_delta,
339251877Speter                                            ctx);
340251877Speter
341251877Speter    return bucket;
342251877Speter}
343251877Speter
344251877Speter
345251877Speter/* ### this really ought to go somewhere else, but... meh.  */
346251877Spetervoid serf_lib_version(int *major, int *minor, int *patch)
347251877Speter{
348251877Speter    *major = SERF_MAJOR_VERSION;
349251877Speter    *minor = SERF_MINOR_VERSION;
350251877Speter    *patch = SERF_PATCH_VERSION;
351251877Speter}
352251877Speter
353251877Speter
354251877Speterconst char *serf_error_string(apr_status_t errcode)
355251877Speter{
356251877Speter    switch (errcode)
357251877Speter    {
358251877Speter    case SERF_ERROR_CLOSING:
359251877Speter        return "The connection is closing";
360251877Speter    case SERF_ERROR_REQUEST_LOST:
361251877Speter        return "A request has been lost";
362251877Speter    case SERF_ERROR_WAIT_CONN:
363251877Speter        return "The connection is blocked, pending further action";
364251877Speter    case SERF_ERROR_DECOMPRESSION_FAILED:
365251877Speter        return "An error occurred during decompression";
366251877Speter    case SERF_ERROR_BAD_HTTP_RESPONSE:
367251877Speter        return "The server sent an improper HTTP response";
368251877Speter    case SERF_ERROR_TRUNCATED_HTTP_RESPONSE:
369251877Speter        return "The server sent a truncated HTTP response body.";
370251877Speter    case SERF_ERROR_ABORTED_CONNECTION:
371251877Speter        return "The server unexpectedly closed the connection.";
372251877Speter    case SERF_ERROR_SSL_COMM_FAILED:
373251877Speter        return "An error occurred during SSL communication";
374251877Speter    case SERF_ERROR_SSL_CERT_FAILED:
375251877Speter        return "An SSL certificate related error occurred ";
376251877Speter    case SERF_ERROR_AUTHN_FAILED:
377251877Speter        return "An error occurred during authentication";
378251877Speter    case SERF_ERROR_AUTHN_NOT_SUPPORTED:
379251877Speter        return "The requested authentication type(s) are not supported";
380251877Speter    case SERF_ERROR_AUTHN_MISSING_ATTRIBUTE:
381251877Speter        return "An authentication attribute is missing";
382251877Speter    case SERF_ERROR_AUTHN_INITALIZATION_FAILED:
383251877Speter        return "Initialization of an authentication type failed";
384251877Speter    case SERF_ERROR_SSLTUNNEL_SETUP_FAILED:
385251877Speter        return "The proxy server returned an error while setting up the "\
386251877Speter               "SSL tunnel.";
387251877Speter    default:
388251877Speter        return NULL;
389251877Speter    }
390251877Speter
391251877Speter    /* NOTREACHED  */
392251877Speter}
393