context.c revision 302408
1261421Sjhibbits/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
2261421Sjhibbits *
3261421Sjhibbits * Licensed under the Apache License, Version 2.0 (the "License");
4261421Sjhibbits * you may not use this file except in compliance with the License.
5261421Sjhibbits * You may obtain a copy of the License at
6261421Sjhibbits *
7261421Sjhibbits *     http://www.apache.org/licenses/LICENSE-2.0
8261421Sjhibbits *
9261421Sjhibbits * Unless required by applicable law or agreed to in writing, software
10261421Sjhibbits * distributed under the License is distributed on an "AS IS" BASIS,
11261421Sjhibbits * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12261421Sjhibbits * See the License for the specific language governing permissions and
13261421Sjhibbits * limitations under the License.
14261421Sjhibbits */
15261421Sjhibbits
16261421Sjhibbits#include <apr_pools.h>
17261421Sjhibbits#include <apr_poll.h>
18261421Sjhibbits#include <apr_version.h>
19261421Sjhibbits
20261421Sjhibbits#include "serf.h"
21261421Sjhibbits#include "serf_bucket_util.h"
22261421Sjhibbits
23261421Sjhibbits#include "serf_private.h"
24261421Sjhibbits
25261421Sjhibbits/**
26261421Sjhibbits * Callback function (implements serf_progress_t). Takes a number of bytes
27261421Sjhibbits * read @a read and bytes written @a written, adds those to the total for this
28261421Sjhibbits * context and notifies an interested party (if any).
29261421Sjhibbits */
30261421Sjhibbitsvoid serf__context_progress_delta(
31261421Sjhibbits    void *progress_baton,
32261421Sjhibbits    apr_off_t read,
33261421Sjhibbits    apr_off_t written)
34261421Sjhibbits{
35261421Sjhibbits    serf_context_t *ctx = progress_baton;
36261421Sjhibbits
37261421Sjhibbits    ctx->progress_read += read;
38261421Sjhibbits    ctx->progress_written += written;
39261421Sjhibbits
40261421Sjhibbits    if (ctx->progress_func)
41261421Sjhibbits        ctx->progress_func(ctx->progress_baton,
42261421Sjhibbits                           ctx->progress_read,
43261421Sjhibbits                           ctx->progress_written);
44261421Sjhibbits}
45261421Sjhibbits
46261421Sjhibbits
47261421Sjhibbits/* Check for dirty connections and update their pollsets accordingly. */
48261421Sjhibbitsstatic apr_status_t check_dirty_pollsets(serf_context_t *ctx)
49261421Sjhibbits{
50261421Sjhibbits    int i;
51261421Sjhibbits
52261421Sjhibbits    /* if we're not dirty, return now. */
53261421Sjhibbits    if (!ctx->dirty_pollset) {
54261421Sjhibbits        return APR_SUCCESS;
55261421Sjhibbits    }
56261421Sjhibbits
57261421Sjhibbits    for (i = ctx->conns->nelts; i--; ) {
58261421Sjhibbits        serf_connection_t *conn = GET_CONN(ctx, i);
59261421Sjhibbits        apr_status_t status;
60261421Sjhibbits
61261421Sjhibbits        /* if this connection isn't dirty, skip it. */
62261421Sjhibbits        if (!conn->dirty_conn) {
63261421Sjhibbits            continue;
64261421Sjhibbits        }
65261421Sjhibbits
66261421Sjhibbits        /* reset this connection's flag before we update. */
67261421Sjhibbits        conn->dirty_conn = 0;
68261421Sjhibbits
69261421Sjhibbits        if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS)
70261421Sjhibbits            return status;
71261421Sjhibbits    }
72261421Sjhibbits
73261421Sjhibbits    /* reset our context flag now */
74261421Sjhibbits    ctx->dirty_pollset = 0;
75261421Sjhibbits
76261421Sjhibbits    return APR_SUCCESS;
77261421Sjhibbits}
78261421Sjhibbits
79261421Sjhibbits
80261421Sjhibbitsstatic apr_status_t pollset_add(void *user_baton,
81261421Sjhibbits                                apr_pollfd_t *pfd,
82261421Sjhibbits                                void *serf_baton)
83261421Sjhibbits{
84261421Sjhibbits    serf_pollset_t *s = (serf_pollset_t*)user_baton;
85261421Sjhibbits    pfd->client_data = serf_baton;
86261421Sjhibbits    return apr_pollset_add(s->pollset, pfd);
87261421Sjhibbits}
88261421Sjhibbits
89261421Sjhibbitsstatic apr_status_t pollset_rm(void *user_baton,
90261421Sjhibbits                               apr_pollfd_t *pfd,
91261421Sjhibbits                               void *serf_baton)
92261421Sjhibbits{
93261421Sjhibbits    serf_pollset_t *s = (serf_pollset_t*)user_baton;
94261421Sjhibbits    pfd->client_data = serf_baton;
95261421Sjhibbits    return apr_pollset_remove(s->pollset, pfd);
96261421Sjhibbits}
97261421Sjhibbits
98261421Sjhibbits
99261421Sjhibbitsvoid serf_config_proxy(serf_context_t *ctx,
100261421Sjhibbits                       apr_sockaddr_t *address)
101261421Sjhibbits{
102261421Sjhibbits    ctx->proxy_address = address;
103261421Sjhibbits}
104261421Sjhibbits
105261421Sjhibbits
106261421Sjhibbitsvoid serf_config_credentials_callback(serf_context_t *ctx,
107261421Sjhibbits                                      serf_credentials_callback_t cred_cb)
108261421Sjhibbits{
109261421Sjhibbits    ctx->cred_cb = cred_cb;
110261421Sjhibbits}
111261421Sjhibbits
112261421Sjhibbits
113261421Sjhibbitsvoid serf_config_authn_types(serf_context_t *ctx,
114261421Sjhibbits                             int authn_types)
115261421Sjhibbits{
116261421Sjhibbits    ctx->authn_types = authn_types;
117261421Sjhibbits}
118261421Sjhibbits
119261421Sjhibbits
120261421Sjhibbitsserf_context_t *serf_context_create_ex(
121261421Sjhibbits    void *user_baton,
122261421Sjhibbits    serf_socket_add_t addf,
123261421Sjhibbits    serf_socket_remove_t rmf,
124261421Sjhibbits    apr_pool_t *pool)
125261421Sjhibbits{
126261421Sjhibbits    serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx));
127261421Sjhibbits
128261421Sjhibbits    ctx->pool = pool;
129261421Sjhibbits
130261421Sjhibbits    if (user_baton != NULL) {
131261421Sjhibbits        ctx->pollset_baton = user_baton;
132261421Sjhibbits        ctx->pollset_add = addf;
133261421Sjhibbits        ctx->pollset_rm = rmf;
134261421Sjhibbits    }
135261421Sjhibbits    else {
136261421Sjhibbits        /* build the pollset with a (default) number of connections */
137261421Sjhibbits        serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps));
138261421Sjhibbits
139261421Sjhibbits        /* ### TODO: As of APR 1.4.x apr_pollset_create_ex can return a status
140261421Sjhibbits           ### other than APR_SUCCESS, so we should handle it.
141261421Sjhibbits           ### Probably move creation of the pollset to later when we have
142261421Sjhibbits           ### the possibility of returning status to the caller.
143261421Sjhibbits         */
144261421Sjhibbits#ifdef BROKEN_WSAPOLL
145261421Sjhibbits        /* APR 1.4.x switched to using WSAPoll() on Win32, but it does not
146261421Sjhibbits         * properly handle errors on a non-blocking sockets (such as
147261421Sjhibbits         * connecting to a server where no listener is active).
148261421Sjhibbits         *
149261421Sjhibbits         * So, sadly, we must force using select() on Win32.
150261421Sjhibbits         *
151261421Sjhibbits         * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E
152261421Sjhibbits         */
153261421Sjhibbits        (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0,
154261421Sjhibbits                                     APR_POLLSET_SELECT);
155261421Sjhibbits#else
156261421Sjhibbits        (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0);
157261421Sjhibbits#endif
158261421Sjhibbits        ctx->pollset_baton = ps;
159261421Sjhibbits        ctx->pollset_add = pollset_add;
160261421Sjhibbits        ctx->pollset_rm = pollset_rm;
161261421Sjhibbits    }
162261421Sjhibbits
163261421Sjhibbits    /* default to a single connection since that is the typical case */
164261421Sjhibbits    ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *));
165261421Sjhibbits
166261421Sjhibbits    /* Initialize progress status */
167261421Sjhibbits    ctx->progress_read = 0;
168261421Sjhibbits    ctx->progress_written = 0;
169261421Sjhibbits
170261421Sjhibbits    ctx->authn_types = SERF_AUTHN_ALL;
171261421Sjhibbits    ctx->server_authn_info = apr_hash_make(pool);
172261421Sjhibbits
173261421Sjhibbits    return ctx;
174261421Sjhibbits}
175261421Sjhibbits
176261421Sjhibbits
177261421Sjhibbitsserf_context_t *serf_context_create(apr_pool_t *pool)
178261421Sjhibbits{
179261421Sjhibbits    return serf_context_create_ex(NULL, NULL, NULL, pool);
180261421Sjhibbits}
181261421Sjhibbits
182261421Sjhibbitsapr_status_t serf_context_prerun(serf_context_t *ctx)
183261421Sjhibbits{
184261421Sjhibbits    apr_status_t status = APR_SUCCESS;
185261421Sjhibbits    if ((status = serf__open_connections(ctx)) != APR_SUCCESS)
186261421Sjhibbits        return status;
187261421Sjhibbits
188261421Sjhibbits    if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS)
189261421Sjhibbits        return status;
190261421Sjhibbits    return status;
191261421Sjhibbits}
192261421Sjhibbits
193261421Sjhibbits
194261421Sjhibbitsapr_status_t serf_event_trigger(
195261421Sjhibbits    serf_context_t *s,
196261421Sjhibbits    void *serf_baton,
197261421Sjhibbits    const apr_pollfd_t *desc)
198261421Sjhibbits{
199261421Sjhibbits    apr_pollfd_t tdesc = { 0 };
200261421Sjhibbits    apr_status_t status = APR_SUCCESS;
201261421Sjhibbits    serf_io_baton_t *io = serf_baton;
202261421Sjhibbits
203261421Sjhibbits    if (io->type == SERF_IO_CONN) {
204261421Sjhibbits        serf_connection_t *conn = io->u.conn;
205261421Sjhibbits        serf_context_t *ctx = conn->ctx;
206261421Sjhibbits
207261421Sjhibbits        /* If this connection has already failed, return the error again, and try
208261421Sjhibbits         * to remove it from the pollset again
209261421Sjhibbits         */
210261421Sjhibbits        if (conn->status) {
211261421Sjhibbits            tdesc.desc_type = APR_POLL_SOCKET;
212261421Sjhibbits            tdesc.desc.s = conn->skt;
213261421Sjhibbits            tdesc.reqevents = conn->reqevents;
214261421Sjhibbits            ctx->pollset_rm(ctx->pollset_baton,
215261421Sjhibbits                            &tdesc, conn);
216261421Sjhibbits            return conn->status;
217261421Sjhibbits        }
218261421Sjhibbits        /* apr_pollset_poll() can return a conn multiple times... */
219261421Sjhibbits        if ((conn->seen_in_pollset & desc->rtnevents) != 0 ||
220261421Sjhibbits            (conn->seen_in_pollset & APR_POLLHUP) != 0) {
221261421Sjhibbits            return APR_SUCCESS;
222261421Sjhibbits        }
223261421Sjhibbits
224261421Sjhibbits        conn->seen_in_pollset |= desc->rtnevents;
225261421Sjhibbits
226261421Sjhibbits        if ((conn->status = serf__process_connection(conn,
227261421Sjhibbits                                         desc->rtnevents)) != APR_SUCCESS) {
228261421Sjhibbits
229261421Sjhibbits            /* it's possible that the connection was already reset and thus the
230261421Sjhibbits               socket cleaned up. */
231261421Sjhibbits            if (conn->skt) {
232261421Sjhibbits                tdesc.desc_type = APR_POLL_SOCKET;
233261421Sjhibbits                tdesc.desc.s = conn->skt;
234261421Sjhibbits                tdesc.reqevents = conn->reqevents;
235261421Sjhibbits                ctx->pollset_rm(ctx->pollset_baton,
236261421Sjhibbits                                &tdesc, conn);
237261421Sjhibbits            }
238261421Sjhibbits            return conn->status;
239261421Sjhibbits        }
240261421Sjhibbits    }
241261421Sjhibbits    else if (io->type == SERF_IO_LISTENER) {
242261421Sjhibbits        serf_listener_t *l = io->u.listener;
243261421Sjhibbits
244261421Sjhibbits        status = serf__process_listener(l);
245261421Sjhibbits
246261421Sjhibbits        if (status) {
247261421Sjhibbits            return status;
248261421Sjhibbits        }
249261421Sjhibbits    }
250261421Sjhibbits    else if (io->type == SERF_IO_CLIENT) {
251261421Sjhibbits        serf_incoming_t *c = io->u.client;
252261421Sjhibbits
253261421Sjhibbits        status = serf__process_client(c, desc->rtnevents);
254261421Sjhibbits
255261421Sjhibbits        if (status) {
256261421Sjhibbits            return status;
257261421Sjhibbits        }
258261421Sjhibbits    }
259261421Sjhibbits    return status;
260261421Sjhibbits}
261261421Sjhibbits
262261421Sjhibbits
263261421Sjhibbitsapr_status_t serf_context_run(
264261421Sjhibbits    serf_context_t *ctx,
265261421Sjhibbits    apr_short_interval_time_t duration,
266261421Sjhibbits    apr_pool_t *pool)
267261421Sjhibbits{
268261421Sjhibbits    apr_status_t status;
269261421Sjhibbits    apr_int32_t num;
270261421Sjhibbits    const apr_pollfd_t *desc;
271261421Sjhibbits    serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton;
272261421Sjhibbits
273261421Sjhibbits    if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) {
274261421Sjhibbits        return status;
275261421Sjhibbits    }
276261421Sjhibbits
277261421Sjhibbits    if ((status = apr_pollset_poll(ps->pollset, duration, &num,
278261421Sjhibbits                                   &desc)) != APR_SUCCESS) {
279261421Sjhibbits        /* EINTR indicates a handled signal happened during the poll call,
280261421Sjhibbits           ignore, the application can safely retry. */
281261421Sjhibbits        if (APR_STATUS_IS_EINTR(status))
282261421Sjhibbits            return APR_SUCCESS;
283261421Sjhibbits
284261421Sjhibbits        /* ### do we still need to dispatch stuff here?
285261421Sjhibbits           ### look at the potential return codes. map to our defined
286261421Sjhibbits           ### return values? ...
287261421Sjhibbits        */
288261421Sjhibbits
289261421Sjhibbits        /* Use the strict documented error for poll timeouts, to allow proper
290261421Sjhibbits           handling of the other timeout types when returned from
291261421Sjhibbits           serf_event_trigger */
292261421Sjhibbits        if (APR_STATUS_IS_TIMEUP(status))
293261421Sjhibbits            return APR_TIMEUP; /* Return the documented error */
294261421Sjhibbits        return status;
295261421Sjhibbits    }
296261421Sjhibbits
297261421Sjhibbits    while (num--) {
298261421Sjhibbits        serf_connection_t *conn = desc->client_data;
299261421Sjhibbits
300261421Sjhibbits        status = serf_event_trigger(ctx, conn, desc);
301261421Sjhibbits        if (status) {
302261421Sjhibbits            return status;
303261421Sjhibbits        }
304261421Sjhibbits
305261421Sjhibbits        desc++;
306261421Sjhibbits    }
307261421Sjhibbits
308261421Sjhibbits    return APR_SUCCESS;
309261421Sjhibbits}
310261421Sjhibbits
311261421Sjhibbits
312261421Sjhibbitsvoid serf_context_set_progress_cb(
313261421Sjhibbits    serf_context_t *ctx,
314261421Sjhibbits    const serf_progress_t progress_func,
315261421Sjhibbits    void *progress_baton)
316261421Sjhibbits{
317261421Sjhibbits    ctx->progress_func = progress_func;
318261421Sjhibbits    ctx->progress_baton = progress_baton;
319261421Sjhibbits}
320261421Sjhibbits
321261421Sjhibbits
322261421Sjhibbitsserf_bucket_t *serf_context_bucket_socket_create(
323261421Sjhibbits    serf_context_t *ctx,
324261421Sjhibbits    apr_socket_t *skt,
325261421Sjhibbits    serf_bucket_alloc_t *allocator)
326261421Sjhibbits{
327261421Sjhibbits    serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator);
328261421Sjhibbits
329261421Sjhibbits    /* Use serf's default bytes read/written callback */
330261421Sjhibbits    serf_bucket_socket_set_read_progress_cb(bucket,
331261421Sjhibbits                                            serf__context_progress_delta,
332261421Sjhibbits                                            ctx);
333261421Sjhibbits
334261421Sjhibbits    return bucket;
335261421Sjhibbits}
336261421Sjhibbits
337261421Sjhibbits
338261421Sjhibbits/* ### this really ought to go somewhere else, but... meh.  */
339261421Sjhibbitsvoid serf_lib_version(int *major, int *minor, int *patch)
340261421Sjhibbits{
341261421Sjhibbits    *major = SERF_MAJOR_VERSION;
342261421Sjhibbits    *minor = SERF_MINOR_VERSION;
343261421Sjhibbits    *patch = SERF_PATCH_VERSION;
344261421Sjhibbits}
345261421Sjhibbits
346261421Sjhibbits
347261421Sjhibbitsconst char *serf_error_string(apr_status_t errcode)
348261421Sjhibbits{
349261421Sjhibbits    switch (errcode)
350261421Sjhibbits    {
351261421Sjhibbits    case SERF_ERROR_CLOSING:
352261421Sjhibbits        return "The connection is closing";
353261421Sjhibbits    case SERF_ERROR_REQUEST_LOST:
354261421Sjhibbits        return "A request has been lost";
355261421Sjhibbits    case SERF_ERROR_WAIT_CONN:
356261421Sjhibbits        return "The connection is blocked, pending further action";
357261421Sjhibbits    case SERF_ERROR_DECOMPRESSION_FAILED:
358261421Sjhibbits        return "An error occurred during decompression";
359261421Sjhibbits    case SERF_ERROR_BAD_HTTP_RESPONSE:
360261421Sjhibbits        return "The server sent an improper HTTP response";
361261421Sjhibbits    case SERF_ERROR_TRUNCATED_HTTP_RESPONSE:
362261421Sjhibbits        return "The server sent a truncated HTTP response body.";
363261421Sjhibbits    case SERF_ERROR_ABORTED_CONNECTION:
364261421Sjhibbits        return "The server unexpectedly closed the connection.";
365261421Sjhibbits    case SERF_ERROR_SSL_COMM_FAILED:
366261421Sjhibbits        return "An error occurred during SSL communication";
367261421Sjhibbits    case SERF_ERROR_SSL_CERT_FAILED:
368261421Sjhibbits        return "An SSL certificate related error occurred ";
369261421Sjhibbits    case SERF_ERROR_AUTHN_FAILED:
370261421Sjhibbits        return "An error occurred during authentication";
371261421Sjhibbits    case SERF_ERROR_AUTHN_NOT_SUPPORTED:
372261421Sjhibbits        return "The requested authentication type(s) are not supported";
373261421Sjhibbits    case SERF_ERROR_AUTHN_MISSING_ATTRIBUTE:
374261421Sjhibbits        return "An authentication attribute is missing";
375261421Sjhibbits    case SERF_ERROR_AUTHN_INITALIZATION_FAILED:
376261421Sjhibbits        return "Initialization of an authentication type failed";
377261421Sjhibbits    case SERF_ERROR_SSLTUNNEL_SETUP_FAILED:
378261421Sjhibbits        return "The proxy server returned an error while setting up the "
379261421Sjhibbits               "SSL tunnel.";
380261421Sjhibbits    default:
381261421Sjhibbits        return NULL;
382261421Sjhibbits    }
383261421Sjhibbits
384261421Sjhibbits    /* NOTREACHED  */
385261421Sjhibbits}
386261421Sjhibbits