1362181Sdim/* ====================================================================
2362181Sdim *    Licensed to the Apache Software Foundation (ASF) under one
3362181Sdim *    or more contributor license agreements.  See the NOTICE file
4362181Sdim *    distributed with this work for additional information
5362181Sdim *    regarding copyright ownership.  The ASF licenses this file
6362181Sdim *    to you under the Apache License, Version 2.0 (the
7362181Sdim *    "License"); you may not use this file except in compliance
8362181Sdim *    with the License.  You may obtain a copy of the License at
9251877Speter *
10362181Sdim *      http://www.apache.org/licenses/LICENSE-2.0
11251877Speter *
12362181Sdim *    Unless required by applicable law or agreed to in writing,
13362181Sdim *    software distributed under the License is distributed on an
14362181Sdim *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15362181Sdim *    KIND, either express or implied.  See the License for the
16362181Sdim *    specific language governing permissions and limitations
17362181Sdim *    under the License.
18362181Sdim * ====================================================================
19251877Speter */
20251877Speter
21251877Speter#include <apr_pools.h>
22251877Speter#include <apr_poll.h>
23251877Speter#include <apr_version.h>
24253895Speter#include <apr_portable.h>
25251877Speter
26251877Speter#include "serf.h"
27251877Speter#include "serf_bucket_util.h"
28251877Speter
29251877Speter#include "serf_private.h"
30251877Speter
31251877Speter/* cleanup for sockets */
32251877Speterstatic apr_status_t clean_skt(void *data)
33251877Speter{
34251877Speter    serf_connection_t *conn = data;
35251877Speter    apr_status_t status = APR_SUCCESS;
36251877Speter
37251877Speter    if (conn->skt) {
38251877Speter        serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt, "cleanup - ");
39251877Speter        status = apr_socket_close(conn->skt);
40251877Speter        conn->skt = NULL;
41251877Speter        serf__log_nopref(SOCK_VERBOSE, "closed socket, status %d\n", status);
42251877Speter    }
43251877Speter
44251877Speter    return status;
45251877Speter}
46251877Speter
47251877Speterstatic apr_status_t clean_resp(void *data)
48251877Speter{
49251877Speter    serf_request_t *request = data;
50251877Speter
51251877Speter    /* The request's RESPOOL is being cleared.  */
52251877Speter
53251877Speter    /* If the response has allocated some buckets, then destroy them (since
54251877Speter       the bucket may hold resources other than memory in RESPOOL). Also
55251877Speter       make sure to set their fields to NULL so connection closure does
56251877Speter       not attempt to free them again.  */
57251877Speter    if (request->resp_bkt) {
58251877Speter        serf_bucket_destroy(request->resp_bkt);
59251877Speter        request->resp_bkt = NULL;
60251877Speter    }
61251877Speter    if (request->req_bkt) {
62251877Speter        serf_bucket_destroy(request->req_bkt);
63251877Speter        request->req_bkt = NULL;
64251877Speter    }
65251877Speter
66251877Speter    /* ### should we worry about debug stuff, like that performed in
67251877Speter       ### destroy_request()? should we worry about calling req->handler
68251877Speter       ### to notify this "cancellation" due to pool clearing?  */
69251877Speter
70251877Speter    /* This pool just got cleared/destroyed. Don't try to destroy the pool
71251877Speter       (again) when the request is canceled.  */
72251877Speter    request->respool = NULL;
73251877Speter
74251877Speter    return APR_SUCCESS;
75251877Speter}
76251877Speter
77251877Speter/* cleanup for conns */
78251877Speterstatic apr_status_t clean_conn(void *data)
79251877Speter{
80251877Speter    serf_connection_t *conn = data;
81251877Speter
82251877Speter    serf__log(CONN_VERBOSE, __FILE__, "cleaning up connection 0x%x\n",
83251877Speter              conn);
84251877Speter    serf_connection_close(conn);
85251877Speter
86251877Speter    return APR_SUCCESS;
87251877Speter}
88251877Speter
89262324Speter/* Check if there is data waiting to be sent over the socket. This can happen
90262324Speter   in two situations:
91262324Speter   - The connection queue has atleast one request with unwritten data.
92262324Speter   - All requests are written and the ssl layer wrote some data while reading
93262324Speter     the response. This can happen when the server triggers a renegotiation,
94262324Speter     e.g. after the first and only request on that connection was received.
95262324Speter   Returns 1 if data is pending on CONN, NULL if not.
96262324Speter   If NEXT_REQ is not NULL, it will be filled in with the next available request
97262324Speter   with unwritten data. */
98262324Speterstatic int
99262324Speterrequest_or_data_pending(serf_request_t **next_req, serf_connection_t *conn)
100262324Speter{
101262324Speter    serf_request_t *request = conn->requests;
102262324Speter
103262324Speter    while (request != NULL && request->req_bkt == NULL &&
104262324Speter           request->writing_started)
105262324Speter        request = request->next;
106262324Speter
107262324Speter    if (next_req)
108262324Speter        *next_req = request;
109262324Speter
110262324Speter    if (request != NULL) {
111262324Speter        return 1;
112262324Speter    } else if (conn->ostream_head) {
113262324Speter        const char *dummy;
114262324Speter        apr_size_t len;
115262324Speter        apr_status_t status;
116262324Speter
117262324Speter        status = serf_bucket_peek(conn->ostream_head, &dummy,
118262324Speter                                  &len);
119262324Speter        if (!SERF_BUCKET_READ_ERROR(status) && len) {
120262324Speter            serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt,
121262324Speter                          "All requests written but still data pending.\n");
122262324Speter            return 1;
123262324Speter        }
124262324Speter    }
125262324Speter
126262324Speter    return 0;
127262324Speter}
128262324Speter
129251877Speter/* Update the pollset for this connection. We tweak the pollset based on
130251877Speter * whether we want to read and/or write, given conditions within the
131251877Speter * connection. If the connection is not (yet) in the pollset, then it
132251877Speter * will be added.
133251877Speter */
134251877Speterapr_status_t serf__conn_update_pollset(serf_connection_t *conn)
135251877Speter{
136251877Speter    serf_context_t *ctx = conn->ctx;
137251877Speter    apr_status_t status;
138251877Speter    apr_pollfd_t desc = { 0 };
139251877Speter
140251877Speter    if (!conn->skt) {
141251877Speter        return APR_SUCCESS;
142251877Speter    }
143251877Speter
144251877Speter    /* Remove the socket from the poll set. */
145251877Speter    desc.desc_type = APR_POLL_SOCKET;
146251877Speter    desc.desc.s = conn->skt;
147251877Speter    desc.reqevents = conn->reqevents;
148251877Speter
149251877Speter    status = ctx->pollset_rm(ctx->pollset_baton,
150362181Sdim                             &desc, &conn->baton);
151251877Speter    if (status && !APR_STATUS_IS_NOTFOUND(status))
152251877Speter        return status;
153251877Speter
154251877Speter    /* Now put it back in with the correct read/write values. */
155251877Speter    desc.reqevents = APR_POLLHUP | APR_POLLERR;
156251877Speter    if (conn->requests &&
157251877Speter        conn->state != SERF_CONN_INIT) {
158251877Speter        /* If there are any outstanding events, then we want to read. */
159251877Speter        /* ### not true. we only want to read IF we have sent some data */
160251877Speter        desc.reqevents |= APR_POLLIN;
161251877Speter
162253895Speter        /* Don't write if OpenSSL told us that it needs to read data first. */
163253895Speter        if (conn->stop_writing != 1) {
164251877Speter
165253895Speter            /* If the connection is not closing down and
166253895Speter             *   has unwritten data or
167253895Speter             *   there are any requests that still have buckets to write out,
168253895Speter             *     then we want to write.
169253895Speter             */
170253895Speter            if (conn->vec_len &&
171253895Speter                conn->state != SERF_CONN_CLOSING)
172253895Speter                desc.reqevents |= APR_POLLOUT;
173253895Speter            else {
174253895Speter
175253895Speter                if ((conn->probable_keepalive_limit &&
176253895Speter                     conn->completed_requests > conn->probable_keepalive_limit) ||
177253895Speter                    (conn->max_outstanding_requests &&
178253895Speter                     conn->completed_requests - conn->completed_responses >=
179251877Speter                     conn->max_outstanding_requests)) {
180253895Speter                        /* we wouldn't try to write any way right now. */
181253895Speter                }
182262324Speter                else if (request_or_data_pending(NULL, conn)) {
183262324Speter                    desc.reqevents |= APR_POLLOUT;
184262324Speter                }
185251877Speter            }
186251877Speter        }
187251877Speter    }
188251877Speter
189251877Speter    /* If we can have async responses, always look for something to read. */
190251877Speter    if (conn->async_responses) {
191251877Speter        desc.reqevents |= APR_POLLIN;
192251877Speter    }
193251877Speter
194251877Speter    /* save our reqevents, so we can pass it in to remove later. */
195251877Speter    conn->reqevents = desc.reqevents;
196251877Speter
197251877Speter    /* Note: even if we don't want to read/write this socket, we still
198251877Speter     * want to poll it for hangups and errors.
199251877Speter     */
200251877Speter    return ctx->pollset_add(ctx->pollset_baton,
201251877Speter                            &desc, &conn->baton);
202251877Speter}
203251877Speter
204251877Speter#ifdef SERF_DEBUG_BUCKET_USE
205251877Speter
206251877Speter/* Make sure all response buckets were drained. */
207251877Speterstatic void check_buckets_drained(serf_connection_t *conn)
208251877Speter{
209251877Speter    serf_request_t *request = conn->requests;
210251877Speter
211251877Speter    for ( ; request ; request = request->next ) {
212251877Speter        if (request->resp_bkt != NULL) {
213251877Speter            /* ### crap. can't do this. this allocator may have un-drained
214251877Speter             * ### REQUEST buckets.
215251877Speter             */
216251877Speter            /* serf_debug__entered_loop(request->resp_bkt->allocator); */
217251877Speter            /* ### for now, pretend we closed the conn (resets the tracking) */
218251877Speter            serf_debug__closed_conn(request->resp_bkt->allocator);
219251877Speter        }
220251877Speter    }
221251877Speter}
222251877Speter
223251877Speter#endif
224251877Speter
225253895Speterstatic void destroy_ostream(serf_connection_t *conn)
226253895Speter{
227253895Speter    if (conn->ostream_head != NULL) {
228253895Speter        serf_bucket_destroy(conn->ostream_head);
229253895Speter        conn->ostream_head = NULL;
230253895Speter        conn->ostream_tail = NULL;
231253895Speter    }
232253895Speter}
233253895Speter
234253895Speterstatic apr_status_t detect_eof(void *baton, serf_bucket_t *aggregate_bucket)
235253895Speter{
236253895Speter    serf_connection_t *conn = baton;
237253895Speter    conn->hit_eof = 1;
238253895Speter    return APR_EAGAIN;
239253895Speter}
240253895Speter
241253895Speterstatic apr_status_t do_conn_setup(serf_connection_t *conn)
242253895Speter{
243253895Speter    apr_status_t status;
244253895Speter    serf_bucket_t *ostream;
245253895Speter
246253895Speter    if (conn->ostream_head == NULL) {
247253895Speter        conn->ostream_head = serf_bucket_aggregate_create(conn->allocator);
248253895Speter    }
249253895Speter
250253895Speter    if (conn->ostream_tail == NULL) {
251253895Speter        conn->ostream_tail = serf__bucket_stream_create(conn->allocator,
252253895Speter                                                        detect_eof,
253253895Speter                                                        conn);
254253895Speter    }
255253895Speter
256253895Speter    ostream = conn->ostream_tail;
257253895Speter
258253895Speter    status = (*conn->setup)(conn->skt,
259253895Speter                            &conn->stream,
260253895Speter                            &ostream,
261253895Speter                            conn->setup_baton,
262253895Speter                            conn->pool);
263253895Speter    if (status) {
264253895Speter        /* extra destroy here since it wasn't added to the head bucket yet. */
265253895Speter        serf_bucket_destroy(conn->ostream_tail);
266253895Speter        destroy_ostream(conn);
267253895Speter        return status;
268253895Speter    }
269253895Speter
270253895Speter    serf_bucket_aggregate_append(conn->ostream_head,
271253895Speter                                 ostream);
272253895Speter
273253895Speter    return status;
274253895Speter}
275253895Speter
276253895Speter/* Set up the input and output stream buckets.
277253895Speter When a tunnel over an http proxy is needed, create a socket bucket and
278253895Speter empty aggregate bucket for sending and receiving unencrypted requests
279253895Speter over the socket.
280253895Speter
281253895Speter After the tunnel is there, or no tunnel was needed, ask the application
282253895Speter to create the input and output buckets, which should take care of the
283253895Speter [en/de]cryption.
284253895Speter */
285253895Speter
286253895Speterstatic apr_status_t prepare_conn_streams(serf_connection_t *conn,
287253895Speter                                         serf_bucket_t **istream,
288253895Speter                                         serf_bucket_t **ostreamt,
289253895Speter                                         serf_bucket_t **ostreamh)
290253895Speter{
291253895Speter    apr_status_t status;
292253895Speter
293253895Speter    if (conn->stream == NULL) {
294253895Speter        conn->latency = apr_time_now() - conn->connect_time;
295253895Speter    }
296253895Speter
297253895Speter    /* Do we need a SSL tunnel first? */
298253895Speter    if (conn->state == SERF_CONN_CONNECTED) {
299253895Speter        /* If the connection does not have an associated bucket, then
300253895Speter         * call the setup callback to get one.
301253895Speter         */
302253895Speter        if (conn->stream == NULL) {
303253895Speter            status = do_conn_setup(conn);
304253895Speter            if (status) {
305253895Speter                return status;
306253895Speter            }
307253895Speter        }
308253895Speter        *ostreamt = conn->ostream_tail;
309253895Speter        *ostreamh = conn->ostream_head;
310253895Speter        *istream = conn->stream;
311253895Speter    } else {
312253895Speter        /* SSL tunnel needed and not set up yet, get a direct unencrypted
313253895Speter         stream for this socket */
314253895Speter        if (conn->stream == NULL) {
315253895Speter            *istream = serf_bucket_socket_create(conn->skt,
316253895Speter                                                 conn->allocator);
317253895Speter        }
318253895Speter        /* Don't create the ostream bucket chain including the ssl_encrypt
319253895Speter         bucket yet. This ensure the CONNECT request is sent unencrypted
320253895Speter         to the proxy. */
321253895Speter        *ostreamt = *ostreamh = conn->ssltunnel_ostream;
322253895Speter    }
323253895Speter
324253895Speter    return APR_SUCCESS;
325253895Speter}
326253895Speter
327251877Speter/* Create and connect sockets for any connections which don't have them
328251877Speter * yet. This is the core of our lazy-connect behavior.
329251877Speter */
330251877Speterapr_status_t serf__open_connections(serf_context_t *ctx)
331251877Speter{
332251877Speter    int i;
333251877Speter
334251877Speter    for (i = ctx->conns->nelts; i--; ) {
335251877Speter        serf_connection_t *conn = GET_CONN(ctx, i);
336253895Speter        serf__authn_info_t *authn_info;
337251877Speter        apr_status_t status;
338251877Speter        apr_socket_t *skt;
339251877Speter
340251877Speter        conn->seen_in_pollset = 0;
341251877Speter
342251877Speter        if (conn->skt != NULL) {
343251877Speter#ifdef SERF_DEBUG_BUCKET_USE
344251877Speter            check_buckets_drained(conn);
345251877Speter#endif
346251877Speter            continue;
347251877Speter        }
348251877Speter
349251877Speter        /* Delay opening until we have something to deliver! */
350251877Speter        if (conn->requests == NULL) {
351251877Speter            continue;
352251877Speter        }
353251877Speter
354251877Speter        apr_pool_clear(conn->skt_pool);
355251877Speter        apr_pool_cleanup_register(conn->skt_pool, conn, clean_skt, clean_skt);
356251877Speter
357251877Speter        status = apr_socket_create(&skt, conn->address->family,
358251877Speter                                   SOCK_STREAM,
359251877Speter#if APR_MAJOR_VERSION > 0
360251877Speter                                   APR_PROTO_TCP,
361251877Speter#endif
362251877Speter                                   conn->skt_pool);
363251877Speter        serf__log(SOCK_VERBOSE, __FILE__,
364251877Speter                  "created socket for conn 0x%x, status %d\n", conn, status);
365251877Speter        if (status != APR_SUCCESS)
366251877Speter            return status;
367251877Speter
368251877Speter        /* Set the socket to be non-blocking */
369251877Speter        if ((status = apr_socket_timeout_set(skt, 0)) != APR_SUCCESS)
370251877Speter            return status;
371251877Speter
372251877Speter        /* Disable Nagle's algorithm */
373251877Speter        if ((status = apr_socket_opt_set(skt,
374251877Speter                                         APR_TCP_NODELAY, 1)) != APR_SUCCESS)
375251877Speter            return status;
376251877Speter
377251877Speter        /* Configured. Store it into the connection now. */
378251877Speter        conn->skt = skt;
379251877Speter
380251877Speter        /* Remember time when we started connecting to server to calculate
381251877Speter           network latency. */
382251877Speter        conn->connect_time = apr_time_now();
383251877Speter
384251877Speter        /* Now that the socket is set up, let's connect it. This should
385251877Speter         * return immediately.
386251877Speter         */
387251877Speter        status = apr_socket_connect(skt, conn->address);
388251877Speter        serf__log_skt(SOCK_VERBOSE, __FILE__, skt,
389251877Speter                      "connected socket for conn 0x%x, status %d\n",
390251877Speter                      conn, status);
391253895Speter        if (status != APR_SUCCESS) {
392251877Speter            if (!APR_STATUS_IS_EINPROGRESS(status))
393251877Speter                return status;
394251877Speter        }
395251877Speter
396251877Speter        /* Flag our pollset as dirty now that we have a new socket. */
397251877Speter        conn->dirty_conn = 1;
398251877Speter        ctx->dirty_pollset = 1;
399251877Speter
400251877Speter        /* If the authentication was already started on another connection,
401251877Speter           prepare this connection (it might be possible to skip some
402251877Speter           part of the handshaking). */
403251877Speter        if (ctx->proxy_address) {
404253895Speter            authn_info = &ctx->proxy_authn_info;
405253895Speter            if (authn_info->scheme) {
406253895Speter                authn_info->scheme->init_conn_func(authn_info->scheme, 407,
407253895Speter                                                   conn, conn->pool);
408253895Speter            }
409251877Speter        }
410251877Speter
411253895Speter        authn_info = serf__get_authn_info_for_server(conn);
412253895Speter        if (authn_info->scheme) {
413253895Speter            authn_info->scheme->init_conn_func(authn_info->scheme, 401,
414253895Speter                                               conn, conn->pool);
415253895Speter        }
416251877Speter
417251877Speter        /* Does this connection require a SSL tunnel over the proxy? */
418251877Speter        if (ctx->proxy_address && strcmp(conn->host_info.scheme, "https") == 0)
419251877Speter            serf__ssltunnel_connect(conn);
420253895Speter        else {
421253895Speter            serf_bucket_t *dummy1, *dummy2;
422253895Speter
423251877Speter            conn->state = SERF_CONN_CONNECTED;
424251877Speter
425253895Speter            status = prepare_conn_streams(conn, &conn->stream,
426253895Speter                                          &dummy1, &dummy2);
427253895Speter            if (status) {
428253895Speter                return status;
429253895Speter            }
430253895Speter        }
431251877Speter    }
432251877Speter
433251877Speter    return APR_SUCCESS;
434251877Speter}
435251877Speter
436262324Speterstatic apr_status_t no_more_writes(serf_connection_t *conn)
437251877Speter{
438251877Speter    /* Note that we should hold new requests until we open our new socket. */
439251877Speter    conn->state = SERF_CONN_CLOSING;
440262324Speter    serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt,
441262324Speter                  "stop writing on conn 0x%x\n", conn);
442251877Speter
443251877Speter    /* Clear our iovec. */
444251877Speter    conn->vec_len = 0;
445251877Speter
446251877Speter    /* Update the pollset to know we don't want to write on this socket any
447251877Speter     * more.
448251877Speter     */
449251877Speter    conn->dirty_conn = 1;
450251877Speter    conn->ctx->dirty_pollset = 1;
451251877Speter    return APR_SUCCESS;
452251877Speter}
453251877Speter
454251877Speter/* Read the 'Connection' header from the response. Return SERF_ERROR_CLOSING if
455251877Speter * the header contains value 'close' indicating the server is closing the
456251877Speter * connection right after this response.
457251877Speter * Otherwise returns APR_SUCCESS.
458251877Speter */
459251877Speterstatic apr_status_t is_conn_closing(serf_bucket_t *response)
460251877Speter{
461251877Speter    serf_bucket_t *hdrs;
462251877Speter    const char *val;
463251877Speter
464251877Speter    hdrs = serf_bucket_response_get_headers(response);
465251877Speter    val = serf_bucket_headers_get(hdrs, "Connection");
466251877Speter    if (val && strcasecmp("close", val) == 0)
467251877Speter        {
468251877Speter            return SERF_ERROR_CLOSING;
469251877Speter        }
470251877Speter
471251877Speter    return APR_SUCCESS;
472251877Speter}
473251877Speter
474251877Speterstatic void link_requests(serf_request_t **list, serf_request_t **tail,
475251877Speter                          serf_request_t *request)
476251877Speter{
477251877Speter    if (*list == NULL) {
478251877Speter        *list = request;
479251877Speter        *tail = request;
480251877Speter    }
481251877Speter    else {
482251877Speter        (*tail)->next = request;
483251877Speter        *tail = request;
484251877Speter    }
485251877Speter}
486251877Speter
487251877Speterstatic apr_status_t destroy_request(serf_request_t *request)
488251877Speter{
489251877Speter    serf_connection_t *conn = request->conn;
490251877Speter
491251877Speter    /* The request and response buckets are no longer needed,
492251877Speter       nor is the request's pool.  */
493251877Speter    if (request->resp_bkt) {
494251877Speter        serf_debug__closed_conn(request->resp_bkt->allocator);
495251877Speter        serf_bucket_destroy(request->resp_bkt);
496251877Speter        request->resp_bkt = NULL;
497251877Speter    }
498251877Speter    if (request->req_bkt) {
499251877Speter        serf_debug__closed_conn(request->req_bkt->allocator);
500251877Speter        serf_bucket_destroy(request->req_bkt);
501251877Speter        request->req_bkt = NULL;
502251877Speter    }
503251877Speter
504251877Speter    serf_debug__bucket_alloc_check(request->allocator);
505251877Speter    if (request->respool) {
506251877Speter        /* ### unregister the pool cleanup for self?  */
507251877Speter        apr_pool_destroy(request->respool);
508251877Speter    }
509251877Speter
510251877Speter    serf_bucket_mem_free(conn->allocator, request);
511251877Speter
512251877Speter    return APR_SUCCESS;
513251877Speter}
514251877Speter
515251877Speterstatic apr_status_t cancel_request(serf_request_t *request,
516251877Speter                                   serf_request_t **list,
517251877Speter                                   int notify_request)
518251877Speter{
519251877Speter    /* If we haven't run setup, then we won't have a handler to call. */
520251877Speter    if (request->handler && notify_request) {
521251877Speter        /* We actually don't care what the handler returns.
522251877Speter         * We have bigger matters at hand.
523251877Speter         */
524251877Speter        (*request->handler)(request, NULL, request->handler_baton,
525251877Speter                            request->respool);
526251877Speter    }
527251877Speter
528251877Speter    if (*list == request) {
529251877Speter        *list = request->next;
530251877Speter    }
531251877Speter    else {
532251877Speter        serf_request_t *scan = *list;
533251877Speter
534251877Speter        while (scan->next && scan->next != request)
535251877Speter            scan = scan->next;
536251877Speter
537251877Speter        if (scan->next) {
538251877Speter            scan->next = scan->next->next;
539251877Speter        }
540251877Speter    }
541251877Speter
542251877Speter    return destroy_request(request);
543251877Speter}
544251877Speter
545251877Speterstatic apr_status_t remove_connection(serf_context_t *ctx,
546251877Speter                                      serf_connection_t *conn)
547251877Speter{
548251877Speter    apr_pollfd_t desc = { 0 };
549251877Speter
550251877Speter    desc.desc_type = APR_POLL_SOCKET;
551251877Speter    desc.desc.s = conn->skt;
552251877Speter    desc.reqevents = conn->reqevents;
553251877Speter
554251877Speter    return ctx->pollset_rm(ctx->pollset_baton,
555362181Sdim                           &desc, &conn->baton);
556251877Speter}
557251877Speter
558251877Speter/* A socket was closed, inform the application. */
559251877Speterstatic void handle_conn_closed(serf_connection_t *conn, apr_status_t status)
560251877Speter{
561251877Speter    (*conn->closed)(conn, conn->closed_baton, status,
562251877Speter                    conn->pool);
563251877Speter}
564251877Speter
565251877Speterstatic apr_status_t reset_connection(serf_connection_t *conn,
566251877Speter                                     int requeue_requests)
567251877Speter{
568251877Speter    serf_context_t *ctx = conn->ctx;
569251877Speter    apr_status_t status;
570251877Speter    serf_request_t *old_reqs;
571251877Speter
572251877Speter    conn->probable_keepalive_limit = conn->completed_responses;
573251877Speter    conn->completed_requests = 0;
574251877Speter    conn->completed_responses = 0;
575251877Speter
576251877Speter    old_reqs = conn->requests;
577251877Speter
578251877Speter    conn->requests = NULL;
579251877Speter    conn->requests_tail = NULL;
580251877Speter
581251877Speter    /* Handle all outstanding requests. These have either not been written yet,
582251877Speter       or have been written but the expected reply wasn't received yet. */
583251877Speter    while (old_reqs) {
584251877Speter        /* If we haven't started to write the connection, bring it over
585251877Speter         * unchanged to our new socket.
586262324Speter         * Do not copy a CONNECT request to the new connection, the ssl tunnel
587262324Speter         * setup code will create a new CONNECT request already.
588251877Speter         */
589262324Speter        if (requeue_requests && !old_reqs->writing_started &&
590262324Speter            !old_reqs->ssltunnel) {
591262324Speter
592251877Speter            serf_request_t *req = old_reqs;
593251877Speter            old_reqs = old_reqs->next;
594251877Speter            req->next = NULL;
595251877Speter            link_requests(&conn->requests, &conn->requests_tail, req);
596251877Speter        }
597251877Speter        else {
598251877Speter            /* Request has been consumed, or we don't want to requeue the
599251877Speter               request. Either way, inform the application that the request
600251877Speter               is cancelled. */
601251877Speter            cancel_request(old_reqs, &old_reqs, requeue_requests);
602251877Speter        }
603251877Speter    }
604251877Speter
605251877Speter    /* Requests queue has been prepared for a new socket, close the old one. */
606251877Speter    if (conn->skt != NULL) {
607251877Speter        remove_connection(ctx, conn);
608251877Speter        status = apr_socket_close(conn->skt);
609251877Speter        serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt,
610251877Speter                      "closed socket, status %d\n", status);
611251877Speter        if (conn->closed != NULL) {
612251877Speter            handle_conn_closed(conn, status);
613251877Speter        }
614251877Speter        conn->skt = NULL;
615251877Speter    }
616251877Speter
617251877Speter    if (conn->stream != NULL) {
618251877Speter        serf_bucket_destroy(conn->stream);
619251877Speter        conn->stream = NULL;
620251877Speter    }
621251877Speter
622251877Speter    destroy_ostream(conn);
623251877Speter
624251877Speter    /* Don't try to resume any writes */
625251877Speter    conn->vec_len = 0;
626251877Speter
627251877Speter    conn->dirty_conn = 1;
628251877Speter    conn->ctx->dirty_pollset = 1;
629251877Speter    conn->state = SERF_CONN_INIT;
630251877Speter
631362181Sdim    conn->hit_eof = 0;
632362181Sdim    conn->connect_time = 0;
633362181Sdim    conn->latency = -1;
634362181Sdim    conn->stop_writing = 0;
635362181Sdim
636251877Speter    serf__log(CONN_VERBOSE, __FILE__, "reset connection 0x%x\n", conn);
637251877Speter
638251877Speter    conn->status = APR_SUCCESS;
639251877Speter
640251877Speter    /* Let our context know that we've 'reset' the socket already. */
641251877Speter    conn->seen_in_pollset |= APR_POLLHUP;
642251877Speter
643251877Speter    /* Found the connection. Closed it. All done. */
644251877Speter    return APR_SUCCESS;
645251877Speter}
646251877Speter
647251877Speterstatic apr_status_t socket_writev(serf_connection_t *conn)
648251877Speter{
649251877Speter    apr_size_t written;
650251877Speter    apr_status_t status;
651251877Speter
652251877Speter    status = apr_socket_sendv(conn->skt, conn->vec,
653251877Speter                              conn->vec_len, &written);
654253895Speter    if (status && !APR_STATUS_IS_EAGAIN(status))
655251877Speter        serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt,
656251877Speter                      "socket_sendv error %d\n", status);
657251877Speter
658251877Speter    /* did we write everything? */
659251877Speter    if (written) {
660251877Speter        apr_size_t len = 0;
661251877Speter        int i;
662251877Speter
663251877Speter        serf__log_skt(SOCK_MSG_VERBOSE, __FILE__, conn->skt,
664251877Speter                      "--- socket_sendv:\n");
665251877Speter
666251877Speter        for (i = 0; i < conn->vec_len; i++) {
667251877Speter            len += conn->vec[i].iov_len;
668251877Speter            if (written < len) {
669251877Speter                serf__log_nopref(SOCK_MSG_VERBOSE, "%.*s",
670251877Speter                                   conn->vec[i].iov_len - (len - written),
671251877Speter                                   conn->vec[i].iov_base);
672251877Speter                if (i) {
673251877Speter                    memmove(conn->vec, &conn->vec[i],
674251877Speter                            sizeof(struct iovec) * (conn->vec_len - i));
675251877Speter                    conn->vec_len -= i;
676251877Speter                }
677251877Speter                conn->vec[0].iov_base = (char *)conn->vec[0].iov_base + (conn->vec[0].iov_len - (len - written));
678251877Speter                conn->vec[0].iov_len = len - written;
679251877Speter                break;
680251877Speter            } else {
681251877Speter                serf__log_nopref(SOCK_MSG_VERBOSE, "%.*s",
682251877Speter                                   conn->vec[i].iov_len, conn->vec[i].iov_base);
683251877Speter            }
684251877Speter        }
685251877Speter        if (len == written) {
686251877Speter            conn->vec_len = 0;
687251877Speter        }
688251877Speter        serf__log_nopref(SOCK_MSG_VERBOSE, "-(%d)-\n", written);
689251877Speter
690251877Speter        /* Log progress information */
691251877Speter        serf__context_progress_delta(conn->ctx, 0, written);
692251877Speter    }
693251877Speter
694251877Speter    return status;
695251877Speter}
696251877Speter
697253895Speterstatic apr_status_t setup_request(serf_request_t *request)
698251877Speter{
699253895Speter    serf_connection_t *conn = request->conn;
700251877Speter    apr_status_t status;
701251877Speter
702253895Speter    /* Now that we are about to serve the request, allocate a pool. */
703253895Speter    apr_pool_create(&request->respool, conn->pool);
704253895Speter    request->allocator = serf_bucket_allocator_create(request->respool,
705253895Speter                                                      NULL, NULL);
706253895Speter    apr_pool_cleanup_register(request->respool, request,
707253895Speter                              clean_resp, clean_resp);
708251877Speter
709253895Speter    /* Fill in the rest of the values for the request. */
710253895Speter    status = request->setup(request, request->setup_baton,
711253895Speter                            &request->req_bkt,
712253895Speter                            &request->acceptor,
713253895Speter                            &request->acceptor_baton,
714253895Speter                            &request->handler,
715253895Speter                            &request->handler_baton,
716253895Speter                            request->respool);
717251877Speter    return status;
718251877Speter}
719251877Speter
720251877Speter/* write data out to the connection */
721251877Speterstatic apr_status_t write_to_connection(serf_connection_t *conn)
722251877Speter{
723251877Speter    if (conn->probable_keepalive_limit &&
724251877Speter        conn->completed_requests > conn->probable_keepalive_limit) {
725251877Speter
726251877Speter        conn->dirty_conn = 1;
727251877Speter        conn->ctx->dirty_pollset = 1;
728251877Speter
729251877Speter        /* backoff for now. */
730251877Speter        return APR_SUCCESS;
731251877Speter    }
732251877Speter
733251877Speter    /* Keep reading and sending until we run out of stuff to read, or
734251877Speter     * writing would block.
735251877Speter     */
736251877Speter    while (1) {
737262324Speter        serf_request_t *request;
738251877Speter        int stop_reading = 0;
739251877Speter        apr_status_t status;
740251877Speter        apr_status_t read_status;
741262324Speter        serf_bucket_t *ostreamt;
742262324Speter        serf_bucket_t *ostreamh;
743251877Speter        int max_outstanding_requests = conn->max_outstanding_requests;
744251877Speter
745251877Speter        /* If we're setting up an ssl tunnel, we can't send real requests
746251877Speter           at yet, as they need to be encrypted and our encrypt buckets
747251877Speter           aren't created yet as we still need to read the unencrypted
748251877Speter           response of the CONNECT request. */
749251877Speter        if (conn->state != SERF_CONN_CONNECTED)
750251877Speter            max_outstanding_requests = 1;
751251877Speter
752251877Speter        if (max_outstanding_requests &&
753251877Speter            conn->completed_requests -
754251877Speter                conn->completed_responses >= max_outstanding_requests) {
755251877Speter            /* backoff for now. */
756251877Speter            return APR_SUCCESS;
757251877Speter        }
758251877Speter
759251877Speter        /* If we have unwritten data, then write what we can. */
760251877Speter        while (conn->vec_len) {
761251877Speter            status = socket_writev(conn);
762251877Speter
763251877Speter            /* If the write would have blocked, then we're done. Don't try
764251877Speter             * to write anything else to the socket.
765251877Speter             */
766251877Speter            if (APR_STATUS_IS_EAGAIN(status))
767251877Speter                return APR_SUCCESS;
768251877Speter            if (APR_STATUS_IS_EPIPE(status) ||
769251877Speter                APR_STATUS_IS_ECONNRESET(status) ||
770251877Speter                APR_STATUS_IS_ECONNABORTED(status))
771262324Speter                return no_more_writes(conn);
772251877Speter            if (status)
773251877Speter                return status;
774251877Speter        }
775251877Speter        /* ### can we have a short write, yet no EAGAIN? a short write
776251877Speter           ### would imply unwritten_len > 0 ... */
777251877Speter        /* assert: unwritten_len == 0. */
778251877Speter
779251877Speter        /* We may need to move forward to a request which has something
780251877Speter         * to write.
781251877Speter         */
782262324Speter        if (!request_or_data_pending(&request, conn)) {
783251877Speter            /* No more requests (with data) are registered with the
784262324Speter             * connection, and no data is pending on the outgoing stream.
785262324Speter             * Let's update the pollset so that we don't try to write to this
786262324Speter             * socket again.
787251877Speter             */
788251877Speter            conn->dirty_conn = 1;
789251877Speter            conn->ctx->dirty_pollset = 1;
790251877Speter            return APR_SUCCESS;
791251877Speter        }
792251877Speter
793251877Speter        status = prepare_conn_streams(conn, &conn->stream, &ostreamt, &ostreamh);
794251877Speter        if (status) {
795251877Speter            return status;
796251877Speter        }
797251877Speter
798262324Speter        if (request) {
799262324Speter            if (request->req_bkt == NULL) {
800262324Speter                read_status = setup_request(request);
801262324Speter                if (read_status) {
802262324Speter                    /* Something bad happened. Propagate any errors. */
803262324Speter                    return read_status;
804262324Speter                }
805251877Speter            }
806251877Speter
807262324Speter            if (!request->writing_started) {
808262324Speter                request->writing_started = 1;
809262324Speter                serf_bucket_aggregate_append(ostreamt, request->req_bkt);
810262324Speter            }
811251877Speter        }
812251877Speter
813251877Speter        /* ### optimize at some point by using read_for_sendfile */
814253895Speter        /* TODO: now that read_iovec will effectively try to return as much
815253895Speter           data as available, we probably don't want to read ALL_AVAIL, but
816253895Speter           a lower number, like the size of one or a few TCP packets, the
817253895Speter           available TCP buffer size ... */
818251877Speter        read_status = serf_bucket_read_iovec(ostreamh,
819251877Speter                                             SERF_READ_ALL_AVAIL,
820251877Speter                                             IOV_MAX,
821251877Speter                                             conn->vec,
822251877Speter                                             &conn->vec_len);
823251877Speter
824251877Speter        if (!conn->hit_eof) {
825253895Speter            if (APR_STATUS_IS_EAGAIN(read_status)) {
826251877Speter                /* We read some stuff, but should not try to read again. */
827251877Speter                stop_reading = 1;
828253895Speter            }
829253895Speter            else if (read_status == SERF_ERROR_WAIT_CONN) {
830253895Speter                /* The bucket told us that it can't provide more data until
831253895Speter                   more data is read from the socket. This normally happens
832253895Speter                   during a SSL handshake.
833251877Speter
834253895Speter                   We should avoid looking for writability for a while so
835253895Speter                   that (hopefully) something will appear in the bucket so
836253895Speter                   we can actually write something. otherwise, we could
837253895Speter                   end up in a CPU spin: socket wants something, but we
838253895Speter                   don't have anything (and keep returning EAGAIN)
839253895Speter                 */
840253895Speter                conn->stop_writing = 1;
841253895Speter                conn->dirty_conn = 1;
842253895Speter                conn->ctx->dirty_pollset = 1;
843251877Speter            }
844251877Speter            else if (read_status && !APR_STATUS_IS_EOF(read_status)) {
845251877Speter                /* Something bad happened. Propagate any errors. */
846251877Speter                return read_status;
847251877Speter            }
848251877Speter        }
849251877Speter
850251877Speter        /* If we got some data, then deliver it. */
851251877Speter        /* ### what to do if we got no data?? is that a problem? */
852251877Speter        if (conn->vec_len > 0) {
853251877Speter            status = socket_writev(conn);
854251877Speter
855251877Speter            /* If we can't write any more, or an error occurred, then
856251877Speter             * we're done here.
857251877Speter             */
858251877Speter            if (APR_STATUS_IS_EAGAIN(status))
859251877Speter                return APR_SUCCESS;
860251877Speter            if (APR_STATUS_IS_EPIPE(status))
861262324Speter                return no_more_writes(conn);
862251877Speter            if (APR_STATUS_IS_ECONNRESET(status) ||
863251877Speter                APR_STATUS_IS_ECONNABORTED(status)) {
864262324Speter                return no_more_writes(conn);
865251877Speter            }
866251877Speter            if (status)
867251877Speter                return status;
868251877Speter        }
869251877Speter
870251877Speter        if (read_status == SERF_ERROR_WAIT_CONN) {
871251877Speter            stop_reading = 1;
872253895Speter            conn->stop_writing = 1;
873253895Speter            conn->dirty_conn = 1;
874253895Speter            conn->ctx->dirty_pollset = 1;
875251877Speter        }
876262324Speter        else if (request && read_status && conn->hit_eof &&
877262324Speter                 conn->vec_len == 0) {
878251877Speter            /* If we hit the end of the request bucket and all of its data has
879251877Speter             * been written, then clear it out to signify that we're done
880251877Speter             * sending the request. On the next iteration through this loop:
881251877Speter             * - if there are remaining bytes they will be written, and as the
882251877Speter             * request bucket will be completely read it will be destroyed then.
883251877Speter             * - we'll see if there are other requests that need to be sent
884251877Speter             * ("pipelining").
885251877Speter             */
886251877Speter            conn->hit_eof = 0;
887251877Speter            serf_bucket_destroy(request->req_bkt);
888251877Speter            request->req_bkt = NULL;
889251877Speter
890251877Speter            /* If our connection has async responses enabled, we're not
891251877Speter             * going to get a reply back, so kill the request.
892251877Speter             */
893251877Speter            if (conn->async_responses) {
894251877Speter                conn->requests = request->next;
895251877Speter                destroy_request(request);
896251877Speter            }
897251877Speter
898251877Speter            conn->completed_requests++;
899251877Speter
900251877Speter            if (conn->probable_keepalive_limit &&
901251877Speter                conn->completed_requests > conn->probable_keepalive_limit) {
902251877Speter                /* backoff for now. */
903251877Speter                stop_reading = 1;
904251877Speter            }
905251877Speter        }
906251877Speter
907251877Speter        if (stop_reading) {
908251877Speter            return APR_SUCCESS;
909251877Speter        }
910251877Speter    }
911251877Speter    /* NOTREACHED */
912251877Speter}
913251877Speter
914251877Speter/* A response message was received from the server, so call
915251877Speter   the handler as specified on the original request. */
916251877Speterstatic apr_status_t handle_response(serf_request_t *request,
917251877Speter                                    apr_pool_t *pool)
918251877Speter{
919251877Speter    apr_status_t status = APR_SUCCESS;
920251877Speter    int consumed_response = 0;
921251877Speter
922251877Speter    /* Only enable the new authentication framework if the program has
923251877Speter     * registered an authentication credential callback.
924251877Speter     *
925251877Speter     * This permits older Serf apps to still handle authentication
926251877Speter     * themselves by not registering credential callbacks.
927251877Speter     */
928251877Speter    if (request->conn->ctx->cred_cb) {
929268960Speter      status = serf__handle_auth_response(&consumed_response,
930268960Speter                                          request,
931268960Speter                                          request->resp_bkt,
932268960Speter                                          request->handler_baton,
933268960Speter                                          pool);
934251877Speter
935268960Speter      /* If there was an error reading the response (maybe there wasn't
936268960Speter         enough data available), don't bother passing the response to the
937268960Speter         application.
938251877Speter
939268960Speter         If the authentication was tried, but failed, pass the response
940268960Speter         to the application, maybe it can do better. */
941268960Speter      if (status) {
942268960Speter          return status;
943268960Speter      }
944251877Speter    }
945251877Speter
946251877Speter    if (!consumed_response) {
947251877Speter        return (*request->handler)(request,
948251877Speter                                   request->resp_bkt,
949251877Speter                                   request->handler_baton,
950251877Speter                                   pool);
951251877Speter    }
952251877Speter
953251877Speter    return status;
954251877Speter}
955251877Speter
956251877Speter/* An async response message was received from the server. */
957251877Speterstatic apr_status_t handle_async_response(serf_connection_t *conn,
958251877Speter                                          apr_pool_t *pool)
959251877Speter{
960251877Speter    apr_status_t status;
961251877Speter
962251877Speter    if (conn->current_async_response == NULL) {
963251877Speter        conn->current_async_response =
964251877Speter            (*conn->async_acceptor)(NULL, conn->stream,
965251877Speter                                    conn->async_acceptor_baton, pool);
966251877Speter    }
967251877Speter
968251877Speter    status = (*conn->async_handler)(NULL, conn->current_async_response,
969251877Speter                                    conn->async_handler_baton, pool);
970251877Speter
971251877Speter    if (APR_STATUS_IS_EOF(status)) {
972251877Speter        serf_bucket_destroy(conn->current_async_response);
973251877Speter        conn->current_async_response = NULL;
974251877Speter        status = APR_SUCCESS;
975251877Speter    }
976251877Speter
977251877Speter    return status;
978251877Speter}
979251877Speter
980253895Speter
981253895Speterapr_status_t
982253895Speterserf__provide_credentials(serf_context_t *ctx,
983253895Speter                          char **username,
984253895Speter                          char **password,
985253895Speter                          serf_request_t *request, void *baton,
986253895Speter                          int code, const char *authn_type,
987253895Speter                          const char *realm,
988253895Speter                          apr_pool_t *pool)
989253895Speter{
990253895Speter    serf_connection_t *conn = request->conn;
991253895Speter    serf_request_t *authn_req = request;
992253895Speter    apr_status_t status;
993253895Speter
994253895Speter    if (request->ssltunnel == 1 &&
995253895Speter        conn->state == SERF_CONN_SETUP_SSLTUNNEL) {
996253895Speter        /* This is a CONNECT request to set up an SSL tunnel over a proxy.
997253895Speter           This request is created by serf, so if the proxy requires
998253895Speter           authentication, we can't ask the application for credentials with
999253895Speter           this request.
1000253895Speter
1001253895Speter           Solution: setup the first request created by the application on
1002253895Speter           this connection, and use that request and its handler_baton to
1003253895Speter           call back to the application. */
1004253895Speter
1005253895Speter        authn_req = request->next;
1006253895Speter        /* assert: app_request != NULL */
1007253895Speter        if (!authn_req)
1008253895Speter            return APR_EGENERAL;
1009253895Speter
1010253895Speter        if (!authn_req->req_bkt) {
1011253895Speter            apr_status_t status;
1012253895Speter
1013253895Speter            status = setup_request(authn_req);
1014253895Speter            /* If we can't setup a request, don't bother setting up the
1015253895Speter               ssl tunnel. */
1016253895Speter            if (status)
1017253895Speter                return status;
1018253895Speter        }
1019253895Speter    }
1020253895Speter
1021253895Speter    /* Ask the application. */
1022253895Speter    status = (*ctx->cred_cb)(username, password,
1023253895Speter                             authn_req, authn_req->handler_baton,
1024253895Speter                             code, authn_type, realm, pool);
1025253895Speter    if (status)
1026253895Speter        return status;
1027253895Speter
1028253895Speter    return APR_SUCCESS;
1029253895Speter}
1030253895Speter
1031251877Speter/* read data from the connection */
1032251877Speterstatic apr_status_t read_from_connection(serf_connection_t *conn)
1033251877Speter{
1034251877Speter    apr_status_t status;
1035251877Speter    apr_pool_t *tmppool;
1036251877Speter    int close_connection = FALSE;
1037251877Speter
1038251877Speter    /* Whatever is coming in on the socket corresponds to the first request
1039251877Speter     * on our chain.
1040251877Speter     */
1041251877Speter    serf_request_t *request = conn->requests;
1042251877Speter
1043253895Speter    /* If the stop_writing flag was set on the connection, reset it now because
1044253895Speter       there is some data to read. */
1045253895Speter    if (conn->stop_writing) {
1046253895Speter        conn->stop_writing = 0;
1047253895Speter        conn->dirty_conn = 1;
1048253895Speter        conn->ctx->dirty_pollset = 1;
1049253895Speter    }
1050253895Speter
1051251877Speter    /* assert: request != NULL */
1052251877Speter
1053251877Speter    if ((status = apr_pool_create(&tmppool, conn->pool)) != APR_SUCCESS)
1054251877Speter        goto error;
1055251877Speter
1056251877Speter    /* Invoke response handlers until we have no more work. */
1057251877Speter    while (1) {
1058251877Speter        serf_bucket_t *dummy1, *dummy2;
1059251877Speter
1060251877Speter        apr_pool_clear(tmppool);
1061251877Speter
1062251877Speter        /* Only interested in the input stream here. */
1063251877Speter        status = prepare_conn_streams(conn, &conn->stream, &dummy1, &dummy2);
1064251877Speter        if (status) {
1065251877Speter            goto error;
1066251877Speter        }
1067251877Speter
1068251877Speter        /* We have a different codepath when we can have async responses. */
1069251877Speter        if (conn->async_responses) {
1070251877Speter            /* TODO What about socket errors? */
1071251877Speter            status = handle_async_response(conn, tmppool);
1072251877Speter            if (APR_STATUS_IS_EAGAIN(status)) {
1073251877Speter                status = APR_SUCCESS;
1074251877Speter                goto error;
1075251877Speter            }
1076251877Speter            if (status) {
1077251877Speter                goto error;
1078251877Speter            }
1079251877Speter            continue;
1080251877Speter        }
1081251877Speter
1082251877Speter        /* We are reading a response for a request we haven't
1083251877Speter         * written yet!
1084251877Speter         *
1085251877Speter         * This shouldn't normally happen EXCEPT:
1086251877Speter         *
1087251877Speter         * 1) when the other end has closed the socket and we're
1088251877Speter         *    pending an EOF return.
1089251877Speter         * 2) Doing the initial SSL handshake - we'll get EAGAIN
1090251877Speter         *    as the SSL buckets will hide the handshake from us
1091251877Speter         *    but not return any data.
1092251877Speter         * 3) When the server sends us an SSL alert.
1093251877Speter         *
1094251877Speter         * In these cases, we should not receive any actual user data.
1095251877Speter         *
1096251877Speter         * 4) When the server sends a error response, like 408 Request timeout.
1097251877Speter         *    This response should be passed to the application.
1098251877Speter         *
1099251877Speter         * If we see an EOF (due to either an expired timeout or the server
1100251877Speter         * sending the SSL 'close notify' shutdown alert), we'll reset the
1101251877Speter         * connection and open a new one.
1102251877Speter         */
1103262324Speter        if (request->req_bkt || !request->writing_started) {
1104251877Speter            const char *data;
1105251877Speter            apr_size_t len;
1106251877Speter
1107251877Speter            status = serf_bucket_peek(conn->stream, &data, &len);
1108251877Speter
1109251877Speter            if (APR_STATUS_IS_EOF(status)) {
1110251877Speter                reset_connection(conn, 1);
1111251877Speter                status = APR_SUCCESS;
1112251877Speter                goto error;
1113251877Speter            }
1114251877Speter            else if (APR_STATUS_IS_EAGAIN(status) && !len) {
1115251877Speter                status = APR_SUCCESS;
1116251877Speter                goto error;
1117251877Speter            } else if (status && !APR_STATUS_IS_EAGAIN(status)) {
1118251877Speter                /* Read error */
1119251877Speter                goto error;
1120251877Speter            }
1121251877Speter
1122251877Speter            /* Unexpected response from the server */
1123251877Speter
1124251877Speter        }
1125251877Speter
1126251877Speter        /* If the request doesn't have a response bucket, then call the
1127251877Speter         * acceptor to get one created.
1128251877Speter         */
1129251877Speter        if (request->resp_bkt == NULL) {
1130251877Speter            request->resp_bkt = (*request->acceptor)(request, conn->stream,
1131251877Speter                                                     request->acceptor_baton,
1132251877Speter                                                     tmppool);
1133251877Speter            apr_pool_clear(tmppool);
1134251877Speter        }
1135251877Speter
1136251877Speter        status = handle_response(request, tmppool);
1137251877Speter
1138251877Speter        /* Some systems will not generate a HUP poll event so we have to
1139251877Speter         * handle the ECONNRESET issue and ECONNABORT here.
1140251877Speter         */
1141251877Speter        if (APR_STATUS_IS_ECONNRESET(status) ||
1142251877Speter            APR_STATUS_IS_ECONNABORTED(status) ||
1143251877Speter            status == SERF_ERROR_REQUEST_LOST) {
1144251877Speter            /* If the connection had ever been good, be optimistic & try again.
1145251877Speter             * If it has never tried again (incl. a retry), fail.
1146251877Speter             */
1147251877Speter            if (conn->completed_responses) {
1148251877Speter                reset_connection(conn, 1);
1149251877Speter                status = APR_SUCCESS;
1150251877Speter            }
1151251877Speter            else if (status == SERF_ERROR_REQUEST_LOST) {
1152251877Speter                status = SERF_ERROR_ABORTED_CONNECTION;
1153251877Speter            }
1154251877Speter            goto error;
1155251877Speter        }
1156251877Speter
1157251877Speter        /* If our response handler says it can't do anything more, we now
1158251877Speter         * treat that as a success.
1159251877Speter         */
1160251877Speter        if (APR_STATUS_IS_EAGAIN(status)) {
1161262324Speter            /* It is possible that while reading the response, the ssl layer
1162262324Speter               has prepared some data to send. If this was the last request,
1163262324Speter               serf will not check for socket writability, so force this here.
1164262324Speter             */
1165262324Speter            if (request_or_data_pending(&request, conn) && !request) {
1166262324Speter                conn->dirty_conn = 1;
1167262324Speter                conn->ctx->dirty_pollset = 1;
1168262324Speter            }
1169251877Speter            status = APR_SUCCESS;
1170251877Speter            goto error;
1171251877Speter        }
1172251877Speter
1173251877Speter        /* If we received APR_SUCCESS, run this loop again. */
1174251877Speter        if (!status) {
1175251877Speter            continue;
1176251877Speter        }
1177251877Speter
1178251877Speter        close_connection = is_conn_closing(request->resp_bkt);
1179251877Speter
1180251877Speter        if (!APR_STATUS_IS_EOF(status) &&
1181251877Speter            close_connection != SERF_ERROR_CLOSING) {
1182251877Speter            /* Whether success, or an error, there is no more to do unless
1183251877Speter             * this request has been completed.
1184251877Speter             */
1185251877Speter            goto error;
1186251877Speter        }
1187251877Speter
1188251877Speter        /* The response has been fully-read, so that means the request has
1189251877Speter         * either been fully-delivered (most likely), or that we don't need to
1190251877Speter         * write the rest of it anymore, e.g. when a 408 Request timeout was
1191251877Speter         $ received.
1192251877Speter         * Remove it from our queue and loop to read another response.
1193251877Speter         */
1194251877Speter        conn->requests = request->next;
1195251877Speter
1196251877Speter        destroy_request(request);
1197251877Speter
1198251877Speter        request = conn->requests;
1199251877Speter
1200251877Speter        /* If we're truly empty, update our tail. */
1201251877Speter        if (request == NULL) {
1202251877Speter            conn->requests_tail = NULL;
1203251877Speter        }
1204251877Speter
1205251877Speter        conn->completed_responses++;
1206251877Speter
1207251877Speter        /* We've to rebuild pollset since completed_responses is changed. */
1208251877Speter        conn->dirty_conn = 1;
1209251877Speter        conn->ctx->dirty_pollset = 1;
1210251877Speter
1211251877Speter        /* This means that we're being advised that the connection is done. */
1212251877Speter        if (close_connection == SERF_ERROR_CLOSING) {
1213251877Speter            reset_connection(conn, 1);
1214251877Speter            if (APR_STATUS_IS_EOF(status))
1215251877Speter                status = APR_SUCCESS;
1216251877Speter            goto error;
1217251877Speter        }
1218251877Speter
1219251877Speter        /* The server is suddenly deciding to serve more responses than we've
1220251877Speter         * seen before.
1221251877Speter         *
1222251877Speter         * Let our requests go.
1223251877Speter         */
1224251877Speter        if (conn->probable_keepalive_limit &&
1225251877Speter            conn->completed_responses > conn->probable_keepalive_limit) {
1226251877Speter            conn->probable_keepalive_limit = 0;
1227251877Speter        }
1228251877Speter
1229251877Speter        /* If we just ran out of requests or have unwritten requests, then
1230251877Speter         * update the pollset. We don't want to read from this socket any
1231251877Speter         * more. We are definitely done with this loop, too.
1232251877Speter         */
1233262324Speter        if (request == NULL || !request->writing_started) {
1234251877Speter            conn->dirty_conn = 1;
1235251877Speter            conn->ctx->dirty_pollset = 1;
1236251877Speter            status = APR_SUCCESS;
1237251877Speter            goto error;
1238251877Speter        }
1239251877Speter    }
1240251877Speter
1241251877Spetererror:
1242251877Speter    apr_pool_destroy(tmppool);
1243251877Speter    return status;
1244251877Speter}
1245251877Speter
1246251877Speter/* process all events on the connection */
1247251877Speterapr_status_t serf__process_connection(serf_connection_t *conn,
1248251877Speter                                      apr_int16_t events)
1249251877Speter{
1250251877Speter    apr_status_t status;
1251251877Speter
1252251877Speter    /* POLLHUP/ERR should come after POLLIN so if there's an error message or
1253251877Speter     * the like sitting on the connection, we give the app a chance to read
1254251877Speter     * it before we trigger a reset condition.
1255251877Speter     */
1256251877Speter    if ((events & APR_POLLIN) != 0) {
1257251877Speter        if ((status = read_from_connection(conn)) != APR_SUCCESS)
1258251877Speter            return status;
1259251877Speter
1260251877Speter        /* If we decided to reset our connection, return now as we don't
1261251877Speter         * want to write.
1262251877Speter         */
1263251877Speter        if ((conn->seen_in_pollset & APR_POLLHUP) != 0) {
1264251877Speter            return APR_SUCCESS;
1265251877Speter        }
1266251877Speter    }
1267251877Speter    if ((events & APR_POLLHUP) != 0) {
1268251877Speter        /* The connection got reset by the server. On Windows this can happen
1269251877Speter           when all data is read, so just cleanup the connection and open
1270251877Speter           a new one.
1271251877Speter           If we haven't had any successful responses on this connection,
1272251877Speter           then error out as it is likely a server issue. */
1273251877Speter        if (conn->completed_responses) {
1274251877Speter            return reset_connection(conn, 1);
1275251877Speter        }
1276251877Speter        return SERF_ERROR_ABORTED_CONNECTION;
1277251877Speter    }
1278251877Speter    if ((events & APR_POLLERR) != 0) {
1279251877Speter        /* We might be talking to a buggy HTTP server that doesn't
1280251877Speter         * do lingering-close.  (httpd < 2.1.8 does this.)
1281251877Speter         *
1282251877Speter         * See:
1283251877Speter         *
1284251877Speter         * http://issues.apache.org/bugzilla/show_bug.cgi?id=35292
1285251877Speter         */
1286251877Speter        if (conn->completed_requests && !conn->probable_keepalive_limit) {
1287251877Speter            return reset_connection(conn, 1);
1288251877Speter        }
1289253895Speter#ifdef SO_ERROR
1290253895Speter        /* If possible, get the error from the platform's socket layer and
1291253895Speter           convert it to an APR status code. */
1292253895Speter        {
1293253895Speter            apr_os_sock_t osskt;
1294253895Speter            if (!apr_os_sock_get(&osskt, conn->skt)) {
1295253895Speter                int error;
1296253895Speter                apr_socklen_t l = sizeof(error);
1297253895Speter
1298262324Speter                if (!getsockopt(osskt, SOL_SOCKET, SO_ERROR, (char*)&error,
1299262324Speter                                &l)) {
1300262324Speter                    status = APR_FROM_OS_ERROR(error);
1301262324Speter
1302262324Speter                    /* Handle fallback for multi-homed servers.
1303262324Speter
1304262324Speter                       ### Improve algorithm to find better than just 'next'?
1305262324Speter
1306262324Speter                       Current Windows versions already handle re-ordering for
1307262324Speter                       api users by using statistics on the recently failed
1308262324Speter                       connections to order the list of addresses. */
1309262324Speter                    if (conn->completed_requests == 0
1310262324Speter                        && conn->address->next != NULL
1311262324Speter                        && (APR_STATUS_IS_ECONNREFUSED(status)
1312262324Speter                            || APR_STATUS_IS_TIMEUP(status)
1313262324Speter                            || APR_STATUS_IS_ENETUNREACH(status))) {
1314262324Speter
1315262324Speter                        conn->address = conn->address->next;
1316262324Speter                        return reset_connection(conn, 1);
1317262324Speter                    }
1318262324Speter
1319262324Speter                    return status;
1320262324Speter                  }
1321253895Speter            }
1322253895Speter        }
1323253895Speter#endif
1324251877Speter        return APR_EGENERAL;
1325251877Speter    }
1326251877Speter    if ((events & APR_POLLOUT) != 0) {
1327251877Speter        if ((status = write_to_connection(conn)) != APR_SUCCESS)
1328251877Speter            return status;
1329251877Speter    }
1330251877Speter    return APR_SUCCESS;
1331251877Speter}
1332251877Speter
1333251877Speterserf_connection_t *serf_connection_create(
1334251877Speter    serf_context_t *ctx,
1335251877Speter    apr_sockaddr_t *address,
1336251877Speter    serf_connection_setup_t setup,
1337251877Speter    void *setup_baton,
1338251877Speter    serf_connection_closed_t closed,
1339251877Speter    void *closed_baton,
1340251877Speter    apr_pool_t *pool)
1341251877Speter{
1342251877Speter    serf_connection_t *conn = apr_pcalloc(pool, sizeof(*conn));
1343251877Speter
1344251877Speter    conn->ctx = ctx;
1345251877Speter    conn->status = APR_SUCCESS;
1346251877Speter    /* Ignore server address if proxy was specified. */
1347251877Speter    conn->address = ctx->proxy_address ? ctx->proxy_address : address;
1348251877Speter    conn->setup = setup;
1349251877Speter    conn->setup_baton = setup_baton;
1350251877Speter    conn->closed = closed;
1351251877Speter    conn->closed_baton = closed_baton;
1352251877Speter    conn->pool = pool;
1353251877Speter    conn->allocator = serf_bucket_allocator_create(pool, NULL, NULL);
1354251877Speter    conn->stream = NULL;
1355251877Speter    conn->ostream_head = NULL;
1356251877Speter    conn->ostream_tail = NULL;
1357251877Speter    conn->baton.type = SERF_IO_CONN;
1358251877Speter    conn->baton.u.conn = conn;
1359251877Speter    conn->hit_eof = 0;
1360251877Speter    conn->state = SERF_CONN_INIT;
1361251877Speter    conn->latency = -1; /* unknown */
1362251877Speter
1363251877Speter    /* Create a subpool for our connection. */
1364251877Speter    apr_pool_create(&conn->skt_pool, conn->pool);
1365251877Speter
1366251877Speter    /* register a cleanup */
1367253895Speter    apr_pool_cleanup_register(conn->pool, conn, clean_conn,
1368253895Speter                              apr_pool_cleanup_null);
1369251877Speter
1370251877Speter    /* Add the connection to the context. */
1371251877Speter    *(serf_connection_t **)apr_array_push(ctx->conns) = conn;
1372251877Speter
1373251877Speter    serf__log(CONN_VERBOSE, __FILE__, "created connection 0x%x\n",
1374251877Speter              conn);
1375251877Speter
1376251877Speter    return conn;
1377251877Speter}
1378251877Speter
1379251877Speterapr_status_t serf_connection_create2(
1380251877Speter    serf_connection_t **conn,
1381251877Speter    serf_context_t *ctx,
1382251877Speter    apr_uri_t host_info,
1383251877Speter    serf_connection_setup_t setup,
1384251877Speter    void *setup_baton,
1385251877Speter    serf_connection_closed_t closed,
1386251877Speter    void *closed_baton,
1387251877Speter    apr_pool_t *pool)
1388251877Speter{
1389251877Speter    apr_status_t status = APR_SUCCESS;
1390251877Speter    serf_connection_t *c;
1391251877Speter    apr_sockaddr_t *host_address = NULL;
1392251877Speter
1393251877Speter    /* Set the port number explicitly, needed to create the socket later. */
1394251877Speter    if (!host_info.port) {
1395251877Speter        host_info.port = apr_uri_port_of_scheme(host_info.scheme);
1396251877Speter    }
1397251877Speter
1398251877Speter    /* Only lookup the address of the server if no proxy server was
1399251877Speter       configured. */
1400251877Speter    if (!ctx->proxy_address) {
1401251877Speter        status = apr_sockaddr_info_get(&host_address,
1402251877Speter                                       host_info.hostname,
1403251877Speter                                       APR_UNSPEC, host_info.port, 0, pool);
1404251877Speter        if (status)
1405251877Speter            return status;
1406251877Speter    }
1407251877Speter
1408251877Speter    c = serf_connection_create(ctx, host_address, setup, setup_baton,
1409251877Speter                               closed, closed_baton, pool);
1410251877Speter
1411251877Speter    /* We're not interested in the path following the hostname. */
1412251877Speter    c->host_url = apr_uri_unparse(c->pool,
1413251877Speter                                  &host_info,
1414262324Speter                                  APR_URI_UNP_OMITPATHINFO |
1415262324Speter                                  APR_URI_UNP_OMITUSERINFO);
1416251877Speter
1417253895Speter    /* Store the host info without the path on the connection. */
1418253895Speter    (void)apr_uri_parse(c->pool, c->host_url, &(c->host_info));
1419253895Speter    if (!c->host_info.port) {
1420253895Speter        c->host_info.port = apr_uri_port_of_scheme(c->host_info.scheme);
1421253895Speter    }
1422253895Speter
1423251877Speter    *conn = c;
1424251877Speter
1425251877Speter    return status;
1426251877Speter}
1427251877Speter
1428251877Speterapr_status_t serf_connection_reset(
1429251877Speter    serf_connection_t *conn)
1430251877Speter{
1431251877Speter    return reset_connection(conn, 0);
1432251877Speter}
1433251877Speter
1434251877Speter
1435251877Speterapr_status_t serf_connection_close(
1436251877Speter    serf_connection_t *conn)
1437251877Speter{
1438251877Speter    int i;
1439251877Speter    serf_context_t *ctx = conn->ctx;
1440251877Speter    apr_status_t status;
1441251877Speter
1442251877Speter    for (i = ctx->conns->nelts; i--; ) {
1443251877Speter        serf_connection_t *conn_seq = GET_CONN(ctx, i);
1444251877Speter
1445251877Speter        if (conn_seq == conn) {
1446251877Speter            while (conn->requests) {
1447251877Speter                serf_request_cancel(conn->requests);
1448251877Speter            }
1449251877Speter            if (conn->skt != NULL) {
1450251877Speter                remove_connection(ctx, conn);
1451251877Speter                status = apr_socket_close(conn->skt);
1452251877Speter                serf__log_skt(SOCK_VERBOSE, __FILE__, conn->skt,
1453251877Speter                              "closed socket, status %d\n",
1454251877Speter                              status);
1455251877Speter                if (conn->closed != NULL) {
1456251877Speter                    handle_conn_closed(conn, status);
1457251877Speter                }
1458251877Speter                conn->skt = NULL;
1459251877Speter            }
1460251877Speter            if (conn->stream != NULL) {
1461251877Speter                serf_bucket_destroy(conn->stream);
1462251877Speter                conn->stream = NULL;
1463251877Speter            }
1464251877Speter
1465251877Speter            destroy_ostream(conn);
1466251877Speter
1467251877Speter            /* Remove the connection from the context. We don't want to
1468251877Speter             * deal with it any more.
1469251877Speter             */
1470251877Speter            if (i < ctx->conns->nelts - 1) {
1471251877Speter                /* move later connections over this one. */
1472251877Speter                memmove(
1473251877Speter                    &GET_CONN(ctx, i),
1474251877Speter                    &GET_CONN(ctx, i + 1),
1475251877Speter                    (ctx->conns->nelts - i - 1) * sizeof(serf_connection_t *));
1476251877Speter            }
1477251877Speter            --ctx->conns->nelts;
1478251877Speter
1479251877Speter            serf__log(CONN_VERBOSE, __FILE__, "closed connection 0x%x\n",
1480251877Speter                      conn);
1481251877Speter
1482251877Speter            /* Found the connection. Closed it. All done. */
1483251877Speter            return APR_SUCCESS;
1484251877Speter        }
1485251877Speter    }
1486251877Speter
1487251877Speter    /* We didn't find the specified connection. */
1488251877Speter    /* ### doc talks about this w.r.t poll structures. use something else? */
1489251877Speter    return APR_NOTFOUND;
1490251877Speter}
1491251877Speter
1492251877Speter
1493251877Spetervoid serf_connection_set_max_outstanding_requests(
1494251877Speter    serf_connection_t *conn,
1495251877Speter    unsigned int max_requests)
1496251877Speter{
1497251877Speter    if (max_requests == 0)
1498251877Speter        serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt,
1499251877Speter                      "Set max. nr. of outstanding requests for this "
1500251877Speter                      "connection to unlimited.\n");
1501251877Speter    else
1502251877Speter        serf__log_skt(CONN_VERBOSE, __FILE__, conn->skt,
1503251877Speter                      "Limit max. nr. of outstanding requests for this "
1504251877Speter                      "connection to %u.\n", max_requests);
1505251877Speter
1506251877Speter    conn->max_outstanding_requests = max_requests;
1507251877Speter}
1508251877Speter
1509251877Speter
1510251877Spetervoid serf_connection_set_async_responses(
1511251877Speter    serf_connection_t *conn,
1512251877Speter    serf_response_acceptor_t acceptor,
1513251877Speter    void *acceptor_baton,
1514251877Speter    serf_response_handler_t handler,
1515251877Speter    void *handler_baton)
1516251877Speter{
1517251877Speter    conn->async_responses = 1;
1518251877Speter    conn->async_acceptor = acceptor;
1519251877Speter    conn->async_acceptor_baton = acceptor_baton;
1520251877Speter    conn->async_handler = handler;
1521251877Speter    conn->async_handler_baton = handler_baton;
1522251877Speter}
1523251877Speter
1524253895Speterstatic serf_request_t *
1525253895Spetercreate_request(serf_connection_t *conn,
1526253895Speter               serf_request_setup_t setup,
1527253895Speter               void *setup_baton,
1528253895Speter               int priority,
1529253895Speter               int ssltunnel)
1530251877Speter{
1531251877Speter    serf_request_t *request;
1532251877Speter
1533251877Speter    request = serf_bucket_mem_alloc(conn->allocator, sizeof(*request));
1534251877Speter    request->conn = conn;
1535251877Speter    request->setup = setup;
1536251877Speter    request->setup_baton = setup_baton;
1537251877Speter    request->handler = NULL;
1538251877Speter    request->respool = NULL;
1539251877Speter    request->req_bkt = NULL;
1540251877Speter    request->resp_bkt = NULL;
1541253895Speter    request->priority = priority;
1542262324Speter    request->writing_started = 0;
1543253895Speter    request->ssltunnel = ssltunnel;
1544251877Speter    request->next = NULL;
1545262324Speter    request->auth_baton = NULL;
1546251877Speter
1547253895Speter    return request;
1548253895Speter}
1549253895Speter
1550253895Speterserf_request_t *serf_connection_request_create(
1551253895Speter    serf_connection_t *conn,
1552253895Speter    serf_request_setup_t setup,
1553253895Speter    void *setup_baton)
1554253895Speter{
1555253895Speter    serf_request_t *request;
1556253895Speter
1557253895Speter    request = create_request(conn, setup, setup_baton,
1558253895Speter                             0, /* priority */
1559253895Speter                             0  /* ssl tunnel */);
1560253895Speter
1561251877Speter    /* Link the request to the end of the request chain. */
1562251877Speter    link_requests(&conn->requests, &conn->requests_tail, request);
1563251877Speter
1564251877Speter    /* Ensure our pollset becomes writable in context run */
1565251877Speter    conn->ctx->dirty_pollset = 1;
1566251877Speter    conn->dirty_conn = 1;
1567251877Speter
1568251877Speter    return request;
1569251877Speter}
1570251877Speter
1571253895Speterstatic serf_request_t *
1572253895Speterpriority_request_create(serf_connection_t *conn,
1573253895Speter                        int ssltunnelreq,
1574253895Speter                        serf_request_setup_t setup,
1575253895Speter                        void *setup_baton)
1576251877Speter{
1577251877Speter    serf_request_t *request;
1578251877Speter    serf_request_t *iter, *prev;
1579251877Speter
1580253895Speter    request = create_request(conn, setup, setup_baton,
1581253895Speter                             1, /* priority */
1582253895Speter                             ssltunnelreq);
1583251877Speter
1584251877Speter    /* Link the new request after the last written request. */
1585251877Speter    iter = conn->requests;
1586251877Speter    prev = NULL;
1587251877Speter
1588251877Speter    /* Find a request that has data which needs to be delivered. */
1589262324Speter    while (iter != NULL && iter->req_bkt == NULL && iter->writing_started) {
1590251877Speter        prev = iter;
1591251877Speter        iter = iter->next;
1592251877Speter    }
1593251877Speter
1594253895Speter    /* A CONNECT request to setup an ssltunnel has absolute priority over all
1595253895Speter       other requests on the connection, so:
1596253895Speter       a. add it first to the queue
1597253895Speter       b. ensure that other priority requests are added after the CONNECT
1598253895Speter          request */
1599253895Speter    if (!request->ssltunnel) {
1600253895Speter        /* Advance to next non priority request */
1601253895Speter        while (iter != NULL && iter->priority) {
1602253895Speter            prev = iter;
1603253895Speter            iter = iter->next;
1604253895Speter        }
1605251877Speter    }
1606251877Speter
1607251877Speter    if (prev) {
1608251877Speter        request->next = iter;
1609251877Speter        prev->next = request;
1610251877Speter    } else {
1611251877Speter        request->next = iter;
1612251877Speter        conn->requests = request;
1613251877Speter    }
1614251877Speter
1615251877Speter    /* Ensure our pollset becomes writable in context run */
1616251877Speter    conn->ctx->dirty_pollset = 1;
1617251877Speter    conn->dirty_conn = 1;
1618251877Speter
1619251877Speter    return request;
1620251877Speter}
1621251877Speter
1622253895Speterserf_request_t *serf_connection_priority_request_create(
1623253895Speter    serf_connection_t *conn,
1624253895Speter    serf_request_setup_t setup,
1625253895Speter    void *setup_baton)
1626253895Speter{
1627253895Speter    return priority_request_create(conn,
1628253895Speter                                   0, /* not a ssltunnel CONNECT request */
1629253895Speter                                   setup, setup_baton);
1630253895Speter}
1631251877Speter
1632253895Speterserf_request_t *serf__ssltunnel_request_create(serf_connection_t *conn,
1633253895Speter                                               serf_request_setup_t setup,
1634253895Speter                                               void *setup_baton)
1635253895Speter{
1636253895Speter    return priority_request_create(conn,
1637253895Speter                                   1, /* This is a ssltunnel CONNECT request */
1638253895Speter                                   setup, setup_baton);
1639253895Speter}
1640253895Speter
1641251877Speterapr_status_t serf_request_cancel(serf_request_t *request)
1642251877Speter{
1643251877Speter    return cancel_request(request, &request->conn->requests, 0);
1644251877Speter}
1645251877Speter
1646251877Speterapr_status_t serf_request_is_written(serf_request_t *request)
1647251877Speter{
1648262324Speter    if (request->writing_started && !request->req_bkt)
1649251877Speter        return APR_SUCCESS;
1650251877Speter
1651251877Speter    return APR_EBUSY;
1652251877Speter}
1653251877Speter
1654251877Speterapr_pool_t *serf_request_get_pool(const serf_request_t *request)
1655251877Speter{
1656251877Speter    return request->respool;
1657251877Speter}
1658251877Speter
1659251877Speter
1660251877Speterserf_bucket_alloc_t *serf_request_get_alloc(
1661251877Speter    const serf_request_t *request)
1662251877Speter{
1663251877Speter    return request->allocator;
1664251877Speter}
1665251877Speter
1666251877Speter
1667251877Speterserf_connection_t *serf_request_get_conn(
1668251877Speter    const serf_request_t *request)
1669251877Speter{
1670251877Speter    return request->conn;
1671251877Speter}
1672251877Speter
1673251877Speter
1674251877Spetervoid serf_request_set_handler(
1675251877Speter    serf_request_t *request,
1676251877Speter    const serf_response_handler_t handler,
1677251877Speter    const void **handler_baton)
1678251877Speter{
1679251877Speter    request->handler = handler;
1680251877Speter    request->handler_baton = handler_baton;
1681251877Speter}
1682251877Speter
1683251877Speter
1684251877Speterserf_bucket_t *serf_request_bucket_request_create(
1685251877Speter    serf_request_t *request,
1686251877Speter    const char *method,
1687251877Speter    const char *uri,
1688251877Speter    serf_bucket_t *body,
1689251877Speter    serf_bucket_alloc_t *allocator)
1690251877Speter{
1691251877Speter    serf_bucket_t *req_bkt, *hdrs_bkt;
1692251877Speter    serf_connection_t *conn = request->conn;
1693251877Speter    serf_context_t *ctx = conn->ctx;
1694253895Speter    int ssltunnel;
1695251877Speter
1696253895Speter    ssltunnel = ctx->proxy_address &&
1697253895Speter                (strcmp(conn->host_info.scheme, "https") == 0);
1698253895Speter
1699251877Speter    req_bkt = serf_bucket_request_create(method, uri, body, allocator);
1700251877Speter    hdrs_bkt = serf_bucket_request_get_headers(req_bkt);
1701251877Speter
1702253895Speter    /* Use absolute uri's in requests to a proxy. USe relative uri's in
1703253895Speter       requests directly to a server or sent through an SSL tunnel. */
1704253895Speter    if (ctx->proxy_address && conn->host_url &&
1705253895Speter        !(ssltunnel && !request->ssltunnel)) {
1706253895Speter
1707251877Speter        serf_bucket_request_set_root(req_bkt, conn->host_url);
1708253895Speter    }
1709251877Speter
1710251877Speter    if (conn->host_info.hostinfo)
1711251877Speter        serf_bucket_headers_setn(hdrs_bkt, "Host",
1712251877Speter                                 conn->host_info.hostinfo);
1713251877Speter
1714253895Speter    /* Setup server authorization headers, unless this is a CONNECT request. */
1715253895Speter    if (!request->ssltunnel) {
1716253895Speter        serf__authn_info_t *authn_info;
1717253895Speter        authn_info = serf__get_authn_info_for_server(conn);
1718253895Speter        if (authn_info->scheme)
1719253895Speter            authn_info->scheme->setup_request_func(HOST, 0, conn, request,
1720251877Speter                                                   method, uri,
1721251877Speter                                                   hdrs_bkt);
1722253895Speter    }
1723251877Speter
1724253895Speter    /* Setup proxy authorization headers.
1725253895Speter       Don't set these headers on the requests to the server if we're using
1726253895Speter       an SSL tunnel, only on the CONNECT request to setup the tunnel. */
1727253895Speter    if (ctx->proxy_authn_info.scheme) {
1728253895Speter        if (strcmp(conn->host_info.scheme, "https") == 0) {
1729253895Speter            if (request->ssltunnel)
1730253895Speter                ctx->proxy_authn_info.scheme->setup_request_func(PROXY, 0, conn,
1731253895Speter                                                                 request,
1732253895Speter                                                                 method, uri,
1733253895Speter                                                                 hdrs_bkt);
1734253895Speter        } else {
1735253895Speter            ctx->proxy_authn_info.scheme->setup_request_func(PROXY, 0, conn,
1736253895Speter                                                             request,
1737253895Speter                                                             method, uri,
1738253895Speter                                                             hdrs_bkt);
1739253895Speter        }
1740253895Speter    }
1741251877Speter
1742251877Speter    return req_bkt;
1743251877Speter}
1744251877Speter
1745251877Speterapr_interval_time_t serf_connection_get_latency(serf_connection_t *conn)
1746251877Speter{
1747251877Speter    if (conn->ctx->proxy_address) {
1748251877Speter        /* Detecting network latency for proxied connection is not implemented
1749251877Speter           yet. */
1750251877Speter        return -1;
1751251877Speter    }
1752251877Speter
1753251877Speter    return conn->latency;
1754251877Speter}
1755