context.c revision 302408
1261421Sjhibbits/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein 2261421Sjhibbits * 3261421Sjhibbits * Licensed under the Apache License, Version 2.0 (the "License"); 4261421Sjhibbits * you may not use this file except in compliance with the License. 5261421Sjhibbits * You may obtain a copy of the License at 6261421Sjhibbits * 7261421Sjhibbits * http://www.apache.org/licenses/LICENSE-2.0 8261421Sjhibbits * 9261421Sjhibbits * Unless required by applicable law or agreed to in writing, software 10261421Sjhibbits * distributed under the License is distributed on an "AS IS" BASIS, 11261421Sjhibbits * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12261421Sjhibbits * See the License for the specific language governing permissions and 13261421Sjhibbits * limitations under the License. 14261421Sjhibbits */ 15261421Sjhibbits 16261421Sjhibbits#include <apr_pools.h> 17261421Sjhibbits#include <apr_poll.h> 18261421Sjhibbits#include <apr_version.h> 19261421Sjhibbits 20261421Sjhibbits#include "serf.h" 21261421Sjhibbits#include "serf_bucket_util.h" 22261421Sjhibbits 23261421Sjhibbits#include "serf_private.h" 24261421Sjhibbits 25261421Sjhibbits/** 26261421Sjhibbits * Callback function (implements serf_progress_t). Takes a number of bytes 27261421Sjhibbits * read @a read and bytes written @a written, adds those to the total for this 28261421Sjhibbits * context and notifies an interested party (if any). 29261421Sjhibbits */ 30261421Sjhibbitsvoid serf__context_progress_delta( 31261421Sjhibbits void *progress_baton, 32261421Sjhibbits apr_off_t read, 33261421Sjhibbits apr_off_t written) 34261421Sjhibbits{ 35261421Sjhibbits serf_context_t *ctx = progress_baton; 36261421Sjhibbits 37261421Sjhibbits ctx->progress_read += read; 38261421Sjhibbits ctx->progress_written += written; 39261421Sjhibbits 40261421Sjhibbits if (ctx->progress_func) 41261421Sjhibbits ctx->progress_func(ctx->progress_baton, 42261421Sjhibbits ctx->progress_read, 43261421Sjhibbits ctx->progress_written); 44261421Sjhibbits} 45261421Sjhibbits 46261421Sjhibbits 47261421Sjhibbits/* Check for dirty connections and update their pollsets accordingly. */ 48261421Sjhibbitsstatic apr_status_t check_dirty_pollsets(serf_context_t *ctx) 49261421Sjhibbits{ 50261421Sjhibbits int i; 51261421Sjhibbits 52261421Sjhibbits /* if we're not dirty, return now. */ 53261421Sjhibbits if (!ctx->dirty_pollset) { 54261421Sjhibbits return APR_SUCCESS; 55261421Sjhibbits } 56261421Sjhibbits 57261421Sjhibbits for (i = ctx->conns->nelts; i--; ) { 58261421Sjhibbits serf_connection_t *conn = GET_CONN(ctx, i); 59261421Sjhibbits apr_status_t status; 60261421Sjhibbits 61261421Sjhibbits /* if this connection isn't dirty, skip it. */ 62261421Sjhibbits if (!conn->dirty_conn) { 63261421Sjhibbits continue; 64261421Sjhibbits } 65261421Sjhibbits 66261421Sjhibbits /* reset this connection's flag before we update. */ 67261421Sjhibbits conn->dirty_conn = 0; 68261421Sjhibbits 69261421Sjhibbits if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS) 70261421Sjhibbits return status; 71261421Sjhibbits } 72261421Sjhibbits 73261421Sjhibbits /* reset our context flag now */ 74261421Sjhibbits ctx->dirty_pollset = 0; 75261421Sjhibbits 76261421Sjhibbits return APR_SUCCESS; 77261421Sjhibbits} 78261421Sjhibbits 79261421Sjhibbits 80261421Sjhibbitsstatic apr_status_t pollset_add(void *user_baton, 81261421Sjhibbits apr_pollfd_t *pfd, 82261421Sjhibbits void *serf_baton) 83261421Sjhibbits{ 84261421Sjhibbits serf_pollset_t *s = (serf_pollset_t*)user_baton; 85261421Sjhibbits pfd->client_data = serf_baton; 86261421Sjhibbits return apr_pollset_add(s->pollset, pfd); 87261421Sjhibbits} 88261421Sjhibbits 89261421Sjhibbitsstatic apr_status_t pollset_rm(void *user_baton, 90261421Sjhibbits apr_pollfd_t *pfd, 91261421Sjhibbits void *serf_baton) 92261421Sjhibbits{ 93261421Sjhibbits serf_pollset_t *s = (serf_pollset_t*)user_baton; 94261421Sjhibbits pfd->client_data = serf_baton; 95261421Sjhibbits return apr_pollset_remove(s->pollset, pfd); 96261421Sjhibbits} 97261421Sjhibbits 98261421Sjhibbits 99261421Sjhibbitsvoid serf_config_proxy(serf_context_t *ctx, 100261421Sjhibbits apr_sockaddr_t *address) 101261421Sjhibbits{ 102261421Sjhibbits ctx->proxy_address = address; 103261421Sjhibbits} 104261421Sjhibbits 105261421Sjhibbits 106261421Sjhibbitsvoid serf_config_credentials_callback(serf_context_t *ctx, 107261421Sjhibbits serf_credentials_callback_t cred_cb) 108261421Sjhibbits{ 109261421Sjhibbits ctx->cred_cb = cred_cb; 110261421Sjhibbits} 111261421Sjhibbits 112261421Sjhibbits 113261421Sjhibbitsvoid serf_config_authn_types(serf_context_t *ctx, 114261421Sjhibbits int authn_types) 115261421Sjhibbits{ 116261421Sjhibbits ctx->authn_types = authn_types; 117261421Sjhibbits} 118261421Sjhibbits 119261421Sjhibbits 120261421Sjhibbitsserf_context_t *serf_context_create_ex( 121261421Sjhibbits void *user_baton, 122261421Sjhibbits serf_socket_add_t addf, 123261421Sjhibbits serf_socket_remove_t rmf, 124261421Sjhibbits apr_pool_t *pool) 125261421Sjhibbits{ 126261421Sjhibbits serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx)); 127261421Sjhibbits 128261421Sjhibbits ctx->pool = pool; 129261421Sjhibbits 130261421Sjhibbits if (user_baton != NULL) { 131261421Sjhibbits ctx->pollset_baton = user_baton; 132261421Sjhibbits ctx->pollset_add = addf; 133261421Sjhibbits ctx->pollset_rm = rmf; 134261421Sjhibbits } 135261421Sjhibbits else { 136261421Sjhibbits /* build the pollset with a (default) number of connections */ 137261421Sjhibbits serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps)); 138261421Sjhibbits 139261421Sjhibbits /* ### TODO: As of APR 1.4.x apr_pollset_create_ex can return a status 140261421Sjhibbits ### other than APR_SUCCESS, so we should handle it. 141261421Sjhibbits ### Probably move creation of the pollset to later when we have 142261421Sjhibbits ### the possibility of returning status to the caller. 143261421Sjhibbits */ 144261421Sjhibbits#ifdef BROKEN_WSAPOLL 145261421Sjhibbits /* APR 1.4.x switched to using WSAPoll() on Win32, but it does not 146261421Sjhibbits * properly handle errors on a non-blocking sockets (such as 147261421Sjhibbits * connecting to a server where no listener is active). 148261421Sjhibbits * 149261421Sjhibbits * So, sadly, we must force using select() on Win32. 150261421Sjhibbits * 151261421Sjhibbits * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E 152261421Sjhibbits */ 153261421Sjhibbits (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0, 154261421Sjhibbits APR_POLLSET_SELECT); 155261421Sjhibbits#else 156261421Sjhibbits (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0); 157261421Sjhibbits#endif 158261421Sjhibbits ctx->pollset_baton = ps; 159261421Sjhibbits ctx->pollset_add = pollset_add; 160261421Sjhibbits ctx->pollset_rm = pollset_rm; 161261421Sjhibbits } 162261421Sjhibbits 163261421Sjhibbits /* default to a single connection since that is the typical case */ 164261421Sjhibbits ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *)); 165261421Sjhibbits 166261421Sjhibbits /* Initialize progress status */ 167261421Sjhibbits ctx->progress_read = 0; 168261421Sjhibbits ctx->progress_written = 0; 169261421Sjhibbits 170261421Sjhibbits ctx->authn_types = SERF_AUTHN_ALL; 171261421Sjhibbits ctx->server_authn_info = apr_hash_make(pool); 172261421Sjhibbits 173261421Sjhibbits return ctx; 174261421Sjhibbits} 175261421Sjhibbits 176261421Sjhibbits 177261421Sjhibbitsserf_context_t *serf_context_create(apr_pool_t *pool) 178261421Sjhibbits{ 179261421Sjhibbits return serf_context_create_ex(NULL, NULL, NULL, pool); 180261421Sjhibbits} 181261421Sjhibbits 182261421Sjhibbitsapr_status_t serf_context_prerun(serf_context_t *ctx) 183261421Sjhibbits{ 184261421Sjhibbits apr_status_t status = APR_SUCCESS; 185261421Sjhibbits if ((status = serf__open_connections(ctx)) != APR_SUCCESS) 186261421Sjhibbits return status; 187261421Sjhibbits 188261421Sjhibbits if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS) 189261421Sjhibbits return status; 190261421Sjhibbits return status; 191261421Sjhibbits} 192261421Sjhibbits 193261421Sjhibbits 194261421Sjhibbitsapr_status_t serf_event_trigger( 195261421Sjhibbits serf_context_t *s, 196261421Sjhibbits void *serf_baton, 197261421Sjhibbits const apr_pollfd_t *desc) 198261421Sjhibbits{ 199261421Sjhibbits apr_pollfd_t tdesc = { 0 }; 200261421Sjhibbits apr_status_t status = APR_SUCCESS; 201261421Sjhibbits serf_io_baton_t *io = serf_baton; 202261421Sjhibbits 203261421Sjhibbits if (io->type == SERF_IO_CONN) { 204261421Sjhibbits serf_connection_t *conn = io->u.conn; 205261421Sjhibbits serf_context_t *ctx = conn->ctx; 206261421Sjhibbits 207261421Sjhibbits /* If this connection has already failed, return the error again, and try 208261421Sjhibbits * to remove it from the pollset again 209261421Sjhibbits */ 210261421Sjhibbits if (conn->status) { 211261421Sjhibbits tdesc.desc_type = APR_POLL_SOCKET; 212261421Sjhibbits tdesc.desc.s = conn->skt; 213261421Sjhibbits tdesc.reqevents = conn->reqevents; 214261421Sjhibbits ctx->pollset_rm(ctx->pollset_baton, 215261421Sjhibbits &tdesc, conn); 216261421Sjhibbits return conn->status; 217261421Sjhibbits } 218261421Sjhibbits /* apr_pollset_poll() can return a conn multiple times... */ 219261421Sjhibbits if ((conn->seen_in_pollset & desc->rtnevents) != 0 || 220261421Sjhibbits (conn->seen_in_pollset & APR_POLLHUP) != 0) { 221261421Sjhibbits return APR_SUCCESS; 222261421Sjhibbits } 223261421Sjhibbits 224261421Sjhibbits conn->seen_in_pollset |= desc->rtnevents; 225261421Sjhibbits 226261421Sjhibbits if ((conn->status = serf__process_connection(conn, 227261421Sjhibbits desc->rtnevents)) != APR_SUCCESS) { 228261421Sjhibbits 229261421Sjhibbits /* it's possible that the connection was already reset and thus the 230261421Sjhibbits socket cleaned up. */ 231261421Sjhibbits if (conn->skt) { 232261421Sjhibbits tdesc.desc_type = APR_POLL_SOCKET; 233261421Sjhibbits tdesc.desc.s = conn->skt; 234261421Sjhibbits tdesc.reqevents = conn->reqevents; 235261421Sjhibbits ctx->pollset_rm(ctx->pollset_baton, 236261421Sjhibbits &tdesc, conn); 237261421Sjhibbits } 238261421Sjhibbits return conn->status; 239261421Sjhibbits } 240261421Sjhibbits } 241261421Sjhibbits else if (io->type == SERF_IO_LISTENER) { 242261421Sjhibbits serf_listener_t *l = io->u.listener; 243261421Sjhibbits 244261421Sjhibbits status = serf__process_listener(l); 245261421Sjhibbits 246261421Sjhibbits if (status) { 247261421Sjhibbits return status; 248261421Sjhibbits } 249261421Sjhibbits } 250261421Sjhibbits else if (io->type == SERF_IO_CLIENT) { 251261421Sjhibbits serf_incoming_t *c = io->u.client; 252261421Sjhibbits 253261421Sjhibbits status = serf__process_client(c, desc->rtnevents); 254261421Sjhibbits 255261421Sjhibbits if (status) { 256261421Sjhibbits return status; 257261421Sjhibbits } 258261421Sjhibbits } 259261421Sjhibbits return status; 260261421Sjhibbits} 261261421Sjhibbits 262261421Sjhibbits 263261421Sjhibbitsapr_status_t serf_context_run( 264261421Sjhibbits serf_context_t *ctx, 265261421Sjhibbits apr_short_interval_time_t duration, 266261421Sjhibbits apr_pool_t *pool) 267261421Sjhibbits{ 268261421Sjhibbits apr_status_t status; 269261421Sjhibbits apr_int32_t num; 270261421Sjhibbits const apr_pollfd_t *desc; 271261421Sjhibbits serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton; 272261421Sjhibbits 273261421Sjhibbits if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) { 274261421Sjhibbits return status; 275261421Sjhibbits } 276261421Sjhibbits 277261421Sjhibbits if ((status = apr_pollset_poll(ps->pollset, duration, &num, 278261421Sjhibbits &desc)) != APR_SUCCESS) { 279261421Sjhibbits /* EINTR indicates a handled signal happened during the poll call, 280261421Sjhibbits ignore, the application can safely retry. */ 281261421Sjhibbits if (APR_STATUS_IS_EINTR(status)) 282261421Sjhibbits return APR_SUCCESS; 283261421Sjhibbits 284261421Sjhibbits /* ### do we still need to dispatch stuff here? 285261421Sjhibbits ### look at the potential return codes. map to our defined 286261421Sjhibbits ### return values? ... 287261421Sjhibbits */ 288261421Sjhibbits 289261421Sjhibbits /* Use the strict documented error for poll timeouts, to allow proper 290261421Sjhibbits handling of the other timeout types when returned from 291261421Sjhibbits serf_event_trigger */ 292261421Sjhibbits if (APR_STATUS_IS_TIMEUP(status)) 293261421Sjhibbits return APR_TIMEUP; /* Return the documented error */ 294261421Sjhibbits return status; 295261421Sjhibbits } 296261421Sjhibbits 297261421Sjhibbits while (num--) { 298261421Sjhibbits serf_connection_t *conn = desc->client_data; 299261421Sjhibbits 300261421Sjhibbits status = serf_event_trigger(ctx, conn, desc); 301261421Sjhibbits if (status) { 302261421Sjhibbits return status; 303261421Sjhibbits } 304261421Sjhibbits 305261421Sjhibbits desc++; 306261421Sjhibbits } 307261421Sjhibbits 308261421Sjhibbits return APR_SUCCESS; 309261421Sjhibbits} 310261421Sjhibbits 311261421Sjhibbits 312261421Sjhibbitsvoid serf_context_set_progress_cb( 313261421Sjhibbits serf_context_t *ctx, 314261421Sjhibbits const serf_progress_t progress_func, 315261421Sjhibbits void *progress_baton) 316261421Sjhibbits{ 317261421Sjhibbits ctx->progress_func = progress_func; 318261421Sjhibbits ctx->progress_baton = progress_baton; 319261421Sjhibbits} 320261421Sjhibbits 321261421Sjhibbits 322261421Sjhibbitsserf_bucket_t *serf_context_bucket_socket_create( 323261421Sjhibbits serf_context_t *ctx, 324261421Sjhibbits apr_socket_t *skt, 325261421Sjhibbits serf_bucket_alloc_t *allocator) 326261421Sjhibbits{ 327261421Sjhibbits serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator); 328261421Sjhibbits 329261421Sjhibbits /* Use serf's default bytes read/written callback */ 330261421Sjhibbits serf_bucket_socket_set_read_progress_cb(bucket, 331261421Sjhibbits serf__context_progress_delta, 332261421Sjhibbits ctx); 333261421Sjhibbits 334261421Sjhibbits return bucket; 335261421Sjhibbits} 336261421Sjhibbits 337261421Sjhibbits 338261421Sjhibbits/* ### this really ought to go somewhere else, but... meh. */ 339261421Sjhibbitsvoid serf_lib_version(int *major, int *minor, int *patch) 340261421Sjhibbits{ 341261421Sjhibbits *major = SERF_MAJOR_VERSION; 342261421Sjhibbits *minor = SERF_MINOR_VERSION; 343261421Sjhibbits *patch = SERF_PATCH_VERSION; 344261421Sjhibbits} 345261421Sjhibbits 346261421Sjhibbits 347261421Sjhibbitsconst char *serf_error_string(apr_status_t errcode) 348261421Sjhibbits{ 349261421Sjhibbits switch (errcode) 350261421Sjhibbits { 351261421Sjhibbits case SERF_ERROR_CLOSING: 352261421Sjhibbits return "The connection is closing"; 353261421Sjhibbits case SERF_ERROR_REQUEST_LOST: 354261421Sjhibbits return "A request has been lost"; 355261421Sjhibbits case SERF_ERROR_WAIT_CONN: 356261421Sjhibbits return "The connection is blocked, pending further action"; 357261421Sjhibbits case SERF_ERROR_DECOMPRESSION_FAILED: 358261421Sjhibbits return "An error occurred during decompression"; 359261421Sjhibbits case SERF_ERROR_BAD_HTTP_RESPONSE: 360261421Sjhibbits return "The server sent an improper HTTP response"; 361261421Sjhibbits case SERF_ERROR_TRUNCATED_HTTP_RESPONSE: 362261421Sjhibbits return "The server sent a truncated HTTP response body."; 363261421Sjhibbits case SERF_ERROR_ABORTED_CONNECTION: 364261421Sjhibbits return "The server unexpectedly closed the connection."; 365261421Sjhibbits case SERF_ERROR_SSL_COMM_FAILED: 366261421Sjhibbits return "An error occurred during SSL communication"; 367261421Sjhibbits case SERF_ERROR_SSL_CERT_FAILED: 368261421Sjhibbits return "An SSL certificate related error occurred "; 369261421Sjhibbits case SERF_ERROR_AUTHN_FAILED: 370261421Sjhibbits return "An error occurred during authentication"; 371261421Sjhibbits case SERF_ERROR_AUTHN_NOT_SUPPORTED: 372261421Sjhibbits return "The requested authentication type(s) are not supported"; 373261421Sjhibbits case SERF_ERROR_AUTHN_MISSING_ATTRIBUTE: 374261421Sjhibbits return "An authentication attribute is missing"; 375261421Sjhibbits case SERF_ERROR_AUTHN_INITALIZATION_FAILED: 376261421Sjhibbits return "Initialization of an authentication type failed"; 377261421Sjhibbits case SERF_ERROR_SSLTUNNEL_SETUP_FAILED: 378261421Sjhibbits return "The proxy server returned an error while setting up the " 379261421Sjhibbits "SSL tunnel."; 380261421Sjhibbits default: 381261421Sjhibbits return NULL; 382261421Sjhibbits } 383261421Sjhibbits 384261421Sjhibbits /* NOTREACHED */ 385261421Sjhibbits} 386261421Sjhibbits