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