context.c revision 262324
119370Spst/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein 219370Spst * 398944Sobrien * Licensed under the Apache License, Version 2.0 (the "License"); 4130803Smarcel * you may not use this file except in compliance with the License. 5130803Smarcel * You may obtain a copy of the License at 619370Spst * 798944Sobrien * http://www.apache.org/licenses/LICENSE-2.0 819370Spst * 998944Sobrien * Unless required by applicable law or agreed to in writing, software 1098944Sobrien * distributed under the License is distributed on an "AS IS" BASIS, 1198944Sobrien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1298944Sobrien * See the License for the specific language governing permissions and 1319370Spst * limitations under the License. 1498944Sobrien */ 1598944Sobrien 1698944Sobrien#include <apr_pools.h> 1798944Sobrien#include <apr_poll.h> 1819370Spst#include <apr_version.h> 1998944Sobrien 2098944Sobrien#include "serf.h" 2198944Sobrien#include "serf_bucket_util.h" 2298944Sobrien 2398944Sobrien#include "serf_private.h" 2446283Sdfr 2519370Spst/** 2619370Spst * Callback function (implements serf_progress_t). Takes a number of bytes 2719370Spst * read @a read and bytes written @a written, adds those to the total for this 2819370Spst * context and notifies an interested party (if any). 2919370Spst */ 3019370Spstvoid serf__context_progress_delta( 3119370Spst void *progress_baton, 3219370Spst apr_off_t read, 3319370Spst apr_off_t written) 3419370Spst{ 3519370Spst serf_context_t *ctx = progress_baton; 36130803Smarcel 3719370Spst ctx->progress_read += read; 3819370Spst ctx->progress_written += written; 3919370Spst 4019370Spst if (ctx->progress_func) 4198944Sobrien ctx->progress_func(ctx->progress_baton, 42130803Smarcel ctx->progress_read, 43130803Smarcel ctx->progress_written); 44130803Smarcel} 45130803Smarcel 46130803Smarcel 47130803Smarcel/* Check for dirty connections and update their pollsets accordingly. */ 4819370Spststatic apr_status_t check_dirty_pollsets(serf_context_t *ctx) 4946283Sdfr{ 5046283Sdfr int i; 5198944Sobrien 5246283Sdfr /* if we're not dirty, return now. */ 5398944Sobrien if (!ctx->dirty_pollset) { 5446283Sdfr return APR_SUCCESS; 5598944Sobrien } 5646283Sdfr 5798944Sobrien for (i = ctx->conns->nelts; i--; ) { 5846283Sdfr serf_connection_t *conn = GET_CONN(ctx, i); 5946283Sdfr apr_status_t status; 6046283Sdfr 6198944Sobrien /* if this connection isn't dirty, skip it. */ 6219370Spst if (!conn->dirty_conn) { 6398944Sobrien continue; 6446283Sdfr } 6598944Sobrien 6619370Spst /* reset this connection's flag before we update. */ 6798944Sobrien conn->dirty_conn = 0; 6819370Spst 6998944Sobrien if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS) 7046283Sdfr return status; 7198944Sobrien } 7219370Spst 7398944Sobrien /* reset our context flag now */ 7419370Spst ctx->dirty_pollset = 0; 7598944Sobrien 7619370Spst return APR_SUCCESS; 7798944Sobrien} 7819370Spst 7998944Sobrien 8019370Spststatic apr_status_t pollset_add(void *user_baton, 8198944Sobrien apr_pollfd_t *pfd, 8219370Spst void *serf_baton) 8398944Sobrien{ 8498944Sobrien serf_pollset_t *s = (serf_pollset_t*)user_baton; 8519370Spst pfd->client_data = serf_baton; 8698944Sobrien return apr_pollset_add(s->pollset, pfd); 8798944Sobrien} 8819370Spst 8998944Sobrienstatic apr_status_t pollset_rm(void *user_baton, 9098944Sobrien apr_pollfd_t *pfd, 9119370Spst void *serf_baton) 9298944Sobrien{ 9398944Sobrien serf_pollset_t *s = (serf_pollset_t*)user_baton; 9498944Sobrien pfd->client_data = serf_baton; 9598944Sobrien return apr_pollset_remove(s->pollset, pfd); 9646283Sdfr} 9798944Sobrien 9898944Sobrien 9998944Sobrienvoid serf_config_proxy(serf_context_t *ctx, 10098944Sobrien apr_sockaddr_t *address) 10198944Sobrien{ 10219370Spst ctx->proxy_address = address; 10398944Sobrien} 10498944Sobrien 10598944Sobrien 10698944Sobrienvoid serf_config_credentials_callback(serf_context_t *ctx, 10798944Sobrien serf_credentials_callback_t cred_cb) 10898944Sobrien{ 10919370Spst ctx->cred_cb = cred_cb; 11019370Spst} 11119370Spst 11219370Spst 11319370Spstvoid serf_config_authn_types(serf_context_t *ctx, 11419370Spst int authn_types) 11519370Spst{ 11619370Spst ctx->authn_types = authn_types; 11719370Spst} 11819370Spst 11919370Spst 12098944Sobrienserf_context_t *serf_context_create_ex( 12198944Sobrien void *user_baton, 12298944Sobrien serf_socket_add_t addf, 12398944Sobrien serf_socket_remove_t rmf, 12498944Sobrien apr_pool_t *pool) 12598944Sobrien{ 12698944Sobrien serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx)); 12719370Spst 12846283Sdfr ctx->pool = pool; 12946283Sdfr 13098944Sobrien if (user_baton != NULL) { 13146283Sdfr ctx->pollset_baton = user_baton; 13298944Sobrien ctx->pollset_add = addf; 13346283Sdfr ctx->pollset_rm = rmf; 13498944Sobrien } 13546283Sdfr else { 136130803Smarcel /* build the pollset with a (default) number of connections */ 13746283Sdfr serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps)); 13846283Sdfr 13946283Sdfr /* ### TODO: As of APR 1.4.x apr_pollset_create_ex can return a status 140130803Smarcel ### other than APR_SUCCESS, so we should handle it. 14119370Spst ### Probably move creation of the pollset to later when we have 14219370Spst ### the possibility of returning status to the caller. 14319370Spst */ 14419370Spst#ifdef BROKEN_WSAPOLL 14519370Spst /* APR 1.4.x switched to using WSAPoll() on Win32, but it does not 14619370Spst * properly handle errors on a non-blocking sockets (such as 14719370Spst * connecting to a server where no listener is active). 14819370Spst * 14919370Spst * So, sadly, we must force using select() on Win32. 150130803Smarcel * 151130803Smarcel * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E 15246283Sdfr */ 15346283Sdfr (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0, 15446283Sdfr APR_POLLSET_SELECT); 15546283Sdfr#else 15646283Sdfr (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0); 15746283Sdfr#endif 15846283Sdfr ctx->pollset_baton = ps; 15946283Sdfr ctx->pollset_add = pollset_add; 160130803Smarcel ctx->pollset_rm = pollset_rm; 161130803Smarcel } 162130803Smarcel 163130803Smarcel /* default to a single connection since that is the typical case */ 164130803Smarcel ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *)); 165130803Smarcel 166130803Smarcel /* Initialize progress status */ 167130803Smarcel ctx->progress_read = 0; 168130803Smarcel ctx->progress_written = 0; 169130803Smarcel 170130803Smarcel ctx->authn_types = SERF_AUTHN_ALL; 171130803Smarcel ctx->server_authn_info = apr_hash_make(pool); 172130803Smarcel 173130803Smarcel return ctx; 174130803Smarcel} 175130803Smarcel 176130803Smarcel 177130803Smarcelserf_context_t *serf_context_create(apr_pool_t *pool) 178130803Smarcel{ 179130803Smarcel return serf_context_create_ex(NULL, NULL, NULL, pool); 180130803Smarcel} 181130803Smarcel 182130803Smarcelapr_status_t serf_context_prerun(serf_context_t *ctx) 183130803Smarcel{ 184130803Smarcel apr_status_t status = APR_SUCCESS; 185130803Smarcel if ((status = serf__open_connections(ctx)) != APR_SUCCESS) 186130803Smarcel return status; 187130803Smarcel 188130803Smarcel if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS) 189130803Smarcel return status; 190130803Smarcel return status; 191130803Smarcel} 192130803Smarcel 193130803Smarcel 194130803Smarcelapr_status_t serf_event_trigger( 195130803Smarcel serf_context_t *s, 196130803Smarcel void *serf_baton, 197130803Smarcel const apr_pollfd_t *desc) 198130803Smarcel{ 19946283Sdfr apr_pollfd_t tdesc = { 0 }; 20046283Sdfr apr_status_t status = APR_SUCCESS; 201130803Smarcel serf_io_baton_t *io = serf_baton; 202130803Smarcel 203130803Smarcel if (io->type == SERF_IO_CONN) { 20446283Sdfr serf_connection_t *conn = io->u.conn; 205130803Smarcel serf_context_t *ctx = conn->ctx; 206130803Smarcel 207130803Smarcel /* If this connection has already failed, return the error again, and try 20846283Sdfr * to remove it from the pollset again 209130803Smarcel */ 210130803Smarcel if (conn->status) { 211130803Smarcel tdesc.desc_type = APR_POLL_SOCKET; 21246283Sdfr tdesc.desc.s = conn->skt; 213130803Smarcel tdesc.reqevents = conn->reqevents; 214130803Smarcel ctx->pollset_rm(ctx->pollset_baton, 215130803Smarcel &tdesc, conn); 216130803Smarcel return conn->status; 217130803Smarcel } 218130803Smarcel /* apr_pollset_poll() can return a conn multiple times... */ 219130803Smarcel if ((conn->seen_in_pollset & desc->rtnevents) != 0 || 220130803Smarcel (conn->seen_in_pollset & APR_POLLHUP) != 0) { 221130803Smarcel return APR_SUCCESS; 222130803Smarcel } 223130803Smarcel 224130803Smarcel conn->seen_in_pollset |= desc->rtnevents; 225130803Smarcel 226130803Smarcel if ((conn->status = serf__process_connection(conn, 22746283Sdfr desc->rtnevents)) != APR_SUCCESS) { 228130803Smarcel 229130803Smarcel /* it's possible that the connection was already reset and thus the 23046283Sdfr socket cleaned up. */ 231130803Smarcel if (conn->skt) { 232130803Smarcel tdesc.desc_type = APR_POLL_SOCKET; 233130803Smarcel tdesc.desc.s = conn->skt; 23446283Sdfr tdesc.reqevents = conn->reqevents; 235130803Smarcel ctx->pollset_rm(ctx->pollset_baton, 236130803Smarcel &tdesc, conn); 237130803Smarcel } 23846283Sdfr return conn->status; 239130803Smarcel } 240130803Smarcel } 24146283Sdfr else if (io->type == SERF_IO_LISTENER) { 242130803Smarcel serf_listener_t *l = io->u.listener; 243130803Smarcel 244130803Smarcel status = serf__process_listener(l); 245130803Smarcel 246130803Smarcel if (status) { 247130803Smarcel return status; 248130803Smarcel } 24946283Sdfr } 250130803Smarcel else if (io->type == SERF_IO_CLIENT) { 251130803Smarcel serf_incoming_t *c = io->u.client; 252130803Smarcel 253130803Smarcel status = serf__process_client(c, desc->rtnevents); 254130803Smarcel 25519370Spst if (status) { 256130803Smarcel return status; 257130803Smarcel } 258130803Smarcel } 259130803Smarcel return status; 26019370Spst} 261130803Smarcel 262130803Smarcel 263130803Smarcelapr_status_t serf_context_run( 26419370Spst serf_context_t *ctx, 265130803Smarcel apr_short_interval_time_t duration, 266130803Smarcel apr_pool_t *pool) 267130803Smarcel{ 268130803Smarcel apr_status_t status; 269130803Smarcel apr_int32_t num; 270130803Smarcel const apr_pollfd_t *desc; 271130803Smarcel serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton; 272130803Smarcel 27346283Sdfr if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) { 274130803Smarcel return status; 275130803Smarcel } 276130803Smarcel 277130803Smarcel if ((status = apr_pollset_poll(ps->pollset, duration, &num, 27846283Sdfr &desc)) != APR_SUCCESS) { 279130803Smarcel /* EINTR indicates a handled signal happened during the poll call, 280130803Smarcel ignore, the application can safely retry. */ 281130803Smarcel if (APR_STATUS_IS_EINTR(status)) 282130803Smarcel return APR_SUCCESS; 283130803Smarcel 284130803Smarcel /* ### do we still need to dispatch stuff here? 285130803Smarcel ### look at the potential return codes. map to our defined 286130803Smarcel ### return values? ... 287130803Smarcel */ 288130803Smarcel 289130803Smarcel /* Use the strict documented error for poll timeouts, to allow proper 290130803Smarcel handling of the other timeout types when returned from 29146283Sdfr serf_event_trigger */ 292130803Smarcel if (APR_STATUS_IS_TIMEUP(status)) 293130803Smarcel return APR_TIMEUP; /* Return the documented error */ 294130803Smarcel return status; 295130803Smarcel } 296130803Smarcel 297130803Smarcel while (num--) { 298130803Smarcel serf_connection_t *conn = desc->client_data; 299130803Smarcel 300130803Smarcel status = serf_event_trigger(ctx, conn, desc); 301130803Smarcel if (status) { 302130803Smarcel return status; 303130803Smarcel } 30446283Sdfr 305130803Smarcel desc++; 306130803Smarcel } 307130803Smarcel 308130803Smarcel return APR_SUCCESS; 309130803Smarcel} 310130803Smarcel 31146283Sdfr 312130803Smarcelvoid serf_context_set_progress_cb( 313130803Smarcel serf_context_t *ctx, 314130803Smarcel const serf_progress_t progress_func, 315130803Smarcel void *progress_baton) 316130803Smarcel{ 317130803Smarcel ctx->progress_func = progress_func; 31819370Spst ctx->progress_baton = progress_baton; 319130803Smarcel} 320130803Smarcel 321130803Smarcel 322130803Smarcelserf_bucket_t *serf_context_bucket_socket_create( 323130803Smarcel serf_context_t *ctx, 32419370Spst apr_socket_t *skt, 325130803Smarcel serf_bucket_alloc_t *allocator) 326130803Smarcel{ 327130803Smarcel serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator); 328130803Smarcel 329130803Smarcel /* Use serf's default bytes read/written callback */ 330130803Smarcel serf_bucket_socket_set_read_progress_cb(bucket, 331130803Smarcel serf__context_progress_delta, 332130803Smarcel ctx); 333130803Smarcel 334130803Smarcel return bucket; 335130803Smarcel} 336130803Smarcel 337130803Smarcel 338130803Smarcel/* ### this really ought to go somewhere else, but... meh. */ 339130803Smarcelvoid serf_lib_version(int *major, int *minor, int *patch) 340130803Smarcel{ 341130803Smarcel *major = SERF_MAJOR_VERSION; 342130803Smarcel *minor = SERF_MINOR_VERSION; 343130803Smarcel *patch = SERF_PATCH_VERSION; 344130803Smarcel} 345130803Smarcel 346130803Smarcel 347130803Smarcelconst char *serf_error_string(apr_status_t errcode) 348130803Smarcel{ 349130803Smarcel switch (errcode) 350130803Smarcel { 351130803Smarcel case SERF_ERROR_CLOSING: 352130803Smarcel return "The connection is closing"; 353130803Smarcel case SERF_ERROR_REQUEST_LOST: 354130803Smarcel return "A request has been lost"; 355130803Smarcel case SERF_ERROR_WAIT_CONN: 356130803Smarcel return "The connection is blocked, pending further action"; 357242936Semaste case SERF_ERROR_DECOMPRESSION_FAILED: 358130803Smarcel return "An error occurred during decompression"; 359130803Smarcel case SERF_ERROR_BAD_HTTP_RESPONSE: 360130803Smarcel return "The server sent an improper HTTP response"; 361130803Smarcel case SERF_ERROR_TRUNCATED_HTTP_RESPONSE: 362130803Smarcel return "The server sent a truncated HTTP response body."; 363130803Smarcel case SERF_ERROR_ABORTED_CONNECTION: 364130803Smarcel return "The server unexpectedly closed the connection."; 365130803Smarcel case SERF_ERROR_SSL_COMM_FAILED: 366130803Smarcel return "An error occurred during SSL communication"; 367130803Smarcel case SERF_ERROR_SSL_CERT_FAILED: 368130803Smarcel return "An SSL certificate related error occurred "; 369130803Smarcel case SERF_ERROR_AUTHN_FAILED: 370130803Smarcel return "An error occurred during authentication"; 371130803Smarcel case SERF_ERROR_AUTHN_NOT_SUPPORTED: 372130803Smarcel return "The requested authentication type(s) are not supported"; 373130803Smarcel case SERF_ERROR_AUTHN_MISSING_ATTRIBUTE: 374130803Smarcel return "An authentication attribute is missing"; 375130803Smarcel case SERF_ERROR_AUTHN_INITALIZATION_FAILED: 376130803Smarcel return "Initialization of an authentication type failed"; 377130803Smarcel case SERF_ERROR_SSLTUNNEL_SETUP_FAILED: 378130803Smarcel return "The proxy server returned an error while setting up the " 379130803Smarcel "SSL tunnel."; 380130803Smarcel default: 381130803Smarcel return NULL; 382130803Smarcel } 383130803Smarcel 384130803Smarcel /* NOTREACHED */ 385130803Smarcel} 386130803Smarcel