OSSL_HTTP_TRANSFER(3ossl)

πŸ’– OSSL_HTTP_TRANSFER(3ossl) πŸ’–

okay bestie, grab your iced coffee because we're diving into OSSL_HTTP_TRANSFER from the OpenSSL manual, section 3ossl πŸ¦‹ this is part 1 and there's more coming, so don't you dare scroll away


✨ NAME ✨

OSSL_HTTP_open, OSSL_HTTP_bio_cb_t, OSSL_HTTP_proxy_connect, OSSL_HTTP_set1_request, OSSL_HTTP_exchange, OSSL_HTTP_get, OSSL_HTTP_transfer, OSSL_HTTP_close β€” these are the HTTP client high-level functions πŸ’… like literally the whole squad of them, all hanging out in one manpage


✨ SYNOPSIS ✨

first things first, you gotta pull in the header, no cap:

#include <openssl/http.h>

then here's the callback type, she's serving function-pointer realness πŸŽ€:

typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg,
                                   int connect, int detail);

and now the whole friend group, in order, exactly as they show up πŸ‘‘

OSSL_HTTP_REQ_CTX *OSSL_HTTP_open(const char *server, const char *port,
                                  const char *proxy, const char *no_proxy,
                                  int use_ssl, BIO *bio, BIO *rbio,
                                  OSSL_HTTP_bio_cb_t bio_update_fn, void *arg,
                                  int buf_size, int overall_timeout);
int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port,
                            const char *proxyuser, const char *proxypass,
                            int timeout, BIO *bio_err, const char *prog);
int OSSL_HTTP_set1_request(OSSL_HTTP_REQ_CTX *rctx, const char *path,
                           const STACK_OF(CONF_VALUE) *headers,
                           const char *content_type, BIO *req,
                           const char *expected_content_type, int expect_asn1,
                           size_t max_resp_len, int timeout, int keep_alive);
BIO *OSSL_HTTP_exchange(OSSL_HTTP_REQ_CTX *rctx, char **redirection_url);
BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
                   BIO *bio, BIO *rbio,
                   OSSL_HTTP_bio_cb_t bio_update_fn, void *arg,
                   int buf_size, const STACK_OF(CONF_VALUE) *headers,
                   const char *expected_content_type, int expect_asn1,
                   size_t max_resp_len, int timeout);
BIO *OSSL_HTTP_transfer(OSSL_HTTP_REQ_CTX **prctx,
                        const char *server, const char *port,
                        const char *path, int use_ssl,
                        const char *proxy, const char *no_proxy,
                        BIO *bio, BIO *rbio,
                        OSSL_HTTP_bio_cb_t bio_update_fn, void *arg,
                        int buf_size, const STACK_OF(CONF_VALUE) *headers,
                        const char *content_type, BIO *req,
                        const char *expected_content_type, int expect_asn1,
                        size_t max_resp_len, int timeout, int keep_alive);
int OSSL_HTTP_close(OSSL_HTTP_REQ_CTX *rctx, int ok);

that's the whole cast list, no notes, she ate πŸ’…βœ¨ more coming in part 2, stay tuned bestie 🫢

πŸ’« DESCRIPTION πŸ’«

Okay bestie, buckle up, this is the meaty part πŸ“

OSSL_HTTP_open() kicks off an HTTP session. It uses the bio argument if that's not NULL, and if it is NULL it goes and connects to a server β€” optionally through a proxy. Main character energy fr fr πŸ‘‘

So usually the OpenSSL build supports sockets, and in that case bio is NULL. When that happens, rbio has to be NULL too and server must not be NULL. The function then builds a network BIO internally with BIO_new_connect(3) to connect to the given server and the optionally given port β€” which defaults to 80 for HTTP or 443 for HTTPS. That internal BIO is what sets up the connection and exchanges one or more request and response. Slay πŸ’…

If bio is given and rbio is NULL, then that bio gets used instead. And if both bio and rbio are given (they can be memory BIOs, for example) then no explicit connection gets set up at all β€” bio is for writing requests and rbio is for reading responses. The vibe here is: as soon as the client has flushed bio, the server must be ready to provide a response or signal a waiting condition via rbio. No chill, no waiting around 😀

If bio is given, it's an error to also pass non-NULL proxy or no_proxy arguments β€” but server and port can still be given, they just support diagnostic output. If bio is NULL, the optional proxy parameter sets an HTTP(S) proxy to use (unless it gets overridden by "no_proxy" settings, iykyk). Quick rundown on proxy defaults:

  • If TLS is not used, it defaults to the environment variable "http_proxy" if set, else "HTTP_PROXY".
  • If use_ssl != 0, it defaults to "https_proxy" if set, else "HTTPS_PROXY".
  • An empty proxy string "" forbids using a proxy, periodt.
  • Otherwise the format is "[http[s]://][userinfo@]host[:port][/path][?query][#fragment]", where any userinfo, path, query, and fragment given is ignored.
  • If the host string is an IPv6 address, it must be wrapped in "[" and "]" β€” no exceptions bestie.
  • The default proxy port number is 80, or 443 in case "https:" is given.

The HTTP client functions connect via the given proxy unless the server is found in the optional list no_proxy of proxy hostnames or IP addresses separated by "," and/or whitespace (if that's not NULL; default is the environment variable "no_proxy" if set, else "NO_PROXY"). Proxying plain HTTP is supported directly, but using a proxy for HTTPS connections needs a suitable callback function such as OSSL_HTTP_proxy_connect(), which we get to below πŸ¦‹

If use_ssl is nonzero, a TLS connection is requested and the bio_update_fn parameter must be provided. No ifs, ands, or buts πŸ’€


✨ the bio_update_fn callback ✨

The bio_update_fn parameter is optional if use_ssl is 0, and it may be used to modify the connection BIO that the HTTP client uses β€” but it cannot be used when both bio and rbio are given. It's a BIO connect/disconnect callback function, and here's its prototype:

BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail)

The callback may modify the BIO handed to it in the bio argument, and it can use an optional custom defined argument arg β€” which could, for instance, point to an SSL_CTX structure. Here's the timing, because timing is everything:

  • During connection establishment, just after calling BIO_do_connect_retry(), the callback is invoked with connect set to 1 and detail set to 1 if use_ssl is nonzero (i.e., HTTPS is requested), else 0.
  • On disconnect, connect is 0 and detail is 1 if no error occurred, else 0.

So like, on connect the callback may push an SSL BIO to implement HTTPS; after disconnect it may do some diagnostic output and pop and free the SSL BIO. The callback function must return either the potentially modified BIO bio or NULL to indicate failure β€” and if it's returning NULL, it should not modify the BIO. Rude to modify and bail, bestie πŸ™ˆ

Here's a simple example that supports TLS connections (but not via a proxy), shown exactly as-is:

BIO *http_tls_cb(BIO *bio, void *arg, int connect, int detail)
{
    if (connect && detail) { /* connecting with TLS */
        SSL_CTX *ctx = (SSL_CTX *)arg;
        BIO *sbio = BIO_new_ssl(ctx, 1);

        bio = sbio != NULL ? BIO_push(sbio, bio) : NULL;
    } else if (!connect) { /* disconnecting */
        BIO *hbio;

        if (!detail) { /* an error has occurred */
            /* optionally add diagnostics here */
        }
        BIO_ssl_shutdown(bio);
        hbio = BIO_pop(bio);
        BIO_free(bio); /* SSL BIO */
        bio = hbio;
    }
    return bio;
}

After disconnect, the modified BIO gets deallocated using BIO_free_all(). And heads up β€” the optional callback function argument arg is not consumed, so the caller has to free it when it's not needed any more. Don't leak, it's not cute 🌸


✨ buf_size and overall_timeout ✨

The buf_size parameter specifies the response header maximum line length. A value <= 0 means that the OSSL_HTTP_DEFAULT_MAX_LINE_LEN (4KiB) is used. Also, buf_size does double duty β€” it's the number of content bytes that are read at a time. Multi-tasking icon πŸ’…

If the overall_timeout parameter is > 0, that indicates the maximum number of seconds the overall HTTP transfer (i.e., connection setup if needed, sending requests, and receiving responses) is allowed to take until completion. A value <= 0 enables waiting indefinitely, i.e., no timeout. Live your truth, wait forever I guess 😭


✨ OSSL_HTTP_proxy_connect() ✨

OSSL_HTTP_proxy_connect() may be used by an above BIO connect callback function to set up an SSL/TLS connection via an HTTPS proxy. It promotes the given BIO bio β€” which represents a connection pre-established with a TLS proxy using the HTTP CONNECT method β€” optionally using proxy client credentials proxyuser and proxypass, to connect with TLS protection ultimately to server and port.

  • If the port argument is NULL or the empty string, it defaults to "443".
  • The server and port arguments must not contain CR or LF characters.
  • If the timeout parameter is > 0, that indicates the maximum number of seconds the connection setup is allowed to take. A value <= 0 enables waiting indefinitely, i.e., no timeout.

Since this function is typically called by applications such as openssl-s_client(1), it uses the bio_err and prog parameters (unless NULL) to print additional diagnostic information in a user-oriented way. So friendly, so demure πŸŽ€


✨ OSSL_HTTP_set1_request() ✨

OSSL_HTTP_set1_request() sets up in rctx the request header and content data and expectations on the response, using the following parameters. If <rctx> indicates using a proxy for HTTP (but not HTTPS), the server host (and optionally port) needs to be placed in the header β€” so it must be present in rctx. For backward compatibility, the server (and optional port) may also be given in the path argument beginning with "http://" (which gives you an absoluteURI). Details:

  • If path is NULL it defaults to "/".
  • If req is NULL the HTTP GET method is used to send the request, else HTTP POST with the contents of req and optional content_type.
  • The length of the data in req does not need to be determined in advance: the BIO will be read on-the-fly while sending the request, which supports streaming. Streaming queen πŸ‘‘
  • The optional list headers may contain additional custom HTTP header lines.
  • The path, headers names and values, and content_type must not contain CR or LF characters. Seriously.
  • The max_resp_len parameter specifies the maximum allowed response content length, where the value 0 indicates no limit.

For the meaning of the expected_content_type, expect_asn1, timeout, and keep_alive parameters, go see OSSL_HTTP_REQ_CTX_set_expected(3).


✨ OSSL_HTTP_exchange() ✨

OSSL_HTTP_exchange() exchanges any form of HTTP request and response as specified by rctx, which must include both connection and request data β€” typically set up using OSSL_HTTP_open() and OSSL_HTTP_set1_request(). It implements the core of the functions described below, so it's basically the main character of this whole operation πŸ’–

  • If the HTTP method is GET and redirection_url is not NULL, that pointer is used to provide any new location the server may return with HTTP code 301 (MOVED_PERMANENTLY) or 302 (FOUND). In this case the function returns NULL and the caller is responsible for deallocating the URL with OPENSSL_free(3).
  • If the response header contains one or more "Content-Length" lines and/or an ASN.1-encoded response is expected, which should include a total length, the length indications received are checked for consistency and for not exceeding any given maximum response length.
  • If an ASN.1-encoded response is expected, the function returns on success the contents buffered in a memory BIO, which does not support streaming.
  • Otherwise it returns directly the read BIO that holds the response contents, which allows a response of indefinite length and may support streaming.

The caller is responsible for freeing the BIO pointer obtained. Every single time. Don't forget 🫢


✨ OSSL_HTTP_get() ✨

OSSL_HTTP_get() uses HTTP GET to obtain data from bio if non-NULL, else from the server contained in the url, and returns it as a BIO.

  • It supports redirection via HTTP status code 301 or 302.
  • Redirection from HTTPS to HTTP is not allowed. Nope. Blocked.
  • It's meant for transfers with a single round trip, so it does not support persistent connections.
  • If bio is non-NULL, any host and port components in the url are not used for connecting, but the hostname is used, as usual, for the "Host" header.
  • Any userinfo and fragment components in the url are ignored.
  • Any query component is handled as part of the path component.
  • If the scheme component of the url is "https", a TLS connection is requested and the bio_update_fn, as described for OSSL_HTTP_open(), must be provided.

Also, the remaining parameters are interpreted as described for OSSL_HTTP_open() and OSSL_HTTP_set1_request(), respectively. And you guessed it β€” the caller is responsible for freeing the BIO pointer obtained 😌


✨ OSSL_HTTP_transfer() ✨

OSSL_HTTP_transfer() exchanges an HTTP request and response over a connection managed via prctx without supporting redirection. It's basically a combo meal β€” it combines OSSL_HTTP_open(), OSSL_HTTP_set1_request(), OSSL_HTTP_exchange(), and OSSL_HTTP_close() πŸ“

  • If prctx is not NULL it reuses any open connection represented by a non-NULL *prctx.
  • It keeps the connection open if a persistent connection is requested or required and this was granted by the server, else it closes the connection and assigns NULL to *prctx.

The remaining parameters are interpreted as described for OSSL_HTTP_open() and OSSL_HTTP_set1_request(), respectively. And yes β€” the caller is responsible for freeing the BIO pointer obtained. It's giving "you break it, you bought it" πŸ’…


✨ OSSL_HTTP_close() ✨

OSSL_HTTP_close() closes the connection and releases rctx. The ok parameter gets passed to any BIO update function given during setup, as described above for OSSL_HTTP_open(). It must be 1 if no error occurred during the HTTP transfer and 0 otherwise. Be honest about it bestie, no lying to the callback πŸ™ˆ


πŸ“ NOTES πŸ“

The names of the environment variables used by this implementation β€” "http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", and "NO_PROXY" β€” were chosen for maximal compatibility with other HTTP client implementations such as wget, curl, and git. Networking besties gotta stick together 🌈

When built with tracing enabled, OSSL_HTTP_transfer() and all functions using it may be traced using OSSL_TRACE_CATEGORY_HTTP. See also OSSL_trace_enabled(3) and openssl-env(7). πŸ‘‘

πŸ’– RETURN VALUES πŸ’–

okay bestie, this is the part where we find out what you actually get back. pay attention, fr fr.

  • OSSL_HTTP_open() β€” slays on success and hands you a OSSL_HTTP_REQ_CTX. if it flops? you get NULL. that's it, that's the whole vibe check. πŸ’…
  • OSSL_HTTP_proxy_connect() and OSSL_HTTP_set1_request() β€” these two return 1 when everything goes right, and 0 when there's an error. classic binary queen behaviour, they don't do in-betweens. ✨
  • OSSL_HTTP_exchange(), OSSL_HTTP_get(), and OSSL_HTTP_transfer() β€” on success these return a memory BIO, and here's the tea: it buffers all the data received if an ASN.1-encoded response is expected. if not, you get a BIO that may support streaming instead. either way the BIO is yours to deal with, so it must be freed by the caller. 🫢
  • on failure, those same three return NULL. and bestie, failure conditions include connection/transfer timeout, parse errors, etc. so many little ways for things to go sideways 😭
the caller is responsible for freeing the BIO pointer obtained. not the library. not your future self. you. no cap. πŸŽ€
  • OSSL_HTTP_close() β€” returns 0 if anything went wrong while disconnecting, else 1. so a 1 here is the little victory lap, and a 0 is the "hmm that didn't go great" moment. πŸ™ˆ

🌈 SEE ALSO 🌈

go read these too, they're all in the same friend group:


πŸ¦‹ HISTORY πŸ¦‹

okay so, lore time: all the functions described here were added in OpenSSL 3.0. so if you're on something older, bestie, this whole page is giving "not for you yet" energy. πŸ˜…


Copyright 2019-2026 The OpenSSL Project Authors. All Rights Reserved.

Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy in the file LICENSE in the source distribution or at <https://www.openssl.org/source/license.html>.


aaaand that's a wrap on the whole page, bestie!! πŸŽ€βœ¨ this one's from 3.5.9, dated 2026-09-29, and it lives under the name OSSL_HTTP_TRANSFER(3ossl). you read the entire thing, which is genuinely iconic of you β€” go hydrate, free your BIOs, and stan a safe disconnect. πŸ’–πŸ«ΆπŸ¦‹

πŸ“¦ where this page comes from