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 |
*/ |
35 |
void 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. */ |
53 |
static 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 |
|
85 |
static 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 |
|
94 |
static 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 |
|
104 |
void serf_config_proxy(serf_context_t *ctx, |
105 |
apr_sockaddr_t *address) |
106 |
{ |
107 |
ctx->proxy_address = address; |
108 |
} |
109 |
|
110 |
|
111 |
void 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 |
|
118 |
void serf_config_authn_types(serf_context_t *ctx, |
119 |
int authn_types) |
120 |
{ |
121 |
ctx->authn_types = authn_types; |
122 |
} |
123 |
|
124 |
|
125 |
serf_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 |
|
182 |
serf_context_t *serf_context_create(apr_pool_t *pool) |
183 |
{ |
184 |
return serf_context_create_ex(NULL, NULL, NULL, pool); |
185 |
} |
186 |
|
187 |
apr_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 |
|
199 |
apr_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 |
|
268 |
apr_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 |
|
317 |
void 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 |
|
327 |
serf_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. */ |
344 |
void 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 |
|
352 |
const 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 |
} |