context.c revision 251877
1/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein 2 * 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 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 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. 14 */ 15 16#include <apr_pools.h> 17#include <apr_poll.h> 18#include <apr_version.h> 19 20#include "serf.h" 21#include "serf_bucket_util.h" 22 23#include "serf_private.h" 24 25/* Older versions of APR do not have the APR_VERSION_AT_LEAST macro. Those 26 implementations are safe. 27 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. 30 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 35#endif 36#endif 37 38 39/** 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). 43 */ 44void serf__context_progress_delta( 45 void *progress_baton, 46 apr_off_t read, 47 apr_off_t written) 48{ 49 serf_context_t *ctx = progress_baton; 50 51 ctx->progress_read += read; 52 ctx->progress_written += written; 53 54 if (ctx->progress_func) 55 ctx->progress_func(ctx->progress_baton, 56 ctx->progress_read, 57 ctx->progress_written); 58} 59 60 61/* Check for dirty connections and update their pollsets accordingly. */ 62static apr_status_t check_dirty_pollsets(serf_context_t *ctx) 63{ 64 int i; 65 66 /* if we're not dirty, return now. */ 67 if (!ctx->dirty_pollset) { 68 return APR_SUCCESS; 69 } 70 71 for (i = ctx->conns->nelts; i--; ) { 72 serf_connection_t *conn = GET_CONN(ctx, i); 73 apr_status_t status; 74 75 /* if this connection isn't dirty, skip it. */ 76 if (!conn->dirty_conn) { 77 continue; 78 } 79 80 /* reset this connection's flag before we update. */ 81 conn->dirty_conn = 0; 82 83 if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS) 84 return status; 85 } 86 87 /* reset our context flag now */ 88 ctx->dirty_pollset = 0; 89 90 return APR_SUCCESS; 91} 92 93 94static apr_status_t pollset_add(void *user_baton, 95 apr_pollfd_t *pfd, 96 void *serf_baton) 97{ 98 serf_pollset_t *s = (serf_pollset_t*)user_baton; 99 pfd->client_data = serf_baton; 100 return apr_pollset_add(s->pollset, pfd); 101} 102 103static apr_status_t pollset_rm(void *user_baton, 104 apr_pollfd_t *pfd, 105 void *serf_baton) 106{ 107 serf_pollset_t *s = (serf_pollset_t*)user_baton; 108 pfd->client_data = serf_baton; 109 return apr_pollset_remove(s->pollset, pfd); 110} 111 112 113void serf_config_proxy(serf_context_t *ctx, 114 apr_sockaddr_t *address) 115{ 116 ctx->proxy_address = address; 117} 118 119 120void serf_config_credentials_callback(serf_context_t *ctx, 121 serf_credentials_callback_t cred_cb) 122{ 123 ctx->cred_cb = cred_cb; 124} 125 126 127void serf_config_authn_types(serf_context_t *ctx, 128 int authn_types) 129{ 130 ctx->authn_types = authn_types; 131} 132 133 134serf_context_t *serf_context_create_ex( 135 void *user_baton, 136 serf_socket_add_t addf, 137 serf_socket_remove_t rmf, 138 apr_pool_t *pool) 139{ 140 serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx)); 141 142 ctx->pool = pool; 143 144 if (user_baton != NULL) { 145 ctx->pollset_baton = user_baton; 146 ctx->pollset_add = addf; 147 ctx->pollset_rm = rmf; 148 } 149 else { 150 /* build the pollset with a (default) number of connections */ 151 serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps)); 152 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. 157 */ 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). 162 * 163 * So, sadly, we must force using select() on Win32. 164 * 165 * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E 166 */ 167 (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0, 168 APR_POLLSET_SELECT); 169#else 170 (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0); 171#endif 172 ctx->pollset_baton = ps; 173 ctx->pollset_add = pollset_add; 174 ctx->pollset_rm = pollset_rm; 175 } 176 177 /* default to a single connection since that is the typical case */ 178 ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *)); 179 180 /* Initialize progress status */ 181 ctx->progress_read = 0; 182 ctx->progress_written = 0; 183 184 ctx->authn_types = SERF_AUTHN_ALL; 185 186 return ctx; 187} 188 189 190serf_context_t *serf_context_create(apr_pool_t *pool) 191{ 192 return serf_context_create_ex(NULL, NULL, NULL, pool); 193} 194 195apr_status_t serf_context_prerun(serf_context_t *ctx) 196{ 197 apr_status_t status = APR_SUCCESS; 198 if ((status = serf__open_connections(ctx)) != APR_SUCCESS) 199 return status; 200 201 if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS) 202 return status; 203 return status; 204} 205 206 207apr_status_t serf_event_trigger( 208 serf_context_t *s, 209 void *serf_baton, 210 const apr_pollfd_t *desc) 211{ 212 apr_pollfd_t tdesc = { 0 }; 213 apr_status_t status = APR_SUCCESS; 214 serf_io_baton_t *io = serf_baton; 215 216 if (io->type == SERF_IO_CONN) { 217 serf_connection_t *conn = io->u.conn; 218 serf_context_t *ctx = conn->ctx; 219 220 /* If this connection has already failed, return the error again, and try 221 * to remove it from the pollset again 222 */ 223 if (conn->status) { 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, 228 &tdesc, conn); 229 return conn->status; 230 } 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) { 234 return APR_SUCCESS; 235 } 236 237 conn->seen_in_pollset |= desc->rtnevents; 238 239 if ((conn->status = serf__process_connection(conn, 240 desc->rtnevents)) != APR_SUCCESS) { 241 242 /* it's possible that the connection was already reset and thus the 243 socket cleaned up. */ 244 if (conn->skt) { 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, 249 &tdesc, conn); 250 } 251 return conn->status; 252 } 253 } 254 else if (io->type == SERF_IO_LISTENER) { 255 serf_listener_t *l = io->u.listener; 256 257 status = serf__process_listener(l); 258 259 if (status) { 260 return status; 261 } 262 } 263 else if (io->type == SERF_IO_CLIENT) { 264 serf_incoming_t *c = io->u.client; 265 266 status = serf__process_client(c, desc->rtnevents); 267 268 if (status) { 269 return status; 270 } 271 } 272 return status; 273} 274 275 276apr_status_t serf_context_run( 277 serf_context_t *ctx, 278 apr_short_interval_time_t duration, 279 apr_pool_t *pool) 280{ 281 apr_status_t status; 282 apr_int32_t num; 283 const apr_pollfd_t *desc; 284 serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton; 285 286 if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) { 287 return status; 288 } 289 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)) 295 return APR_SUCCESS; 296 297 /* ### do we still need to dispatch stuff here? 298 ### look at the potential return codes. map to our defined 299 ### return values? ... 300 */ 301 return status; 302 } 303 304 while (num--) { 305 serf_connection_t *conn = desc->client_data; 306 307 status = serf_event_trigger(ctx, conn, desc); 308 if (status) { 309 return status; 310 } 311 312 desc++; 313 } 314 315 return APR_SUCCESS; 316} 317 318 319void serf_context_set_progress_cb( 320 serf_context_t *ctx, 321 const serf_progress_t progress_func, 322 void *progress_baton) 323{ 324 ctx->progress_func = progress_func; 325 ctx->progress_baton = progress_baton; 326} 327 328 329serf_bucket_t *serf_context_bucket_socket_create( 330 serf_context_t *ctx, 331 apr_socket_t *skt, 332 serf_bucket_alloc_t *allocator) 333{ 334 serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator); 335 336 /* Use serf's default bytes read/written callback */ 337 serf_bucket_socket_set_read_progress_cb(bucket, 338 serf__context_progress_delta, 339 ctx); 340 341 return bucket; 342} 343 344 345/* ### this really ought to go somewhere else, but... meh. */ 346void serf_lib_version(int *major, int *minor, int *patch) 347{ 348 *major = SERF_MAJOR_VERSION; 349 *minor = SERF_MINOR_VERSION; 350 *patch = SERF_PATCH_VERSION; 351} 352 353 354const char *serf_error_string(apr_status_t errcode) 355{ 356 switch (errcode) 357 { 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 "\ 386 "SSL tunnel."; 387 default: 388 return NULL; 389 } 390 391 /* NOTREACHED */ 392} 393