Index: serf_private.h =================================================================== --- serf_private.h (revision 1280) +++ serf_private.h (working copy) @@ -1,9 +1,9 @@ -/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein + /* Copyright 2002-2004 Justin Erenkrantz and Greg Stein * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed 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 @@ -30,6 +30,8 @@ #define SERF_IO_CONN (2) #define SERF_IO_LISTENER (3) +typedef struct serf__authn_scheme_t serf__authn_scheme_t; + typedef struct serf_io_baton_t { int type; union { @@ -62,6 +64,8 @@ struct serf_request_t { serf_bucket_t *resp_bkt; + int written; + struct serf_request_t *next; }; @@ -70,6 +74,14 @@ typedef struct serf_pollset_t { apr_pollset_t *pollset; } serf_pollset_t; +typedef struct serf__authn_info_t { + const char *realm; + + const serf__authn_scheme_t *scheme; + + void *baton; +} serf__authn_info_t; + struct serf_context_t { /* the pool used for self and for other allocations */ apr_pool_t *pool; @@ -93,6 +105,15 @@ struct serf_context_t { void *progress_baton; apr_off_t progress_read; apr_off_t progress_written; + + /* authentication info for this context, shared by all connections. */ + serf__authn_info_t authn_info; + serf__authn_info_t proxy_authn_info; + + /* List of authn types supported by the client.*/ + int authn_types; + /* Callback function used to get credentials for a realm. */ + serf_credentials_callback_t cred_cb; }; struct serf_listener_t { @@ -184,9 +205,96 @@ struct serf_connection_t { /* Host info. */ const char *host_url; apr_uri_t host_info; + + /* connection and authentication scheme specific information */ + void *authn_baton; + void *proxy_authn_baton; }; +/*** Authentication handler declarations ***/ +/** + * For each authentication scheme we need a handler function of type + * serf__auth_handler_func_t. This function will be called when an + * authentication challenge is received in a session. + */ +typedef apr_status_t +(*serf__auth_handler_func_t)(int code, + serf_request_t *request, + serf_bucket_t *response, + const char *auth_hdr, + const char *auth_attr, + void *baton, + apr_pool_t *pool); + +/** + * For each authentication scheme we need an initialization function of type + * serf__init_conn_func_t. This function will be called when a new + * connection is opened. + */ +typedef apr_status_t +(*serf__init_conn_func_t)(int code, + serf_connection_t *conn, + apr_pool_t *pool); + +/** + * For each authentication scheme we need a setup_request function of type + * serf__setup_request_func_t. This function will be called when a + * new serf_request_t object is created and should fill in the correct + * authentication headers (if needed). + */ +typedef apr_status_t +(*serf__setup_request_func_t)(int code, + serf_connection_t *conn, + const char *method, + const char *uri, + serf_bucket_t *hdrs_bkt); + +/** + * This function will be called when a response is received, so that the + * scheme handler can validate the Authentication related response headers + * (if needed). + */ +typedef apr_status_t +(*serf__validate_response_func_t)(serf_request_t *request, + serf_bucket_t *response, + apr_pool_t *pool); + +/** + * serf__authn_scheme_t: vtable for an authn scheme provider. + */ +struct serf__authn_scheme_t { + /* The http status code that's handled by this authentication scheme. + Normal values are 401 for server authentication and 407 for proxy + authentication */ + int code; + + /* The name of this authentication scheme. This should be a case + sensitive match of the string sent in the HTTP authentication header. */ + const char *name; + + /* Internal code used for this authn type. */ + int type; + + /* The initialization function if any; otherwise, NULL */ + serf__init_conn_func_t init_conn_func; + + /* The authentication handler function */ + serf__auth_handler_func_t handle_func; + + /* Function to set up the authentication header of a request */ + serf__setup_request_func_t setup_request_func; + + /* Function to validate the authentication header of a response */ + serf__validate_response_func_t validate_response_func; +}; + +apr_status_t serf__handle_auth_response(int *consumed_response, + serf_request_t *request, + serf_bucket_t *response, + void *baton, + apr_pool_t *pool); + /* fromt context.c */ void serf__context_progress_delta(void *progress_baton, apr_off_t read, apr_off_t written); Index: NOTICE =================================================================== --- NOTICE (revision 1280) +++ NOTICE (working copy) @@ -1,2 +1,3 @@ This product includes software developed by -The Apache Software Foundation (http://www.apache.org/). +The Apache Software Foundation (http://www.apache.org/) and +by the Subversion Corporation (http://subversion.org). Index: serf.h =================================================================== --- serf.h (revision 1280) +++ serf.h (working copy) @@ -72,6 +72,15 @@ typedef struct serf_request_t serf_request_t; */ #define SERF_ERROR_REQUEST_LOST (APR_OS_START_USERERR + SERF_ERROR_RANGE + 2) +/* General authentication related errors */ +#define SERF_ERROR_AUTHN_FAILED (APR_OS_START_USERERR + SERF_ERROR_RANGE + 90) + +/* None of the available authn mechanisms for the request are supported */ +#define SERF_ERROR_AUTHN_NOT_SUPPORTED (APR_OS_START_USERERR + SERF_ERROR_RANGE + 91) + +/* Authn was requested by the server but the header lacked some attribute */ +#define SERF_ERROR_AUTHN_MISSING_ATTRIBUTE (APR_OS_START_USERERR + SERF_ERROR_RANGE + 92) + /** * Create a new context for serf operations. * @@ -296,6 +305,14 @@ typedef apr_status_t (*serf_response_handler_t)(se apr_pool_t *pool); /** + */ +typedef apr_status_t (*serf_credentials_callback_t)(char **username, char **password, + serf_request_t *request, void *baton, + int code, const char *authn_type, + const char *realm, + apr_pool_t *pool); + +/** * Create a new connection associated with the @a ctx serf context. * * A connection will be created to (eventually) connect to the address @@ -536,6 +553,22 @@ SERF_DECLARE(void) serf_config_proxy( serf_context_t *ctx, apr_sockaddr_t *address); +/* Supported authentication types. */ +#define SERF_AUTHN_NONE 0x00 +#define SERF_AUTHN_BASIC 0x01 +#define SERF_AUTHN_DIGEST 0x02 +#define SERF_AUTHN_NTLM 0x04 +#define SERF_AUTHN_NEGOTIATE 0x08 +#define SERF_AUTHN_ALL 0xFF + +SERF_DECLARE(void) serf_config_authn_types( + serf_context_t *ctx, + int authn_types); + +SERF_DECLARE(void) serf_config_credentials_callback( + serf_context_t *ctx, + serf_credentials_callback_t cred_cb); + /* ### maybe some connection control functions for flood? */ /*** Special bucket creation functions ***/ Index: serfmake =================================================================== --- serfmake (revision 1280) +++ serfmake (working copy) @@ -34,6 +34,8 @@ LIB_FILES = [ ('buckets', 'ssl_buckets'), ('buckets', 'barrier_buckets'), ('buckets', 'chunk_buckets'), + ('auth', 'auth'), + ('auth', 'auth_basic'), ] TEST_DEPS = [ @@ -252,7 +254,7 @@ class Builder: self._add_dep(prog, [lib, obj], cmd) def _add_compile(self, src, obj, hdrs): - cmd = '%s --silent --mode=compile %s %s %s %s -c -o %s %s' % ( + cmd = '%s --silent --mode=compile %s -g %s %s %s -c -o %s %s' % ( self.LIBTOOL, self.CC, self.CFLAGS, self.CPPFLAGS, self.INCLUDES, obj.fname, src.fname) self._add_dep(obj, [src] + hdrs, cmd) Index: auth/auth.c =================================================================== --- auth/auth.c (revision 0) +++ auth/auth.c (revision 0) @@ -0,0 +1,341 @@ +/* Copyright 2009 Justin Erenkrantz and Greg Stein + * + * Licensed 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 "serf.h" +#include "serf_private.h" +#include "auth.h" + +#include +#include + +static apr_status_t +default_auth_response_handler(serf_request_t *request, + serf_bucket_t *response, + apr_pool_t *pool) +{ + return APR_SUCCESS; +} + +static const serf__authn_scheme_t serf_authn_schemes[] = { + { + 401, + "Basic", + SERF_AUTHN_BASIC, + serf__init_basic_connection, + serf__handle_basic_auth, + serf__setup_request_basic_auth, + default_auth_response_handler, + }, +/* { + 407, + "Basic", + SERF_AUTHN_BASIC, + serf__init_proxy_basic_connection, + serf__handle_proxy_basic_auth, + serf__setup_request_proxy_basic_auth, + default_auth_response_handler, + }, + { + 401, + "Digest", + SERF_AUTHN_DIGEST, + serf__init_digest_connection, + serf__handle_digest_auth, + serf__setup_request_digest_auth, + serf__validate_response_digest_auth, + },*/ + + /* ADD NEW AUTHENTICATION IMPLEMENTATIONS HERE (as they're written) */ + + /* sentinel */ + { 0 } +}; + + +/** + * Baton passed to the response header callback function + */ +typedef struct { + int code; + apr_status_t status; + const char *header; + serf_request_t *request; + serf_bucket_t *response; + void *baton; + apr_pool_t *pool; + const serf__authn_scheme_t *scheme; + const char *last_scheme_name; +} auth_baton_t; + +/* Reads and discards all bytes in the response body. */ +static apr_status_t discard_body(serf_bucket_t *response) +{ + apr_status_t status; + const char *data; + apr_size_t len; + + while (1) { + status = serf_bucket_read(response, SERF_READ_ALL_AVAIL, &data, &len); + + if (status) { + return status; + } + + /* feed me */ + } +} + +/** + * handle_auth_header is called for each header in the response. It filters + * out the Authenticate headers (WWW or Proxy depending on what's needed) and + * tries to find a matching scheme handler. + * + * Returns a non-0 value of a matching handler was found. + */ +static int handle_auth_header(void *baton, + const char *key, + const char *header) +{ + auth_baton_t *ab = baton; + int scheme_found = FALSE; + const char *auth_name; + const char *auth_attr; + const serf__authn_scheme_t *scheme = NULL; + serf_connection_t *conn = ab->request->conn; + serf_context_t *ctx = conn->ctx; + + /* We're only interested in xxxx-Authenticate headers. */ + if (strcmp(key, ab->header) != 0) + return 0; + + /* Extract the authentication scheme name, and prepare for reading + the attributes. */ + auth_attr = strchr(header, ' '); + if (auth_attr) { + auth_name = apr_pstrmemdup(ab->pool, header, auth_attr - header); + ++auth_attr; + } + else + auth_name = header; + + ab->last_scheme_name = auth_name; + + /* Find the matching authentication handler. + Note that we don't reuse the auth scheme stored in the session, + as that may have changed. (ex. fallback from ntlm to basic.) */ + for (scheme = serf_authn_schemes; scheme->code != 0; ++scheme) { + if (ab->code == scheme->code && + strcmp(auth_name, scheme->name) == 0 && + ctx->authn_types & scheme->type) { + serf__auth_handler_func_t handler = scheme->handle_func; + apr_status_t status; + + /* If this is the first time we use this scheme in this context, + make sure to initialize the authentication part of the context + first. */ + if (ab->code == 401 && ctx->authn_info.scheme != scheme) { + status = scheme->init_conn_func(ab->code, conn, ctx->pool); + if (!status) + ctx->authn_info.scheme = scheme; + else + ctx->authn_info.scheme = NULL; + } + else if (ab->code == 407 && ctx->proxy_authn_info.scheme != scheme) { + status = scheme->init_conn_func(ab->code, conn, ctx->pool); + if (!status) + ctx->proxy_authn_info.scheme = scheme; + else + ctx->proxy_authn_info.scheme = NULL; + } + + if (!status) { + scheme_found = TRUE; + ab->scheme = scheme; + status = handler(ab->code, ab->request, ab->response, + header, auth_attr, ab->baton, ctx->pool); + } + + /* If the authentication fails, cache the error for now. Try the + next available scheme. If there's none raise the error. */ + if (status) { + scheme_found = FALSE; + scheme = NULL; + ab->status = status; + } + + break; + } + } + + /* If a matching scheme handler was found, we can stop iterating + over the response headers - so return a non-0 value. */ + return scheme_found; +} + +/* Dispatch authentication handling. This function matches the possible + authentication mechanisms with those available. Server and proxy + authentication are evaluated separately. */ +static apr_status_t dispatch_auth(int code, + serf_request_t *request, + serf_bucket_t *response, + void *baton, + apr_pool_t *pool) +{ + serf_context_t *ctx = request->conn->ctx; + serf_bucket_t *hdrs; + serf__authn_info_t authn_info = (code == 401 ? ctx->authn_info : + ctx->proxy_authn_info); + + if (code == 401 || code == 407) { + auth_baton_t ab = { 0 }; + const char *auth_hdr; + apr_status_t status; + + ab.code = code; + ab.status = APR_SUCCESS; + ab.request = request; + ab.response = response; + ab.baton = baton; + ab.pool = pool; + + /* Before iterating over all authn headers, check if there are any. */ + if (code == 401) + ab.header = "WWW-Authenticate"; + else + ab.header = "Proxy-Authenticate"; + + hdrs = serf_bucket_response_get_headers(response); + auth_hdr = serf_bucket_headers_get(hdrs, ab.header); + + if (!auth_hdr) { + return SERF_ERROR_AUTHN_FAILED; + } + + /* Iterate over all headers. Try to find a matching authentication scheme + handler. + + Note: it is possible to have multiple Authentication: headers. We do + not want to combine them (per normal header combination rules) as that + would make it hard to parse. Instead, we want to individually parse + and handle each header in the response, looking for one that we can + work with. + */ + serf_bucket_headers_do(hdrs, + handle_auth_header, + &ab); + if (ab.status != APR_SUCCESS) + return ab.status; + + if (!ab.scheme || ab.scheme->name == NULL) { + /* No matching authentication found. */ + return SERF_ERROR_AUTHN_NOT_SUPPORTED; + } + } else { + /* Validate the response authn headers if needed. */ + + } + + return APR_SUCCESS; +} + +/* Read the headers of the response and try the available + handlers if authentication or validation is needed. */ +apr_status_t serf__handle_auth_response(int *consumed_response, + serf_request_t *request, + serf_bucket_t *response, + void *baton, + apr_pool_t *pool) +{ + apr_status_t status; + serf_status_line sl; + + *consumed_response = 0; + + status = serf_bucket_response_status(response, &sl); + if (SERF_BUCKET_READ_ERROR(status)) { + return status; + } + if (!sl.version && (APR_STATUS_IS_EOF(status) || + APR_STATUS_IS_EAGAIN(status))) { + return status; + } + + status = serf_bucket_response_wait_for_headers(response); + if (status) { + if (!APR_STATUS_IS_EOF(status)) { + return status; + } + + /* If status is APR_EOF, there were no headers to read. + This can be ok in some situations, and it definitely + means there's no authentication requested now. */ + return APR_SUCCESS; + } + + /* Don't bother handling the authentication request if the response + wasn't received completely yet. Serf will call serf__handle_auth_response + again when more data is received. */ + if (! APR_STATUS_IS_EAGAIN(status)) { + status = dispatch_auth(sl.code, request, response, baton, pool); + + if (status != APR_SUCCESS) { + return status; + } + + if (sl.code == 401 || sl.code == 407) { + /* Authentication requested. */ + status = discard_body(response); + *consumed_response = 1; + + /* Requeue the request with the necessary auth headers. */ + /* ### Application doesn't know about this request! */ + serf_connection_priority_request_create(request->conn, + request->setup, + request->setup_baton); + + return status; + } + + return APR_SUCCESS; + } + + return status; +} + +/** + * base64 encode the authentication data and build an authentication + * header in this format: + * [SCHEME] [BASE64 of auth DATA] + */ +void serf__encode_auth_header(const char **header, + const char *scheme, + const char *data, apr_size_t data_len, + apr_pool_t *pool) +{ + apr_size_t encoded_len, scheme_len; + char *ptr; + + encoded_len = apr_base64_encode_len(data_len); + scheme_len = strlen(scheme); + + ptr = apr_palloc(pool, encoded_len + scheme_len + 1); + *header = ptr; + + apr_cpystrn(ptr, scheme, scheme_len + 1); + ptr += scheme_len; + *ptr++ = ' '; + + apr_base64_encode(ptr, data, data_len); +} Index: auth/auth_basic.c =================================================================== --- auth/auth_basic.c (revision 0) +++ auth/auth_basic.c (revision 0) @@ -0,0 +1,145 @@ +/* Copyright 2009 Justin Erenkrantz and Greg Stein + * + * Licensed 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. + */ + +/*** Basic authentication ***/ + +#include +#include +#include + +#include +#include +#include + +typedef struct basic_authn_info_t { + const char *header; + const char *value; +} basic_authn_info_t; + +apr_status_t +serf__handle_basic_auth(int code, + serf_request_t *request, + serf_bucket_t *response, + const char *auth_hdr, + const char *auth_attr, + void *baton, + apr_pool_t *pool) +{ + const char *tmp; + apr_size_t tmp_len; + serf_connection_t *conn = request->conn; + serf_context_t *ctx = conn->ctx; + basic_authn_info_t *basic_info = (code == 401) ? conn->authn_baton : + conn->proxy_authn_baton; + serf__authn_info_t *authn_info = (code == 401) ? &ctx->authn_info : + &ctx->proxy_authn_info; + apr_status_t status; + apr_pool_t *cred_pool; + char *username, *password; + + /* Can't do Basic authentication if there's no callback to get + username & password. */ + if (!ctx->cred_cb) { + return SERF_ERROR_AUTHN_FAILED; + } + + if (!authn_info->realm) { + char *realm_name = NULL; + const char *eq = strchr(auth_attr, '='); + apr_port_t port; + + if (eq && strncasecmp(auth_attr, "realm", 5) == 0) { + realm_name = apr_pstrdup(pool, eq + 1); + if (realm_name[0] == '\"') { + apr_size_t realm_len; + + realm_len = strlen(realm_name); + if (realm_name[realm_len - 1] == '\"') { + realm_name[realm_len - 1] = '\0'; + realm_name++; + } + } + } + + if (!realm_name) { + return SERF_ERROR_AUTHN_MISSING_ATTRIBUTE; + } + + authn_info->realm = apr_psprintf(conn->pool, "<%s://%s:%d> %s", + conn->host_info.scheme, + conn->host_info.hostname, + conn->host_info.port, + realm_name); + } + + /* Ask the application for credentials */ + apr_pool_create(&cred_pool, pool); + status = (*ctx->cred_cb)(&username, &password, request, baton, + code, authn_info->scheme->name, + authn_info->realm, cred_pool); + if (status) { + apr_pool_destroy(cred_pool); + return status; + } + + tmp = apr_pstrcat(conn->pool, username, ":", password, NULL); + tmp_len = strlen(tmp); + apr_pool_destroy(cred_pool); + + serf__encode_auth_header(&basic_info->value, + authn_info->scheme->name, + tmp, tmp_len, pool); + basic_info->header = (code == 401) ? "Authorization" : "Proxy-Authorization"; + + return APR_SUCCESS; +} + +apr_status_t +serf__init_basic_connection(int code, + serf_connection_t *conn, + apr_pool_t *pool) +{ + if (code == 401) { + conn->authn_baton = apr_pcalloc(pool, sizeof(basic_authn_info_t)); + } else { + conn->proxy_authn_baton = apr_pcalloc(pool, sizeof(basic_authn_info_t)); + } + + return APR_SUCCESS; +} + +apr_status_t +serf__setup_request_basic_auth(int code, + serf_connection_t *conn, + const char *method, + const char *uri, + serf_bucket_t *hdrs_bkt) +{ + serf_context_t *ctx = conn->ctx; + basic_authn_info_t *authn_info; + + if (code == 401) { + authn_info = conn->authn_baton; + } else { + authn_info = conn->proxy_authn_baton; + } + + if (authn_info && authn_info->header && authn_info->value) { + serf_bucket_headers_setn(hdrs_bkt, authn_info->header, authn_info->value); + return APR_SUCCESS; + } + + return SERF_ERROR_AUTHN_FAILED; +} Index: auth/auth.h =================================================================== --- auth/auth.h (revision 0) +++ auth/auth.h (revision 0) @@ -0,0 +1,49 @@ +/* Copyright 2009 Justin Erenkrantz and Greg Stein + * + * Licensed 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. + */ + +#ifndef AUTH_H +#define AUTH_H + +#ifdef __cplusplus +extern "C" { +#endif + +void serf__encode_auth_header(const char **header, const char *protocol, + const char *data, apr_size_t data_len, + apr_pool_t *pool); + +apr_status_t serf__handle_basic_auth(int code, + serf_request_t *request, + serf_bucket_t *response, + const char *auth_hdr, + const char *auth_attr, + void *baton, + apr_pool_t *pool); + +apr_status_t serf__init_basic_connection(int code, + serf_connection_t *conn, + apr_pool_t *pool); + +apr_status_t serf__setup_request_basic_auth(int code, + serf_connection_t *conn, + const char *method, + const char *uri, + serf_bucket_t *hdrs_bkt); + +#ifdef __cplusplus +} +#endif + +#endif /* !AUTH_H */ Index: context.c =================================================================== --- context.c (revision 1280) +++ context.c (working copy) @@ -102,6 +102,18 @@ SERF_DECLARE(void) serf_config_proxy(serf_context_ ctx->proxy_address = address; } +SERF_DECLARE(void) serf_config_credentials_callback(serf_context_t *ctx, + serf_credentials_callback_t cred_cb) +{ + ctx->cred_cb = cred_cb; +} + +SERF_DECLARE(void) serf_config_authn_types(serf_context_t *ctx, + int authn_types) +{ + ctx->authn_types = authn_types; +} + SERF_DECLARE(serf_context_t *) serf_context_create_ex(void *user_baton, serf_socket_add_t addf, serf_socket_remove_t rmf, @@ -132,6 +144,8 @@ SERF_DECLARE(serf_context_t *) serf_context_create ctx->progress_read = 0; ctx->progress_written = 0; + ctx->authn_types = SERF_AUTHN_ALL; + return ctx; } Index: outgoing.c =================================================================== --- outgoing.c (revision 1281) +++ outgoing.c (working copy) @@ -107,7 +107,7 @@ apr_status_t serf__conn_update_pollset(serf_connec } else { while (request != NULL && request->req_bkt == NULL && - request->setup == NULL) + request->written) request = request->next; if (request != NULL) desc.reqevents |= APR_POLLOUT; @@ -344,6 +344,20 @@ static void destroy_ostream(serf_connection_t *con } } + +/* A socket was closed, inform the application. */ +static void handle_conn_closed(serf_connection_t *conn, apr_status_t status) +{ + (*conn->closed)(conn, conn->closed_baton, status, + conn->pool); + + /* Restart the authentication phase on this new connection. */ + if (conn->ctx->authn_info.scheme) { + conn->ctx->authn_info.scheme->init_conn_func(401, conn, + conn->ctx->pool); + } +} + static apr_status_t reset_connection(serf_connection_t *conn, int requeue_requests) { @@ -372,7 +386,7 @@ static apr_status_t reset_connection(serf_connecti /* If we haven't started to write the connection, bring it over * unchanged to our new socket. Otherwise, call the cancel function. */ - if (requeue_requests && old_reqs->setup) { + if (requeue_requests && !old_reqs->written) { serf_request_t *req = old_reqs; old_reqs = old_reqs->next; req->next = NULL; @@ -397,8 +411,7 @@ static apr_status_t reset_connection(serf_connecti remove_connection(ctx, conn); status = apr_socket_close(conn->skt); if (conn->closed != NULL) { - (*conn->closed)(conn, conn->closed_baton, status, - conn->pool); + handle_conn_closed(conn, status); } conn->skt = NULL; } @@ -517,7 +530,7 @@ static apr_status_t write_to_connection(serf_conne /* Find a request that has data which needs to be delivered. */ while (request != NULL && - request->req_bkt == NULL && request->setup == NULL) + request->req_bkt == NULL && request->written) request = request->next; /* assert: request != NULL || conn->vec_len */ @@ -559,7 +572,7 @@ static apr_status_t write_to_connection(serf_conne * to write. */ while (request != NULL && - request->req_bkt == NULL && request->setup == NULL) + request->req_bkt == NULL && request->written) request = request->next; if (request == NULL) { @@ -604,7 +617,7 @@ static apr_status_t write_to_connection(serf_conne return read_status; } - request->setup = NULL; + request->written = 1; serf_bucket_aggregate_append(conn->ostream_tail, request->req_bkt); } @@ -687,12 +700,32 @@ static apr_status_t handle_response(serf_request_t apr_pool_t *pool) { apr_status_t status; + int consumed_response = 0; - status = (*request->handler)(request, - request->resp_bkt, - request->handler_baton, - pool); + status = serf__handle_auth_response(&consumed_response, + request, + request->resp_bkt, + request->handler_baton, + pool); + /* If there was an error reading the response (maybe there wasn't + enough data available), don't bother passing the response to the + application. + + If the authentication was tried, but failed, pass the response + to the application, maybe it can do better. */ + if (APR_STATUS_IS_EOF(status) || + APR_STATUS_IS_EAGAIN(status)) { + return status; + } + + if (!consumed_response) { + return (*request->handler)(request, + request->resp_bkt, + request->handler_baton, + pool); + } + return status; } @@ -704,8 +737,7 @@ static apr_status_t read_from_connection(serf_conn int close_connection = FALSE; /* Whatever is coming in on the socket corresponds to the first request - * on our chain. - */ + on our chain. */ serf_request_t *request = conn->requests; /* assert: request != NULL */ @@ -743,7 +775,7 @@ static apr_status_t read_from_connection(serf_conn * If we see an EOF (due to an expired timeout), we'll reset the * connection and open a new one. */ - if (request->req_bkt || request->setup) { + if (request->req_bkt || !request->written) { const char *data; apr_size_t len; @@ -861,7 +893,7 @@ static apr_status_t read_from_connection(serf_conn * update the pollset. We don't want to read from this socket any * more. We are definitely done with this loop, too. */ - if (request == NULL || request->setup) { + if (request == NULL || !request->written) { conn->dirty_conn = 1; conn->ctx->dirty_pollset = 1; status = APR_SUCCESS; @@ -1016,8 +1048,7 @@ SERF_DECLARE(apr_status_t) serf_connection_close( remove_connection(ctx, conn); status = apr_socket_close(conn->skt); if (conn->closed != NULL) { - (*conn->closed)(conn, conn->closed_baton, status, - conn->pool); + handle_conn_closed(conn, status); } conn->skt = NULL; } @@ -1070,6 +1101,7 @@ SERF_DECLARE(serf_request_t *) serf_connection_req request->respool = NULL; request->req_bkt = NULL; request->resp_bkt = NULL; + request->written = 0; request->next = NULL; /* Link the request to the end of the request chain. */ @@ -1103,6 +1135,7 @@ SERF_DECLARE(serf_request_t *) serf_connection_pri request->respool = NULL; request->req_bkt = NULL; request->resp_bkt = NULL; + request->written = 0; request->next = NULL; /* Link the new request after the last written request, but before all @@ -1116,7 +1149,7 @@ SERF_DECLARE(serf_request_t *) serf_connection_pri prev = NULL; /* Find a request that has data which needs to be delivered. */ - while (iter != NULL && iter->req_bkt == NULL && iter->setup == NULL) { + while (iter != NULL && iter->req_bkt == NULL && iter->written) { prev = iter; iter = iter->next; } @@ -1182,17 +1215,29 @@ SERF_DECLARE(serf_bucket_t *) serf_request_bucket_ serf_bucket_alloc_t *allocator) { serf_bucket_t *req_bkt, *hdrs_bkt; + serf_connection_t *conn = request->conn; + serf_context_t *ctx = conn->ctx; req_bkt = serf_bucket_request_create(method, uri, body, allocator); hdrs_bkt = serf_bucket_request_get_headers(req_bkt); /* Proxy? */ - if (request->conn->ctx->proxy_address && request->conn->host_url) - serf_bucket_request_set_root(req_bkt, request->conn->host_url); + if (ctx->proxy_address && conn->host_url) + serf_bucket_request_set_root(req_bkt, conn->host_url); - if (request->conn->host_info.hostname) + if (conn->host_info.hostname) serf_bucket_headers_setn(hdrs_bkt, "Host", - request->conn->host_info.hostname); + conn->host_info.hostname); + /* Setup server authorization headers */ + if (ctx->authn_info.scheme) + ctx->authn_info.scheme->setup_request_func(401, conn, method, uri, + hdrs_bkt); + + /* Setup proxy authorization headers */ + if (ctx->proxy_authn_info.scheme) + ctx->proxy_authn_info.scheme->setup_request_func(407, conn, method, + uri, hdrs_bkt); + return req_bkt; }