1 /* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <apr_pools.h>
18 #include <apr_version.h>
21 #include "serf_bucket_util.h"
23 #include "serf_private.h"
25 /* Older versions of APR do not have the APR_VERSION_AT_LEAST macro. Those
26 implementations are safe.
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.
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
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).
44 void serf__context_progress_delta(
49 serf_context_t *ctx = progress_baton;
51 ctx->progress_read += read;
52 ctx->progress_written += written;
54 if (ctx->progress_func)
55 ctx->progress_func(ctx->progress_baton,
57 ctx->progress_written);
61 /* Check for dirty connections and update their pollsets accordingly. */
62 static apr_status_t check_dirty_pollsets(serf_context_t *ctx)
66 /* if we're not dirty, return now. */
67 if (!ctx->dirty_pollset) {
71 for (i = ctx->conns->nelts; i--; ) {
72 serf_connection_t *conn = GET_CONN(ctx, i);
75 /* if this connection isn't dirty, skip it. */
76 if (!conn->dirty_conn) {
80 /* reset this connection's flag before we update. */
83 if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS)
87 /* reset our context flag now */
88 ctx->dirty_pollset = 0;
94 static apr_status_t pollset_add(void *user_baton,
98 serf_pollset_t *s = (serf_pollset_t*)user_baton;
99 pfd->client_data = serf_baton;
100 return apr_pollset_add(s->pollset, pfd);
103 static apr_status_t pollset_rm(void *user_baton,
107 serf_pollset_t *s = (serf_pollset_t*)user_baton;
108 pfd->client_data = serf_baton;
109 return apr_pollset_remove(s->pollset, pfd);
113 void serf_config_proxy(serf_context_t *ctx,
114 apr_sockaddr_t *address)
116 ctx->proxy_address = address;
120 void serf_config_credentials_callback(serf_context_t *ctx,
121 serf_credentials_callback_t cred_cb)
123 ctx->cred_cb = cred_cb;
127 void serf_config_authn_types(serf_context_t *ctx,
130 ctx->authn_types = authn_types;
134 serf_context_t *serf_context_create_ex(
136 serf_socket_add_t addf,
137 serf_socket_remove_t rmf,
140 serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx));
144 if (user_baton != NULL) {
145 ctx->pollset_baton = user_baton;
146 ctx->pollset_add = addf;
147 ctx->pollset_rm = rmf;
150 /* build the pollset with a (default) number of connections */
151 serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps));
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.
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).
163 * So, sadly, we must force using select() on Win32.
165 * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E
167 (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0,
170 (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0);
172 ctx->pollset_baton = ps;
173 ctx->pollset_add = pollset_add;
174 ctx->pollset_rm = pollset_rm;
177 /* default to a single connection since that is the typical case */
178 ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *));
180 /* Initialize progress status */
181 ctx->progress_read = 0;
182 ctx->progress_written = 0;
184 ctx->authn_types = SERF_AUTHN_ALL;
190 serf_context_t *serf_context_create(apr_pool_t *pool)
192 return serf_context_create_ex(NULL, NULL, NULL, pool);
195 apr_status_t serf_context_prerun(serf_context_t *ctx)
197 apr_status_t status = APR_SUCCESS;
198 if ((status = serf__open_connections(ctx)) != APR_SUCCESS)
201 if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS)
207 apr_status_t serf_event_trigger(
210 const apr_pollfd_t *desc)
212 apr_pollfd_t tdesc = { 0 };
213 apr_status_t status = APR_SUCCESS;
214 serf_io_baton_t *io = serf_baton;
216 if (io->type == SERF_IO_CONN) {
217 serf_connection_t *conn = io->u.conn;
218 serf_context_t *ctx = conn->ctx;
220 /* If this connection has already failed, return the error again, and try
221 * to remove it from the pollset again
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,
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) {
237 conn->seen_in_pollset |= desc->rtnevents;
239 if ((conn->status = serf__process_connection(conn,
240 desc->rtnevents)) != APR_SUCCESS) {
242 /* it's possible that the connection was already reset and thus the
243 socket cleaned up. */
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,
254 else if (io->type == SERF_IO_LISTENER) {
255 serf_listener_t *l = io->u.listener;
257 status = serf__process_listener(l);
263 else if (io->type == SERF_IO_CLIENT) {
264 serf_incoming_t *c = io->u.client;
266 status = serf__process_client(c, desc->rtnevents);
276 apr_status_t serf_context_run(
278 apr_short_interval_time_t duration,
283 const apr_pollfd_t *desc;
284 serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton;
286 if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) {
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))
297 /* ### do we still need to dispatch stuff here?
298 ### look at the potential return codes. map to our defined
299 ### return values? ...
305 serf_connection_t *conn = desc->client_data;
307 status = serf_event_trigger(ctx, conn, desc);
319 void serf_context_set_progress_cb(
321 const serf_progress_t progress_func,
322 void *progress_baton)
324 ctx->progress_func = progress_func;
325 ctx->progress_baton = progress_baton;
329 serf_bucket_t *serf_context_bucket_socket_create(
332 serf_bucket_alloc_t *allocator)
334 serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator);
336 /* Use serf's default bytes read/written callback */
337 serf_bucket_socket_set_read_progress_cb(bucket,
338 serf__context_progress_delta,
345 /* ### this really ought to go somewhere else, but... meh. */
346 void serf_lib_version(int *major, int *minor, int *patch)
348 *major = SERF_MAJOR_VERSION;
349 *minor = SERF_MINOR_VERSION;
350 *patch = SERF_PATCH_VERSION;
354 const char *serf_error_string(apr_status_t errcode)
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 "\