1251877Speter/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein 2251877Speter * 3251877Speter * Licensed under the Apache License, Version 2.0 (the "License"); 4251877Speter * you may not use this file except in compliance with the License. 5251877Speter * You may obtain a copy of the License at 6251877Speter * 7251877Speter * http://www.apache.org/licenses/LICENSE-2.0 8251877Speter * 9251877Speter * Unless required by applicable law or agreed to in writing, software 10251877Speter * distributed under the License is distributed on an "AS IS" BASIS, 11251877Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12251877Speter * See the License for the specific language governing permissions and 13251877Speter * limitations under the License. 14251877Speter */ 15251877Speter 16251877Speter#include <apr_pools.h> 17251877Speter#include <apr_poll.h> 18251877Speter#include <apr_version.h> 19251877Speter 20251877Speter#include "serf.h" 21251877Speter#include "serf_bucket_util.h" 22251877Speter 23251877Speter#include "serf_private.h" 24251877Speter 25251877Speter/** 26251877Speter * Callback function (implements serf_progress_t). Takes a number of bytes 27251877Speter * read @a read and bytes written @a written, adds those to the total for this 28251877Speter * context and notifies an interested party (if any). 29251877Speter */ 30251877Spetervoid serf__context_progress_delta( 31251877Speter void *progress_baton, 32251877Speter apr_off_t read, 33251877Speter apr_off_t written) 34251877Speter{ 35251877Speter serf_context_t *ctx = progress_baton; 36251877Speter 37251877Speter ctx->progress_read += read; 38251877Speter ctx->progress_written += written; 39251877Speter 40251877Speter if (ctx->progress_func) 41251877Speter ctx->progress_func(ctx->progress_baton, 42251877Speter ctx->progress_read, 43251877Speter ctx->progress_written); 44251877Speter} 45251877Speter 46251877Speter 47251877Speter/* Check for dirty connections and update their pollsets accordingly. */ 48251877Speterstatic apr_status_t check_dirty_pollsets(serf_context_t *ctx) 49251877Speter{ 50251877Speter int i; 51251877Speter 52251877Speter /* if we're not dirty, return now. */ 53251877Speter if (!ctx->dirty_pollset) { 54251877Speter return APR_SUCCESS; 55251877Speter } 56251877Speter 57251877Speter for (i = ctx->conns->nelts; i--; ) { 58251877Speter serf_connection_t *conn = GET_CONN(ctx, i); 59251877Speter apr_status_t status; 60251877Speter 61251877Speter /* if this connection isn't dirty, skip it. */ 62251877Speter if (!conn->dirty_conn) { 63251877Speter continue; 64251877Speter } 65251877Speter 66251877Speter /* reset this connection's flag before we update. */ 67251877Speter conn->dirty_conn = 0; 68251877Speter 69251877Speter if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS) 70251877Speter return status; 71251877Speter } 72251877Speter 73251877Speter /* reset our context flag now */ 74251877Speter ctx->dirty_pollset = 0; 75251877Speter 76251877Speter return APR_SUCCESS; 77251877Speter} 78251877Speter 79251877Speter 80251877Speterstatic apr_status_t pollset_add(void *user_baton, 81251877Speter apr_pollfd_t *pfd, 82251877Speter void *serf_baton) 83251877Speter{ 84251877Speter serf_pollset_t *s = (serf_pollset_t*)user_baton; 85251877Speter pfd->client_data = serf_baton; 86251877Speter return apr_pollset_add(s->pollset, pfd); 87251877Speter} 88251877Speter 89251877Speterstatic apr_status_t pollset_rm(void *user_baton, 90251877Speter apr_pollfd_t *pfd, 91251877Speter void *serf_baton) 92251877Speter{ 93251877Speter serf_pollset_t *s = (serf_pollset_t*)user_baton; 94251877Speter pfd->client_data = serf_baton; 95251877Speter return apr_pollset_remove(s->pollset, pfd); 96251877Speter} 97251877Speter 98251877Speter 99251877Spetervoid serf_config_proxy(serf_context_t *ctx, 100251877Speter apr_sockaddr_t *address) 101251877Speter{ 102251877Speter ctx->proxy_address = address; 103251877Speter} 104251877Speter 105251877Speter 106251877Spetervoid serf_config_credentials_callback(serf_context_t *ctx, 107251877Speter serf_credentials_callback_t cred_cb) 108251877Speter{ 109251877Speter ctx->cred_cb = cred_cb; 110251877Speter} 111251877Speter 112251877Speter 113251877Spetervoid serf_config_authn_types(serf_context_t *ctx, 114251877Speter int authn_types) 115251877Speter{ 116251877Speter ctx->authn_types = authn_types; 117251877Speter} 118251877Speter 119251877Speter 120251877Speterserf_context_t *serf_context_create_ex( 121251877Speter void *user_baton, 122251877Speter serf_socket_add_t addf, 123251877Speter serf_socket_remove_t rmf, 124251877Speter apr_pool_t *pool) 125251877Speter{ 126251877Speter serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx)); 127251877Speter 128251877Speter ctx->pool = pool; 129251877Speter 130251877Speter if (user_baton != NULL) { 131251877Speter ctx->pollset_baton = user_baton; 132251877Speter ctx->pollset_add = addf; 133251877Speter ctx->pollset_rm = rmf; 134251877Speter } 135251877Speter else { 136251877Speter /* build the pollset with a (default) number of connections */ 137251877Speter serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps)); 138251877Speter 139251877Speter /* ### TODO: As of APR 1.4.x apr_pollset_create_ex can return a status 140251877Speter ### other than APR_SUCCESS, so we should handle it. 141251877Speter ### Probably move creation of the pollset to later when we have 142251877Speter ### the possibility of returning status to the caller. 143251877Speter */ 144251877Speter#ifdef BROKEN_WSAPOLL 145251877Speter /* APR 1.4.x switched to using WSAPoll() on Win32, but it does not 146251877Speter * properly handle errors on a non-blocking sockets (such as 147251877Speter * connecting to a server where no listener is active). 148251877Speter * 149251877Speter * So, sadly, we must force using select() on Win32. 150251877Speter * 151251877Speter * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E 152251877Speter */ 153251877Speter (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0, 154251877Speter APR_POLLSET_SELECT); 155251877Speter#else 156251877Speter (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0); 157251877Speter#endif 158251877Speter ctx->pollset_baton = ps; 159251877Speter ctx->pollset_add = pollset_add; 160251877Speter ctx->pollset_rm = pollset_rm; 161251877Speter } 162251877Speter 163251877Speter /* default to a single connection since that is the typical case */ 164251877Speter ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *)); 165251877Speter 166251877Speter /* Initialize progress status */ 167251877Speter ctx->progress_read = 0; 168251877Speter ctx->progress_written = 0; 169251877Speter 170251877Speter ctx->authn_types = SERF_AUTHN_ALL; 171253895Speter ctx->server_authn_info = apr_hash_make(pool); 172251877Speter 173251877Speter return ctx; 174251877Speter} 175251877Speter 176251877Speter 177251877Speterserf_context_t *serf_context_create(apr_pool_t *pool) 178251877Speter{ 179251877Speter return serf_context_create_ex(NULL, NULL, NULL, pool); 180251877Speter} 181251877Speter 182251877Speterapr_status_t serf_context_prerun(serf_context_t *ctx) 183251877Speter{ 184251877Speter apr_status_t status = APR_SUCCESS; 185251877Speter if ((status = serf__open_connections(ctx)) != APR_SUCCESS) 186251877Speter return status; 187251877Speter 188251877Speter if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS) 189251877Speter return status; 190251877Speter return status; 191251877Speter} 192251877Speter 193251877Speter 194251877Speterapr_status_t serf_event_trigger( 195251877Speter serf_context_t *s, 196251877Speter void *serf_baton, 197251877Speter const apr_pollfd_t *desc) 198251877Speter{ 199251877Speter apr_pollfd_t tdesc = { 0 }; 200251877Speter apr_status_t status = APR_SUCCESS; 201251877Speter serf_io_baton_t *io = serf_baton; 202251877Speter 203251877Speter if (io->type == SERF_IO_CONN) { 204251877Speter serf_connection_t *conn = io->u.conn; 205251877Speter serf_context_t *ctx = conn->ctx; 206251877Speter 207251877Speter /* If this connection has already failed, return the error again, and try 208251877Speter * to remove it from the pollset again 209251877Speter */ 210251877Speter if (conn->status) { 211251877Speter tdesc.desc_type = APR_POLL_SOCKET; 212251877Speter tdesc.desc.s = conn->skt; 213251877Speter tdesc.reqevents = conn->reqevents; 214251877Speter ctx->pollset_rm(ctx->pollset_baton, 215251877Speter &tdesc, conn); 216251877Speter return conn->status; 217251877Speter } 218251877Speter /* apr_pollset_poll() can return a conn multiple times... */ 219251877Speter if ((conn->seen_in_pollset & desc->rtnevents) != 0 || 220251877Speter (conn->seen_in_pollset & APR_POLLHUP) != 0) { 221251877Speter return APR_SUCCESS; 222251877Speter } 223251877Speter 224251877Speter conn->seen_in_pollset |= desc->rtnevents; 225251877Speter 226251877Speter if ((conn->status = serf__process_connection(conn, 227251877Speter desc->rtnevents)) != APR_SUCCESS) { 228251877Speter 229251877Speter /* it's possible that the connection was already reset and thus the 230251877Speter socket cleaned up. */ 231251877Speter if (conn->skt) { 232251877Speter tdesc.desc_type = APR_POLL_SOCKET; 233251877Speter tdesc.desc.s = conn->skt; 234251877Speter tdesc.reqevents = conn->reqevents; 235251877Speter ctx->pollset_rm(ctx->pollset_baton, 236251877Speter &tdesc, conn); 237251877Speter } 238251877Speter return conn->status; 239251877Speter } 240251877Speter } 241251877Speter else if (io->type == SERF_IO_LISTENER) { 242251877Speter serf_listener_t *l = io->u.listener; 243251877Speter 244251877Speter status = serf__process_listener(l); 245251877Speter 246251877Speter if (status) { 247251877Speter return status; 248251877Speter } 249251877Speter } 250251877Speter else if (io->type == SERF_IO_CLIENT) { 251251877Speter serf_incoming_t *c = io->u.client; 252251877Speter 253251877Speter status = serf__process_client(c, desc->rtnevents); 254251877Speter 255251877Speter if (status) { 256251877Speter return status; 257251877Speter } 258251877Speter } 259251877Speter return status; 260251877Speter} 261251877Speter 262251877Speter 263251877Speterapr_status_t serf_context_run( 264251877Speter serf_context_t *ctx, 265251877Speter apr_short_interval_time_t duration, 266251877Speter apr_pool_t *pool) 267251877Speter{ 268251877Speter apr_status_t status; 269251877Speter apr_int32_t num; 270251877Speter const apr_pollfd_t *desc; 271251877Speter serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton; 272251877Speter 273251877Speter if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) { 274251877Speter return status; 275251877Speter } 276251877Speter 277251877Speter if ((status = apr_pollset_poll(ps->pollset, duration, &num, 278251877Speter &desc)) != APR_SUCCESS) { 279251877Speter /* EINTR indicates a handled signal happened during the poll call, 280251877Speter ignore, the application can safely retry. */ 281251877Speter if (APR_STATUS_IS_EINTR(status)) 282251877Speter return APR_SUCCESS; 283251877Speter 284251877Speter /* ### do we still need to dispatch stuff here? 285251877Speter ### look at the potential return codes. map to our defined 286251877Speter ### return values? ... 287251877Speter */ 288262339Speter 289262339Speter /* Use the strict documented error for poll timeouts, to allow proper 290262339Speter handling of the other timeout types when returned from 291262339Speter serf_event_trigger */ 292262339Speter if (APR_STATUS_IS_TIMEUP(status)) 293262339Speter return APR_TIMEUP; /* Return the documented error */ 294251877Speter return status; 295251877Speter } 296251877Speter 297251877Speter while (num--) { 298251877Speter serf_connection_t *conn = desc->client_data; 299251877Speter 300251877Speter status = serf_event_trigger(ctx, conn, desc); 301251877Speter if (status) { 302251877Speter return status; 303251877Speter } 304251877Speter 305251877Speter desc++; 306251877Speter } 307251877Speter 308251877Speter return APR_SUCCESS; 309251877Speter} 310251877Speter 311251877Speter 312251877Spetervoid serf_context_set_progress_cb( 313251877Speter serf_context_t *ctx, 314251877Speter const serf_progress_t progress_func, 315251877Speter void *progress_baton) 316251877Speter{ 317251877Speter ctx->progress_func = progress_func; 318251877Speter ctx->progress_baton = progress_baton; 319251877Speter} 320251877Speter 321251877Speter 322251877Speterserf_bucket_t *serf_context_bucket_socket_create( 323251877Speter serf_context_t *ctx, 324251877Speter apr_socket_t *skt, 325251877Speter serf_bucket_alloc_t *allocator) 326251877Speter{ 327251877Speter serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator); 328251877Speter 329251877Speter /* Use serf's default bytes read/written callback */ 330251877Speter serf_bucket_socket_set_read_progress_cb(bucket, 331251877Speter serf__context_progress_delta, 332251877Speter ctx); 333251877Speter 334251877Speter return bucket; 335251877Speter} 336251877Speter 337251877Speter 338251877Speter/* ### this really ought to go somewhere else, but... meh. */ 339251877Spetervoid serf_lib_version(int *major, int *minor, int *patch) 340251877Speter{ 341251877Speter *major = SERF_MAJOR_VERSION; 342251877Speter *minor = SERF_MINOR_VERSION; 343251877Speter *patch = SERF_PATCH_VERSION; 344251877Speter} 345251877Speter 346251877Speter 347251877Speterconst char *serf_error_string(apr_status_t errcode) 348251877Speter{ 349251877Speter switch (errcode) 350251877Speter { 351251877Speter case SERF_ERROR_CLOSING: 352251877Speter return "The connection is closing"; 353251877Speter case SERF_ERROR_REQUEST_LOST: 354251877Speter return "A request has been lost"; 355251877Speter case SERF_ERROR_WAIT_CONN: 356251877Speter return "The connection is blocked, pending further action"; 357251877Speter case SERF_ERROR_DECOMPRESSION_FAILED: 358251877Speter return "An error occurred during decompression"; 359251877Speter case SERF_ERROR_BAD_HTTP_RESPONSE: 360251877Speter return "The server sent an improper HTTP response"; 361251877Speter case SERF_ERROR_TRUNCATED_HTTP_RESPONSE: 362251877Speter return "The server sent a truncated HTTP response body."; 363251877Speter case SERF_ERROR_ABORTED_CONNECTION: 364251877Speter return "The server unexpectedly closed the connection."; 365251877Speter case SERF_ERROR_SSL_COMM_FAILED: 366251877Speter return "An error occurred during SSL communication"; 367251877Speter case SERF_ERROR_SSL_CERT_FAILED: 368251877Speter return "An SSL certificate related error occurred "; 369251877Speter case SERF_ERROR_AUTHN_FAILED: 370251877Speter return "An error occurred during authentication"; 371251877Speter case SERF_ERROR_AUTHN_NOT_SUPPORTED: 372251877Speter return "The requested authentication type(s) are not supported"; 373251877Speter case SERF_ERROR_AUTHN_MISSING_ATTRIBUTE: 374251877Speter return "An authentication attribute is missing"; 375251877Speter case SERF_ERROR_AUTHN_INITALIZATION_FAILED: 376251877Speter return "Initialization of an authentication type failed"; 377251877Speter case SERF_ERROR_SSLTUNNEL_SETUP_FAILED: 378253895Speter return "The proxy server returned an error while setting up the " 379251877Speter "SSL tunnel."; 380251877Speter default: 381251877Speter return NULL; 382251877Speter } 383251877Speter 384251877Speter /* NOTREACHED */ 385251877Speter} 386