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