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