/* ====================================================================
 *    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.
 * ====================================================================
 */

#ifndef SERF_H
#define SERF_H

/**
 * @file serf.h
 * @brief Main serf header file
 */

#include <apr.h>
#include <apr_errno.h>
#include <apr_allocator.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include <apr_network_io.h>
#include <apr_time.h>
#include <apr_poll.h>
#include <apr_uri.h>
#include <apr_version.h>
#if !APR_VERSION_AT_LEAST(1, 3, 0)
#error "The APR version must be 1.3.0 or newer"
#endif

#ifdef __cplusplus
extern "C" {
#endif

/* Forward declare some structures */
typedef struct serf_context_t serf_context_t;

typedef struct serf_bucket_t serf_bucket_t;
typedef struct serf_bucket_type_t serf_bucket_type_t;
typedef struct serf_bucket_alloc_t serf_bucket_alloc_t;

typedef struct serf_connection_t serf_connection_t;
typedef struct serf_listener_t serf_listener_t;
typedef struct serf_incoming_t serf_incoming_t;
typedef struct serf_incoming_request_t serf_incoming_request_t;

typedef struct serf_request_t serf_request_t;

typedef struct serf_config_t serf_config_t;

/**
 * @defgroup serf high-level constructs
 * @ingroup serf
 * @{
 */

/**
 * Serf-specific error codes
 */
#define SERF_ERROR_RANGE 100
#define SERF_ERROR_START (APR_OS_START_USERERR + SERF_ERROR_RANGE)

/* This code is for when this is the last response on this connection:
 * i.e. do not send any more requests on this connection or expect
 * any more responses.
 */
#define SERF_ERROR_CLOSING (SERF_ERROR_START + 1)
/* This code is for when the connection terminated before the request
 * could be processed on the other side.
 */
#define SERF_ERROR_REQUEST_LOST (SERF_ERROR_START + 2)
/* This code is for when the connection is blocked - we can not proceed
 * until something happens - generally due to SSL negotiation-like behavior
 * where a write() is blocked until a read() is processed.
 */
#define SERF_ERROR_WAIT_CONN (SERF_ERROR_START + 3)
/* This code is for when something went wrong during deflating compressed
 * data e.g. a CRC error. */
#define SERF_ERROR_DECOMPRESSION_FAILED (SERF_ERROR_START + 4)
/* This code is for when a response received from a http server is not in
 * http-compliant syntax. */
#define SERF_ERROR_BAD_HTTP_RESPONSE (SERF_ERROR_START + 5)
/* The server sent less data than what was announced. */
#define SERF_ERROR_TRUNCATED_HTTP_RESPONSE (SERF_ERROR_START + 6)
/* The proxy server returned an error while setting up the SSL tunnel. */
#define SERF_ERROR_SSLTUNNEL_SETUP_FAILED (SERF_ERROR_START + 7)
/* The server unexpectedly closed the connection prematurely. */
#define SERF_ERROR_ABORTED_CONNECTION (SERF_ERROR_START + 8)
/* Generic 'The line too long'. Used internally. */
#define SERF_ERROR_LINE_TOO_LONG (SERF_ERROR_START + 9)
/* The HTTP response status line too long. */
#define SERF_ERROR_STATUS_LINE_TOO_LONG (SERF_ERROR_START + 10)
/* The HTTP response header too long. */
#define SERF_ERROR_RESPONSE_HEADER_TOO_LONG (SERF_ERROR_START + 11)
/* The connection to the server timed out. */
#define SERF_ERROR_CONNECTION_TIMEDOUT (SERF_ERROR_START + 12)
/* The stream returned less data than was expected */
#define SERF_ERROR_TRUNCATED_STREAM (SERF_ERROR_START + 13)
/* The stream is empty */
#define SERF_ERROR_EMPTY_STREAM (SERF_ERROR_START + 14)
/* An empty read was returned. */
#define SERF_ERROR_EMPTY_READ (SERF_ERROR_START + 15)

/* Http-2 stream errors, mapped into our error range */
#define SERF_ERROR_HTTP2_NO_ERROR (SERF_ERROR_START + 50)
#define SERF_ERROR_HTTP2_PROTOCOL_ERROR (SERF_ERROR_START + 51)
#define SERF_ERROR_HTTP2_INTERNAL_ERROR (SERF_ERROR_START + 52)
#define SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR (SERF_ERROR_START + 53)
#define SERF_ERROR_HTTP2_SETTINGS_TIMEOUT (SERF_ERROR_START + 54)
#define SERF_ERROR_HTTP2_STREAM_CLOSED (SERF_ERROR_START + 55)
#define SERF_ERROR_HTTP2_FRAME_SIZE_ERROR (SERF_ERROR_START + 56)
#define SERF_ERROR_HTTP2_REFUSED_STREAM (SERF_ERROR_START + 57)
#define SERF_ERROR_HTTP2_CANCEL (SERF_ERROR_START + 58)
#define SERF_ERROR_HTTP2_COMPRESSION_ERROR (SERF_ERROR_START + 59)
#define SERF_ERROR_HTTP2_CONNECT_ERROR (SERF_ERROR_START + 60)
#define SERF_ERROR_HTTP2_ENHANCE_YOUR_CALM (SERF_ERROR_START + 61)
#define SERF_ERROR_HTTP2_INADEQUATE_SECURITY (SERF_ERROR_START + 62)
#define SERF_ERROR_HTTP2_HTTP_1_1_REQUIRED (SERF_ERROR_START + 63)

/* SSL certificates related errors */
#define SERF_ERROR_SSL_CERT_FAILED (SERF_ERROR_START + 70)

/* SSL communications related errors */
#define SERF_ERROR_SSL_COMM_FAILED (SERF_ERROR_START + 71)

/* SSL handshake failed */
#define SERF_ERROR_SSL_SETUP_FAILED (SERF_ERROR_START + 72)

/* Serf-internal error code, raised when the server initiates SSL renegotiation
   on a connection that uses HTTP pipelining. */
#define SERF_ERROR_SSL_NEGOTIATE_IN_PROGRESS (SERF_ERROR_START + 73)

/* OCSP responder says that the certificate is revoked. */
#define SERF_ERROR_SSL_OCSP_RESPONSE_CERT_REVOKED (SERF_ERROR_START + 74)

/* OCSP responder says that the certificate is unknown. */
#define SERF_ERROR_SSL_OCSP_RESPONSE_CERT_UNKNOWN (SERF_ERROR_START + 75)

/* The response from an OCSP responder was not valid. */
#define SERF_ERROR_SSL_OCSP_RESPONSE_INVALID (SERF_ERROR_START + 76)

#define SERF_OCSP_UNGOOD_ERROR(status) ((status) \
    && ((SERF_ERROR_SSL_OCSP_CERT_REVOKED == (status)) \
        ||(SERF_ERROR_SSL_OCSP_CERT_UNKNOWN == (status))))

/* General authentication related errors */
#define SERF_ERROR_AUTHN_FAILED (SERF_ERROR_START + 90)

/* None of the available authn mechanisms for the request are supported */
#define SERF_ERROR_AUTHN_NOT_SUPPORTED (SERF_ERROR_START + 91)

/* Authn was requested by the server but the header lacked some attribute  */
#define SERF_ERROR_AUTHN_MISSING_ATTRIBUTE (SERF_ERROR_START + 92)

/* Authentication handler initialization related errors */
#define SERF_ERROR_AUTHN_INITALIZATION_FAILED (SERF_ERROR_START + 93)

/* The user credentials were rejected by the server */
#define SERF_ERROR_AUTHN_CREDENTIALS_REJECTED (SERF_ERROR_START + 94)

/* Error code reserved for use in the test suite. */
#define SERF_ERROR_ISSUE_IN_TESTSUITE (SERF_ERROR_START + 99)

/* This macro groups errors potentially raised when reading a http response.  */
#define SERF_BAD_RESPONSE_ERROR(status) ((status) \
    && ((SERF_ERROR_DECOMPRESSION_FAILED == (status)) \
        ||(SERF_ERROR_BAD_HTTP_RESPONSE == (status)) \
        ||(SERF_ERROR_TRUNCATED_HTTP_RESPONSE == (status))))

/**
 * Return a string that describes the specified error code.
 *
 * If the error code is not one of the above Serf error codes, then
 * NULL will be returned.
 *
 * Note regarding lifetime: the string is a statically-allocated constant
 */
const char *serf_error_string(apr_status_t errcode);

/**
 * The source of an error callback invocation.
 *
 * @since New in 1.5.
 */
/* Bit masks for error sources. */
#define SERF_ERROR_CB_MASK        0x00ff
#define SERF_ERROR_CB_GLOBAL      0x0001
#define SERF_ERROR_CB_CONTEXT     0x0002
#define SERF_ERROR_CB_OUTGOING    0x0004
#define SERF_ERROR_CB_INCOMING    0x0008
#define SERF_ERROR_CB_REQUEST     0x0010
#define SERF_ERROR_CB_RESPONSE    0x0020

/* The following flag can be bitwise-combined with any of the above
   values to indicate that the message originated an SSL context. */
#define SERF_ERROR_CB_SSL_CONTEXT 0x0100

/**
 * A callback that, when set, will be called for out-of-band error reporting.
 *
 * A callback can be set on any of three levels: globally, for the context, or
 * the outgoing or incoming connection. Incoming and outgoing requests and
 * responses do not have their own error handlers, but indicate with the source
 * flags where the message originated. The default implementations will will
 * send messages up this hierarchy until a custom callback is found, or the
 * default global callback drops the message to the floor. This is the error
 * callback hierarchy:
 * ```
 *     Level                    Registration function
 *
 *     Global                   serf_error_callback_set()
 *       Context                serf_context_error_callback_set()
 *         Connection           serf_connection_error_callback_set()
 *         Incoming             serf_incoming_error_callback_set()
 * ```
 * In addition, any of those handlers can be called from within an SSL
 * processing context, which is indicated by the flag on the message source.
 *
 * The @a baton is the object provided to the callback registration, and
 * @a source is one of the @c SERF_ERROR_CB_* values, above.
 *
 * The @a message lasts only as long as the callback invocation. The caller
 * must make a copy of the message it it wants to keep it for longer.
 *
 * It is possible that for a given error multiple strings will be returned
 * in multiple callbacks. The caller may choose to handle all strings, or
 * may choose to ignore all strings but the last most detailed one.
 *
 * @since New in 1.5.
 */
typedef apr_status_t (*serf_error_cb_t)(
    void *baton,
    unsigned source,
    apr_status_t status,
    const char *message);

/**
 * Register the global error @a callback, replacing any previous version.
 *
 * @note This function is NOT thread-safe, and calls to the callback are not
 *       serialized. Users are responsible for making the registration and
 *       the callback implementation safe for their application.
 *
 * @since New in 1.5.
 */
void serf_global_error_callback_set(
    serf_error_cb_t callback,
    void *baton);

/**
 * Register the context-specific error callback.
 *
 * Like serf_error_callback_set() except that it affects the given
 * context @a ctx and, since contexts may not be accessed from multiple
 * threads, serialization is not a concern.
 *
 * The lifetime of @a baton must be longer than the lifetime of the context.
 *
 * @since New in 1.5.
 */
void serf_context_error_callback_set(
    serf_context_t *ctx,
    serf_error_cb_t callback,
    void *baton);

/**
 * Register the connection-specific error callback.
 *
 * Like serf_context_error_callback_set() but for connections.
 *
 * The lifetime of @a baton must be longer than the lifetime of the connection.
 *
 * @since New in 1.5.
 */
void serf_connection_error_callback_set(
    serf_connection_t *conn,
    serf_error_cb_t callback,
    void *baton);

/**
 * Register the incoming-connection-specific error callback.
 *
 * Like serf_context_error_callback_set() but for incoming connections.
 *
 * The lifetime of @a baton must be longer than the lifetime of the
 * incoming connection.
 *
 * @since New in 1.5.
 */
void serf_incoming_error_callback_set(
    serf_incoming_t *client,
    serf_error_cb_t callback,
    void *baton);

/**
 * Create a new context for serf operations.
 *
 * A serf context defines a control loop which processes multiple
 * connections simultaneously.
 *
 * The context will be allocated within @a pool.
 */
serf_context_t *serf_context_create(
    apr_pool_t *pool);

/**
 * Callback function. Add a socket to the externally managed poll set.
 *
 * Both @a pfd and @a serf_baton should be used when calling serf_event_trigger
 * later.
 */
typedef apr_status_t (*serf_socket_add_t)(
    void *user_baton,
    apr_pollfd_t *pfd,
    void *serf_baton);

/**
 * Callback function. Remove the socket, identified by both @a pfd and
 * @a serf_baton from the externally managed poll set.
 */
typedef apr_status_t (*serf_socket_remove_t)(
    void *user_baton,
    apr_pollfd_t *pfd,
    void *serf_baton);

/**
 * Create a new context for serf operations.
 *
 * Use this function to make serf not use its internal control loop, but
 * instead rely on an external event loop. Serf will use the @a addf and @a rmf
 * callbacks to notify of any event on a connection. The @a user_baton will be
 * passed through the addf and rmf callbacks.
 *
 * The context will be allocated within @a pool.
 */
serf_context_t *serf_context_create_ex(
    void *user_baton,
    serf_socket_add_t addf,
    serf_socket_remove_t rmf,
    apr_pool_t *pool);

/**
 * Make serf process events on a connection, identified by both @a pfd and
 * @a serf_baton.
 *
 * Any outbound data is delivered, and incoming data is made available to
 * the associated response handlers and their buckets.
 *
 * If any data is processed (incoming or outgoing), then this function will
 * return with APR_SUCCESS.
 */
apr_status_t serf_event_trigger(
    serf_context_t *s,
    void *serf_baton,
    const apr_pollfd_t *pfd);

/** @see serf_context_run should not block at all. */
#define SERF_DURATION_NOBLOCK 0
/** @see serf_context_run should run for (nearly) "forever". */
#define SERF_DURATION_FOREVER 2000000000        /* approx 1^31 */

/**
 * Run the main networking control loop.
 *
 * The set of connections defined by the serf context @a ctx are processed.
 * Any outbound data is delivered, and incoming data is made available to
 * the associated response handlers and their buckets. This function will
 * block on the network for no longer than @a duration microseconds.
 *
 * If any data is processed (incoming or outgoing), then this function will
 * return with APR_SUCCESS. Typically, the caller will just want to call it
 * again to continue processing data.
 *
 * If no activity occurs within the specified timeout duration, then
 * APR_TIMEUP is returned.
 *
 * All temporary allocations will be made in @a pool.
 */
apr_status_t serf_context_run(
    serf_context_t *ctx,
    apr_short_interval_time_t duration,
    apr_pool_t *pool);


apr_status_t serf_context_prerun(
    serf_context_t *ctx);

/**
 * Callback function for progress information. @a progress indicates cumulative
 * number of bytes read or written, for the whole context.
 */
typedef void (*serf_progress_t)(
    void *progress_baton,
    apr_off_t read,
    apr_off_t write);

/**
 * Sets the progress callback function. @a progress_func will be called every
 * time bytes are read of or written on a socket.
 */
void serf_context_set_progress_cb(
    serf_context_t *ctx,
    const serf_progress_t progress_func,
    void *progress_baton);

/** @} */

/**
 * @defgroup serf connections and requests
 * @ingroup serf
 * @{
 */

/**
 * When a connection is established, the application needs to wrap some
 * buckets around @a skt to enable serf to process incoming responses. This
 * is the control point for assembling connection-level processing logic
 * around the given socket.
 *
 * The @a setup_baton is the baton established at connection creation time.
 *
 * This callback corresponds to reading from the server. Since this is an
 * on-demand activity, we use a callback. The corresponding write operation
 * is based on the @see serf_request_deliver function, where the application
 * can assemble the appropriate bucket(s) before delivery.
 *
 * The returned bucket should live at least as long as the connection itself.
 * It is assumed that an appropriate allocator is passed in @a setup_baton.
 * ### we may want to create a connection-level allocator and pass that
 * ### along. however, that allocator would *only* be used for this
 * ### callback. it may be wasteful to create a per-conn allocator, so this
 * ### baton-based, app-responsible form might be best.
 *
 * Responsibility for the buckets is passed to the serf library. They will be
 * destroyed when the connection is closed.
 *
 * All temporary allocations should be made in @a pool.
 */
typedef apr_status_t (*serf_connection_setup_t)(
    apr_socket_t *skt,
    serf_bucket_t **read_bkt,
    serf_bucket_t **write_bkt,
    void *setup_baton,
    apr_pool_t *pool);

/**
 * ### need to update docco w.r.t socket. became "stream" recently.
 * ### the stream does not have a barrier, this callback should generally
 * ### add a barrier around the stream before incorporating it into a
 * ### response bucket stack.
 * ### should serf add the barrier automatically to protect its data
 * ### structure? i.e. the passed bucket becomes owned rather than
 * ### borrowed. that might suit overall semantics better.
 * Accept an incoming response for @a request, and its @a socket. A bucket
 * for the response should be constructed and returned. This is the control
 * point for assembling the appropriate wrapper buckets around the socket to
 * enable processing of the incoming response.
 *
 * The @a acceptor_baton is the baton provided when the specified request
 * was created.
 *
 * The request's pool and bucket allocator should be used for any allocations
 * that need to live for the duration of the response. Care should be taken
 * to bound the amount of memory stored in this pool -- to ensure that
 * allocations are not proportional to the amount of data in the response.
 *
 * Responsibility for the bucket is passed to the serf library. It will be
 * destroyed when the response has been fully read (the bucket returns an
 * APR_EOF status from its read functions).
 *
 * All temporary allocations should be made in @a pool.
 */
/* ### do we need to return an error? */
typedef serf_bucket_t * (*serf_response_acceptor_t)(
    serf_request_t *request,
    serf_bucket_t *stream,
    void *acceptor_baton,
    apr_pool_t *pool);

/**
 * Notification callback for when a connection closes.
 *
 * This callback is used to inform an application that the @a conn
 * connection has been (abnormally) closed. The @a closed_baton is the
 * baton provided when the connection was first opened. The reason for
 * closure is given in @a why, and will be APR_SUCCESS if the application
 * requested closure (by clearing the pool used to allocate this
 * connection or calling serf_connection_close).
 *
 * All temporary allocations should be made in @a pool.
 */
typedef void (*serf_connection_closed_t)(
    serf_connection_t *conn,
    void *closed_baton,
    apr_status_t why,
    apr_pool_t *pool);

/**
 * Like serf_connection_closed_t, but applies to incoming connections.
 *
 * @since New in 1.5.
 */
typedef apr_status_t (*serf_incoming_closed_t)(
    serf_incoming_t *incoming,
    void *closed_baton,
    apr_status_t why,
    apr_pool_t *pool);

/**
 * Response data has arrived and should be processed.
 *
 * Whenever response data for @a request arrives (initially, or continued data
 * arrival), this handler is invoked. The response data is available in the
 * @a response bucket. The @a handler_baton is passed along from the baton
 * provided by the request setup callback (@see serf_request_setup_t).
 *
 * The handler MUST process data from the @a response bucket until the
 * bucket's read function states it would block (see APR_STATUS_IS_EAGAIN).
 * The handler is invoked only when new data arrives. If no further data
 * arrives, and the handler does not process all available data, then the
 * system can result in a deadlock around the unprocessed, but read, data.
 *
 * The handler should return APR_EOF when the response has been fully read.
 * If calling the handler again would block, APR_EAGAIN should be returned.
 * If the handler should be invoked again, simply return APR_SUCCESS.
 *
 * Note: if the connection closed (at the request of the application, or
 * because of an (abnormal) termination) while a request is being delivered,
 * or before a response arrives, then @a response will be NULL. This is the
 * signal that the request was not delivered properly, and no further
 * response should be expected (this callback will not be invoked again).
 * If a request is injected into the connection (during this callback's
 * execution, or otherwise), then the connection will be reopened.
 *
 * All temporary allocations should be made in @a pool.
 */
typedef apr_status_t (*serf_response_handler_t)(
    serf_request_t *request,
    serf_bucket_t *response,
    void *handler_baton,
    apr_pool_t *pool);

/**
 * Callback function to be implemented by the application, so that serf
 * can handle server and proxy authentication.
 * code = 401 (server) or 407 (proxy).
 * baton = the baton passed to serf_context_run.
 * authn_type = one of "Basic", "Digest", or the name of a user-defined
 *              authentication scheme if it uses this callback.
 *
 * @see SERF_AUTHN_CODE_HOST
 * @see SERF_AUTHN_CODE_PROXY
 */
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.
 *
 * If no proxy server is configured, a connection will be created to
 * (eventually) connect to the address specified by @a address. The address must
 * live at least as long as @a pool (thus, as long as the connection object).
 * If a proxy server is configured, @a address will be ignored.
 *
 * The connection object will be allocated within @a pool. Clearing or
 * destroying this pool will close the connection, and terminate any
 * outstanding requests or responses.
 *
 * The @a setup callback will be invoked, and @a setup_baton passed to it,
 * once the connection is actually established. @see serf_connection_setup_t.
 *
 * When the connection is closed (upon request or because of an error),
 * then the @a closed callback is invoked, and @a closed_baton is passed.
 *
 * NULL may be passed for @a setup and @a closed; default implementations
 * will be used.
 *
 * @note the connection is not made immediately. It will be opened
 * asynchronously on the next call to serf_context_run().
 */
serf_connection_t *serf_connection_create(
    serf_context_t *ctx,
    apr_sockaddr_t *address,
    serf_connection_setup_t setup,
    void *setup_baton,
    serf_connection_closed_t closed,
    void *closed_baton,
    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
 * specified by @a host_info (either directly or through a proxy), which
 * must live at least as long as @a pool (thus, as long as the connection
 * object). The @a host_info will also be used for setting request headers.
 *
 * The connection object will be allocated within @a pool. Clearing or
 * destroying this pool will close the connection, and terminate any
 * outstanding requests or responses.
 *
 * The @a setup callback will be invoked, and @a setup_baton passed to it,
 * once the connection is actually established. @see serf_connection_setup_t.
 *
 * When the connection is closed (upon request or because of an error),
 * then the @a closed callback is invoked, and @a closed_baton is passed.
 *
 * NULL may be passed for @a setup and @a closed; default implementations
 * will be used.
 *
 * @note The connection is not made immediately. It will be opened
 * asynchronously on the next call to serf_context_run().
 */
apr_status_t serf_connection_create2(
    serf_connection_t **conn,
    serf_context_t *ctx,
    apr_uri_t host_info,
    serf_connection_setup_t setup,
    void *setup_baton,
    serf_connection_closed_t closed,
    void *closed_baton,
    apr_pool_t *pool);

/**
 * Notification callback when an address has been resolved.
 *
 * The @a ctx and @a resolved_baton arguments are the same that were passed
 * to serf_address_resolve_async().
 *
 * @a status contains the result of the address resolution. If it is not
 * @c APR_SUCCESS, then @a host_address is invalid and should be ignored.
 *
 * The resolved @a host_address is ephemeral, allocated in @a pool and lives
 * only for the duration of the callback. If it is not consumed, it should be
 * copied to a more permanent pool, using for example apr_sockaddr_info_copy().
 *
 * All temporary allocations should be made in @a pool.
 *
 * @since New in 1.5.
 */
typedef void (*serf_address_resolved_t)(
    serf_context_t *ctx,
    void *resolved_baton,
    apr_sockaddr_t *host_address,
    apr_status_t status,
    apr_pool_t *pool);

/**
 * Asynchronously resolve an address.
 *
 * The address represented by @a host_info is intended to be used to create
 * new connections in @a ctx; see, for example, serf_connection_create().
 * However, unlike in the connection creation functions, the address will be
 * resolved regardless of proxy configuration. In order to avoid unnecessary
 * address resolution, use serf_connection_create_async(), which does take
 * proxy configuration into account.
 *
 * The @a resolved callback will be called during a subsequent call to
 * serf_context_run() or serf_context_prerun() and will receive the same
 * @a ctx and @a resolved_baton that are provided here.
 *
 * The lifetime of all function arguments except @a pool must extend until
 * either @a resolved is called or an error is reported.
 *
 * All temporary allocations should be made in @a pool.
 *
 * @since New in 1.5.
 */
apr_status_t serf_address_resolve_async(
    serf_context_t *ctx,
    apr_uri_t host_info,
    serf_address_resolved_t resolved,
    void *resolved_baton,
    apr_pool_t *pool);


/**
 * Notification callback when a connection has been created.
 *
 * The @a ctx and @a created_baton arguments are the same that were passed
 * to serf_connection_create_async().
 *
 * @a status contains the result of the connection creation. If it is not
 * @c APR_SUCCESS, then @a conn is invalid and should be ignored.
 *
 * The created @a conn is allocated in the pool that was passed to
 * serf_connection_create_async(); this is @b not the same as @a pool.
 *
 * All temporary allocations should be made in @a pool.
 *
 * @since New in 1.5.
 */
typedef void (*serf_connection_created_t)(
    serf_context_t *ctx,
    void *created_baton,
    serf_connection_t *conn,
    apr_status_t status,
    apr_pool_t *pool);

/**
 * Asynchronously create a new connection associated with
 * the @a ctx serf context.
 *
 * Like serf_connection_create2() except that address resolution is performed
 * asynchronously, similarly to serf_address_resolve_async(). Address resolution
 * will be skipped if a proxy is configured; in this case, the function becomes
 * synchronous and effectively equivalent to serf_connection_create2().
 *
 * The @a created callback with @a created_baton is called when the connection
 * is created but before it is opened. Note that depending on the configuration
 * of @a ctx and @a host_info, the connection may be created and this callback
 * be invoked synchronously during the scope of this function call.
 *
 * @since New in 1.5.
 */
apr_status_t serf_connection_create_async(
    serf_context_t *ctx,
    apr_uri_t host_info,
    serf_connection_created_t created,
    void *created_baton,
    serf_connection_setup_t setup,
    void *setup_baton,
    serf_connection_closed_t closed,
    void *closed_baton,
    apr_pool_t *pool);


typedef apr_status_t (*serf_accept_client_t)(
    serf_context_t *ctx,
    serf_listener_t *l,
    void *accept_baton,
    apr_socket_t *insock,
    apr_pool_t *pool);

apr_status_t serf_listener_create(
    serf_listener_t **listener,
    serf_context_t *ctx,
    const char *host,
    apr_uint16_t port,
    void *accept_baton,
    serf_accept_client_t accept_func,
    apr_pool_t *pool);

typedef apr_status_t (*serf_incoming_request_handler_t)(
    serf_incoming_request_t *req,
    serf_bucket_t *request,
    void *handler_baton,
    apr_pool_t *pool);

typedef apr_status_t (*serf_incoming_response_setup_t)(
    serf_bucket_t **resp_bkt,
    serf_incoming_request_t *req,
    void *setup_baton,
    serf_bucket_alloc_t *allocator,
    apr_pool_t *pool);

typedef apr_status_t (*serf_incoming_request_setup_t)(
    serf_bucket_t **req_bkt,
    serf_bucket_t *stream,
    serf_incoming_request_t *req,
    void *request_baton,
    serf_incoming_request_handler_t *handler,
    void **handler_baton,
    serf_incoming_response_setup_t *response_setup,
    void **response_setup_baton,
    apr_pool_t *pool);

/* ### Deprecated: can't do anything with request */
typedef apr_status_t(*serf_incoming_request_cb_t)(
  serf_context_t *ctx,
  serf_incoming_request_t *req,
  void *request_baton,
  apr_pool_t *pool);

/* ### Deprecated: Misses ssl support and actual
       request handling. */
apr_status_t serf_incoming_create(
    serf_incoming_t **client,
    serf_context_t *ctx,
    apr_socket_t *insock,
    void *request_baton,
    serf_incoming_request_cb_t request,
    apr_pool_t *pool);

/**
 * Creates a new client associated with @a ctx for socket @a insock. The client
 * takes responsibility for @a client_pool and will destroy it after the
 * connection is closed. Typically this would be the same pool as where the
 * incoming socket @a insock is allocated in.
 *
 * This non-standard behavior is needed to support listeners inside the same
 * @a ctx instance without leaking memory for each used connections. Callers
 * might want to create a specific client pool if they use a non-standard
 * listening pattern.
 *
 * Once the connection is setup @a setup will be called with @a setup_baton
 * to setup the connection's bucket support.
 *
 * When the connection closed @a closed will be called with @a closed_baton to
 * notify that the client and its pool are about to be destroyed.
 *
 * Once the connection is fully setup incoming requests will be routed to @a
 * req_setup with @a req_setup_baton, to handle processing.
 *
 * @since New in 1.5.
 */
apr_status_t serf_incoming_create2(
    serf_incoming_t **client,
    serf_context_t *ctx,
    apr_socket_t *insock,
    serf_connection_setup_t setup,
    void *setup_baton,
    serf_incoming_closed_t closed,
    void *closed_baton,
    serf_incoming_request_setup_t req_setup,
    void *req_setup_baton,
    apr_pool_t *client_pool);

/**
 * Allows creating a response before the request is completely
 * read. Will call the response create function if it hasn't
 * been called yet.
 *
 * @since New in 1.5.
 */
apr_status_t serf_incoming_response_create(
    serf_incoming_request_t *request);

/**
 * Reset the connection, but re-open the socket again.
 */
apr_status_t serf_connection_reset(
    serf_connection_t *conn);

/**
 * Close the connection associated with @a conn and cancel all pending requests.
 *
 * The closed callback passed to serf_connection_create() will be invoked
 * with APR_SUCCESS.
 */
apr_status_t serf_connection_close(
    serf_connection_t *conn);

/**
 * Sets the maximum number of outstanding requests @a max_requests on the
 * connection @a conn. Setting max_requests to 0 means unlimited (the default).
 * Ex.: setting max_requests to 1 means a request is sent when a response on the
 * previous request was received and handled.
 *
 * In general, serf tends to take around 16KB per outstanding request.
 */
void serf_connection_set_max_outstanding_requests(
    serf_connection_t *conn,
    unsigned int max_requests);

void serf_connection_set_async_responses(
    serf_connection_t *conn,
    serf_response_acceptor_t acceptor,
    void *acceptor_baton,
    serf_response_handler_t handler,
    void *handler_baton);

typedef enum serf_connection_framing_type_t {
  SERF_CONNECTION_FRAMING_TYPE_NONE = 0,
  SERF_CONNECTION_FRAMING_TYPE_HTTP1,
  SERF_CONNECTION_FRAMING_TYPE_HTTP2,
  SERF_CONNECTION_FRAMING_TYPE_FCGI
} serf_connection_framing_type_t;

/**
* Sets the connection framing on the connection to the specified type. The
* NONE type specifies that the framing type is undetermined yet and no
* requests should be written to the connection until the framing type is
* set. Connections default to HTTP1 framing.
*
* @since New in 1.5.
*/
void serf_connection_set_framing_type(
  serf_connection_t *conn,
  serf_connection_framing_type_t framing_type);

/**
 * @since New in 1.5.
 */
void serf_incoming_set_framing_type(
    serf_incoming_t *client,
    serf_connection_framing_type_t framing_type);

/**
 * Setup the @a request for delivery on its connection.
 *
 * Right before this is invoked, @a pool will be built within the
 * connection's pool for the request to use.  The associated response will
 * be allocated within that subpool. An associated bucket allocator will
 * be built. These items may be fetched from the request object through
 * @see serf_request_get_pool or @see serf_request_get_alloc.
 *
 * The content of the request is specified by the @a req_bkt bucket. When
 * a response arrives, the @a acceptor callback will be invoked (along with
 * the @a acceptor_baton) to produce a response bucket. That bucket will then
 * be passed to @a handler, along with the @a handler_baton.
 *
 * The responsibility for the request bucket is passed to the request
 * object. When the request is done with the bucket, it will be destroyed.
 */
typedef apr_status_t (*serf_request_setup_t)(
    serf_request_t *request,
    void *setup_baton,
    serf_bucket_t **req_bkt,
    serf_response_acceptor_t *acceptor,
    void **acceptor_baton,
    serf_response_handler_t *handler,
    void **handler_baton,
    apr_pool_t *pool);

/**
 * Construct a request object for the @a conn connection.
 *
 * When it is time to deliver the request, the @a setup callback will
 * be invoked with the @a setup_baton passed into it to complete the
 * construction of the request object.
 *
 * If the request has not (yet) been delivered, then it may be canceled
 * with @see serf_request_cancel.
 *
 * Invoking any calls other than @see serf_request_cancel before the setup
 * callback executes is not supported.
 */
serf_request_t *serf_connection_request_create(
    serf_connection_t *conn,
    serf_request_setup_t setup,
    void *setup_baton);

/**
 * Construct a request object for the @a conn connection, add it in the
 * list as the next to-be-written request before all unwritten requests.
 *
 * When it is time to deliver the request, the @a setup callback will
 * be invoked with the @a setup_baton passed into it to complete the
 * construction of the request object.
 *
 * If the request has not (yet) been delivered, then it may be canceled
 * with @see serf_request_cancel.
 *
 * Invoking any calls other than @see serf_request_cancel before the setup
 * callback executes is not supported.
 */
serf_request_t *serf_connection_priority_request_create(
    serf_connection_t *conn,
    serf_request_setup_t setup,
    void *setup_baton);

/**
 * The default request priority
 *
 * @since New in 1.5.
 */
#define SERF_REQUEST_PRIORITY_DEFAULT 0x1000

/**
 * Updates the request's priority information. Some protocol implementations,
 * such as HTTP/2 may use this information for response scheduling. The
 * actual behavior depends on the server, intermediate proxies and of course
 * the protocol implementation.
 *
 * It is recommended to prioritize a request before sending it to the server,
 * as that avoids race conditions and receiving unwanted results.
 *
 * If @a depends_on is set, then the request is marked as dependent on
 * @a depends_on, and the result of @a request will only be received if
 * no progress can be made on @a depends_on itself.
 *
 * @a priority is used to relatively prioritize multiple dependencies on the
 * same target. Passing 0 will keep the original priority. In case of HTTP/2
 * this value is mapped to a 8 bit value by ignoring the lowest 8 bits.
 *
 * By default a request is created at priority SERF_REQUEST_PRIORITY_DEFAULT.
 *
 * If @a exclusive is set to TRUE, then all existing dependencies on @a
 * depends_on will be updated to now depend on @a request, to make @a
 * request the only dependency of @a request. When FALSE, request will just
 * be added as a dependency.
 *
 * @since New in 1.5.
 */
void serf_connection_request_prioritize(serf_request_t *request,
                                        serf_request_t *depends_on,
                                        apr_uint16_t priority,
                                        int exclusive);

/**
 * Returns detected network latency for the @a conn connection. Negative
 * value means that latency is unknown.
 */
apr_interval_time_t serf_connection_get_latency(serf_connection_t *conn);

/**
 * Returns the number of requests waiting to be sent over connection CONN.
 *
 * @since New in 1.5.
 */
unsigned int serf_connection_queued_requests(serf_connection_t *conn);

/**
 * Returns the total number of requests for which a response hasn't been
 * received yet on connection CONN. This includes requests:
 * - that are queued but not sent.
 * - that have been sent but no response has been completely received yet.
 *
 * @since New in 1.5.
 */
unsigned int serf_connection_pending_requests(serf_connection_t *conn);

/**
 * Check if a @a request has been completely written.
 *
 * Returns APR_SUCCESS if the request was written completely on the connection.
 * Returns APR_EBUSY if the request is not yet or partially written.
 */
apr_status_t serf_request_is_written(
    serf_request_t *request);

/**
 * Cancel the request specified by the @a request object.
 *
 * If the request has been scheduled for delivery, then its response
 * handler will be run, passing NULL for the response bucket.
 *
 * If the request has already been (partially or fully) delivered, then
 * APR_EBUSY is returned and the request is *NOT* canceled. To properly
 * cancel the request, the connection must be closed (by clearing or
 * destroying its associated pool).
 */
apr_status_t serf_request_cancel(
    serf_request_t *request);

/**
 * Return the pool associated with @a request.
 *
 * WARNING: be very careful about the kinds of things placed into this
 * pool. In particular, all allocation should be bounded in size, rather
 * than proportional to any data stream.
 */
apr_pool_t *serf_request_get_pool(
    const serf_request_t *request);

/**
 * Return the bucket allocator associated with @a request.
 */
serf_bucket_alloc_t *serf_request_get_alloc(
    const serf_request_t *request);

/**
 * Return the connection associated with @a request.
 */
serf_connection_t *serf_request_get_conn(
    const serf_request_t *request);

/**
 * Update the @a handler and @a handler_baton for this @a request.
 *
 * This can be called after the request has started processing -
 * subsequent data will be delivered to this new handler.
 */
void serf_request_set_handler(
    serf_request_t *request,
    const serf_response_handler_t handler,
    const void **handler_baton);

/**
 * Configure proxy server settings, to be used by all connections associated
 * with the @a ctx serf context.
 *
 * The next connection will be created to connect to the proxy server
 * specified by @a address. The address must live at least as long as the
 * serf context.
 */
void serf_config_proxy(
    serf_context_t *ctx,
    apr_sockaddr_t *address);

/**
 * Define the authentication handlers that serf will try on incoming requests.
 *
 * @see @c SERF_AUTHN_ALL etc.
 */
void serf_config_authn_types(
    serf_context_t *ctx,
    int authn_types);

/**
 * Set the credentials callback handler.
 */
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 ***/

/**
 * Create a bucket of type 'socket bucket'.
 * This is basically a wrapper around @a serf_bucket_socket_create, which
 * initializes the bucket using connection and/or context specific settings.
 */
serf_bucket_t *serf_context_bucket_socket_create(
    serf_context_t *ctx,
    apr_socket_t *skt,
    serf_bucket_alloc_t *allocator);

/**
 * Create a bucket of type 'request bucket'.
 * This is basically a wrapper around @a serf_bucket_request_create, which
 * initializes the bucket using request, connection and/or context specific
 * settings.
 *
 * This function will set following header(s):
 * - Host: if the connection was created serf_connection_create2()
 *         or serf_connection_create_async()
 */
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);

/** @} */

/**
 * @defgroup serf authentication
 * @ingroup serf
 * @{
 *
 * Interaction during authentication handshake for user-defined schemes:
 * ```
 * scheme                     serf                      peer
 *    |                         |                        |
 *    |                         +------> request ------->+
 *    |                         |                        |
 *    |                         +<---- authenticate <----+ (401 or 407)
 *    +<------ init-conn <------+                        |
 *    |                         |                        |
 *    |     (pipelining off) <--+ (optional)             |
 *    |    (get credentials) <--+ (optional)             |
 *    |                         |                        |
 *    +<-------- handle <-------+                        |
 *    |                         |                        |
 *    |   (reset pipelining) <--+ (optional)             |
 *    |                         |                        |
 *    +<---- setup-requiest <---+                        |
 *    |                         |                        |
 *    |   (reset pipelining) <--+ (optional)             |
 *    |                         +---> request + authn -->+
 *    |                         |                        |
 *    |                         +<------ response <------+
 *    +<-- validate-response <--+                        |
 *    |                         |                        |
 *    |   (reset pipelining) <--+ (optional)             |
 *    |                         |                        |
 * ```
 */

/* Supported authentication types. */
#define SERF_AUTHN_NONE      0x00 /**< Authentication type: None */
#define SERF_AUTHN_BASIC     0x01 /**< Authentication type: Basic */
#define SERF_AUTHN_DIGEST    0x02 /**< Authentication type: Digest */
#define SERF_AUTHN_NTLM      0x04 /**< Authentication type: NTLM */
#define SERF_AUTHN_NEGOTIATE 0x08 /**< Authentication type: Negotiate */
#define SERF_AUTHN_ALL      ~0x00 /**< All authentication types */

/* For user-defined authentication callbacks: these the sources of an
   authentication callback. */
#define SERF_AUTHN_CODE_HOST  401 /**< Authentication request from a host */
#define SERF_AUTHN_CODE_PROXY 407 /**< Authentication requset from a proxy */

/* Flags returned from the init-connection callback. */
#define SERF_AUTHN_FLAG_NONE  0x00 /**< Authn flags: None */
#define SERF_AUTHN_FLAG_PIPE  0x01 /**< Authn flags: Allow pipelining */
#define SERF_AUTHN_FLAG_CREDS 0x02 /**< Authn flags: Require credentials */

/**
 * Callback for user-defined authentication scheme providers.
 *
 * Called to initialize the connection for this authentication scheme. Return
 * the connection-specific @a authn_baton allocated from @a result_pool, that
 * will be passed to the other callbacks when called for the same connection.
 *
 * @a baton is the user data pointer passed to serf_authn_register_scheme().
 *
 * @a code is the HTTP status code from the response that caused the call to
 * this callback; either @c SERF_AUTHN_CODE_HOST when the peer is a server, or
 * @c SERF_AUTHN_CODE_PROXY when proxy authentication is required.
 *
 * Use @a scratch_pool for temporary allocations.
 *
 * @since New in 1.5.
 */
typedef apr_status_t
(*serf_authn_init_conn_func_t)(void **authn_baton,
                               void *baton, int code,
                               apr_pool_t *result_pool,
                               apr_pool_t *scratch_pool);

/**
 * Callback for user-defined authentication scheme providers.
 *
 * Return a unique identifier of the server (or service) in @a realm_name,
 * allocated from @a result_pool.
 *
 * @a baton is the user data pointer passed to serf_authn_register_scheme().
 *
 * @a authn_baton is the pointer returned from the init-connection callback.
 *
 * @a authn_header and @a authn_parameters come from the authentication header
 * (WWW-Authenticate or Proxy-Authenticate) recevied in a server response.
 *
 * @a authn_header is the header value, i.e., "scheme <parameters>";
 *
 * @a authn_parameters is a dictionary of the authentication parameters
 * and their values, e.g., `realm="Wonderland"`. The keys are always folded
 * to lower case. If the parameter is a single token, it is returned in the
 * dictionary as the value of the empty string key ("").
 * @see https://www.rfc-editor.org/rfc/rfc9110.html#section-11.2
 *
 * If the scheme flag @a SERF_AUTHN_FLAG_PIPE is *not* set, pipelining will be
 * disabled on the connection after this callback succeeds.
 *
 * Use @a scratch_pool for temporary allocations.
 *
 * @since New in 1.5.
 */
typedef apr_status_t
(*serf_authn_get_realm_func_t)(const char **realm_name,
                               void *baton,
                               void *authn_baton,
                               const char *authn_header,
                               apr_hash_t *authn_parameters,
                               apr_pool_t *result_pool,
                               apr_pool_t *scratch_pool);

/**
 * Callback for user-defined authentication scheme providers.
 *
 * Called after the init-conn function has succeeded to prepare (cache) the
 * credentials for this connection, usually in @a auth_baton.
 *
 * @a baton, @a authn_baton, @a authn_header and @a authn_parameters have the
 * same meaning as in the get-realm function; @a code is the same as in the
 * init-conn function.
 *
 * @a response_header is either "Authorization" or "Proxy-Authorization",
 * depending on the type of the peer for the authentication handshake.
 *
 * @a username and @a password are optional (may ba null), provided by the
 * credentials callback, which is called automatically if the scheme flag
 * @c SERF_AUTHN_FLAG_CREDS is set.
 *
 * @a request is the pending request and @a response is the response that
 * caused this callback to be called.
 *
 * If the scheme flag @c SERF_AUTHN_FLAG_PIPE is *not* set, return a boolean
 * value in @a reset_pipelining to indicate whether pipelining on @a conn
 * should be restored to the value before the init-conn callback was invoked.
 * The value of this flag is set to @c false by the caller, so the
 * implementation does not have to modify it if the pipelining state should
 * remain unchanged. This parameter has the same meaning in the
 * serf_authn_setup_request_func_t and serf_authn_validate_response_func_t
 * callbacks and will only take effect the first time its value @c true.
 *
 * Use @a scratch_pool for temporary allocations.
 *
 * @since New in 1.5.
 */
typedef apr_status_t
(*serf_authn_handle_func_t)(int *reset_pipelining,
                            void *baton,
                            void *authn_baton,
                            int code,
                            const char *authn_header,
                            apr_hash_t *authn_parameters,
                            const char *response_header,
                            const char *username,
                            const char *password,
                            serf_request_t *request,
                            serf_bucket_t *response,
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool);

/**
 * Callback for user-defined authentication scheme providers.
 *
 * Called every time a new authenticated @a request is being prepared for the
 * connection @a conn, in order to add credentials etc. to the request.
 *
 * @a baton and @a authn_baton are the same as in the handle function.
 *
 * @a method and @a uri are the requests attributes and @a headers are the
 * request headers where the credentials are usually set.
 *
 * For the meaning of @a reset_pipelining, see serf_authn_handle_func_t.
 *
 * Use @a scratch_pool for temporary allocations.
 *
 * @since New in 1.5.
 */
typedef apr_status_t
(*serf_authn_setup_request_func_t)(int *reset_pipelining,
                                   void *baton,
                                   void *authn_baton,
                                   serf_connection_t *conn,
                                   serf_request_t *request,
                                   const char *method,
                                   const char *uri,
                                   serf_bucket_t *headers,
                                   apr_pool_t *scratch_pool);

/**
 * Callback for user-defined authentication scheme providers.
 *
 * Called every time a @a response with status @a code to the authenticated @a
 * request is received from the connection @a conn.
 *
 * @a baton and @a authn_baton are the same as in the setup-request function
 *
 * @a authn_info_parameters is a dictionary with the same structure as the
 * `authn_parameters` argument to `serf_authn_get_realm_func_t`; except that
 * they're extracted from the Authentication-Info or Proxy-Authentication-Info
 * response header. This argument will be NULL @a response does  not contain
 * one of those headers.
 *
 * For the meaning of @a reset_pipelining, see serf_authn_handle_func_t.
 *
 * Use @a scratch_pool for temporary allocations.
 *
 * @since New in 1.5.
 */
typedef apr_status_t
(*serf_authn_validate_response_func_t)(int *reset_pipelining,
                                       void *baton,
                                       void *authn_baton,
                                       int code,
                                       serf_connection_t *conn,
                                       apr_hash_t *authn_info_parameters,
                                       serf_request_t *request,
                                       serf_bucket_t *response,
                                       apr_pool_t *scratch_pool);

/**
 * Register an autehtication scheme.
 *
 * The @a name is the name of the authentication scheme as it appears in the
 * authorization headers. It must be a valid token as defined in RFC-9110
 * (see reference, below).
 *
 * @a baton will be passed unchanged to the callbacks.
 *
 * @a flags is a bitmask of @c APR_AUTHN_FLAG_* constants that define the
 * scheme's requirements; e.g., whether the credentials callback should be
 * invoked, or whether pipelining should be disabled while the authentication
 * handshake is in progress.
 *
 * @a init_conn, @a handle, @a setup_request and @a validate_response are the
 * callbacks that implement the authentication handshake for this scheme.
 *
 * The number returned in @a type can be used as a bit mask in
 * serf_config_authn_types(). If an error occurs during registration,
 * @a type will be set to @c SERF_AUTHN_NONE.
 *
 * The @a ctx is used only for logging.
 *
 * Internal structures related to this provider will be allocated from @a
 * result_pool, so take care that it lives as long as the autehtication scheme
 * is registered. Ideally, this pool should have a longer lifetime than any of
 * the pools used in calls to the serf_contex_create() family of functions.
 *
 * @see https://www.rfc-editor.org/rfc/rfc9110#section-11.1
 * @since New in 1.5
 */
apr_status_t serf_authn_register_scheme(
    int *type,
    serf_context_t *ctx,
    const char *name, void *baton, int flags,
    serf_authn_init_conn_func_t init_conn,
    serf_authn_get_realm_func_t get_realm,
    serf_authn_handle_func_t handle,
    serf_authn_setup_request_func_t setup_request,
    serf_authn_validate_response_func_t validate_response,
    apr_pool_t *result_pool);

/**
 * Unregister an uthentication scheme.
 *
 * Removes the scheme, identified by @a type that was returned from and
 * @a name that was supplied to serf_authn_register_scheme(), from the
 * list of supported authentication schemes. Uses @a scratch_pool for
 * temporary allocations; this pool can be destroyed after the function
 * has returned.
 *
 * The @a ctx is used only for logging.
 *
 * NOTE: Unregistering a scheme should be avoided, unless you can be absolutely
 *       sure that there are no outstanding requests, responses, connections or
 *       contexts that refer to it. There is no internal reference counting or
 *       other mechanism to ensure that the scheme remains accessible while it's
 *       in use. So in short: *do not* use this function at all.
 *
 * @since New in 1.5
 */
apr_status_t serf_authn_unregister_scheme(serf_context_t *ctx,
                                          int type,
                                          const char *name,
                                          apr_pool_t *scratch_pool);

/** @} */

/**
 * @defgroup serf buckets
 * @ingroup serf
 * @{
 */

/** Pass as REQUESTED to the read function of a bucket to read, consume,
 * and return all available data.
 */
#define SERF_READ_ALL_AVAIL ((apr_size_t)-1)

/** Acceptable newline types for bucket->readline(). */
#define SERF_NEWLINE_CR    0x0001
#define SERF_NEWLINE_CRLF  0x0002
#define SERF_NEWLINE_LF    0x0004
#define SERF_NEWLINE_ANY   0x0007

/** Used to indicate that a newline is not present in the data buffer. */
/* ### should we make this zero? */
#define SERF_NEWLINE_NONE  0x0008

/** Used to indicate that a CR was found at the end of a buffer, and CRLF
 * was acceptable. It may be that the LF is present, but it needs to be
 * read first.
 *
 * Note: an alternative to using this symbol would be for callers to see
 * the SERF_NEWLINE_CR return value, and know that some "end of buffer" was
 * reached. While this works well for @see serf_util_readline, it does not
 * necessary work as well for buckets (there is no obvious "end of buffer",
 * although there is an "end of bucket"). The other problem with that
 * alternative is that developers might miss the condition. This symbol
 * calls out the possibility and ensures that callers will watch for it.
 */
#define SERF_NEWLINE_CRLF_SPLIT 0x0010

/**
 * Used to indicate that length of remaining data in bucket is unknown. See
 * serf_bucket_type_t->get_remaining().
 *
 * @since New in 1.5.
 */
#define SERF_LENGTH_UNKNOWN ((apr_uint64_t) -1)

struct serf_bucket_type_t {

    /** name of this bucket type */
    const char *name;

    /**
     * Read (and consume) up to @a requested bytes from @a bucket.
     *
     * A pointer to the data will be returned in @a data, and its length
     * is specified by @a len.
     *
     * The data will exist until one of two conditions occur:
     *
     * 1) this bucket is destroyed
     * 2) another call to any read function, get_remaining() or to peek()
     *
     * If an application needs the data to exist for a longer duration,
     * then it must make a copy.
     */
    apr_status_t (*read)(serf_bucket_t *bucket, apr_size_t requested,
                         const char **data, apr_size_t *len);

    /**
     * Read (and consume) a line of data from @a bucket.
     *
     * The acceptable forms of a newline are given by @a acceptable, and
     * the type found is returned in @a found. If a newline is not present
     * in the returned data, then SERF_NEWLINE_NONE is stored into @a found.
     *
     * A pointer to the data is returned in @a data, and its length is
     * specified by @a len. The data will include the newline, if present.
     *
     * Note that there is no way to limit the amount of data returned
     * by this function. @see serf_bucket_limited_readline().
     *
     * The lifetime of the data is the same as that of the @see read
     * function above.
     */
    apr_status_t (*readline)(serf_bucket_t *bucket, int acceptable,
                             int *found,
                             const char **data, apr_size_t *len);

    /**
     * Read a set of pointer/length pairs from the bucket.
     *
     * The size of the @a vecs array is specified by @a vecs_size. The
     * bucket should fill in elements of the array, and return the number
     * used in @a vecs_used.
     *
     * Each element of @a vecs should specify a pointer to a block of
     * data and a length of that data.
     *
     * The total length of all data elements should not exceed the
     * amount specified in @a requested.
     *
     * The lifetime of the data is the same as that of the @see read
     * function above.
     */
    apr_status_t (*read_iovec)(serf_bucket_t *bucket, apr_size_t requested,
                               int vecs_size, struct iovec *vecs,
                               int *vecs_used);

    /**
     * Read data from the bucket in a form suitable for apr_socket_sendfile()
     *
     * On input, hdtr->numheaders and hdtr->numtrailers specify the size
     * of the hdtr->headers and hdtr->trailers arrays, respectively. The
     * bucket should fill in the headers and trailers, up to the specified
     * limits, and set numheaders and numtrailers to the number of iovecs
     * filled in for each item.
     *
     * @a file should be filled in with a file that can be read. If a file
     * is not available or appropriate, then NULL should be stored. The
     * file offset for the data should be stored in @a offset, and the
     * length of that data should be stored in @a len. If a file is not
     * returned, then @a offset and @a len should be ignored.
     *
     * The file position is not required to correspond to @a offset, and
     * the caller may manipulate it at will.
     *
     * The total length of all data elements, and the portion of the
     * file should not exceed the amount specified in @a requested.
     *
     * The lifetime of the data is the same as that of the @see read
     * function above.
     */
    apr_status_t (*read_for_sendfile)(serf_bucket_t *bucket,
                                      apr_size_t requested, apr_hdtr_t *hdtr,
                                      apr_file_t **file, apr_off_t *offset,
                                      apr_size_t *len);

    /**
     * Look within @a bucket for a bucket of the given @a type. The bucket
     * must be the "initial" data because it will be consumed by this
     * function. If the given bucket type is available, then read and consume
     * it, and return it to the caller.
     *
     * This function is usually used by readers that have custom handling
     * for specific bucket types (e.g. looking for a file bucket to pass
     * to apr_socket_sendfile).
     *
     * If a bucket of the given type is not found, then NULL is returned.
     *
     * The returned bucket becomes the responsibility of the caller. When
     * the caller is done with the bucket, it should be destroyed.
     */
    serf_bucket_t * (*read_bucket)(serf_bucket_t *bucket,
                                   const serf_bucket_type_t *type);

    /**
     * Peek, but don't consume, the data in @a bucket.
     *
     * Since this function is non-destructive, the implicit read size is
     * SERF_READ_ALL_AVAIL. The caller can then use whatever amount is
     * appropriate.
     *
     * The @a data parameter will point to the data, and @a len will
     * specify how much data is available. The lifetime of the data follows
     * the same rules as the @see read function above.
     *
     * Note: if the peek does not return enough data for your particular
     * use, then you must read/consume some first, then peek again.
     *
     * If the returned data represents all available data, then APR_EOF
     * will be returned. Since this function does not consume data, it
     * can return the same data repeatedly rather than blocking; thus,
     * APR_EAGAIN will never be returned.
     */
    apr_status_t (*peek)(serf_bucket_t *bucket,
                         const char **data, apr_size_t *len);

    /**
     * Destroy @a bucket, along with any associated resources.
     */
    void (*destroy)(serf_bucket_t *bucket);

    /* The following members are valid only if read_bucket equals to
     * serf_buckets_are_v2() (or in a future spec _v3, etc.). */

    /* Real pointer to read_bucket() method when read_bucket is
     * serf_buckets_are_v2().
     *
     * @since New in 1.5 / Buckets v2.
     */
    serf_bucket_t * (*read_bucket_v2)(serf_bucket_t *bucket,
                                      const serf_bucket_type_t *type);

    /* Returns length of remaining data to be read in @a bucket. Returns
     * SERF_LENGTH_UNKNOWN if length is unknown.
     *
     * @since New in 1.5 / Buckets v2.
     */
    apr_uint64_t (*get_remaining)(serf_bucket_t *bucket);

    /* Provides a reference to a config object containing all configuration
     * values relevant for this bucket.
     *
     * @since New in 1.5 / Buckets v2
     */
    apr_status_t (*set_config)(serf_bucket_t *bucket, serf_config_t *config);

    /* ### apr buckets have 'copy', 'split', and 'setaside' functions.
       ### not sure whether those will be needed in this bucket model.
    */
};

/**
 * Should the use and lifecycle of buckets be tracked?
 *
 * When tracking, the system will ensure several semantic requirements
 * of bucket use:
 *
 *   - if a bucket returns APR_EAGAIN, one of its read functions should
 *     not be called immediately. the context's run loop should be called.
 *     ### and for APR_EOF, too?
 *   - all buckets must be drained of input before returning to the
 *     context's run loop.
 *   - buckets should not be destroyed before they return APR_EOF unless
 *     the connection is closed for some reason.
 *
 * Undefine this symbol to avoid the tracking (and a performance gain).
 *
 * ### we may want to examine when/how we provide this. should it always
 * ### be compiled in? and apps select it before including this header?
 */
/* #define SERF_DEBUG_BUCKET_USE */

/* Predefined value for read_bucket vtable member to declare v2 buckets
 * vtable.
 *
 * @since New in 1.5.
 */
serf_bucket_t * serf_buckets_are_v2(serf_bucket_t *bucket,
                                    const serf_bucket_type_t *type);

/** Gets the serf bucket type of the bucket if the bucket implements at least
 * buckets version, or if not a bucket type providing a default implementation
 *
 * @since New in 1.5.
 */
const serf_bucket_type_t *serf_get_type(serf_bucket_t *bucket,
                                        int min_version);

/* Internal macros for tracking bucket use. */
#ifdef SERF_DEBUG_BUCKET_USE
#define SERF__RECREAD(b,s) serf_debug__record_read(b,s)
#else
#define SERF__RECREAD(b,s) (s)
#endif

#define serf_bucket_read(b,r,d,l) SERF__RECREAD(b, (b)->type->read(b,r,d,l))
#define serf_bucket_readline(b,a,f,d,l) \
    SERF__RECREAD(b, (b)->type->readline(b,a,f,d,l))
#define serf_bucket_read_iovec(b,r,s,v,u) \
    SERF__RECREAD(b, (b)->type->read_iovec(b,r,s,v,u))
#define serf_bucket_read_for_sendfile(b,r,h,f,o,l) \
    SERF__RECREAD(b, (b)->type->read_for_sendfile(b,r,h,f,o,l))
#define serf_bucket_read_bucket(b,t) ((b)->type->read_bucket(b,t))
#define serf_bucket_peek(b,d,l) ((b)->type->peek(b,d,l))
#define serf_bucket_destroy(b) ((b)->type->destroy(b))
#define serf_bucket_get_remaining(b) (serf_get_type(b, 2)->get_remaining(b))
#define serf_bucket_set_config(b,c) (serf_get_type(b, 2)->set_config(b, c))

/**
 * Check whether a real error occurred. Note that bucket read functions
 * can return EOF and EAGAIN as part of their "normal" operation, so they
 * should not be considered an error.
 */
#define SERF_BUCKET_READ_ERROR(status) ((status) \
                                        && !APR_STATUS_IS_EOF(status) \
                                        && !APR_STATUS_IS_EAGAIN(status) \
                                        && (SERF_ERROR_WAIT_CONN != status))


struct serf_bucket_t {

    /** the type of this bucket */
    const serf_bucket_type_t *type;

    /** bucket-private data */
    void *data;

    /** the allocator used for this bucket (needed at destroy time) */
    serf_bucket_alloc_t *allocator;
};


/**
 * Generic macro to construct "is TYPE" macros.
 */
#define SERF_BUCKET_CHECK(b, btype) ((b)->type == &serf_bucket_type_ ## btype)


/** @} */


/**
 * Notification callback for a block that was not returned to the bucket
 * allocator when its pool was destroyed.
 *
 * The block of memory is given by @a block. The baton provided when the
 * allocator was constructed is passed as @a unfreed_baton.
 */
typedef void (*serf_unfreed_func_t)(
    void *unfreed_baton,
    void *block);

/**
 * Create a new allocator for buckets.
 *
 * All buckets are associated with a serf bucket allocator. This allocator
 * will be created within @a pool and will be destroyed when that pool is
 * cleared or destroyed.
 *
 * When the allocator is destroyed, if any allocations were not explicitly
 * returned (by calling serf_bucket_mem_free), then the @a unfreed callback
 * will be invoked for each block. @a unfreed_baton will be passed to the
 * callback.
 *
 * If @a unfreed is NULL, then the library will invoke the abort() stdlib
 * call. Any failure to return memory is a bug in the application, and an
 * abort can assist with determining what kinds of memory were not freed.
 */
serf_bucket_alloc_t *serf_bucket_allocator_create(
    apr_pool_t *pool,
    serf_unfreed_func_t unfreed,
    void *unfreed_baton);

/**
 * Return the pool that was used for this @a allocator.
 *
 * WARNING: the use of this pool for allocations requires a very
 *   detailed understanding of pool behaviors, the bucket system,
 *   and knowledge of the bucket's use within the overall pattern
 *   of request/response behavior.
 *
 * See design-guide.txt for more information about pool usage.
 */
apr_pool_t *serf_bucket_allocator_get_pool(
    const serf_bucket_alloc_t *allocator);


/**
 * Utility structure for reading a complete line of input from a bucket.
 *
 * Since it is entirely possible for a line to be broken by APR_EAGAIN,
 * this structure can be used to accumulate the data until a complete line
 * has been read from a bucket.
 */

/* This limit applies to the line buffer functions. If an application needs
 * longer lines, then they will need to manually handle line buffering.
 */
#define SERF_LINEBUF_LIMIT 8000

typedef struct serf_linebuf_t {

    /* Current state of the buffer. */
    enum {
        SERF_LINEBUF_EMPTY,
        SERF_LINEBUF_READY,
        SERF_LINEBUF_PARTIAL,
        SERF_LINEBUF_CRLF_SPLIT
    } state;

    /* How much of the buffer have we used? */
    apr_size_t used;

    /**
     * The line is read into this buffer, minus CR/LF.
     *
     * NOTE: Before serf 1.5 the buffer IS NOT NUL terminated
     *       and @a used holds the line length.
     *
     * Since serf 1.5 the buffer is always NUL terminated.
     **/
    char line[SERF_LINEBUF_LIMIT];

} serf_linebuf_t;

/**
 * Initialize the @a linebuf structure.
 */
void serf_linebuf_init(serf_linebuf_t *linebuf);

/**
 * Fetch a line of text from @a bucket, accumulating the line into
 * @a linebuf. @a acceptable specifies the types of newlines which are
 * acceptable for this fetch.
 *
 * ### we should return a data/len pair so that we can avoid a copy,
 * ### rather than having callers look into our state and line buffer.
 */
apr_status_t serf_linebuf_fetch(
    serf_linebuf_t *linebuf,
    serf_bucket_t *bucket,
    int acceptable);

/*** Configuration store declarations ***/

typedef const apr_uint32_t serf_config_key_t;

/* The left-most byte of the int32 key holds the category (bit flags).
   The other bytes are a number representing the key.

   Serf will not use the second byte for its own keys, so applications can
   use this byte to define custom keys.
 */
typedef enum serf_config_categories_t {
    SERF_CONFIG_PER_CONTEXT    = 0x10000000,
    SERF_CONFIG_PER_HOST       = 0x20000000,
    SERF_CONFIG_PER_CONNECTION = 0x40000000
} serf_config_categories_t;

#define SERF_CONFIG_HOST_NAME       (SERF_CONFIG_PER_HOST | 0x000001)
#define SERF_CONFIG_HOST_PORT       (SERF_CONFIG_PER_HOST | 0x000002)
#define SERF_CONFIG_CONN_LOCALIP    (SERF_CONFIG_PER_CONNECTION | 0x000001)
#define SERF_CONFIG_CONN_REMOTEIP   (SERF_CONFIG_PER_CONNECTION | 0x000002)
#define SERF_CONFIG_CONN_PIPELINING (SERF_CONFIG_PER_CONNECTION | 0x000003)
#define SERF_CONFIG_CTX_LOGBATON    (SERF_CONFIG_PER_CONTEXT | 0x000001)

/* Configuration values stored in the configuration store:

   Category     Key          Value Type
   --------     ---          ----------
   Context      logbaton     log_baton_t *
   Context      proxyauthn   apr_hash_t * (not implemented)
   Connection   localip      const char *
   Connection   remoteip     const char *
   Host         hostname     const char *
   Host         hostport     const char *
   Host         authn        apr_hash_t * (not implemented)
*/

/**
 * Set a value of type const char * for configuration item CATEGORY+KEY.
 *
 * @since New in 1.5.
 */
apr_status_t serf_config_set_string(serf_config_t *config,
                                    serf_config_key_t key,
                                    const char *value);
/**
 * Copy a value of type const char * and set it for configuration item
 * CATEGORY+KEY.
 *
 * @since New in 1.5.
 */
apr_status_t serf_config_set_stringc(serf_config_t *config,
                                     serf_config_key_t key,
                                     const char *value);

/**
 * Set a value of generic type for configuration item CATEGORY+KEY.
 * See @a serf_set_config_string for COPY_FLAGS description.
 *
 * @since New in 1.5.
 */
apr_status_t serf_config_set_stringf(serf_config_t *config,
                                     serf_config_key_t key,
                                     apr_pool_t *scratch_pool,
                                     const char *fmt, ...);

/**
 * Set a value of generic type for configuration item CATEGORY+KEY.
 * See @a serf_set_config_string for COPY_FLAGS description.
 *
 * @since New in 1.5.
 */
apr_status_t serf_config_set_object(serf_config_t *config,
                                    serf_config_key_t key,
                                    void *value);

/**
 * Get the value for configuration item CATEGORY+KEY. The value's type will
 * be fixed, see the above table.
 * Returns APR_EINVAL when getting a key from a category that this config
 * object doesn't contain, APR_SUCCESS otherwise.
 *
 * @since New in 1.5.
 */
apr_status_t serf_config_get_string(serf_config_t *config,
                                    serf_config_key_t key,
                                    const char **value);

apr_status_t serf_config_get_object(serf_config_t *config,
                                    serf_config_key_t key,
                                    void **value);

/**
 * Remove the value for configuration item CATEGORY+KEY from the configuration
 * store.
 *
 * @since New in 1.5.
 */
apr_status_t serf_config_remove_value(serf_config_t *config,
                                      serf_config_key_t key);

/*** Serf logging API ***/

/* Ordered list of log levels, more detailed log levels include less
   detailed levels. (e.g. level DEBUG also logs ERROR, WARNING & INFO messages).
 */
#define SERF_LOG_ERROR   0x0001
#define SERF_LOG_WARNING 0x0002
#define SERF_LOG_INFO    0x0004
#define SERF_LOG_DEBUG   0x0008
#define SERF_LOG_NONE    0x0000

/* List of components, used as a mask. */
#define SERF_LOGCOMP_ALL_MSG  0xFFFF /* All components, including message
                                        content */
#define SERF_LOGCOMP_RAWMSG   0x0100 /* logs requests and responses directly on
                                        the socket layer. */
#define SERF_LOGCOMP_SSLMSG   0x0200 /* logs decrypted requests and responses. */

#define SERF_LOGCOMP_ALL      0x00FF /* All components, no message content */
#define SERF_LOGCOMP_SSL      0x0001 /* The SSL component */
#define SERF_LOGCOMP_AUTHN    0x0002 /* Authentication components */
#define SERF_LOGCOMP_CONN     0x0004 /* Connection-related events */
#define SERF_LOGCOMP_COMPR    0x0008 /* The compression (deflate) component */
#define SERF_LOGCOMP_PROTOCOL 0x0010 /* The protocol components */
#define SERF_LOGCOMP_NONE     0x0000

typedef struct serf_log_output_t serf_log_output_t;
typedef struct serf_log_layout_t serf_log_layout_t;

/* The default log layout. It's format is:
   [TIMESTAMP] [LOG LEVEL] [l:LOCALIP:PORT r:REMOTEIP:PORT] FILENAME MESSAGE
 */
#define SERF_LOG_DEFAULT_LAYOUT ((serf_log_layout_t *)NULL)

/* TODO: it's not yet possible to define custom layouts */

/**
 * Create a stream output for log info. This can be used with one of the
 * standard streams stderr or stdout.
 * LAYOUT should be SERF_LOG_DEFAULT_LAYOUT (there's no alternative for now).
 * The lifetime of POOL should be at least the same as that of CTX, but it can
 * be used by multiple contexts.
 *
 * @since New in 1.5.
 */
apr_status_t serf_logging_create_stream_output(serf_log_output_t **output,
                                               serf_context_t *ctx,
                                               apr_uint32_t level,
                                               apr_uint32_t comp_mask,
                                               serf_log_layout_t *layout,
                                               FILE *fp,
                                               apr_pool_t *pool);

/**
 * Define an output handler for a log level and a (set of) log component(s).
 * OUTPUT is the object returned by one of the serf_logging_create_XXX_output
 * factory functions.
 *
 * @since New in 1.5.
 */
apr_status_t serf_logging_add_output(serf_context_t *ctx,
                                     const serf_log_output_t *output);


/* Internal functions for bucket use and lifecycle tracking.
   ### Some of these are directly or via Macros used by third party
   ### applications, such as Apache Subversion */
apr_status_t serf_debug__record_read(
    const serf_bucket_t *bucket,
    apr_status_t status);
void serf_debug__entered_loop(
    serf_bucket_alloc_t *allocator);
void serf_debug__closed_conn(
    serf_bucket_alloc_t *allocator);
void serf_debug__bucket_destroy(
    const serf_bucket_t *bucket);
void serf_debug__bucket_alloc_check(
    serf_bucket_alloc_t *allocator);

/* Version info */
#define SERF_MAJOR_VERSION 2
#define SERF_MINOR_VERSION 0
#define SERF_PATCH_VERSION 0

/* Version number string */
#define SERF_VERSION_STRING APR_STRINGIFY(SERF_MAJOR_VERSION) "." \
                            APR_STRINGIFY(SERF_MINOR_VERSION) "." \
                            APR_STRINGIFY(SERF_PATCH_VERSION)

/**
 * Check at compile time if the Serf version is at least a certain
 * level.
 * @param major The major version component of the version checked
 * for (e.g., the "1" of "1.3.0").
 * @param minor The minor version component of the version checked
 * for (e.g., the "3" of "1.3.0").
 * @param patch The patch level component of the version checked
 * for (e.g., the "0" of "1.3.0").
 */
#define SERF_VERSION_AT_LEAST(major,minor,patch)                         \
(((major) < SERF_MAJOR_VERSION)                                          \
  || ((major) == SERF_MAJOR_VERSION && (minor) < SERF_MINOR_VERSION)     \
   || ((major) == SERF_MAJOR_VERSION && (minor) == SERF_MINOR_VERSION && \
            (patch) <= SERF_PATCH_VERSION))


/**
 * Returns the version of the library the application has linked/loaded.
 * Values are returned in @a major, @a minor, and @a patch.
 *
 * Applications will want to use this function to verify compatibility,
 * especially while serf has not reached a 1.0 milestone. APIs and
 * semantics may change drastically until the library hits 1.0.
 */
void serf_lib_version(
    int *major,
    int *minor,
    int *patch);


#ifdef __cplusplus
}
#endif


/*
 * Every user of serf will want to deal with our various bucket types.
 * Go ahead and include that header right now.
 *
 * Note: make sure this occurs outside of the C++ namespace block
 */
#include "serf_bucket_types.h"


#endif    /* !SERF_H */
