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