| /* ==================================================================== |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * ==================================================================== |
| */ |
| |
| #include <stdlib.h> |
| |
| #include <apr_pools.h> |
| #include <apr_poll.h> |
| #include <apr_version.h> |
| #include <apr_portable.h> |
| #include <apr_strings.h> |
| |
| #include "serf.h" |
| #include "serf_bucket_util.h" |
| |
| #include "serf_private.h" |
| |
| static apr_status_t clean_resp(void *data) |
| { |
| serf_request_t *request = data; |
| apr_pool_t *respool = request->respool; |
| |
| /* Note that this function must handle rehooking several |
| variables to a different request! |
| |
| See serf_connection__request_requeue() */ |
| |
| request->respool = NULL; |
| |
| /* The request's RESPOOL is being cleared. */ |
| if (respool && request->writing >= SERF_WRITING_STARTED |
| && request->writing < SERF_WRITING_FINISHED) { |
| |
| /* HOUSTON, WE HAVE A PROBLEM. |
| |
| We created buckets inside the pool that is now cleaned and |
| stored them in an aggregate that still lives on. |
| |
| This happens when the application decides that it doesn't |
| need the connection any more and clears the pool of the |
| connection, of which the request pool is a subpool. |
| |
| Let's ask the connection to take care of things */ |
| serf__connection_pre_cleanup(request->conn); |
| } |
| |
| #ifdef SERF_DEBUG_BUCKET_USE |
| if (respool && request->allocator) { |
| serf_debug__closed_conn(request->allocator); |
| } |
| #endif |
| |
| /* If the response has allocated some buckets, then destroy them (since |
| the bucket may hold resources other than memory in RESPOOL). Also |
| make sure to set their fields to NULL so connection closure does |
| not attempt to free them again. */ |
| if (request->resp_bkt) { |
| serf_bucket_destroy(request->resp_bkt); |
| request->resp_bkt = NULL; |
| } |
| if (request->req_bkt) { |
| if (request->writing == SERF_WRITING_NONE) |
| serf_bucket_destroy(request->req_bkt); |
| request->req_bkt = NULL; |
| } |
| |
| #ifdef SERF_DEBUG_BUCKET_USE |
| if (respool && request->allocator) { |
| serf_debug__bucket_alloc_check(request->allocator); |
| } |
| #endif |
| |
| request->allocator = NULL; |
| |
| return APR_SUCCESS; |
| } |
| |
| void serf__link_requests(serf_request_t **list, serf_request_t **tail, |
| serf_request_t *request) |
| { |
| if (*list == NULL) { |
| *list = request; |
| *tail = request; |
| } |
| else { |
| (*tail)->next = request; |
| *tail = request; |
| } |
| } |
| |
| apr_status_t serf__destroy_request(serf_request_t *request) |
| { |
| serf_connection_t *conn = request->conn; |
| |
| if (request->depends_first && request->depends_on) { |
| apr_uint64_t total = 0; |
| serf_request_t *r, **pr; |
| apr_uint64_t rqd = request->dep_priority; |
| |
| /* Calculate total priority of descendants */ |
| for (r = request->depends_first; r; r = r->depends_next) { |
| total += r->dep_priority; |
| } |
| |
| /* Apply now, as if they depend on the parent */ |
| for (r = request->depends_first; r; r = r->depends_next) { |
| if (total) { |
| |
| r->dep_priority = (apr_uint16_t)(rqd * r->dep_priority |
| / total); |
| } |
| else |
| r->dep_priority = 0; |
| |
| r->depends_on = request->depends_on; |
| } |
| |
| /* Remove us from parent */ |
| pr = &request->depends_on->depends_first; |
| while (*pr) { |
| if (*pr == request) { |
| *pr = request->depends_next; |
| break; |
| } |
| |
| pr = &(*pr)->depends_next; |
| } |
| |
| /* And append all our descendants */ |
| *pr = request->depends_first; |
| |
| request->depends_on = NULL; |
| request->depends_first = NULL; |
| request->depends_next = NULL; |
| } |
| else if (request->depends_first) { |
| /* Dependencies will lose their parent */ |
| serf_request_t *r, *next; |
| |
| for (r = request->depends_first; r; r = next) { |
| next = r->next; |
| |
| r->depends_on = NULL; |
| r->depends_next = NULL; |
| } |
| request->depends_first = NULL; |
| } |
| else if (request->depends_on) { |
| serf_request_t **pr; |
| |
| /* Remove us from parent */ |
| pr = &request->depends_on->depends_first; |
| while (*pr) { |
| if (*pr == request) { |
| *pr = request->depends_next; |
| break; |
| } |
| |
| pr = &(*pr)->depends_next; |
| } |
| request->depends_on = NULL; |
| } |
| |
| if (request->writing >= SERF_WRITING_STARTED |
| && request->writing < SERF_WRITING_FINISHED) { |
| |
| /* Schedule for destroy when it is safe again. |
| |
| Destroying now will destroy memory of buckets that we |
| may still need. |
| */ |
| serf__link_requests(&conn->done_reqs, &conn->done_reqs_tail, |
| request); |
| } |
| else { |
| |
| if (request->respool) { |
| apr_pool_t *pool = request->respool; |
| |
| apr_pool_cleanup_run(pool, request, clean_resp); |
| apr_pool_destroy(pool); |
| } |
| |
| serf_bucket_mem_free(conn->allocator, request); |
| } |
| |
| return APR_SUCCESS; |
| } |
| |
| apr_status_t serf__cancel_request(serf_request_t *request, |
| serf_request_t **list, |
| int notify_request) |
| { |
| /* If we haven't run setup, then we won't have a handler to call. */ |
| if (request->handler && notify_request) { |
| /* We actually don't care what the handler returns. |
| * We have bigger matters at hand. |
| */ |
| (void)request->handler(request, NULL, request->handler_baton, |
| request->respool); |
| |
| request->handler = NULL; |
| } |
| |
| if (request->conn && request->conn->perform_cancel_request) { |
| request->conn->perform_cancel_request(request, |
| SERF_ERROR_HTTP2_CANCEL); |
| } |
| |
| if (*list == request) { |
| *list = request->next; |
| } |
| else { |
| serf_request_t *scan = *list; |
| |
| while (scan->next && scan->next != request) |
| scan = scan->next; |
| |
| if (scan->next) { |
| scan->next = scan->next->next; |
| } |
| } |
| |
| return serf__destroy_request(request); |
| } |
| |
| /* Calculate the length of a linked list of requests. */ |
| unsigned int serf__req_list_length(serf_request_t *req) |
| { |
| unsigned int length = 0; |
| |
| while (req) { |
| length++; |
| req = req->next; |
| } |
| |
| return length; |
| } |
| |
| apr_status_t serf__setup_request(serf_request_t *request) |
| { |
| serf_connection_t *conn = request->conn; |
| apr_status_t status; |
| |
| /* Now that we are about to serve the request, allocate a pool. */ |
| apr_pool_create(&request->respool, conn->pool); |
| request->allocator = serf_bucket_allocator_create(request->respool, |
| NULL, NULL); |
| apr_pool_cleanup_register(request->respool, request, |
| clean_resp, apr_pool_cleanup_null); |
| |
| /* Fill in the rest of the values for the request. */ |
| status = request->setup(request, request->setup_baton, |
| &request->req_bkt, |
| &request->acceptor, |
| &request->acceptor_baton, |
| &request->handler, |
| &request->handler_baton, |
| request->respool); |
| return status; |
| } |
| |
| /* A response message was received from the server, so call |
| the handler as specified on the original request. */ |
| apr_status_t serf__handle_response(serf_request_t *request, |
| apr_pool_t *pool) |
| { |
| bool consumed_response = false; |
| |
| /* Only enable the new authentication framework if the program has |
| * registered an authentication credential callback. |
| * |
| * This permits older Serf apps to still handle authentication |
| * themselves by not registering credential callbacks. |
| */ |
| if (!request->auth_done && request->conn->ctx->cred_cb) { |
| apr_status_t status; |
| |
| if (!SERF_BUCKET_IS_RESPONSE(request->resp_bkt)) { |
| request->auth_done = true; |
| status = APR_SUCCESS; |
| } |
| else |
| status = serf__handle_auth_response(&consumed_response, |
| request, |
| request->resp_bkt, |
| pool); |
| |
| if (SERF_BUCKET_READ_ERROR(status)) { |
| |
| /* There was an error while checking the authentication headers of |
| the response. We need to inform the application - which |
| hasn't seen this response yet - of the error. |
| |
| These are the possible causes of the error: |
| |
| 1. A communication error while reading the response status line, |
| headers or while discarding the response body: pass the |
| response unchanged to the application, it will see the same |
| error as serf did. |
| |
| 2. A 401/407 response status for a supported authn scheme that |
| resulted in authn failure: |
| Pass the response as received to the application, the response |
| body can contain an error description. Terminate the response |
| body with the AUTHN error instead of APR_EOF. |
| |
| 3. A 401/407 response status for a supported authn scheme that |
| resulted in an unknown error returned by the application in |
| the credentials callback (Basic/Digest): |
| Handle the same as 2. |
| |
| 4. A 2xx response status for a supported authn scheme that |
| resulted in authn failure: |
| Pass the response headers to the application. The response |
| body is untrusted, so we should drop it and return the AUTHN |
| error instead of APR_EOF. |
| |
| serf__handle_auth_response will already discard the response |
| body, so we can handle this case the same as 2. |
| |
| In summary, all these cases can be handled in the same way: call |
| the application's response handler with the response bucket, but |
| make sure that the application sees error code STATUS instead of |
| APR_EOF after reading the response body. |
| */ |
| |
| serf__bucket_response_set_error_on_eof(request->resp_bkt, status); |
| |
| /* Ignore the application's status code here, use the error status |
| from serf__handle_auth_response. */ |
| (void)(*request->handler)(request, |
| request->resp_bkt, |
| request->handler_baton, |
| pool); |
| } |
| |
| if (status) |
| return status; |
| } |
| |
| if (!consumed_response) { |
| return (*request->handler)(request, |
| request->resp_bkt, |
| request->handler_baton, |
| pool); |
| } |
| |
| return APR_SUCCESS; |
| } |
| |
| apr_status_t |
| serf__provide_credentials(serf_context_t *ctx, |
| char **username, |
| char **password, |
| serf_request_t *request, |
| int code, const char *authn_type, |
| const char *realm, |
| apr_pool_t *pool) |
| { |
| serf_connection_t *conn = request->conn; |
| serf_request_t *authn_req = request; |
| apr_status_t status; |
| |
| if (request->ssltunnel == 1 && |
| conn->state == SERF_CONN_SETUP_SSLTUNNEL) { |
| /* This is a CONNECT request to set up an SSL tunnel over a proxy. |
| This request is created by serf, so if the proxy requires |
| authentication, we can't ask the application for credentials with |
| this request. |
| |
| Solution: setup the first request created by the application on |
| this connection, and use that request and its handler_baton to |
| call back to the application. */ |
| |
| /* request->next will be NULL if this was the last request written */ |
| authn_req = request->next; |
| if (!authn_req) |
| authn_req = conn->unwritten_reqs; |
| |
| /* assert: app_request != NULL */ |
| if (!authn_req) |
| return APR_EGENERAL; |
| |
| if (!authn_req->req_bkt) { |
| status = serf__setup_request(authn_req); |
| /* If we can't setup a request, don't bother setting up the |
| ssl tunnel. */ |
| if (status) |
| return status; |
| } |
| } |
| |
| /* Ask the application. */ |
| status = (*ctx->cred_cb)(username, password, |
| authn_req, authn_req->handler_baton, |
| code, authn_type, realm, pool); |
| if (status) |
| return status; |
| |
| return APR_SUCCESS; |
| } |
| |
| static serf_request_t * |
| create_request(serf_connection_t *conn, |
| serf_request_setup_t setup, |
| void *setup_baton, |
| bool priority, |
| bool ssltunnel) |
| { |
| serf_request_t *request; |
| |
| request = serf_bucket_mem_alloc(conn->allocator, sizeof(*request)); |
| request->conn = conn; |
| request->setup = setup; |
| request->setup_baton = setup_baton; |
| request->handler = NULL; |
| request->acceptor = NULL; |
| request->respool = NULL; |
| request->req_bkt = NULL; |
| request->resp_bkt = NULL; |
| request->priority = priority; |
| request->writing = SERF_WRITING_NONE; |
| request->ssltunnel = ssltunnel; |
| request->auth_done = FALSE; |
| request->next = NULL; |
| request->auth_baton = NULL; |
| request->protocol_baton = NULL; |
| request->depends_on = NULL; |
| request->depends_next = NULL; |
| request->depends_first = NULL; |
| request->dep_priority = SERF_REQUEST_PRIORITY_DEFAULT; |
| |
| return request; |
| } |
| |
| serf_request_t *serf_connection_request_create( |
| serf_connection_t *conn, |
| serf_request_setup_t setup, |
| void *setup_baton) |
| { |
| serf_request_t *request; |
| |
| request = create_request(conn, setup, setup_baton, |
| false /* priority */, |
| false /* ssl tunnel */); |
| |
| /* Link the request to the end of the request chain. */ |
| serf__link_requests(&conn->unwritten_reqs, &conn->unwritten_reqs_tail, request); |
| conn->nr_of_unwritten_reqs++; |
| |
| /* Ensure our pollset becomes writable in context run */ |
| serf_io__set_pollset_dirty(&conn->io); |
| |
| return request; |
| } |
| |
| static void |
| insert_priority_request(serf_request_t *request) |
| { |
| serf_request_t *iter, *prev; |
| serf_connection_t *conn = request->conn; |
| |
| /* Link the new request after the last written request. */ |
| iter = conn->unwritten_reqs; |
| prev = NULL; |
| |
| /* TODO: what if a request is partially written? */ |
| /* Find a request that has data which needs to be delivered. */ |
| while (iter != NULL && iter->req_bkt == NULL |
| && (iter->writing >= SERF_WRITING_STARTED)) { |
| prev = iter; |
| iter = iter->next; |
| } |
| |
| /* A CONNECT request to setup an ssltunnel has absolute priority over all |
| other requests on the connection, so: |
| a. add it first to the queue |
| b. ensure that other priority requests are added after the CONNECT |
| request */ |
| if (!request->ssltunnel) { |
| /* Advance to next non priority request */ |
| while (iter != NULL && iter->priority) { |
| prev = iter; |
| iter = iter->next; |
| } |
| } |
| |
| if (prev) { |
| request->next = iter; |
| prev->next = request; |
| } else { |
| request->next = iter; |
| conn->unwritten_reqs = request; |
| } |
| conn->nr_of_unwritten_reqs++; |
| |
| /* Ensure our pollset becomes writable in context run */ |
| serf_io__set_pollset_dirty(&conn->io); |
| } |
| |
| static serf_request_t * |
| priority_request_create(serf_connection_t *conn, |
| int ssltunnelreq, |
| serf_request_setup_t setup, |
| void *setup_baton) |
| { |
| serf_request_t *request; |
| |
| request = create_request(conn, setup, setup_baton, |
| true /* priority */, |
| ssltunnelreq); |
| |
| insert_priority_request(request); |
| |
| return request; |
| } |
| |
| serf_request_t *serf_connection_priority_request_create( |
| serf_connection_t *conn, |
| serf_request_setup_t setup, |
| void *setup_baton) |
| { |
| return priority_request_create(conn, |
| 0, /* not a ssltunnel CONNECT request */ |
| setup, setup_baton); |
| } |
| |
| serf_request_t *serf__ssltunnel_request_create(serf_connection_t *conn, |
| serf_request_setup_t setup, |
| void *setup_baton) |
| { |
| return priority_request_create(conn, |
| 1, /* This is a ssltunnel CONNECT request */ |
| setup, setup_baton); |
| } |
| |
| /* Serf request_handler_t to discard a request */ |
| static apr_status_t discard_response_handler(serf_request_t *request, |
| serf_bucket_t *response, |
| void *handler_baton, |
| apr_pool_t *pool) |
| { |
| apr_status_t status; |
| apr_size_t len; |
| |
| do |
| { |
| const char *data; |
| |
| status = serf_bucket_read(response, SERF_READ_ALL_AVAIL, &data, &len); |
| } while (!status && len); |
| |
| return status; |
| } |
| |
| apr_status_t serf_connection__request_requeue(serf_request_t *request) |
| { |
| serf_request_t *discard; |
| serf_request_t **pr; |
| |
| /* For some reason, somebody wants to restart this request, that was |
| at least partially written and partially read. Most likely to |
| slightly change it. (E.g. to add auth headers). |
| |
| To handle this we have to do two things: we have to handle remaining |
| incoming data on the existing request. And we have to make the request |
| ready to write it again.. preferably as the first new request. |
| */ |
| |
| /* Setup a new request to handle discarding the remaining body */ |
| discard = create_request(request->conn, |
| NULL, NULL, |
| request->priority, |
| request->ssltunnel); |
| |
| discard->respool = request->respool; |
| discard->allocator = request->allocator; |
| discard->req_bkt = request->req_bkt; |
| discard->resp_bkt = request->resp_bkt; |
| |
| discard->setup = NULL; /* Already setup */ |
| discard->setup_baton = NULL; |
| |
| discard->acceptor = NULL; |
| discard->acceptor_baton = NULL; /* Already accepted */ |
| |
| discard->handler = discard_response_handler; |
| discard->handler_baton = NULL; |
| |
| discard->protocol_baton = request->protocol_baton; |
| discard->auth_done = request->auth_done; |
| discard->auth_baton = request->auth_baton; |
| |
| discard->writing = request->writing; |
| |
| if (discard->respool) { |
| apr_pool_cleanup_kill(discard->respool, request, clean_resp); |
| apr_pool_cleanup_register(discard->respool, discard, |
| clean_resp, apr_pool_cleanup_null); |
| } |
| |
| if (request->depends_on) { |
| /* Update request dependencies */ |
| discard->depends_on = request->depends_on; |
| |
| for (pr = &discard->depends_on->depends_first; |
| *pr; |
| pr = &(*pr)->depends_next) |
| { |
| if (*pr == request) { |
| *pr = discard; |
| discard->depends_next = request->depends_next; |
| request->depends_next = NULL; |
| request->depends_on = NULL; |
| break; |
| } |
| } |
| } |
| if (request->depends_first) { |
| serf_request_t *r; |
| |
| for (r = request->depends_first; r; r = r->depends_next) { |
| r->depends_on = discard; |
| } |
| discard->depends_first = request->depends_first; |
| request->depends_first = NULL; |
| } |
| |
| /* And now make the discard request take the place of the old request */ |
| for (pr = &request->conn->written_reqs; *pr; pr = &(*pr)->next) { |
| if (*pr == request) { |
| *pr = discard; |
| discard->next = request->next; |
| request->next = NULL; |
| |
| if (!discard->next) |
| request->conn->written_reqs_tail = discard; |
| break; |
| } |
| } |
| |
| /* Tell the protocol handler (if any) that we are no longer looking at the |
| request */ |
| |
| if (request->conn->perform_cancel_request) |
| request->conn->perform_cancel_request(discard, SERF_ERROR_HTTP2_CANCEL); |
| |
| /* And now unhook the existing request */ |
| request->allocator = NULL; |
| request->respool = NULL; |
| request->req_bkt = NULL; |
| request->resp_bkt = NULL; |
| request->writing = SERF_WRITING_NONE; |
| request->auth_baton = NULL; |
| request->protocol_baton = NULL; |
| |
| request->acceptor = NULL; |
| request->acceptor_baton = NULL; |
| request->handler = NULL; |
| request->handler_baton = NULL; |
| request->auth_done = false; |
| |
| if (discard->depends_first || discard->depends_on) { |
| serf_connection_request_prioritize(request, discard, 0xFFFF, |
| TRUE); |
| } |
| |
| insert_priority_request(request); |
| |
| return APR_SUCCESS; |
| } |
| |
| apr_status_t serf_request_cancel(serf_request_t *request) |
| { |
| serf_connection_t *conn = request->conn; |
| serf_request_t *tmp = conn->unwritten_reqs; |
| |
| /* Find out which queue holds the request */ |
| while (tmp != NULL && tmp != request) |
| tmp = tmp->next; |
| |
| if (tmp) |
| return serf__cancel_request(request, &conn->unwritten_reqs, 0); |
| else |
| return serf__cancel_request(request, &conn->written_reqs, 0); |
| |
| } |
| |
| void serf_connection_request_prioritize(serf_request_t *request, |
| serf_request_t *depends_on, |
| apr_uint16_t priority, |
| int exclusive) |
| { |
| if (request->depends_on != depends_on) { |
| serf_request_t *r; |
| |
| if (depends_on->conn != request->conn || depends_on == request) |
| abort(); |
| |
| /* If we are indirectly made dependent on ourself, we first |
| reprioritize the descendant on our current parent. See |
| https://tools.ietf.org/html/rfc7540#section-5.3.3 |
| |
| If a stream is made dependent on one of its own dependencies, the |
| formerly dependent stream is first moved to be dependent on the |
| reprioritized stream's previous parent. The moved dependency |
| retains its weight. */ |
| |
| r = depends_on; |
| |
| while (r && r != request && r->depends_on) |
| r = r->depends_on; |
| |
| if (r == request) |
| { |
| serf_connection_request_prioritize(depends_on, |
| request->depends_on, |
| depends_on->dep_priority, |
| false /* exclusive */); |
| } |
| |
| if (request->depends_on) { |
| /* Ok, we can now update our dependency */ |
| |
| serf_request_t **pr = &request->depends_on->depends_first; |
| |
| while (*pr) { |
| if (*pr == request) { |
| *pr = request->depends_next; |
| break; |
| } |
| pr = &(*pr)->depends_next; |
| } |
| } |
| |
| request->depends_on = depends_on; |
| |
| if (depends_on) { |
| if (exclusive) { |
| r = depends_on->depends_first; |
| |
| while (r) { |
| r->depends_on = request; |
| |
| if (r->depends_next) |
| r = r->depends_next; |
| else |
| break; |
| } |
| |
| if (r) { |
| r->depends_next = request->depends_first; |
| r->depends_first = depends_on->depends_first; |
| } |
| request->depends_next = NULL; |
| } |
| else { |
| request->depends_next = depends_on->depends_first; |
| } |
| depends_on->depends_first = request; |
| } |
| else |
| request->depends_next = NULL; |
| } |
| |
| if (priority) |
| request->dep_priority = priority; |
| |
| /* And now tell the protocol about this */ |
| if (request->conn->perform_prioritize_request) |
| request->conn->perform_prioritize_request(request, exclusive != 0); |
| } |
| |
| |
| apr_status_t serf_request_is_written(serf_request_t *request) |
| { |
| if (request->writing >= SERF_WRITING_FINISHED) |
| return APR_SUCCESS; |
| |
| return APR_EBUSY; |
| } |
| |
| apr_pool_t *serf_request_get_pool(const serf_request_t *request) |
| { |
| return request->respool; |
| } |
| |
| |
| serf_bucket_alloc_t *serf_request_get_alloc( |
| const serf_request_t *request) |
| { |
| return request->allocator; |
| } |
| |
| |
| serf_connection_t *serf_request_get_conn( |
| const serf_request_t *request) |
| { |
| return request->conn; |
| } |
| |
| |
| void serf_request_set_handler( |
| serf_request_t *request, |
| const serf_response_handler_t handler, |
| const void **handler_baton) |
| { |
| request->handler = handler; |
| request->handler_baton = handler_baton; |
| } |
| |
| |
| serf_bucket_t *serf_request_bucket_request_create( |
| serf_request_t *request, |
| const char *method, |
| const char *uri, |
| serf_bucket_t *body, |
| serf_bucket_alloc_t *allocator) |
| { |
| serf_bucket_t *req_bkt; |
| serf_bucket_t *hdrs_bkt; |
| serf_connection_t *conn = request->conn; |
| serf_context_t *ctx = conn->ctx; |
| int tunneled; |
| |
| tunneled = ctx->proxy_address |
| && (strcmp(conn->host_info.scheme, "https") == 0); |
| |
| req_bkt = serf_bucket_request_create(method, uri, body, allocator); |
| hdrs_bkt = serf_bucket_request_get_headers(req_bkt); |
| |
| /* Use absolute uri's in requests to a proxy. USe relative uri's in |
| requests directly to a server or sent through an SSL tunnel. */ |
| if (ctx->proxy_address && conn->host_url && !tunneled) |
| { |
| serf_bucket_request_set_root(req_bkt, conn->host_url); |
| } |
| |
| if (conn->host_info.hostinfo) |
| { |
| serf_bucket_headers_setn(hdrs_bkt, "Host", conn->host_info.hostinfo); |
| } |
| |
| /* Setup server authentication headers. */ |
| serf__auth_setup_request(HOST, request, method, uri, hdrs_bkt); |
| |
| /* Setup proxy authentication headers, unless we're tunneling. */ |
| if (!tunneled) |
| serf__auth_setup_request(PROXY, request, method, uri, hdrs_bkt); |
| |
| return req_bkt; |
| } |