context.c revision 251877
1/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <apr_pools.h>
17#include <apr_poll.h>
18#include <apr_version.h>
19
20#include "serf.h"
21#include "serf_bucket_util.h"
22
23#include "serf_private.h"
24
25/* Older versions of APR do not have the APR_VERSION_AT_LEAST macro. Those
26   implementations are safe.
27
28   If the macro *is* defined, and we're on WIN32, and APR is version 1.4.0,
29   then we have a broken WSAPoll() implementation.
30
31   See serf_context_create_ex() below.  */
32#if defined(APR_VERSION_AT_LEAST) && defined(WIN32)
33#if APR_VERSION_AT_LEAST(1,4,0)
34#define BROKEN_WSAPOLL
35#endif
36#endif
37
38
39/**
40 * Callback function (implements serf_progress_t). Takes a number of bytes
41 * read @a read and bytes written @a written, adds those to the total for this
42 * context and notifies an interested party (if any).
43 */
44void serf__context_progress_delta(
45    void *progress_baton,
46    apr_off_t read,
47    apr_off_t written)
48{
49    serf_context_t *ctx = progress_baton;
50
51    ctx->progress_read += read;
52    ctx->progress_written += written;
53
54    if (ctx->progress_func)
55        ctx->progress_func(ctx->progress_baton,
56                           ctx->progress_read,
57                           ctx->progress_written);
58}
59
60
61/* Check for dirty connections and update their pollsets accordingly. */
62static apr_status_t check_dirty_pollsets(serf_context_t *ctx)
63{
64    int i;
65
66    /* if we're not dirty, return now. */
67    if (!ctx->dirty_pollset) {
68        return APR_SUCCESS;
69    }
70
71    for (i = ctx->conns->nelts; i--; ) {
72        serf_connection_t *conn = GET_CONN(ctx, i);
73        apr_status_t status;
74
75        /* if this connection isn't dirty, skip it. */
76        if (!conn->dirty_conn) {
77            continue;
78        }
79
80        /* reset this connection's flag before we update. */
81        conn->dirty_conn = 0;
82
83        if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS)
84            return status;
85    }
86
87    /* reset our context flag now */
88    ctx->dirty_pollset = 0;
89
90    return APR_SUCCESS;
91}
92
93
94static apr_status_t pollset_add(void *user_baton,
95                                apr_pollfd_t *pfd,
96                                void *serf_baton)
97{
98    serf_pollset_t *s = (serf_pollset_t*)user_baton;
99    pfd->client_data = serf_baton;
100    return apr_pollset_add(s->pollset, pfd);
101}
102
103static apr_status_t pollset_rm(void *user_baton,
104                               apr_pollfd_t *pfd,
105                               void *serf_baton)
106{
107    serf_pollset_t *s = (serf_pollset_t*)user_baton;
108    pfd->client_data = serf_baton;
109    return apr_pollset_remove(s->pollset, pfd);
110}
111
112
113void serf_config_proxy(serf_context_t *ctx,
114                       apr_sockaddr_t *address)
115{
116    ctx->proxy_address = address;
117}
118
119
120void serf_config_credentials_callback(serf_context_t *ctx,
121                                      serf_credentials_callback_t cred_cb)
122{
123    ctx->cred_cb = cred_cb;
124}
125
126
127void serf_config_authn_types(serf_context_t *ctx,
128                             int authn_types)
129{
130    ctx->authn_types = authn_types;
131}
132
133
134serf_context_t *serf_context_create_ex(
135    void *user_baton,
136    serf_socket_add_t addf,
137    serf_socket_remove_t rmf,
138    apr_pool_t *pool)
139{
140    serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx));
141
142    ctx->pool = pool;
143
144    if (user_baton != NULL) {
145        ctx->pollset_baton = user_baton;
146        ctx->pollset_add = addf;
147        ctx->pollset_rm = rmf;
148    }
149    else {
150        /* build the pollset with a (default) number of connections */
151        serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps));
152
153        /* ### TODO: As of APR 1.4.x apr_pollset_create_ex can return a status
154           ### other than APR_SUCCESS, so we should handle it.
155           ### Probably move creation of the pollset to later when we have
156           ### the possibility of returning status to the caller.
157         */
158#ifdef BROKEN_WSAPOLL
159        /* APR 1.4.x switched to using WSAPoll() on Win32, but it does not
160         * properly handle errors on a non-blocking sockets (such as
161         * connecting to a server where no listener is active).
162         *
163         * So, sadly, we must force using select() on Win32.
164         *
165         * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E
166         */
167        (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0,
168                                     APR_POLLSET_SELECT);
169#else
170        (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0);
171#endif
172        ctx->pollset_baton = ps;
173        ctx->pollset_add = pollset_add;
174        ctx->pollset_rm = pollset_rm;
175    }
176
177    /* default to a single connection since that is the typical case */
178    ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *));
179
180    /* Initialize progress status */
181    ctx->progress_read = 0;
182    ctx->progress_written = 0;
183
184    ctx->authn_types = SERF_AUTHN_ALL;
185
186    return ctx;
187}
188
189
190serf_context_t *serf_context_create(apr_pool_t *pool)
191{
192    return serf_context_create_ex(NULL, NULL, NULL, pool);
193}
194
195apr_status_t serf_context_prerun(serf_context_t *ctx)
196{
197    apr_status_t status = APR_SUCCESS;
198    if ((status = serf__open_connections(ctx)) != APR_SUCCESS)
199        return status;
200
201    if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS)
202        return status;
203    return status;
204}
205
206
207apr_status_t serf_event_trigger(
208    serf_context_t *s,
209    void *serf_baton,
210    const apr_pollfd_t *desc)
211{
212    apr_pollfd_t tdesc = { 0 };
213    apr_status_t status = APR_SUCCESS;
214    serf_io_baton_t *io = serf_baton;
215
216    if (io->type == SERF_IO_CONN) {
217        serf_connection_t *conn = io->u.conn;
218        serf_context_t *ctx = conn->ctx;
219
220        /* If this connection has already failed, return the error again, and try
221         * to remove it from the pollset again
222         */
223        if (conn->status) {
224            tdesc.desc_type = APR_POLL_SOCKET;
225            tdesc.desc.s = conn->skt;
226            tdesc.reqevents = conn->reqevents;
227            ctx->pollset_rm(ctx->pollset_baton,
228                            &tdesc, conn);
229            return conn->status;
230        }
231        /* apr_pollset_poll() can return a conn multiple times... */
232        if ((conn->seen_in_pollset & desc->rtnevents) != 0 ||
233            (conn->seen_in_pollset & APR_POLLHUP) != 0) {
234            return APR_SUCCESS;
235        }
236
237        conn->seen_in_pollset |= desc->rtnevents;
238
239        if ((conn->status = serf__process_connection(conn,
240                                         desc->rtnevents)) != APR_SUCCESS) {
241
242            /* it's possible that the connection was already reset and thus the
243               socket cleaned up. */
244            if (conn->skt) {
245                tdesc.desc_type = APR_POLL_SOCKET;
246                tdesc.desc.s = conn->skt;
247                tdesc.reqevents = conn->reqevents;
248                ctx->pollset_rm(ctx->pollset_baton,
249                                &tdesc, conn);
250            }
251            return conn->status;
252        }
253    }
254    else if (io->type == SERF_IO_LISTENER) {
255        serf_listener_t *l = io->u.listener;
256
257        status = serf__process_listener(l);
258
259        if (status) {
260            return status;
261        }
262    }
263    else if (io->type == SERF_IO_CLIENT) {
264        serf_incoming_t *c = io->u.client;
265
266        status = serf__process_client(c, desc->rtnevents);
267
268        if (status) {
269            return status;
270        }
271    }
272    return status;
273}
274
275
276apr_status_t serf_context_run(
277    serf_context_t *ctx,
278    apr_short_interval_time_t duration,
279    apr_pool_t *pool)
280{
281    apr_status_t status;
282    apr_int32_t num;
283    const apr_pollfd_t *desc;
284    serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton;
285
286    if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) {
287        return status;
288    }
289
290    if ((status = apr_pollset_poll(ps->pollset, duration, &num,
291                                   &desc)) != APR_SUCCESS) {
292        /* EINTR indicates a handled signal happened during the poll call,
293           ignore, the application can safely retry. */
294        if (APR_STATUS_IS_EINTR(status))
295            return APR_SUCCESS;
296
297        /* ### do we still need to dispatch stuff here?
298           ### look at the potential return codes. map to our defined
299           ### return values? ...
300        */
301        return status;
302    }
303
304    while (num--) {
305        serf_connection_t *conn = desc->client_data;
306
307        status = serf_event_trigger(ctx, conn, desc);
308        if (status) {
309            return status;
310        }
311
312        desc++;
313    }
314
315    return APR_SUCCESS;
316}
317
318
319void serf_context_set_progress_cb(
320    serf_context_t *ctx,
321    const serf_progress_t progress_func,
322    void *progress_baton)
323{
324    ctx->progress_func = progress_func;
325    ctx->progress_baton = progress_baton;
326}
327
328
329serf_bucket_t *serf_context_bucket_socket_create(
330    serf_context_t *ctx,
331    apr_socket_t *skt,
332    serf_bucket_alloc_t *allocator)
333{
334    serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator);
335
336    /* Use serf's default bytes read/written callback */
337    serf_bucket_socket_set_read_progress_cb(bucket,
338                                            serf__context_progress_delta,
339                                            ctx);
340
341    return bucket;
342}
343
344
345/* ### this really ought to go somewhere else, but... meh.  */
346void serf_lib_version(int *major, int *minor, int *patch)
347{
348    *major = SERF_MAJOR_VERSION;
349    *minor = SERF_MINOR_VERSION;
350    *patch = SERF_PATCH_VERSION;
351}
352
353
354const char *serf_error_string(apr_status_t errcode)
355{
356    switch (errcode)
357    {
358    case SERF_ERROR_CLOSING:
359        return "The connection is closing";
360    case SERF_ERROR_REQUEST_LOST:
361        return "A request has been lost";
362    case SERF_ERROR_WAIT_CONN:
363        return "The connection is blocked, pending further action";
364    case SERF_ERROR_DECOMPRESSION_FAILED:
365        return "An error occurred during decompression";
366    case SERF_ERROR_BAD_HTTP_RESPONSE:
367        return "The server sent an improper HTTP response";
368    case SERF_ERROR_TRUNCATED_HTTP_RESPONSE:
369        return "The server sent a truncated HTTP response body.";
370    case SERF_ERROR_ABORTED_CONNECTION:
371        return "The server unexpectedly closed the connection.";
372    case SERF_ERROR_SSL_COMM_FAILED:
373        return "An error occurred during SSL communication";
374    case SERF_ERROR_SSL_CERT_FAILED:
375        return "An SSL certificate related error occurred ";
376    case SERF_ERROR_AUTHN_FAILED:
377        return "An error occurred during authentication";
378    case SERF_ERROR_AUTHN_NOT_SUPPORTED:
379        return "The requested authentication type(s) are not supported";
380    case SERF_ERROR_AUTHN_MISSING_ATTRIBUTE:
381        return "An authentication attribute is missing";
382    case SERF_ERROR_AUTHN_INITALIZATION_FAILED:
383        return "Initialization of an authentication type failed";
384    case SERF_ERROR_SSLTUNNEL_SETUP_FAILED:
385        return "The proxy server returned an error while setting up the "\
386               "SSL tunnel.";
387    default:
388        return NULL;
389    }
390
391    /* NOTREACHED  */
392}
393