]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/serf/context.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / contrib / serf / 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 /**
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  */
30 void 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. */
48 static 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
80 static 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
89 static 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
99 void serf_config_proxy(serf_context_t *ctx,
100                        apr_sockaddr_t *address)
101 {
102     ctx->proxy_address = address;
103 }
104
105
106 void 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
113 void serf_config_authn_types(serf_context_t *ctx,
114                              int authn_types)
115 {
116     ctx->authn_types = authn_types;
117 }
118
119
120 serf_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
177 serf_context_t *serf_context_create(apr_pool_t *pool)
178 {
179     return serf_context_create_ex(NULL, NULL, NULL, pool);
180 }
181
182 apr_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
194 apr_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
263 apr_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
289         /* Use the strict documented error for poll timeouts, to allow proper
290            handling of the other timeout types when returned from
291            serf_event_trigger */
292         if (APR_STATUS_IS_TIMEUP(status))
293             return APR_TIMEUP; /* Return the documented error */
294         return status;
295     }
296
297     while (num--) {
298         serf_connection_t *conn = desc->client_data;
299
300         status = serf_event_trigger(ctx, conn, desc);
301         if (status) {
302             return status;
303         }
304
305         desc++;
306     }
307
308     return APR_SUCCESS;
309 }
310
311
312 void serf_context_set_progress_cb(
313     serf_context_t *ctx,
314     const serf_progress_t progress_func,
315     void *progress_baton)
316 {
317     ctx->progress_func = progress_func;
318     ctx->progress_baton = progress_baton;
319 }
320
321
322 serf_bucket_t *serf_context_bucket_socket_create(
323     serf_context_t *ctx,
324     apr_socket_t *skt,
325     serf_bucket_alloc_t *allocator)
326 {
327     serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator);
328
329     /* Use serf's default bytes read/written callback */
330     serf_bucket_socket_set_read_progress_cb(bucket,
331                                             serf__context_progress_delta,
332                                             ctx);
333
334     return bucket;
335 }
336
337
338 /* ### this really ought to go somewhere else, but... meh.  */
339 void serf_lib_version(int *major, int *minor, int *patch)
340 {
341     *major = SERF_MAJOR_VERSION;
342     *minor = SERF_MINOR_VERSION;
343     *patch = SERF_PATCH_VERSION;
344 }
345
346
347 const char *serf_error_string(apr_status_t errcode)
348 {
349     switch (errcode)
350     {
351     case SERF_ERROR_CLOSING:
352         return "The connection is closing";
353     case SERF_ERROR_REQUEST_LOST:
354         return "A request has been lost";
355     case SERF_ERROR_WAIT_CONN:
356         return "The connection is blocked, pending further action";
357     case SERF_ERROR_DECOMPRESSION_FAILED:
358         return "An error occurred during decompression";
359     case SERF_ERROR_BAD_HTTP_RESPONSE:
360         return "The server sent an improper HTTP response";
361     case SERF_ERROR_TRUNCATED_HTTP_RESPONSE:
362         return "The server sent a truncated HTTP response body.";
363     case SERF_ERROR_ABORTED_CONNECTION:
364         return "The server unexpectedly closed the connection.";
365     case SERF_ERROR_SSL_COMM_FAILED:
366         return "An error occurred during SSL communication";
367     case SERF_ERROR_SSL_CERT_FAILED:
368         return "An SSL certificate related error occurred ";
369     case SERF_ERROR_AUTHN_FAILED:
370         return "An error occurred during authentication";
371     case SERF_ERROR_AUTHN_NOT_SUPPORTED:
372         return "The requested authentication type(s) are not supported";
373     case SERF_ERROR_AUTHN_MISSING_ATTRIBUTE:
374         return "An authentication attribute is missing";
375     case SERF_ERROR_AUTHN_INITALIZATION_FAILED:
376         return "Initialization of an authentication type failed";
377     case SERF_ERROR_SSLTUNNEL_SETUP_FAILED:
378         return "The proxy server returned an error while setting up the "
379                "SSL tunnel.";
380     default:
381         return NULL;
382     }
383
384     /* NOTREACHED  */
385 }