context.c revision 362181
1/* ==================================================================== 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 * ==================================================================== 19 */ 20 21#include <apr_pools.h> 22#include <apr_poll.h> 23#include <apr_version.h> 24 25#include "serf.h" 26#include "serf_bucket_util.h" 27 28#include "serf_private.h" 29 30/** 31 * Callback function (implements serf_progress_t). Takes a number of bytes 32 * read @a read and bytes written @a written, adds those to the total for this 33 * context and notifies an interested party (if any). 34 */ 35void serf__context_progress_delta( 36 void *progress_baton, 37 apr_off_t read, 38 apr_off_t written) 39{ 40 serf_context_t *ctx = progress_baton; 41 42 ctx->progress_read += read; 43 ctx->progress_written += written; 44 45 if (ctx->progress_func) 46 ctx->progress_func(ctx->progress_baton, 47 ctx->progress_read, 48 ctx->progress_written); 49} 50 51 52/* Check for dirty connections and update their pollsets accordingly. */ 53static apr_status_t check_dirty_pollsets(serf_context_t *ctx) 54{ 55 int i; 56 57 /* if we're not dirty, return now. */ 58 if (!ctx->dirty_pollset) { 59 return APR_SUCCESS; 60 } 61 62 for (i = ctx->conns->nelts; i--; ) { 63 serf_connection_t *conn = GET_CONN(ctx, i); 64 apr_status_t status; 65 66 /* if this connection isn't dirty, skip it. */ 67 if (!conn->dirty_conn) { 68 continue; 69 } 70 71 /* reset this connection's flag before we update. */ 72 conn->dirty_conn = 0; 73 74 if ((status = serf__conn_update_pollset(conn)) != APR_SUCCESS) 75 return status; 76 } 77 78 /* reset our context flag now */ 79 ctx->dirty_pollset = 0; 80 81 return APR_SUCCESS; 82} 83 84 85static apr_status_t pollset_add(void *user_baton, 86 apr_pollfd_t *pfd, 87 void *serf_baton) 88{ 89 serf_pollset_t *s = (serf_pollset_t*)user_baton; 90 pfd->client_data = serf_baton; 91 return apr_pollset_add(s->pollset, pfd); 92} 93 94static apr_status_t pollset_rm(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_remove(s->pollset, pfd); 101} 102 103 104void serf_config_proxy(serf_context_t *ctx, 105 apr_sockaddr_t *address) 106{ 107 ctx->proxy_address = address; 108} 109 110 111void serf_config_credentials_callback(serf_context_t *ctx, 112 serf_credentials_callback_t cred_cb) 113{ 114 ctx->cred_cb = cred_cb; 115} 116 117 118void serf_config_authn_types(serf_context_t *ctx, 119 int authn_types) 120{ 121 ctx->authn_types = authn_types; 122} 123 124 125serf_context_t *serf_context_create_ex( 126 void *user_baton, 127 serf_socket_add_t addf, 128 serf_socket_remove_t rmf, 129 apr_pool_t *pool) 130{ 131 serf_context_t *ctx = apr_pcalloc(pool, sizeof(*ctx)); 132 133 ctx->pool = pool; 134 135 if (user_baton != NULL) { 136 ctx->pollset_baton = user_baton; 137 ctx->pollset_add = addf; 138 ctx->pollset_rm = rmf; 139 } 140 else { 141 /* build the pollset with a (default) number of connections */ 142 serf_pollset_t *ps = apr_pcalloc(pool, sizeof(*ps)); 143 144 /* ### TODO: As of APR 1.4.x apr_pollset_create_ex can return a status 145 ### other than APR_SUCCESS, so we should handle it. 146 ### Probably move creation of the pollset to later when we have 147 ### the possibility of returning status to the caller. 148 */ 149#ifdef BROKEN_WSAPOLL 150 /* APR 1.4.x switched to using WSAPoll() on Win32, but it does not 151 * properly handle errors on a non-blocking sockets (such as 152 * connecting to a server where no listener is active). 153 * 154 * So, sadly, we must force using select() on Win32. 155 * 156 * http://mail-archives.apache.org/mod_mbox/apr-dev/201105.mbox/%3CBANLkTin3rBCecCBRvzUA5B-14u-NWxR_Kg@mail.gmail.com%3E 157 */ 158 (void) apr_pollset_create_ex(&ps->pollset, MAX_CONN, pool, 0, 159 APR_POLLSET_SELECT); 160#else 161 (void) apr_pollset_create(&ps->pollset, MAX_CONN, pool, 0); 162#endif 163 ctx->pollset_baton = ps; 164 ctx->pollset_add = pollset_add; 165 ctx->pollset_rm = pollset_rm; 166 } 167 168 /* default to a single connection since that is the typical case */ 169 ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *)); 170 171 /* Initialize progress status */ 172 ctx->progress_read = 0; 173 ctx->progress_written = 0; 174 175 ctx->authn_types = SERF_AUTHN_ALL; 176 ctx->server_authn_info = apr_hash_make(pool); 177 178 return ctx; 179} 180 181 182serf_context_t *serf_context_create(apr_pool_t *pool) 183{ 184 return serf_context_create_ex(NULL, NULL, NULL, pool); 185} 186 187apr_status_t serf_context_prerun(serf_context_t *ctx) 188{ 189 apr_status_t status = APR_SUCCESS; 190 if ((status = serf__open_connections(ctx)) != APR_SUCCESS) 191 return status; 192 193 if ((status = check_dirty_pollsets(ctx)) != APR_SUCCESS) 194 return status; 195 return status; 196} 197 198 199apr_status_t serf_event_trigger( 200 serf_context_t *s, 201 void *serf_baton, 202 const apr_pollfd_t *desc) 203{ 204 apr_pollfd_t tdesc = { 0 }; 205 apr_status_t status = APR_SUCCESS; 206 serf_io_baton_t *io = serf_baton; 207 208 if (io->type == SERF_IO_CONN) { 209 serf_connection_t *conn = io->u.conn; 210 serf_context_t *ctx = conn->ctx; 211 212 /* If this connection has already failed, return the error again, and try 213 * to remove it from the pollset again 214 */ 215 if (conn->status) { 216 tdesc.desc_type = APR_POLL_SOCKET; 217 tdesc.desc.s = conn->skt; 218 tdesc.reqevents = conn->reqevents; 219 ctx->pollset_rm(ctx->pollset_baton, 220 &tdesc, &conn->baton); 221 return conn->status; 222 } 223 /* apr_pollset_poll() can return a conn multiple times... */ 224 if ((conn->seen_in_pollset & desc->rtnevents) != 0 || 225 (conn->seen_in_pollset & APR_POLLHUP) != 0) { 226 return APR_SUCCESS; 227 } 228 229 conn->seen_in_pollset |= desc->rtnevents; 230 231 if ((conn->status = serf__process_connection(conn, 232 desc->rtnevents)) != APR_SUCCESS) { 233 234 /* it's possible that the connection was already reset and thus the 235 socket cleaned up. */ 236 if (conn->skt) { 237 tdesc.desc_type = APR_POLL_SOCKET; 238 tdesc.desc.s = conn->skt; 239 tdesc.reqevents = conn->reqevents; 240 ctx->pollset_rm(ctx->pollset_baton, 241 &tdesc, &conn->baton); 242 } 243 return conn->status; 244 } 245 } 246 else if (io->type == SERF_IO_LISTENER) { 247 serf_listener_t *l = io->u.listener; 248 249 status = serf__process_listener(l); 250 251 if (status) { 252 return status; 253 } 254 } 255 else if (io->type == SERF_IO_CLIENT) { 256 serf_incoming_t *c = io->u.client; 257 258 status = serf__process_client(c, desc->rtnevents); 259 260 if (status) { 261 return status; 262 } 263 } 264 return status; 265} 266 267 268apr_status_t serf_context_run( 269 serf_context_t *ctx, 270 apr_short_interval_time_t duration, 271 apr_pool_t *pool) 272{ 273 apr_status_t status; 274 apr_int32_t num; 275 const apr_pollfd_t *desc; 276 serf_pollset_t *ps = (serf_pollset_t*)ctx->pollset_baton; 277 278 if ((status = serf_context_prerun(ctx)) != APR_SUCCESS) { 279 return status; 280 } 281 282 if ((status = apr_pollset_poll(ps->pollset, duration, &num, 283 &desc)) != APR_SUCCESS) { 284 /* EINTR indicates a handled signal happened during the poll call, 285 ignore, the application can safely retry. */ 286 if (APR_STATUS_IS_EINTR(status)) 287 return APR_SUCCESS; 288 289 /* ### do we still need to dispatch stuff here? 290 ### look at the potential return codes. map to our defined 291 ### return values? ... 292 */ 293 294 /* Use the strict documented error for poll timeouts, to allow proper 295 handling of the other timeout types when returned from 296 serf_event_trigger */ 297 if (APR_STATUS_IS_TIMEUP(status)) 298 return APR_TIMEUP; /* Return the documented error */ 299 return status; 300 } 301 302 while (num--) { 303 serf_io_baton_t *io = desc->client_data; 304 305 status = serf_event_trigger(ctx, io, desc); 306 if (status) { 307 return status; 308 } 309 310 desc++; 311 } 312 313 return APR_SUCCESS; 314} 315 316 317void serf_context_set_progress_cb( 318 serf_context_t *ctx, 319 const serf_progress_t progress_func, 320 void *progress_baton) 321{ 322 ctx->progress_func = progress_func; 323 ctx->progress_baton = progress_baton; 324} 325 326 327serf_bucket_t *serf_context_bucket_socket_create( 328 serf_context_t *ctx, 329 apr_socket_t *skt, 330 serf_bucket_alloc_t *allocator) 331{ 332 serf_bucket_t *bucket = serf_bucket_socket_create(skt, allocator); 333 334 /* Use serf's default bytes read/written callback */ 335 serf_bucket_socket_set_read_progress_cb(bucket, 336 serf__context_progress_delta, 337 ctx); 338 339 return bucket; 340} 341 342 343/* ### this really ought to go somewhere else, but... meh. */ 344void serf_lib_version(int *major, int *minor, int *patch) 345{ 346 *major = SERF_MAJOR_VERSION; 347 *minor = SERF_MINOR_VERSION; 348 *patch = SERF_PATCH_VERSION; 349} 350 351 352const char *serf_error_string(apr_status_t errcode) 353{ 354 switch (errcode) 355 { 356 case SERF_ERROR_CLOSING: 357 return "The connection is closing"; 358 case SERF_ERROR_REQUEST_LOST: 359 return "A request has been lost"; 360 case SERF_ERROR_WAIT_CONN: 361 return "The connection is blocked, pending further action"; 362 case SERF_ERROR_DECOMPRESSION_FAILED: 363 return "An error occurred during decompression"; 364 case SERF_ERROR_BAD_HTTP_RESPONSE: 365 return "The server sent an improper HTTP response"; 366 case SERF_ERROR_TRUNCATED_HTTP_RESPONSE: 367 return "The server sent a truncated HTTP response body."; 368 case SERF_ERROR_ABORTED_CONNECTION: 369 return "The server unexpectedly closed the connection."; 370 case SERF_ERROR_SSL_COMM_FAILED: 371 return "An error occurred during SSL communication"; 372 case SERF_ERROR_SSL_CERT_FAILED: 373 return "An SSL certificate related error occurred "; 374 case SERF_ERROR_AUTHN_FAILED: 375 return "An error occurred during authentication"; 376 case SERF_ERROR_AUTHN_NOT_SUPPORTED: 377 return "The requested authentication type(s) are not supported"; 378 case SERF_ERROR_AUTHN_MISSING_ATTRIBUTE: 379 return "An authentication attribute is missing"; 380 case SERF_ERROR_AUTHN_INITALIZATION_FAILED: 381 return "Initialization of an authentication type failed"; 382 case SERF_ERROR_SSLTUNNEL_SETUP_FAILED: 383 return "The proxy server returned an error while setting up the " 384 "SSL tunnel."; 385 default: 386 return NULL; 387 } 388 389 /* NOTREACHED */ 390} 391