1251877Speter/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein 2251877Speter * 3251877Speter * Licensed under the Apache License, Version 2.0 (the "License"); 4251877Speter * you may not use this file except in compliance with the License. 5251877Speter * You may obtain a copy of the License at 6251877Speter * 7251877Speter * http://www.apache.org/licenses/LICENSE-2.0 8251877Speter * 9251877Speter * Unless required by applicable law or agreed to in writing, software 10251877Speter * distributed under the License is distributed on an "AS IS" BASIS, 11251877Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12251877Speter * See the License for the specific language governing permissions and 13251877Speter * limitations under the License. 14251877Speter */ 15251877Speter 16251877Speter#include <apr_pools.h> 17251877Speter#include <apr_poll.h> 18251877Speter#include <apr_version.h> 19253895Speter#include <apr_portable.h> 20251877Speter 21251877Speter#include "serf.h" 22251877Speter#include "serf_bucket_util.h" 23251877Speter 24251877Speter#include "serf_private.h" 25251877Speter 26251877Speter/* cleanup for sockets */ 27251877Speterstatic apr_status_t clean_skt(void *data) 28251877Speter{ 29251877Speter serf_connection_t *conn = data; 30251877Speter apr_status_t status = APR_SUCCESS; 31251877Speter 32251877Speter if (conn->skt) { 33251877Speter serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt, "cleanup - "); 34251877Speter status = apr_socket_close(conn->skt); 35251877Speter conn->skt = NULL; 36251877Speter serf__log_nopref(SOCK_VERBOSE, "closed socket, status %d\n", status); 37251877Speter } 38251877Speter 39251877Speter return status; 40251877Speter} 41251877Speter 42251877Speterstatic apr_status_t clean_resp(void *data) 43251877Speter{ 44251877Speter serf_request_t *request = data; 45251877Speter 46251877Speter /* The request's RESPOOL is being cleared. */ 47251877Speter 48251877Speter /* If the response has allocated some buckets, then destroy them (since 49251877Speter the bucket may hold resources other than memory in RESPOOL). Also 50251877Speter make sure to set their fields to NULL so connection closure does 51251877Speter not attempt to free them again. */ 52251877Speter if (request->resp_bkt) { 53251877Speter serf_bucket_destroy(request->resp_bkt); 54251877Speter request->resp_bkt = NULL; 55251877Speter } 56251877Speter if (request->req_bkt) { 57251877Speter serf_bucket_destroy(request->req_bkt); 58251877Speter request->req_bkt = NULL; 59251877Speter } 60251877Speter 61251877Speter /* ### should we worry about debug stuff, like that performed in 62251877Speter ### destroy_request()? should we worry about calling req->handler 63251877Speter ### to notify this "cancellation" due to pool clearing? */ 64251877Speter 65251877Speter /* This pool just got cleared/destroyed. Don't try to destroy the pool 66251877Speter (again) when the request is canceled. */ 67251877Speter request->respool = NULL; 68251877Speter 69251877Speter return APR_SUCCESS; 70251877Speter} 71251877Speter 72251877Speter/* cleanup for conns */ 73251877Speterstatic apr_status_t clean_conn(void *data) 74251877Speter{ 75251877Speter serf_connection_t *conn = data; 76251877Speter 77251877Speter serf__log(CONN_VERBOSE, __FILE__, "cleaning up connection 0x%x\n", 78251877Speter conn); 79251877Speter serf_connection_close(conn); 80251877Speter 81251877Speter return APR_SUCCESS; 82251877Speter} 83251877Speter 84262339Speter/* Check if there is data waiting to be sent over the socket. This can happen 85262339Speter in two situations: 86262339Speter - The connection queue has atleast one request with unwritten data. 87262339Speter - All requests are written and the ssl layer wrote some data while reading 88262339Speter the response. This can happen when the server triggers a renegotiation, 89262339Speter e.g. after the first and only request on that connection was received. 90262339Speter Returns 1 if data is pending on CONN, NULL if not. 91262339Speter If NEXT_REQ is not NULL, it will be filled in with the next available request 92262339Speter with unwritten data. */ 93262339Speterstatic int 94262339Speterrequest_or_data_pending(serf_request_t **next_req, serf_connection_t *conn) 95262339Speter{ 96262339Speter serf_request_t *request = conn->requests; 97262339Speter 98262339Speter while (request != NULL && request->req_bkt == NULL && 99262339Speter request->writing_started) 100262339Speter request = request->next; 101262339Speter 102262339Speter if (next_req) 103262339Speter *next_req = request; 104262339Speter 105262339Speter if (request != NULL) { 106262339Speter return 1; 107262339Speter } else if (conn->ostream_head) { 108262339Speter const char *dummy; 109262339Speter apr_size_t len; 110262339Speter apr_status_t status; 111262339Speter 112262339Speter status = serf_bucket_peek(conn->ostream_head, &dummy, 113262339Speter &len); 114262339Speter if (!SERF_BUCKET_READ_ERROR(status) && len) { 115262339Speter serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt, 116262339Speter "All requests written but still data pending.\n"); 117262339Speter return 1; 118262339Speter } 119262339Speter } 120262339Speter 121262339Speter return 0; 122262339Speter} 123262339Speter 124251877Speter/* Update the pollset for this connection. We tweak the pollset based on 125251877Speter * whether we want to read and/or write, given conditions within the 126251877Speter * connection. If the connection is not (yet) in the pollset, then it 127251877Speter * will be added. 128251877Speter */ 129251877Speterapr_status_t serf__conn_update_pollset(serf_connection_t *conn) 130251877Speter{ 131251877Speter serf_context_t *ctx = conn->ctx; 132251877Speter apr_status_t status; 133251877Speter apr_pollfd_t desc = { 0 }; 134251877Speter 135251877Speter if (!conn->skt) { 136251877Speter return APR_SUCCESS; 137251877Speter } 138251877Speter 139251877Speter /* Remove the socket from the poll set. */ 140251877Speter desc.desc_type = APR_POLL_SOCKET; 141251877Speter desc.desc.s = conn->skt; 142251877Speter desc.reqevents = conn->reqevents; 143251877Speter 144251877Speter status = ctx->pollset_rm(ctx->pollset_baton, 145251877Speter &desc, conn); 146251877Speter if (status && !APR_STATUS_IS_NOTFOUND(status)) 147251877Speter return status; 148251877Speter 149251877Speter /* Now put it back in with the correct read/write values. */ 150251877Speter desc.reqevents = APR_POLLHUP | APR_POLLERR; 151251877Speter if (conn->requests && 152251877Speter conn->state != SERF_CONN_INIT) { 153251877Speter /* If there are any outstanding events, then we want to read. */ 154251877Speter /* ### not true. we only want to read IF we have sent some data */ 155251877Speter desc.reqevents |= APR_POLLIN; 156251877Speter 157253895Speter /* Don't write if OpenSSL told us that it needs to read data first. */ 158253895Speter if (conn->stop_writing != 1) { 159251877Speter 160253895Speter /* If the connection is not closing down and 161253895Speter * has unwritten data or 162253895Speter * there are any requests that still have buckets to write out, 163253895Speter * then we want to write. 164253895Speter */ 165253895Speter if (conn->vec_len && 166253895Speter conn->state != SERF_CONN_CLOSING) 167253895Speter desc.reqevents |= APR_POLLOUT; 168253895Speter else { 169253895Speter 170253895Speter if ((conn->probable_keepalive_limit && 171253895Speter conn->completed_requests > conn->probable_keepalive_limit) || 172253895Speter (conn->max_outstanding_requests && 173253895Speter conn->completed_requests - conn->completed_responses >= 174251877Speter conn->max_outstanding_requests)) { 175253895Speter /* we wouldn't try to write any way right now. */ 176253895Speter } 177262339Speter else if (request_or_data_pending(NULL, conn)) { 178262339Speter desc.reqevents |= APR_POLLOUT; 179262339Speter } 180251877Speter } 181251877Speter } 182251877Speter } 183251877Speter 184251877Speter /* If we can have async responses, always look for something to read. */ 185251877Speter if (conn->async_responses) { 186251877Speter desc.reqevents |= APR_POLLIN; 187251877Speter } 188251877Speter 189251877Speter /* save our reqevents, so we can pass it in to remove later. */ 190251877Speter conn->reqevents = desc.reqevents; 191251877Speter 192251877Speter /* Note: even if we don't want to read/write this socket, we still 193251877Speter * want to poll it for hangups and errors. 194251877Speter */ 195251877Speter return ctx->pollset_add(ctx->pollset_baton, 196251877Speter &desc, &conn->baton); 197251877Speter} 198251877Speter 199251877Speter#ifdef SERF_DEBUG_BUCKET_USE 200251877Speter 201251877Speter/* Make sure all response buckets were drained. */ 202251877Speterstatic void check_buckets_drained(serf_connection_t *conn) 203251877Speter{ 204251877Speter serf_request_t *request = conn->requests; 205251877Speter 206251877Speter for ( ; request ; request = request->next ) { 207251877Speter if (request->resp_bkt != NULL) { 208251877Speter /* ### crap. can't do this. this allocator may have un-drained 209251877Speter * ### REQUEST buckets. 210251877Speter */ 211251877Speter /* serf_debug__entered_loop(request->resp_bkt->allocator); */ 212251877Speter /* ### for now, pretend we closed the conn (resets the tracking) */ 213251877Speter serf_debug__closed_conn(request->resp_bkt->allocator); 214251877Speter } 215251877Speter } 216251877Speter} 217251877Speter 218251877Speter#endif 219251877Speter 220253895Speterstatic void destroy_ostream(serf_connection_t *conn) 221253895Speter{ 222253895Speter if (conn->ostream_head != NULL) { 223253895Speter serf_bucket_destroy(conn->ostream_head); 224253895Speter conn->ostream_head = NULL; 225253895Speter conn->ostream_tail = NULL; 226253895Speter } 227253895Speter} 228253895Speter 229253895Speterstatic apr_status_t detect_eof(void *baton, serf_bucket_t *aggregate_bucket) 230253895Speter{ 231253895Speter serf_connection_t *conn = baton; 232253895Speter conn->hit_eof = 1; 233253895Speter return APR_EAGAIN; 234253895Speter} 235253895Speter 236253895Speterstatic apr_status_t do_conn_setup(serf_connection_t *conn) 237253895Speter{ 238253895Speter apr_status_t status; 239253895Speter serf_bucket_t *ostream; 240253895Speter 241253895Speter if (conn->ostream_head == NULL) { 242253895Speter conn->ostream_head = serf_bucket_aggregate_create(conn->allocator); 243253895Speter } 244253895Speter 245253895Speter if (conn->ostream_tail == NULL) { 246253895Speter conn->ostream_tail = serf__bucket_stream_create(conn->allocator, 247253895Speter detect_eof, 248253895Speter conn); 249253895Speter } 250253895Speter 251253895Speter ostream = conn->ostream_tail; 252253895Speter 253253895Speter status = (*conn->setup)(conn->skt, 254253895Speter &conn->stream, 255253895Speter &ostream, 256253895Speter conn->setup_baton, 257253895Speter conn->pool); 258253895Speter if (status) { 259253895Speter /* extra destroy here since it wasn't added to the head bucket yet. */ 260253895Speter serf_bucket_destroy(conn->ostream_tail); 261253895Speter destroy_ostream(conn); 262253895Speter return status; 263253895Speter } 264253895Speter 265253895Speter serf_bucket_aggregate_append(conn->ostream_head, 266253895Speter ostream); 267253895Speter 268253895Speter return status; 269253895Speter} 270253895Speter 271253895Speter/* Set up the input and output stream buckets. 272253895Speter When a tunnel over an http proxy is needed, create a socket bucket and 273253895Speter empty aggregate bucket for sending and receiving unencrypted requests 274253895Speter over the socket. 275253895Speter 276253895Speter After the tunnel is there, or no tunnel was needed, ask the application 277253895Speter to create the input and output buckets, which should take care of the 278253895Speter [en/de]cryption. 279253895Speter */ 280253895Speter 281253895Speterstatic apr_status_t prepare_conn_streams(serf_connection_t *conn, 282253895Speter serf_bucket_t **istream, 283253895Speter serf_bucket_t **ostreamt, 284253895Speter serf_bucket_t **ostreamh) 285253895Speter{ 286253895Speter apr_status_t status; 287253895Speter 288253895Speter if (conn->stream == NULL) { 289253895Speter conn->latency = apr_time_now() - conn->connect_time; 290253895Speter } 291253895Speter 292253895Speter /* Do we need a SSL tunnel first? */ 293253895Speter if (conn->state == SERF_CONN_CONNECTED) { 294253895Speter /* If the connection does not have an associated bucket, then 295253895Speter * call the setup callback to get one. 296253895Speter */ 297253895Speter if (conn->stream == NULL) { 298253895Speter status = do_conn_setup(conn); 299253895Speter if (status) { 300253895Speter return status; 301253895Speter } 302253895Speter } 303253895Speter *ostreamt = conn->ostream_tail; 304253895Speter *ostreamh = conn->ostream_head; 305253895Speter *istream = conn->stream; 306253895Speter } else { 307253895Speter /* SSL tunnel needed and not set up yet, get a direct unencrypted 308253895Speter stream for this socket */ 309253895Speter if (conn->stream == NULL) { 310253895Speter *istream = serf_bucket_socket_create(conn->skt, 311253895Speter conn->allocator); 312253895Speter } 313253895Speter /* Don't create the ostream bucket chain including the ssl_encrypt 314253895Speter bucket yet. This ensure the CONNECT request is sent unencrypted 315253895Speter to the proxy. */ 316253895Speter *ostreamt = *ostreamh = conn->ssltunnel_ostream; 317253895Speter } 318253895Speter 319253895Speter return APR_SUCCESS; 320253895Speter} 321253895Speter 322251877Speter/* Create and connect sockets for any connections which don't have them 323251877Speter * yet. This is the core of our lazy-connect behavior. 324251877Speter */ 325251877Speterapr_status_t serf__open_connections(serf_context_t *ctx) 326251877Speter{ 327251877Speter int i; 328251877Speter 329251877Speter for (i = ctx->conns->nelts; i--; ) { 330251877Speter serf_connection_t *conn = GET_CONN(ctx, i); 331253895Speter serf__authn_info_t *authn_info; 332251877Speter apr_status_t status; 333251877Speter apr_socket_t *skt; 334251877Speter 335251877Speter conn->seen_in_pollset = 0; 336251877Speter 337251877Speter if (conn->skt != NULL) { 338251877Speter#ifdef SERF_DEBUG_BUCKET_USE 339251877Speter check_buckets_drained(conn); 340251877Speter#endif 341251877Speter continue; 342251877Speter } 343251877Speter 344251877Speter /* Delay opening until we have something to deliver! */ 345251877Speter if (conn->requests == NULL) { 346251877Speter continue; 347251877Speter } 348251877Speter 349251877Speter apr_pool_clear(conn->skt_pool); 350251877Speter apr_pool_cleanup_register(conn->skt_pool, conn, clean_skt, clean_skt); 351251877Speter 352251877Speter status = apr_socket_create(&skt, conn->address->family, 353251877Speter SOCK_STREAM, 354251877Speter#if APR_MAJOR_VERSION > 0 355251877Speter APR_PROTO_TCP, 356251877Speter#endif 357251877Speter conn->skt_pool); 358251877Speter serf__log(SOCK_VERBOSE, __FILE__, 359251877Speter "created socket for conn 0x%x, status %d\n", conn, status); 360251877Speter if (status != APR_SUCCESS) 361251877Speter return status; 362251877Speter 363251877Speter /* Set the socket to be non-blocking */ 364251877Speter if ((status = apr_socket_timeout_set(skt, 0)) != APR_SUCCESS) 365251877Speter return status; 366251877Speter 367251877Speter /* Disable Nagle's algorithm */ 368251877Speter if ((status = apr_socket_opt_set(skt, 369251877Speter APR_TCP_NODELAY, 1)) != APR_SUCCESS) 370251877Speter return status; 371251877Speter 372251877Speter /* Configured. Store it into the connection now. */ 373251877Speter conn->skt = skt; 374251877Speter 375251877Speter /* Remember time when we started connecting to server to calculate 376251877Speter network latency. */ 377251877Speter conn->connect_time = apr_time_now(); 378251877Speter 379251877Speter /* Now that the socket is set up, let's connect it. This should 380251877Speter * return immediately. 381251877Speter */ 382251877Speter status = apr_socket_connect(skt, conn->address); 383251877Speter serf__log_skt(SOCK_VERBOSE, __FILE__, skt, 384251877Speter "connected socket for conn 0x%x, status %d\n", 385251877Speter conn, status); 386253895Speter if (status != APR_SUCCESS) { 387251877Speter if (!APR_STATUS_IS_EINPROGRESS(status)) 388251877Speter return status; 389251877Speter } 390251877Speter 391251877Speter /* Flag our pollset as dirty now that we have a new socket. */ 392251877Speter conn->dirty_conn = 1; 393251877Speter ctx->dirty_pollset = 1; 394251877Speter 395251877Speter /* If the authentication was already started on another connection, 396251877Speter prepare this connection (it might be possible to skip some 397251877Speter part of the handshaking). */ 398251877Speter if (ctx->proxy_address) { 399253895Speter authn_info = &ctx->proxy_authn_info; 400253895Speter if (authn_info->scheme) { 401253895Speter authn_info->scheme->init_conn_func(authn_info->scheme, 407, 402253895Speter conn, conn->pool); 403253895Speter } 404251877Speter } 405251877Speter 406253895Speter authn_info = serf__get_authn_info_for_server(conn); 407253895Speter if (authn_info->scheme) { 408253895Speter authn_info->scheme->init_conn_func(authn_info->scheme, 401, 409253895Speter conn, conn->pool); 410253895Speter } 411251877Speter 412251877Speter /* Does this connection require a SSL tunnel over the proxy? */ 413251877Speter if (ctx->proxy_address && strcmp(conn->host_info.scheme, "https") == 0) 414251877Speter serf__ssltunnel_connect(conn); 415253895Speter else { 416253895Speter serf_bucket_t *dummy1, *dummy2; 417253895Speter 418251877Speter conn->state = SERF_CONN_CONNECTED; 419251877Speter 420253895Speter status = prepare_conn_streams(conn, &conn->stream, 421253895Speter &dummy1, &dummy2); 422253895Speter if (status) { 423253895Speter return status; 424253895Speter } 425253895Speter } 426251877Speter } 427251877Speter 428251877Speter return APR_SUCCESS; 429251877Speter} 430251877Speter 431262339Speterstatic apr_status_t no_more_writes(serf_connection_t *conn) 432251877Speter{ 433251877Speter /* Note that we should hold new requests until we open our new socket. */ 434251877Speter conn->state = SERF_CONN_CLOSING; 435262339Speter serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt, 436262339Speter "stop writing on conn 0x%x\n", conn); 437251877Speter 438251877Speter /* Clear our iovec. */ 439251877Speter conn->vec_len = 0; 440251877Speter 441251877Speter /* Update the pollset to know we don't want to write on this socket any 442251877Speter * more. 443251877Speter */ 444251877Speter conn->dirty_conn = 1; 445251877Speter conn->ctx->dirty_pollset = 1; 446251877Speter return APR_SUCCESS; 447251877Speter} 448251877Speter 449251877Speter/* Read the 'Connection' header from the response. Return SERF_ERROR_CLOSING if 450251877Speter * the header contains value 'close' indicating the server is closing the 451251877Speter * connection right after this response. 452251877Speter * Otherwise returns APR_SUCCESS. 453251877Speter */ 454251877Speterstatic apr_status_t is_conn_closing(serf_bucket_t *response) 455251877Speter{ 456251877Speter serf_bucket_t *hdrs; 457251877Speter const char *val; 458251877Speter 459251877Speter hdrs = serf_bucket_response_get_headers(response); 460251877Speter val = serf_bucket_headers_get(hdrs, "Connection"); 461251877Speter if (val && strcasecmp("close", val) == 0) 462251877Speter { 463251877Speter return SERF_ERROR_CLOSING; 464251877Speter } 465251877Speter 466251877Speter return APR_SUCCESS; 467251877Speter} 468251877Speter 469251877Speterstatic void link_requests(serf_request_t **list, serf_request_t **tail, 470251877Speter serf_request_t *request) 471251877Speter{ 472251877Speter if (*list == NULL) { 473251877Speter *list = request; 474251877Speter *tail = request; 475251877Speter } 476251877Speter else { 477251877Speter (*tail)->next = request; 478251877Speter *tail = request; 479251877Speter } 480251877Speter} 481251877Speter 482251877Speterstatic apr_status_t destroy_request(serf_request_t *request) 483251877Speter{ 484251877Speter serf_connection_t *conn = request->conn; 485251877Speter 486251877Speter /* The request and response buckets are no longer needed, 487251877Speter nor is the request's pool. */ 488251877Speter if (request->resp_bkt) { 489251877Speter serf_debug__closed_conn(request->resp_bkt->allocator); 490251877Speter serf_bucket_destroy(request->resp_bkt); 491251877Speter request->resp_bkt = NULL; 492251877Speter } 493251877Speter if (request->req_bkt) { 494251877Speter serf_debug__closed_conn(request->req_bkt->allocator); 495251877Speter serf_bucket_destroy(request->req_bkt); 496251877Speter request->req_bkt = NULL; 497251877Speter } 498251877Speter 499251877Speter serf_debug__bucket_alloc_check(request->allocator); 500251877Speter if (request->respool) { 501251877Speter /* ### unregister the pool cleanup for self? */ 502251877Speter apr_pool_destroy(request->respool); 503251877Speter } 504251877Speter 505251877Speter serf_bucket_mem_free(conn->allocator, request); 506251877Speter 507251877Speter return APR_SUCCESS; 508251877Speter} 509251877Speter 510251877Speterstatic apr_status_t cancel_request(serf_request_t *request, 511251877Speter serf_request_t **list, 512251877Speter int notify_request) 513251877Speter{ 514251877Speter /* If we haven't run setup, then we won't have a handler to call. */ 515251877Speter if (request->handler && notify_request) { 516251877Speter /* We actually don't care what the handler returns. 517251877Speter * We have bigger matters at hand. 518251877Speter */ 519251877Speter (*request->handler)(request, NULL, request->handler_baton, 520251877Speter request->respool); 521251877Speter } 522251877Speter 523251877Speter if (*list == request) { 524251877Speter *list = request->next; 525251877Speter } 526251877Speter else { 527251877Speter serf_request_t *scan = *list; 528251877Speter 529251877Speter while (scan->next && scan->next != request) 530251877Speter scan = scan->next; 531251877Speter 532251877Speter if (scan->next) { 533251877Speter scan->next = scan->next->next; 534251877Speter } 535251877Speter } 536251877Speter 537251877Speter return destroy_request(request); 538251877Speter} 539251877Speter 540251877Speterstatic apr_status_t remove_connection(serf_context_t *ctx, 541251877Speter serf_connection_t *conn) 542251877Speter{ 543251877Speter apr_pollfd_t desc = { 0 }; 544251877Speter 545251877Speter desc.desc_type = APR_POLL_SOCKET; 546251877Speter desc.desc.s = conn->skt; 547251877Speter desc.reqevents = conn->reqevents; 548251877Speter 549251877Speter return ctx->pollset_rm(ctx->pollset_baton, 550251877Speter &desc, conn); 551251877Speter} 552251877Speter 553251877Speter/* A socket was closed, inform the application. */ 554251877Speterstatic void handle_conn_closed(serf_connection_t *conn, apr_status_t status) 555251877Speter{ 556251877Speter (*conn->closed)(conn, conn->closed_baton, status, 557251877Speter conn->pool); 558251877Speter} 559251877Speter 560251877Speterstatic apr_status_t reset_connection(serf_connection_t *conn, 561251877Speter int requeue_requests) 562251877Speter{ 563251877Speter serf_context_t *ctx = conn->ctx; 564251877Speter apr_status_t status; 565251877Speter serf_request_t *old_reqs; 566251877Speter 567251877Speter conn->probable_keepalive_limit = conn->completed_responses; 568251877Speter conn->completed_requests = 0; 569251877Speter conn->completed_responses = 0; 570251877Speter 571251877Speter old_reqs = conn->requests; 572251877Speter 573251877Speter conn->requests = NULL; 574251877Speter conn->requests_tail = NULL; 575251877Speter 576251877Speter /* Handle all outstanding requests. These have either not been written yet, 577251877Speter or have been written but the expected reply wasn't received yet. */ 578251877Speter while (old_reqs) { 579251877Speter /* If we haven't started to write the connection, bring it over 580251877Speter * unchanged to our new socket. 581262339Speter * Do not copy a CONNECT request to the new connection, the ssl tunnel 582262339Speter * setup code will create a new CONNECT request already. 583251877Speter */ 584262339Speter if (requeue_requests && !old_reqs->writing_started && 585262339Speter !old_reqs->ssltunnel) { 586262339Speter 587251877Speter serf_request_t *req = old_reqs; 588251877Speter old_reqs = old_reqs->next; 589251877Speter req->next = NULL; 590251877Speter link_requests(&conn->requests, &conn->requests_tail, req); 591251877Speter } 592251877Speter else { 593251877Speter /* Request has been consumed, or we don't want to requeue the 594251877Speter request. Either way, inform the application that the request 595251877Speter is cancelled. */ 596251877Speter cancel_request(old_reqs, &old_reqs, requeue_requests); 597251877Speter } 598251877Speter } 599251877Speter 600251877Speter /* Requests queue has been prepared for a new socket, close the old one. */ 601251877Speter if (conn->skt != NULL) { 602251877Speter remove_connection(ctx, conn); 603251877Speter status = apr_socket_close(conn->skt); 604251877Speter serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt, 605251877Speter "closed socket, status %d\n", status); 606251877Speter if (conn->closed != NULL) { 607251877Speter handle_conn_closed(conn, status); 608251877Speter } 609251877Speter conn->skt = NULL; 610251877Speter } 611251877Speter 612251877Speter if (conn->stream != NULL) { 613251877Speter serf_bucket_destroy(conn->stream); 614251877Speter conn->stream = NULL; 615251877Speter } 616251877Speter 617251877Speter destroy_ostream(conn); 618251877Speter 619251877Speter /* Don't try to resume any writes */ 620251877Speter conn->vec_len = 0; 621251877Speter 622251877Speter conn->dirty_conn = 1; 623251877Speter conn->ctx->dirty_pollset = 1; 624251877Speter conn->state = SERF_CONN_INIT; 625251877Speter 626251877Speter serf__log(CONN_VERBOSE, __FILE__, "reset connection 0x%x\n", conn); 627251877Speter 628251877Speter conn->status = APR_SUCCESS; 629251877Speter 630251877Speter /* Let our context know that we've 'reset' the socket already. */ 631251877Speter conn->seen_in_pollset |= APR_POLLHUP; 632251877Speter 633251877Speter /* Found the connection. Closed it. All done. */ 634251877Speter return APR_SUCCESS; 635251877Speter} 636251877Speter 637251877Speterstatic apr_status_t socket_writev(serf_connection_t *conn) 638251877Speter{ 639251877Speter apr_size_t written; 640251877Speter apr_status_t status; 641251877Speter 642251877Speter status = apr_socket_sendv(conn->skt, conn->vec, 643251877Speter conn->vec_len, &written); 644253895Speter if (status && !APR_STATUS_IS_EAGAIN(status)) 645251877Speter serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt, 646251877Speter "socket_sendv error %d\n", status); 647251877Speter 648251877Speter /* did we write everything? */ 649251877Speter if (written) { 650251877Speter apr_size_t len = 0; 651251877Speter int i; 652251877Speter 653251877Speter serf__log_skt(SOCK_MSG_VERBOSE, __FILE__, conn->skt, 654251877Speter "--- socket_sendv:\n"); 655251877Speter 656251877Speter for (i = 0; i < conn->vec_len; i++) { 657251877Speter len += conn->vec[i].iov_len; 658251877Speter if (written < len) { 659251877Speter serf__log_nopref(SOCK_MSG_VERBOSE, "%.*s", 660251877Speter conn->vec[i].iov_len - (len - written), 661251877Speter conn->vec[i].iov_base); 662251877Speter if (i) { 663251877Speter memmove(conn->vec, &conn->vec[i], 664251877Speter sizeof(struct iovec) * (conn->vec_len - i)); 665251877Speter conn->vec_len -= i; 666251877Speter } 667251877Speter conn->vec[0].iov_base = (char *)conn->vec[0].iov_base + (conn->vec[0].iov_len - (len - written)); 668251877Speter conn->vec[0].iov_len = len - written; 669251877Speter break; 670251877Speter } else { 671251877Speter serf__log_nopref(SOCK_MSG_VERBOSE, "%.*s", 672251877Speter conn->vec[i].iov_len, conn->vec[i].iov_base); 673251877Speter } 674251877Speter } 675251877Speter if (len == written) { 676251877Speter conn->vec_len = 0; 677251877Speter } 678251877Speter serf__log_nopref(SOCK_MSG_VERBOSE, "-(%d)-\n", written); 679251877Speter 680251877Speter /* Log progress information */ 681251877Speter serf__context_progress_delta(conn->ctx, 0, written); 682251877Speter } 683251877Speter 684251877Speter return status; 685251877Speter} 686251877Speter 687253895Speterstatic apr_status_t setup_request(serf_request_t *request) 688251877Speter{ 689253895Speter serf_connection_t *conn = request->conn; 690251877Speter apr_status_t status; 691251877Speter 692253895Speter /* Now that we are about to serve the request, allocate a pool. */ 693253895Speter apr_pool_create(&request->respool, conn->pool); 694253895Speter request->allocator = serf_bucket_allocator_create(request->respool, 695253895Speter NULL, NULL); 696253895Speter apr_pool_cleanup_register(request->respool, request, 697253895Speter clean_resp, clean_resp); 698251877Speter 699253895Speter /* Fill in the rest of the values for the request. */ 700253895Speter status = request->setup(request, request->setup_baton, 701253895Speter &request->req_bkt, 702253895Speter &request->acceptor, 703253895Speter &request->acceptor_baton, 704253895Speter &request->handler, 705253895Speter &request->handler_baton, 706253895Speter request->respool); 707251877Speter return status; 708251877Speter} 709251877Speter 710251877Speter/* write data out to the connection */ 711251877Speterstatic apr_status_t write_to_connection(serf_connection_t *conn) 712251877Speter{ 713251877Speter if (conn->probable_keepalive_limit && 714251877Speter conn->completed_requests > conn->probable_keepalive_limit) { 715251877Speter 716251877Speter conn->dirty_conn = 1; 717251877Speter conn->ctx->dirty_pollset = 1; 718251877Speter 719251877Speter /* backoff for now. */ 720251877Speter return APR_SUCCESS; 721251877Speter } 722251877Speter 723251877Speter /* Keep reading and sending until we run out of stuff to read, or 724251877Speter * writing would block. 725251877Speter */ 726251877Speter while (1) { 727262339Speter serf_request_t *request; 728251877Speter int stop_reading = 0; 729251877Speter apr_status_t status; 730251877Speter apr_status_t read_status; 731262339Speter serf_bucket_t *ostreamt; 732262339Speter serf_bucket_t *ostreamh; 733251877Speter int max_outstanding_requests = conn->max_outstanding_requests; 734251877Speter 735251877Speter /* If we're setting up an ssl tunnel, we can't send real requests 736251877Speter at yet, as they need to be encrypted and our encrypt buckets 737251877Speter aren't created yet as we still need to read the unencrypted 738251877Speter response of the CONNECT request. */ 739251877Speter if (conn->state != SERF_CONN_CONNECTED) 740251877Speter max_outstanding_requests = 1; 741251877Speter 742251877Speter if (max_outstanding_requests && 743251877Speter conn->completed_requests - 744251877Speter conn->completed_responses >= max_outstanding_requests) { 745251877Speter /* backoff for now. */ 746251877Speter return APR_SUCCESS; 747251877Speter } 748251877Speter 749251877Speter /* If we have unwritten data, then write what we can. */ 750251877Speter while (conn->vec_len) { 751251877Speter status = socket_writev(conn); 752251877Speter 753251877Speter /* If the write would have blocked, then we're done. Don't try 754251877Speter * to write anything else to the socket. 755251877Speter */ 756251877Speter if (APR_STATUS_IS_EAGAIN(status)) 757251877Speter return APR_SUCCESS; 758251877Speter if (APR_STATUS_IS_EPIPE(status) || 759251877Speter APR_STATUS_IS_ECONNRESET(status) || 760251877Speter APR_STATUS_IS_ECONNABORTED(status)) 761262339Speter return no_more_writes(conn); 762251877Speter if (status) 763251877Speter return status; 764251877Speter } 765251877Speter /* ### can we have a short write, yet no EAGAIN? a short write 766251877Speter ### would imply unwritten_len > 0 ... */ 767251877Speter /* assert: unwritten_len == 0. */ 768251877Speter 769251877Speter /* We may need to move forward to a request which has something 770251877Speter * to write. 771251877Speter */ 772262339Speter if (!request_or_data_pending(&request, conn)) { 773251877Speter /* No more requests (with data) are registered with the 774262339Speter * connection, and no data is pending on the outgoing stream. 775262339Speter * Let's update the pollset so that we don't try to write to this 776262339Speter * socket again. 777251877Speter */ 778251877Speter conn->dirty_conn = 1; 779251877Speter conn->ctx->dirty_pollset = 1; 780251877Speter return APR_SUCCESS; 781251877Speter } 782251877Speter 783251877Speter status = prepare_conn_streams(conn, &conn->stream, &ostreamt, &ostreamh); 784251877Speter if (status) { 785251877Speter return status; 786251877Speter } 787251877Speter 788262339Speter if (request) { 789262339Speter if (request->req_bkt == NULL) { 790262339Speter read_status = setup_request(request); 791262339Speter if (read_status) { 792262339Speter /* Something bad happened. Propagate any errors. */ 793262339Speter return read_status; 794262339Speter } 795251877Speter } 796251877Speter 797262339Speter if (!request->writing_started) { 798262339Speter request->writing_started = 1; 799262339Speter serf_bucket_aggregate_append(ostreamt, request->req_bkt); 800262339Speter } 801251877Speter } 802251877Speter 803251877Speter /* ### optimize at some point by using read_for_sendfile */ 804253895Speter /* TODO: now that read_iovec will effectively try to return as much 805253895Speter data as available, we probably don't want to read ALL_AVAIL, but 806253895Speter a lower number, like the size of one or a few TCP packets, the 807253895Speter available TCP buffer size ... */ 808251877Speter read_status = serf_bucket_read_iovec(ostreamh, 809251877Speter SERF_READ_ALL_AVAIL, 810251877Speter IOV_MAX, 811251877Speter conn->vec, 812251877Speter &conn->vec_len); 813251877Speter 814251877Speter if (!conn->hit_eof) { 815253895Speter if (APR_STATUS_IS_EAGAIN(read_status)) { 816251877Speter /* We read some stuff, but should not try to read again. */ 817251877Speter stop_reading = 1; 818253895Speter } 819253895Speter else if (read_status == SERF_ERROR_WAIT_CONN) { 820253895Speter /* The bucket told us that it can't provide more data until 821253895Speter more data is read from the socket. This normally happens 822253895Speter during a SSL handshake. 823251877Speter 824253895Speter We should avoid looking for writability for a while so 825253895Speter that (hopefully) something will appear in the bucket so 826253895Speter we can actually write something. otherwise, we could 827253895Speter end up in a CPU spin: socket wants something, but we 828253895Speter don't have anything (and keep returning EAGAIN) 829253895Speter */ 830253895Speter conn->stop_writing = 1; 831253895Speter conn->dirty_conn = 1; 832253895Speter conn->ctx->dirty_pollset = 1; 833251877Speter } 834251877Speter else if (read_status && !APR_STATUS_IS_EOF(read_status)) { 835251877Speter /* Something bad happened. Propagate any errors. */ 836251877Speter return read_status; 837251877Speter } 838251877Speter } 839251877Speter 840251877Speter /* If we got some data, then deliver it. */ 841251877Speter /* ### what to do if we got no data?? is that a problem? */ 842251877Speter if (conn->vec_len > 0) { 843251877Speter status = socket_writev(conn); 844251877Speter 845251877Speter /* If we can't write any more, or an error occurred, then 846251877Speter * we're done here. 847251877Speter */ 848251877Speter if (APR_STATUS_IS_EAGAIN(status)) 849251877Speter return APR_SUCCESS; 850251877Speter if (APR_STATUS_IS_EPIPE(status)) 851262339Speter return no_more_writes(conn); 852251877Speter if (APR_STATUS_IS_ECONNRESET(status) || 853251877Speter APR_STATUS_IS_ECONNABORTED(status)) { 854262339Speter return no_more_writes(conn); 855251877Speter } 856251877Speter if (status) 857251877Speter return status; 858251877Speter } 859251877Speter 860251877Speter if (read_status == SERF_ERROR_WAIT_CONN) { 861251877Speter stop_reading = 1; 862253895Speter conn->stop_writing = 1; 863253895Speter conn->dirty_conn = 1; 864253895Speter conn->ctx->dirty_pollset = 1; 865251877Speter } 866262339Speter else if (request && read_status && conn->hit_eof && 867262339Speter conn->vec_len == 0) { 868251877Speter /* If we hit the end of the request bucket and all of its data has 869251877Speter * been written, then clear it out to signify that we're done 870251877Speter * sending the request. On the next iteration through this loop: 871251877Speter * - if there are remaining bytes they will be written, and as the 872251877Speter * request bucket will be completely read it will be destroyed then. 873251877Speter * - we'll see if there are other requests that need to be sent 874251877Speter * ("pipelining"). 875251877Speter */ 876251877Speter conn->hit_eof = 0; 877251877Speter serf_bucket_destroy(request->req_bkt); 878251877Speter request->req_bkt = NULL; 879251877Speter 880251877Speter /* If our connection has async responses enabled, we're not 881251877Speter * going to get a reply back, so kill the request. 882251877Speter */ 883251877Speter if (conn->async_responses) { 884251877Speter conn->requests = request->next; 885251877Speter destroy_request(request); 886251877Speter } 887251877Speter 888251877Speter conn->completed_requests++; 889251877Speter 890251877Speter if (conn->probable_keepalive_limit && 891251877Speter conn->completed_requests > conn->probable_keepalive_limit) { 892251877Speter /* backoff for now. */ 893251877Speter stop_reading = 1; 894251877Speter } 895251877Speter } 896251877Speter 897251877Speter if (stop_reading) { 898251877Speter return APR_SUCCESS; 899251877Speter } 900251877Speter } 901251877Speter /* NOTREACHED */ 902251877Speter} 903251877Speter 904251877Speter/* A response message was received from the server, so call 905251877Speter the handler as specified on the original request. */ 906251877Speterstatic apr_status_t handle_response(serf_request_t *request, 907251877Speter apr_pool_t *pool) 908251877Speter{ 909251877Speter apr_status_t status = APR_SUCCESS; 910251877Speter int consumed_response = 0; 911251877Speter 912251877Speter /* Only enable the new authentication framework if the program has 913251877Speter * registered an authentication credential callback. 914251877Speter * 915251877Speter * This permits older Serf apps to still handle authentication 916251877Speter * themselves by not registering credential callbacks. 917251877Speter */ 918251877Speter if (request->conn->ctx->cred_cb) { 919251877Speter status = serf__handle_auth_response(&consumed_response, 920251877Speter request, 921251877Speter request->resp_bkt, 922251877Speter request->handler_baton, 923251877Speter pool); 924251877Speter 925251877Speter /* If there was an error reading the response (maybe there wasn't 926251877Speter enough data available), don't bother passing the response to the 927251877Speter application. 928251877Speter 929251877Speter If the authentication was tried, but failed, pass the response 930251877Speter to the application, maybe it can do better. */ 931262339Speter if (status) { 932251877Speter return status; 933251877Speter } 934251877Speter } 935251877Speter 936251877Speter if (!consumed_response) { 937251877Speter return (*request->handler)(request, 938251877Speter request->resp_bkt, 939251877Speter request->handler_baton, 940251877Speter pool); 941251877Speter } 942251877Speter 943251877Speter return status; 944251877Speter} 945251877Speter 946251877Speter/* An async response message was received from the server. */ 947251877Speterstatic apr_status_t handle_async_response(serf_connection_t *conn, 948251877Speter apr_pool_t *pool) 949251877Speter{ 950251877Speter apr_status_t status; 951251877Speter 952251877Speter if (conn->current_async_response == NULL) { 953251877Speter conn->current_async_response = 954251877Speter (*conn->async_acceptor)(NULL, conn->stream, 955251877Speter conn->async_acceptor_baton, pool); 956251877Speter } 957251877Speter 958251877Speter status = (*conn->async_handler)(NULL, conn->current_async_response, 959251877Speter conn->async_handler_baton, pool); 960251877Speter 961251877Speter if (APR_STATUS_IS_EOF(status)) { 962251877Speter serf_bucket_destroy(conn->current_async_response); 963251877Speter conn->current_async_response = NULL; 964251877Speter status = APR_SUCCESS; 965251877Speter } 966251877Speter 967251877Speter return status; 968251877Speter} 969251877Speter 970253895Speter 971253895Speterapr_status_t 972253895Speterserf__provide_credentials(serf_context_t *ctx, 973253895Speter char **username, 974253895Speter char **password, 975253895Speter serf_request_t *request, void *baton, 976253895Speter int code, const char *authn_type, 977253895Speter const char *realm, 978253895Speter apr_pool_t *pool) 979253895Speter{ 980253895Speter serf_connection_t *conn = request->conn; 981253895Speter serf_request_t *authn_req = request; 982253895Speter apr_status_t status; 983253895Speter 984253895Speter if (request->ssltunnel == 1 && 985253895Speter conn->state == SERF_CONN_SETUP_SSLTUNNEL) { 986253895Speter /* This is a CONNECT request to set up an SSL tunnel over a proxy. 987253895Speter This request is created by serf, so if the proxy requires 988253895Speter authentication, we can't ask the application for credentials with 989253895Speter this request. 990253895Speter 991253895Speter Solution: setup the first request created by the application on 992253895Speter this connection, and use that request and its handler_baton to 993253895Speter call back to the application. */ 994253895Speter 995253895Speter authn_req = request->next; 996253895Speter /* assert: app_request != NULL */ 997253895Speter if (!authn_req) 998253895Speter return APR_EGENERAL; 999253895Speter 1000253895Speter if (!authn_req->req_bkt) { 1001253895Speter apr_status_t status; 1002253895Speter 1003253895Speter status = setup_request(authn_req); 1004253895Speter /* If we can't setup a request, don't bother setting up the 1005253895Speter ssl tunnel. */ 1006253895Speter if (status) 1007253895Speter return status; 1008253895Speter } 1009253895Speter } 1010253895Speter 1011253895Speter /* Ask the application. */ 1012253895Speter status = (*ctx->cred_cb)(username, password, 1013253895Speter authn_req, authn_req->handler_baton, 1014253895Speter code, authn_type, realm, pool); 1015253895Speter if (status) 1016253895Speter return status; 1017253895Speter 1018253895Speter return APR_SUCCESS; 1019253895Speter} 1020253895Speter 1021251877Speter/* read data from the connection */ 1022251877Speterstatic apr_status_t read_from_connection(serf_connection_t *conn) 1023251877Speter{ 1024251877Speter apr_status_t status; 1025251877Speter apr_pool_t *tmppool; 1026251877Speter int close_connection = FALSE; 1027251877Speter 1028251877Speter /* Whatever is coming in on the socket corresponds to the first request 1029251877Speter * on our chain. 1030251877Speter */ 1031251877Speter serf_request_t *request = conn->requests; 1032251877Speter 1033253895Speter /* If the stop_writing flag was set on the connection, reset it now because 1034253895Speter there is some data to read. */ 1035253895Speter if (conn->stop_writing) { 1036253895Speter conn->stop_writing = 0; 1037253895Speter conn->dirty_conn = 1; 1038253895Speter conn->ctx->dirty_pollset = 1; 1039253895Speter } 1040253895Speter 1041251877Speter /* assert: request != NULL */ 1042251877Speter 1043251877Speter if ((status = apr_pool_create(&tmppool, conn->pool)) != APR_SUCCESS) 1044251877Speter goto error; 1045251877Speter 1046251877Speter /* Invoke response handlers until we have no more work. */ 1047251877Speter while (1) { 1048251877Speter serf_bucket_t *dummy1, *dummy2; 1049251877Speter 1050251877Speter apr_pool_clear(tmppool); 1051251877Speter 1052251877Speter /* Only interested in the input stream here. */ 1053251877Speter status = prepare_conn_streams(conn, &conn->stream, &dummy1, &dummy2); 1054251877Speter if (status) { 1055251877Speter goto error; 1056251877Speter } 1057251877Speter 1058251877Speter /* We have a different codepath when we can have async responses. */ 1059251877Speter if (conn->async_responses) { 1060251877Speter /* TODO What about socket errors? */ 1061251877Speter status = handle_async_response(conn, tmppool); 1062251877Speter if (APR_STATUS_IS_EAGAIN(status)) { 1063251877Speter status = APR_SUCCESS; 1064251877Speter goto error; 1065251877Speter } 1066251877Speter if (status) { 1067251877Speter goto error; 1068251877Speter } 1069251877Speter continue; 1070251877Speter } 1071251877Speter 1072251877Speter /* We are reading a response for a request we haven't 1073251877Speter * written yet! 1074251877Speter * 1075251877Speter * This shouldn't normally happen EXCEPT: 1076251877Speter * 1077251877Speter * 1) when the other end has closed the socket and we're 1078251877Speter * pending an EOF return. 1079251877Speter * 2) Doing the initial SSL handshake - we'll get EAGAIN 1080251877Speter * as the SSL buckets will hide the handshake from us 1081251877Speter * but not return any data. 1082251877Speter * 3) When the server sends us an SSL alert. 1083251877Speter * 1084251877Speter * In these cases, we should not receive any actual user data. 1085251877Speter * 1086251877Speter * 4) When the server sends a error response, like 408 Request timeout. 1087251877Speter * This response should be passed to the application. 1088251877Speter * 1089251877Speter * If we see an EOF (due to either an expired timeout or the server 1090251877Speter * sending the SSL 'close notify' shutdown alert), we'll reset the 1091251877Speter * connection and open a new one. 1092251877Speter */ 1093262339Speter if (request->req_bkt || !request->writing_started) { 1094251877Speter const char *data; 1095251877Speter apr_size_t len; 1096251877Speter 1097251877Speter status = serf_bucket_peek(conn->stream, &data, &len); 1098251877Speter 1099251877Speter if (APR_STATUS_IS_EOF(status)) { 1100251877Speter reset_connection(conn, 1); 1101251877Speter status = APR_SUCCESS; 1102251877Speter goto error; 1103251877Speter } 1104251877Speter else if (APR_STATUS_IS_EAGAIN(status) && !len) { 1105251877Speter status = APR_SUCCESS; 1106251877Speter goto error; 1107251877Speter } else if (status && !APR_STATUS_IS_EAGAIN(status)) { 1108251877Speter /* Read error */ 1109251877Speter goto error; 1110251877Speter } 1111251877Speter 1112251877Speter /* Unexpected response from the server */ 1113251877Speter 1114251877Speter } 1115251877Speter 1116251877Speter /* If the request doesn't have a response bucket, then call the 1117251877Speter * acceptor to get one created. 1118251877Speter */ 1119251877Speter if (request->resp_bkt == NULL) { 1120251877Speter request->resp_bkt = (*request->acceptor)(request, conn->stream, 1121251877Speter request->acceptor_baton, 1122251877Speter tmppool); 1123251877Speter apr_pool_clear(tmppool); 1124251877Speter } 1125251877Speter 1126251877Speter status = handle_response(request, tmppool); 1127251877Speter 1128251877Speter /* Some systems will not generate a HUP poll event so we have to 1129251877Speter * handle the ECONNRESET issue and ECONNABORT here. 1130251877Speter */ 1131251877Speter if (APR_STATUS_IS_ECONNRESET(status) || 1132251877Speter APR_STATUS_IS_ECONNABORTED(status) || 1133251877Speter status == SERF_ERROR_REQUEST_LOST) { 1134251877Speter /* If the connection had ever been good, be optimistic & try again. 1135251877Speter * If it has never tried again (incl. a retry), fail. 1136251877Speter */ 1137251877Speter if (conn->completed_responses) { 1138251877Speter reset_connection(conn, 1); 1139251877Speter status = APR_SUCCESS; 1140251877Speter } 1141251877Speter else if (status == SERF_ERROR_REQUEST_LOST) { 1142251877Speter status = SERF_ERROR_ABORTED_CONNECTION; 1143251877Speter } 1144251877Speter goto error; 1145251877Speter } 1146251877Speter 1147251877Speter /* If our response handler says it can't do anything more, we now 1148251877Speter * treat that as a success. 1149251877Speter */ 1150251877Speter if (APR_STATUS_IS_EAGAIN(status)) { 1151262339Speter /* It is possible that while reading the response, the ssl layer 1152262339Speter has prepared some data to send. If this was the last request, 1153262339Speter serf will not check for socket writability, so force this here. 1154262339Speter */ 1155262339Speter if (request_or_data_pending(&request, conn) && !request) { 1156262339Speter conn->dirty_conn = 1; 1157262339Speter conn->ctx->dirty_pollset = 1; 1158262339Speter } 1159251877Speter status = APR_SUCCESS; 1160251877Speter goto error; 1161251877Speter } 1162251877Speter 1163251877Speter /* If we received APR_SUCCESS, run this loop again. */ 1164251877Speter if (!status) { 1165251877Speter continue; 1166251877Speter } 1167251877Speter 1168251877Speter close_connection = is_conn_closing(request->resp_bkt); 1169251877Speter 1170251877Speter if (!APR_STATUS_IS_EOF(status) && 1171251877Speter close_connection != SERF_ERROR_CLOSING) { 1172251877Speter /* Whether success, or an error, there is no more to do unless 1173251877Speter * this request has been completed. 1174251877Speter */ 1175251877Speter goto error; 1176251877Speter } 1177251877Speter 1178251877Speter /* The response has been fully-read, so that means the request has 1179251877Speter * either been fully-delivered (most likely), or that we don't need to 1180251877Speter * write the rest of it anymore, e.g. when a 408 Request timeout was 1181251877Speter $ received. 1182251877Speter * Remove it from our queue and loop to read another response. 1183251877Speter */ 1184251877Speter conn->requests = request->next; 1185251877Speter 1186251877Speter destroy_request(request); 1187251877Speter 1188251877Speter request = conn->requests; 1189251877Speter 1190251877Speter /* If we're truly empty, update our tail. */ 1191251877Speter if (request == NULL) { 1192251877Speter conn->requests_tail = NULL; 1193251877Speter } 1194251877Speter 1195251877Speter conn->completed_responses++; 1196251877Speter 1197251877Speter /* We've to rebuild pollset since completed_responses is changed. */ 1198251877Speter conn->dirty_conn = 1; 1199251877Speter conn->ctx->dirty_pollset = 1; 1200251877Speter 1201251877Speter /* This means that we're being advised that the connection is done. */ 1202251877Speter if (close_connection == SERF_ERROR_CLOSING) { 1203251877Speter reset_connection(conn, 1); 1204251877Speter if (APR_STATUS_IS_EOF(status)) 1205251877Speter status = APR_SUCCESS; 1206251877Speter goto error; 1207251877Speter } 1208251877Speter 1209251877Speter /* The server is suddenly deciding to serve more responses than we've 1210251877Speter * seen before. 1211251877Speter * 1212251877Speter * Let our requests go. 1213251877Speter */ 1214251877Speter if (conn->probable_keepalive_limit && 1215251877Speter conn->completed_responses > conn->probable_keepalive_limit) { 1216251877Speter conn->probable_keepalive_limit = 0; 1217251877Speter } 1218251877Speter 1219251877Speter /* If we just ran out of requests or have unwritten requests, then 1220251877Speter * update the pollset. We don't want to read from this socket any 1221251877Speter * more. We are definitely done with this loop, too. 1222251877Speter */ 1223262339Speter if (request == NULL || !request->writing_started) { 1224251877Speter conn->dirty_conn = 1; 1225251877Speter conn->ctx->dirty_pollset = 1; 1226251877Speter status = APR_SUCCESS; 1227251877Speter goto error; 1228251877Speter } 1229251877Speter } 1230251877Speter 1231251877Spetererror: 1232251877Speter apr_pool_destroy(tmppool); 1233251877Speter return status; 1234251877Speter} 1235251877Speter 1236251877Speter/* process all events on the connection */ 1237251877Speterapr_status_t serf__process_connection(serf_connection_t *conn, 1238251877Speter apr_int16_t events) 1239251877Speter{ 1240251877Speter apr_status_t status; 1241251877Speter 1242251877Speter /* POLLHUP/ERR should come after POLLIN so if there's an error message or 1243251877Speter * the like sitting on the connection, we give the app a chance to read 1244251877Speter * it before we trigger a reset condition. 1245251877Speter */ 1246251877Speter if ((events & APR_POLLIN) != 0) { 1247251877Speter if ((status = read_from_connection(conn)) != APR_SUCCESS) 1248251877Speter return status; 1249251877Speter 1250251877Speter /* If we decided to reset our connection, return now as we don't 1251251877Speter * want to write. 1252251877Speter */ 1253251877Speter if ((conn->seen_in_pollset & APR_POLLHUP) != 0) { 1254251877Speter return APR_SUCCESS; 1255251877Speter } 1256251877Speter } 1257251877Speter if ((events & APR_POLLHUP) != 0) { 1258251877Speter /* The connection got reset by the server. On Windows this can happen 1259251877Speter when all data is read, so just cleanup the connection and open 1260251877Speter a new one. 1261251877Speter If we haven't had any successful responses on this connection, 1262251877Speter then error out as it is likely a server issue. */ 1263251877Speter if (conn->completed_responses) { 1264251877Speter return reset_connection(conn, 1); 1265251877Speter } 1266251877Speter return SERF_ERROR_ABORTED_CONNECTION; 1267251877Speter } 1268251877Speter if ((events & APR_POLLERR) != 0) { 1269251877Speter /* We might be talking to a buggy HTTP server that doesn't 1270251877Speter * do lingering-close. (httpd < 2.1.8 does this.) 1271251877Speter * 1272251877Speter * See: 1273251877Speter * 1274251877Speter * http://issues.apache.org/bugzilla/show_bug.cgi?id=35292 1275251877Speter */ 1276251877Speter if (conn->completed_requests && !conn->probable_keepalive_limit) { 1277251877Speter return reset_connection(conn, 1); 1278251877Speter } 1279253895Speter#ifdef SO_ERROR 1280253895Speter /* If possible, get the error from the platform's socket layer and 1281253895Speter convert it to an APR status code. */ 1282253895Speter { 1283253895Speter apr_os_sock_t osskt; 1284253895Speter if (!apr_os_sock_get(&osskt, conn->skt)) { 1285253895Speter int error; 1286253895Speter apr_socklen_t l = sizeof(error); 1287253895Speter 1288262339Speter if (!getsockopt(osskt, SOL_SOCKET, SO_ERROR, (char*)&error, 1289262339Speter &l)) { 1290262339Speter status = APR_FROM_OS_ERROR(error); 1291262339Speter 1292262339Speter /* Handle fallback for multi-homed servers. 1293262339Speter 1294262339Speter ### Improve algorithm to find better than just 'next'? 1295262339Speter 1296262339Speter Current Windows versions already handle re-ordering for 1297262339Speter api users by using statistics on the recently failed 1298262339Speter connections to order the list of addresses. */ 1299262339Speter if (conn->completed_requests == 0 1300262339Speter && conn->address->next != NULL 1301262339Speter && (APR_STATUS_IS_ECONNREFUSED(status) 1302262339Speter || APR_STATUS_IS_TIMEUP(status) 1303262339Speter || APR_STATUS_IS_ENETUNREACH(status))) { 1304262339Speter 1305262339Speter conn->address = conn->address->next; 1306262339Speter return reset_connection(conn, 1); 1307262339Speter } 1308262339Speter 1309262339Speter return status; 1310262339Speter } 1311253895Speter } 1312253895Speter } 1313253895Speter#endif 1314251877Speter return APR_EGENERAL; 1315251877Speter } 1316251877Speter if ((events & APR_POLLOUT) != 0) { 1317251877Speter if ((status = write_to_connection(conn)) != APR_SUCCESS) 1318251877Speter return status; 1319251877Speter } 1320251877Speter return APR_SUCCESS; 1321251877Speter} 1322251877Speter 1323251877Speterserf_connection_t *serf_connection_create( 1324251877Speter serf_context_t *ctx, 1325251877Speter apr_sockaddr_t *address, 1326251877Speter serf_connection_setup_t setup, 1327251877Speter void *setup_baton, 1328251877Speter serf_connection_closed_t closed, 1329251877Speter void *closed_baton, 1330251877Speter apr_pool_t *pool) 1331251877Speter{ 1332251877Speter serf_connection_t *conn = apr_pcalloc(pool, sizeof(*conn)); 1333251877Speter 1334251877Speter conn->ctx = ctx; 1335251877Speter conn->status = APR_SUCCESS; 1336251877Speter /* Ignore server address if proxy was specified. */ 1337251877Speter conn->address = ctx->proxy_address ? ctx->proxy_address : address; 1338251877Speter conn->setup = setup; 1339251877Speter conn->setup_baton = setup_baton; 1340251877Speter conn->closed = closed; 1341251877Speter conn->closed_baton = closed_baton; 1342251877Speter conn->pool = pool; 1343251877Speter conn->allocator = serf_bucket_allocator_create(pool, NULL, NULL); 1344251877Speter conn->stream = NULL; 1345251877Speter conn->ostream_head = NULL; 1346251877Speter conn->ostream_tail = NULL; 1347251877Speter conn->baton.type = SERF_IO_CONN; 1348251877Speter conn->baton.u.conn = conn; 1349251877Speter conn->hit_eof = 0; 1350251877Speter conn->state = SERF_CONN_INIT; 1351251877Speter conn->latency = -1; /* unknown */ 1352251877Speter 1353251877Speter /* Create a subpool for our connection. */ 1354251877Speter apr_pool_create(&conn->skt_pool, conn->pool); 1355251877Speter 1356251877Speter /* register a cleanup */ 1357253895Speter apr_pool_cleanup_register(conn->pool, conn, clean_conn, 1358253895Speter apr_pool_cleanup_null); 1359251877Speter 1360251877Speter /* Add the connection to the context. */ 1361251877Speter *(serf_connection_t **)apr_array_push(ctx->conns) = conn; 1362251877Speter 1363251877Speter serf__log(CONN_VERBOSE, __FILE__, "created connection 0x%x\n", 1364251877Speter conn); 1365251877Speter 1366251877Speter return conn; 1367251877Speter} 1368251877Speter 1369251877Speterapr_status_t serf_connection_create2( 1370251877Speter serf_connection_t **conn, 1371251877Speter serf_context_t *ctx, 1372251877Speter apr_uri_t host_info, 1373251877Speter serf_connection_setup_t setup, 1374251877Speter void *setup_baton, 1375251877Speter serf_connection_closed_t closed, 1376251877Speter void *closed_baton, 1377251877Speter apr_pool_t *pool) 1378251877Speter{ 1379251877Speter apr_status_t status = APR_SUCCESS; 1380251877Speter serf_connection_t *c; 1381251877Speter apr_sockaddr_t *host_address = NULL; 1382251877Speter 1383251877Speter /* Set the port number explicitly, needed to create the socket later. */ 1384251877Speter if (!host_info.port) { 1385251877Speter host_info.port = apr_uri_port_of_scheme(host_info.scheme); 1386251877Speter } 1387251877Speter 1388251877Speter /* Only lookup the address of the server if no proxy server was 1389251877Speter configured. */ 1390251877Speter if (!ctx->proxy_address) { 1391251877Speter status = apr_sockaddr_info_get(&host_address, 1392251877Speter host_info.hostname, 1393251877Speter APR_UNSPEC, host_info.port, 0, pool); 1394251877Speter if (status) 1395251877Speter return status; 1396251877Speter } 1397251877Speter 1398251877Speter c = serf_connection_create(ctx, host_address, setup, setup_baton, 1399251877Speter closed, closed_baton, pool); 1400251877Speter 1401251877Speter /* We're not interested in the path following the hostname. */ 1402251877Speter c->host_url = apr_uri_unparse(c->pool, 1403251877Speter &host_info, 1404262339Speter APR_URI_UNP_OMITPATHINFO | 1405262339Speter APR_URI_UNP_OMITUSERINFO); 1406251877Speter 1407253895Speter /* Store the host info without the path on the connection. */ 1408253895Speter (void)apr_uri_parse(c->pool, c->host_url, &(c->host_info)); 1409253895Speter if (!c->host_info.port) { 1410253895Speter c->host_info.port = apr_uri_port_of_scheme(c->host_info.scheme); 1411253895Speter } 1412253895Speter 1413251877Speter *conn = c; 1414251877Speter 1415251877Speter return status; 1416251877Speter} 1417251877Speter 1418251877Speterapr_status_t serf_connection_reset( 1419251877Speter serf_connection_t *conn) 1420251877Speter{ 1421251877Speter return reset_connection(conn, 0); 1422251877Speter} 1423251877Speter 1424251877Speter 1425251877Speterapr_status_t serf_connection_close( 1426251877Speter serf_connection_t *conn) 1427251877Speter{ 1428251877Speter int i; 1429251877Speter serf_context_t *ctx = conn->ctx; 1430251877Speter apr_status_t status; 1431251877Speter 1432251877Speter for (i = ctx->conns->nelts; i--; ) { 1433251877Speter serf_connection_t *conn_seq = GET_CONN(ctx, i); 1434251877Speter 1435251877Speter if (conn_seq == conn) { 1436251877Speter while (conn->requests) { 1437251877Speter serf_request_cancel(conn->requests); 1438251877Speter } 1439251877Speter if (conn->skt != NULL) { 1440251877Speter remove_connection(ctx, conn); 1441251877Speter status = apr_socket_close(conn->skt); 1442251877Speter serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt, 1443251877Speter "closed socket, status %d\n", 1444251877Speter status); 1445251877Speter if (conn->closed != NULL) { 1446251877Speter handle_conn_closed(conn, status); 1447251877Speter } 1448251877Speter conn->skt = NULL; 1449251877Speter } 1450251877Speter if (conn->stream != NULL) { 1451251877Speter serf_bucket_destroy(conn->stream); 1452251877Speter conn->stream = NULL; 1453251877Speter } 1454251877Speter 1455251877Speter destroy_ostream(conn); 1456251877Speter 1457251877Speter /* Remove the connection from the context. We don't want to 1458251877Speter * deal with it any more. 1459251877Speter */ 1460251877Speter if (i < ctx->conns->nelts - 1) { 1461251877Speter /* move later connections over this one. */ 1462251877Speter memmove( 1463251877Speter &GET_CONN(ctx, i), 1464251877Speter &GET_CONN(ctx, i + 1), 1465251877Speter (ctx->conns->nelts - i - 1) * sizeof(serf_connection_t *)); 1466251877Speter } 1467251877Speter --ctx->conns->nelts; 1468251877Speter 1469251877Speter serf__log(CONN_VERBOSE, __FILE__, "closed connection 0x%x\n", 1470251877Speter conn); 1471251877Speter 1472251877Speter /* Found the connection. Closed it. All done. */ 1473251877Speter return APR_SUCCESS; 1474251877Speter } 1475251877Speter } 1476251877Speter 1477251877Speter /* We didn't find the specified connection. */ 1478251877Speter /* ### doc talks about this w.r.t poll structures. use something else? */ 1479251877Speter return APR_NOTFOUND; 1480251877Speter} 1481251877Speter 1482251877Speter 1483251877Spetervoid serf_connection_set_max_outstanding_requests( 1484251877Speter serf_connection_t *conn, 1485251877Speter unsigned int max_requests) 1486251877Speter{ 1487251877Speter if (max_requests == 0) 1488251877Speter serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt, 1489251877Speter "Set max. nr. of outstanding requests for this " 1490251877Speter "connection to unlimited.\n"); 1491251877Speter else 1492251877Speter serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt, 1493251877Speter "Limit max. nr. of outstanding requests for this " 1494251877Speter "connection to %u.\n", max_requests); 1495251877Speter 1496251877Speter conn->max_outstanding_requests = max_requests; 1497251877Speter} 1498251877Speter 1499251877Speter 1500251877Spetervoid serf_connection_set_async_responses( 1501251877Speter serf_connection_t *conn, 1502251877Speter serf_response_acceptor_t acceptor, 1503251877Speter void *acceptor_baton, 1504251877Speter serf_response_handler_t handler, 1505251877Speter void *handler_baton) 1506251877Speter{ 1507251877Speter conn->async_responses = 1; 1508251877Speter conn->async_acceptor = acceptor; 1509251877Speter conn->async_acceptor_baton = acceptor_baton; 1510251877Speter conn->async_handler = handler; 1511251877Speter conn->async_handler_baton = handler_baton; 1512251877Speter} 1513251877Speter 1514253895Speterstatic serf_request_t * 1515253895Spetercreate_request(serf_connection_t *conn, 1516253895Speter serf_request_setup_t setup, 1517253895Speter void *setup_baton, 1518253895Speter int priority, 1519253895Speter int ssltunnel) 1520251877Speter{ 1521251877Speter serf_request_t *request; 1522251877Speter 1523251877Speter request = serf_bucket_mem_alloc(conn->allocator, sizeof(*request)); 1524251877Speter request->conn = conn; 1525251877Speter request->setup = setup; 1526251877Speter request->setup_baton = setup_baton; 1527251877Speter request->handler = NULL; 1528251877Speter request->respool = NULL; 1529251877Speter request->req_bkt = NULL; 1530251877Speter request->resp_bkt = NULL; 1531253895Speter request->priority = priority; 1532262339Speter request->writing_started = 0; 1533253895Speter request->ssltunnel = ssltunnel; 1534251877Speter request->next = NULL; 1535262339Speter request->auth_baton = NULL; 1536251877Speter 1537253895Speter return request; 1538253895Speter} 1539253895Speter 1540253895Speterserf_request_t *serf_connection_request_create( 1541253895Speter serf_connection_t *conn, 1542253895Speter serf_request_setup_t setup, 1543253895Speter void *setup_baton) 1544253895Speter{ 1545253895Speter serf_request_t *request; 1546253895Speter 1547253895Speter request = create_request(conn, setup, setup_baton, 1548253895Speter 0, /* priority */ 1549253895Speter 0 /* ssl tunnel */); 1550253895Speter 1551251877Speter /* Link the request to the end of the request chain. */ 1552251877Speter link_requests(&conn->requests, &conn->requests_tail, request); 1553251877Speter 1554251877Speter /* Ensure our pollset becomes writable in context run */ 1555251877Speter conn->ctx->dirty_pollset = 1; 1556251877Speter conn->dirty_conn = 1; 1557251877Speter 1558251877Speter return request; 1559251877Speter} 1560251877Speter 1561253895Speterstatic serf_request_t * 1562253895Speterpriority_request_create(serf_connection_t *conn, 1563253895Speter int ssltunnelreq, 1564253895Speter serf_request_setup_t setup, 1565253895Speter void *setup_baton) 1566251877Speter{ 1567251877Speter serf_request_t *request; 1568251877Speter serf_request_t *iter, *prev; 1569251877Speter 1570253895Speter request = create_request(conn, setup, setup_baton, 1571253895Speter 1, /* priority */ 1572253895Speter ssltunnelreq); 1573251877Speter 1574251877Speter /* Link the new request after the last written request. */ 1575251877Speter iter = conn->requests; 1576251877Speter prev = NULL; 1577251877Speter 1578251877Speter /* Find a request that has data which needs to be delivered. */ 1579262339Speter while (iter != NULL && iter->req_bkt == NULL && iter->writing_started) { 1580251877Speter prev = iter; 1581251877Speter iter = iter->next; 1582251877Speter } 1583251877Speter 1584253895Speter /* A CONNECT request to setup an ssltunnel has absolute priority over all 1585253895Speter other requests on the connection, so: 1586253895Speter a. add it first to the queue 1587253895Speter b. ensure that other priority requests are added after the CONNECT 1588253895Speter request */ 1589253895Speter if (!request->ssltunnel) { 1590253895Speter /* Advance to next non priority request */ 1591253895Speter while (iter != NULL && iter->priority) { 1592253895Speter prev = iter; 1593253895Speter iter = iter->next; 1594253895Speter } 1595251877Speter } 1596251877Speter 1597251877Speter if (prev) { 1598251877Speter request->next = iter; 1599251877Speter prev->next = request; 1600251877Speter } else { 1601251877Speter request->next = iter; 1602251877Speter conn->requests = request; 1603251877Speter } 1604251877Speter 1605251877Speter /* Ensure our pollset becomes writable in context run */ 1606251877Speter conn->ctx->dirty_pollset = 1; 1607251877Speter conn->dirty_conn = 1; 1608251877Speter 1609251877Speter return request; 1610251877Speter} 1611251877Speter 1612253895Speterserf_request_t *serf_connection_priority_request_create( 1613253895Speter serf_connection_t *conn, 1614253895Speter serf_request_setup_t setup, 1615253895Speter void *setup_baton) 1616253895Speter{ 1617253895Speter return priority_request_create(conn, 1618253895Speter 0, /* not a ssltunnel CONNECT request */ 1619253895Speter setup, setup_baton); 1620253895Speter} 1621251877Speter 1622253895Speterserf_request_t *serf__ssltunnel_request_create(serf_connection_t *conn, 1623253895Speter serf_request_setup_t setup, 1624253895Speter void *setup_baton) 1625253895Speter{ 1626253895Speter return priority_request_create(conn, 1627253895Speter 1, /* This is a ssltunnel CONNECT request */ 1628253895Speter setup, setup_baton); 1629253895Speter} 1630253895Speter 1631251877Speterapr_status_t serf_request_cancel(serf_request_t *request) 1632251877Speter{ 1633251877Speter return cancel_request(request, &request->conn->requests, 0); 1634251877Speter} 1635251877Speter 1636251877Speterapr_status_t serf_request_is_written(serf_request_t *request) 1637251877Speter{ 1638262339Speter if (request->writing_started && !request->req_bkt) 1639251877Speter return APR_SUCCESS; 1640251877Speter 1641251877Speter return APR_EBUSY; 1642251877Speter} 1643251877Speter 1644251877Speterapr_pool_t *serf_request_get_pool(const serf_request_t *request) 1645251877Speter{ 1646251877Speter return request->respool; 1647251877Speter} 1648251877Speter 1649251877Speter 1650251877Speterserf_bucket_alloc_t *serf_request_get_alloc( 1651251877Speter const serf_request_t *request) 1652251877Speter{ 1653251877Speter return request->allocator; 1654251877Speter} 1655251877Speter 1656251877Speter 1657251877Speterserf_connection_t *serf_request_get_conn( 1658251877Speter const serf_request_t *request) 1659251877Speter{ 1660251877Speter return request->conn; 1661251877Speter} 1662251877Speter 1663251877Speter 1664251877Spetervoid serf_request_set_handler( 1665251877Speter serf_request_t *request, 1666251877Speter const serf_response_handler_t handler, 1667251877Speter const void **handler_baton) 1668251877Speter{ 1669251877Speter request->handler = handler; 1670251877Speter request->handler_baton = handler_baton; 1671251877Speter} 1672251877Speter 1673251877Speter 1674251877Speterserf_bucket_t *serf_request_bucket_request_create( 1675251877Speter serf_request_t *request, 1676251877Speter const char *method, 1677251877Speter const char *uri, 1678251877Speter serf_bucket_t *body, 1679251877Speter serf_bucket_alloc_t *allocator) 1680251877Speter{ 1681251877Speter serf_bucket_t *req_bkt, *hdrs_bkt; 1682251877Speter serf_connection_t *conn = request->conn; 1683251877Speter serf_context_t *ctx = conn->ctx; 1684253895Speter int ssltunnel; 1685251877Speter 1686253895Speter ssltunnel = ctx->proxy_address && 1687253895Speter (strcmp(conn->host_info.scheme, "https") == 0); 1688253895Speter 1689251877Speter req_bkt = serf_bucket_request_create(method, uri, body, allocator); 1690251877Speter hdrs_bkt = serf_bucket_request_get_headers(req_bkt); 1691251877Speter 1692253895Speter /* Use absolute uri's in requests to a proxy. USe relative uri's in 1693253895Speter requests directly to a server or sent through an SSL tunnel. */ 1694253895Speter if (ctx->proxy_address && conn->host_url && 1695253895Speter !(ssltunnel && !request->ssltunnel)) { 1696253895Speter 1697251877Speter serf_bucket_request_set_root(req_bkt, conn->host_url); 1698253895Speter } 1699251877Speter 1700251877Speter if (conn->host_info.hostinfo) 1701251877Speter serf_bucket_headers_setn(hdrs_bkt, "Host", 1702251877Speter conn->host_info.hostinfo); 1703251877Speter 1704253895Speter /* Setup server authorization headers, unless this is a CONNECT request. */ 1705253895Speter if (!request->ssltunnel) { 1706253895Speter serf__authn_info_t *authn_info; 1707253895Speter authn_info = serf__get_authn_info_for_server(conn); 1708253895Speter if (authn_info->scheme) 1709253895Speter authn_info->scheme->setup_request_func(HOST, 0, conn, request, 1710251877Speter method, uri, 1711251877Speter hdrs_bkt); 1712253895Speter } 1713251877Speter 1714253895Speter /* Setup proxy authorization headers. 1715253895Speter Don't set these headers on the requests to the server if we're using 1716253895Speter an SSL tunnel, only on the CONNECT request to setup the tunnel. */ 1717253895Speter if (ctx->proxy_authn_info.scheme) { 1718253895Speter if (strcmp(conn->host_info.scheme, "https") == 0) { 1719253895Speter if (request->ssltunnel) 1720253895Speter ctx->proxy_authn_info.scheme->setup_request_func(PROXY, 0, conn, 1721253895Speter request, 1722253895Speter method, uri, 1723253895Speter hdrs_bkt); 1724253895Speter } else { 1725253895Speter ctx->proxy_authn_info.scheme->setup_request_func(PROXY, 0, conn, 1726253895Speter request, 1727253895Speter method, uri, 1728253895Speter hdrs_bkt); 1729253895Speter } 1730253895Speter } 1731251877Speter 1732251877Speter return req_bkt; 1733251877Speter} 1734251877Speter 1735251877Speterapr_interval_time_t serf_connection_get_latency(serf_connection_t *conn) 1736251877Speter{ 1737251877Speter if (conn->ctx->proxy_address) { 1738251877Speter /* Detecting network latency for proxied connection is not implemented 1739251877Speter yet. */ 1740251877Speter return -1; 1741251877Speter } 1742251877Speter 1743251877Speter return conn->latency; 1744251877Speter} 1745