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