]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - context.c
Import serf-1.2.1
[FreeBSD/FreeBSD.git] / context.c
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  */
44 void 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. */
62 static 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
94 static 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
103 static 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
113 void serf_config_proxy(serf_context_t *ctx,
114                        apr_sockaddr_t *address)
115 {
116     ctx->proxy_address = address;
117 }
118
119
120 void 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
127 void serf_config_authn_types(serf_context_t *ctx,
128                              int authn_types)
129 {
130     ctx->authn_types = authn_types;
131 }
132
133
134 serf_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
190 serf_context_t *serf_context_create(apr_pool_t *pool)
191 {
192     return serf_context_create_ex(NULL, NULL, NULL, pool);
193 }
194
195 apr_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
207 apr_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
276 apr_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
319 void 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
329 serf_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.  */
346 void 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
354 const 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 }