[openssl] master update

dev at ddvo.net dev at ddvo.net
Wed May 12 13:12:17 UTC 2021


The branch master has been updated
       via  8f965908a53b4f0c5a735739e8a273a3a33a976e (commit)
       via  4329f361ce75973ceca9d440e8430580ee515070 (commit)
      from  202cbdd2fc37257870eeb61629d8d4d6709df7f1 (commit)


- Log -----------------------------------------------------------------
commit 8f965908a53b4f0c5a735739e8a273a3a33a976e
Author: Dr. David von Oheimb <David.von.Oheimb at siemens.com>
Date:   Wed May 5 00:09:43 2021 +0200

    HTTP client: Minimal changes that include the improved API
    
    This is a minimal version of pull request #15053 including all the
    proposed improvements to the HTTP client API and its documentation
    but only those code adaptations strictly needed for it.
    
    The proposed new features include
    * support for persistent connections (keep-alive),
    * generalization to arbitrary request and response types, and
    * support for streaming BIOs for request and response data.
    
    The related API changes include:
    * Split the monolithic OSSL_HTTP_transfer() into OSSL_HTTP_open(),
      OSSL_HTTP_set_request(), a lean OSSL_HTTP_transfer(), and OSSL_HTTP_close().
    * Split the timeout functionality accordingly and improve default behavior.
    * Extract part of OSSL_HTTP_REQ_CTX_new() to OSSL_HTTP_REQ_CTX_set_expected().
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15147)

commit 4329f361ce75973ceca9d440e8430580ee515070
Author: Dr. David von Oheimb <David.von.Oheimb at siemens.com>
Date:   Fri May 7 17:16:48 2021 +0200

    Add ASN1_item_i2d_mem_bio(); document and improve also ASN1_item_d2i_bio()
    
    ASN1_item_d2i_bio(): Do not report errors in queue on BIO input being NULL
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15147)

-----------------------------------------------------------------------

Summary of changes:
 CHANGES.md                      |  18 ++-
 apps/lib/apps.c                 |  33 +++--
 crypto/asn1/a_d2i_fp.c          |   2 +
 crypto/asn1/a_i2d_fp.c          |  18 +++
 crypto/cmp/cmp_http.c           |  30 +++--
 crypto/http/http_client.c       | 243 +++++++++++++++++------------------
 crypto/http/http_local.h        |  15 ---
 crypto/ocsp/ocsp_http.c         |  25 ++--
 crypto/x509/x_all.c             |  14 ++-
 doc/build.info                  |   6 +
 doc/man1/openssl-cmp.pod.in     |  12 +-
 doc/man3/ASN1_item_d2i_bio.pod  |  45 +++++++
 doc/man3/OCSP_sendreq_new.pod   |  46 +++----
 doc/man3/OSSL_HTTP_REQ_CTX.pod  | 157 ++++++++++++++++-------
 doc/man3/OSSL_HTTP_transfer.pod | 272 ++++++++++++++++++++++------------------
 doc/man3/X509_load_http.pod     |   2 +-
 include/crypto/httperr.h        |   2 +-
 include/openssl/asn1.h.in       |   3 +-
 include/openssl/cmp.h.in        |  40 +++---
 include/openssl/http.h          |  81 ++++++------
 include/openssl/httperr.h       |   2 +
 include/openssl/ocsp.h.in       |  30 ++---
 test/cmp_ctx_test.c             |   7 +-
 test/http_test.c                |  38 +++---
 util/libcrypto.num              |  16 ++-
 util/missingcrypto.txt          |   1 -
 util/other.syms                 |   3 +-
 27 files changed, 673 insertions(+), 488 deletions(-)
 create mode 100644 doc/man3/ASN1_item_d2i_bio.pod

diff --git a/CHANGES.md b/CHANGES.md
index 80a7bc7075..e4e33e4e88 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -358,18 +358,20 @@ OpenSSL 3.0
  * Deprecated the type OCSP_REQ_CTX and the functions OCSP_REQ_CTX_new(),
    OCSP_REQ_CTX_free(), OCSP_REQ_CTX_http(), OCSP_REQ_CTX_add1_header(),
    OCSP_REQ_CTX_i2d() and its special form OCSP_REQ_CTX_set1_req(),
-   OCSP_REQ_CTX_nbio(), OCSP_REQ_CTX_nbio_d2i(),
+   OCSP_REQ_CTX_nbio(),
+   OCSP_REQ_CTX_nbio_d2i() and its special form OCSP_sendreq_nbio(),
    OCSP_REQ_CTX_get0_mem_bio() and OCSP_set_max_response_length().  These
    were used to collect all necessary data to form a HTTP request, and to
    perform the HTTP transfer with that request.  With OpenSSL 3.0, the
    type is OSSL_HTTP_REQ_CTX, and the deprecated functions are replaced
    with OSSL_HTTP_REQ_CTX_new(), OSSL_HTTP_REQ_CTX_free(),
    OSSL_HTTP_REQ_CTX_set_request_line(), OSSL_HTTP_REQ_CTX_add1_header(),
-   OSSL_HTTP_REQ_CTX_i2d(), OSSL_HTTP_REQ_CTX_nbio(),
-   OSSL_HTTP_REQ_CTX_sendreq_d2i(), OSSL_HTTP_REQ_CTX_get0_mem_bio() and
+   OSSL_HTTP_REQ_CTX_set1_req(),
+   OSSL_HTTP_REQ_CTX_nbio(), OSSL_HTTP_REQ_CTX_nbio_d2i(),
+   OSSL_HTTP_REQ_CTX_get0_mem_bio(), and
    OSSL_HTTP_REQ_CTX_set_max_response_length().
 
-   *Rich Salz and Richard Levitte*
+   *Rich Salz, Richard Levitte, and David von Oheimb*
 
  * Deprecated `X509_http_nbio()` and `X509_CRL_http_nbio()`,
    which are superseded by `X509_load_http()` and `X509_CRL_load_http()`.
@@ -812,8 +814,12 @@ OpenSSL 3.0
    *David von Oheimb, Martin Peylo*
 
  * Generalized the HTTP client code from `crypto/ocsp/` into `crpyto/http/`.
-   The legacy OCSP-focused and only partly documented API is retained for
-   backward compatibility. See L<OSSL_CMP_MSG_http_perform(3)> etc. for details.
+   It supports arbitrary request and response content types, GET redirection,
+   TLS, connections via HTTP(S) proxies, connections and exchange via
+   user-defined BIOs (allowing implicit connections), persistent connections,
+   and timeout checks.  See L<OSSL_HTTP_transfer(3)> etc. for details.
+   The legacy OCSP-focused (and only partly documented) API
+   is retained for backward compatibility, while most of it is deprecated.
 
    *David von Oheimb*
 
diff --git a/apps/lib/apps.c b/apps/lib/apps.c
index f0a9ffc93a..67e089bcd4 100644
--- a/apps/lib/apps.c
+++ b/apps/lib/apps.c
@@ -2479,6 +2479,7 @@ ASN1_VALUE *app_http_get_asn1(const char *url, const char *proxy,
     char *server;
     char *port;
     int use_ssl;
+    BIO *mem;
     ASN1_VALUE *resp = NULL;
 
     if (url == NULL || it == NULL) {
@@ -2500,10 +2501,13 @@ ASN1_VALUE *app_http_get_asn1(const char *url, const char *proxy,
     info.use_proxy = proxy != NULL;
     info.timeout = timeout;
     info.ssl_ctx = ssl_ctx;
-    resp = OSSL_HTTP_get_asn1(url, proxy, no_proxy,
-                              NULL, NULL, app_http_tls_cb, &info,
-                              headers, 0 /* maxline */, 0 /* max_resp_len */,
-                              timeout, expected_content_type, it);
+    mem = OSSL_HTTP_get(url, proxy, no_proxy, NULL /* bio */, NULL /* rbio */,
+                        app_http_tls_cb, &info, 0 /* buf_size */, headers,
+                        expected_content_type, 1 /* expect_asn1 */,
+                        HTTP_DEFAULT_MAX_RESP_LEN, timeout);
+    resp = ASN1_item_d2i_bio(it, mem, NULL);
+    BIO_free(mem);
+
  end:
     OPENSSL_free(server);
     OPENSSL_free(port);
@@ -2520,18 +2524,27 @@ ASN1_VALUE *app_http_post_asn1(const char *host, const char *port,
                                long timeout, const ASN1_ITEM *rsp_it)
 {
     APP_HTTP_TLS_INFO info;
+    BIO *rsp, *req_mem = ASN1_item_i2d_mem_bio(req_it, req);
+    ASN1_VALUE *res;
 
+    if (req_mem == NULL)
+        return NULL;
     info.server = host;
     info.port = port;
     info.use_proxy = proxy != NULL;
     info.timeout = timeout;
     info.ssl_ctx = ssl_ctx;
-    return OSSL_HTTP_post_asn1(host, port, path, ssl_ctx != NULL,
-                               proxy, no_proxy,
-                               NULL, NULL, app_http_tls_cb, &info,
-                               headers, content_type, req, req_it,
-                               0 /* maxline */,
-                               0 /* max_resp_len */, timeout, NULL, rsp_it);
+    rsp = OSSL_HTTP_transfer(NULL, host, port, path, ssl_ctx != NULL,
+                             proxy, no_proxy, NULL /* bio */, NULL /* rbio */,
+                             app_http_tls_cb, &info,
+                             0 /* buf_size */, headers, content_type, req_mem,
+                             NULL /* expected_ct */, 1 /* expect_asn1 */,
+                             HTTP_DEFAULT_MAX_RESP_LEN, timeout,
+                             0 /* keep_alive */);
+    BIO_free(req_mem);
+    res = ASN1_item_d2i_bio(rsp_it, rsp, NULL);
+    BIO_free(rsp);
+    return res;
 }
 
 #endif
diff --git a/crypto/asn1/a_d2i_fp.c b/crypto/asn1/a_d2i_fp.c
index b6faa0f2ae..2c7acb34e0 100644
--- a/crypto/asn1/a_d2i_fp.c
+++ b/crypto/asn1/a_d2i_fp.c
@@ -62,6 +62,8 @@ void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x)
     void *ret = NULL;
     int len;
 
+    if (in == NULL)
+        return NULL;
     len = asn1_d2i_read_bio(in, &b);
     if (len < 0)
         goto err;
diff --git a/crypto/asn1/a_i2d_fp.c b/crypto/asn1/a_i2d_fp.c
index efc839e615..482ee627b1 100644
--- a/crypto/asn1/a_i2d_fp.c
+++ b/crypto/asn1/a_i2d_fp.c
@@ -109,3 +109,21 @@ int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, const void *x)
     OPENSSL_free(b);
     return ret;
 }
+
+BIO *ASN1_item_i2d_mem_bio(const ASN1_ITEM *it, const ASN1_VALUE *val)
+{
+    BIO *res;
+
+    if (it == NULL || val == NULL) {
+        ERR_raise(ERR_LIB_ASN1, ERR_R_PASSED_NULL_PARAMETER);
+        return NULL;
+    }
+
+    if ((res = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    if (ASN1_item_i2d_bio(it, res, val) <= 0) {
+        BIO_free(res);
+        res = NULL;
+    }
+    return res;
+}
diff --git a/crypto/cmp/cmp_http.c b/crypto/cmp/cmp_http.c
index 215c47c7c5..a358622feb 100644
--- a/crypto/cmp/cmp_http.c
+++ b/crypto/cmp/cmp_http.c
@@ -37,9 +37,11 @@ OSSL_CMP_MSG *OSSL_CMP_MSG_http_perform(OSSL_CMP_CTX *ctx,
 {
     char server_port[32] = { '\0' };
     STACK_OF(CONF_VALUE) *headers = NULL;
-    const char *const content_type_pkix = "application/pkixcmp";
+    const char content_type_pkix[] = "application/pkixcmp";
     int tls_used;
-    OSSL_CMP_MSG *res;
+    const ASN1_ITEM *it = ASN1_ITEM_rptr(OSSL_CMP_MSG);
+    BIO *req_mem, *rsp;
+    OSSL_CMP_MSG *res = NULL;
 
     if (ctx == NULL || req == NULL) {
         ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT);
@@ -48,6 +50,8 @@ OSSL_CMP_MSG *OSSL_CMP_MSG_http_perform(OSSL_CMP_CTX *ctx,
 
     if (!X509V3_add_value("Pragma", "no-cache", &headers))
         return NULL;
+    if ((req_mem = ASN1_item_i2d_mem_bio(it, (const ASN1_VALUE *)req)) == NULL)
+        goto err;
 
     if (ctx->serverPort != 0)
         BIO_snprintf(server_port, sizeof(server_port), "%d", ctx->serverPort);
@@ -55,15 +59,21 @@ OSSL_CMP_MSG *OSSL_CMP_MSG_http_perform(OSSL_CMP_CTX *ctx,
     tls_used = OSSL_CMP_CTX_get_http_cb_arg(ctx) != NULL;
     ossl_cmp_log2(DEBUG, ctx, "connecting to CMP server %s%s",
                   ctx->server, tls_used ? " using TLS" : "");
-    res = (OSSL_CMP_MSG *)
-        OSSL_HTTP_post_asn1(ctx->server, server_port, ctx->serverPath,
-                            tls_used, ctx->proxy, ctx->no_proxy, NULL, NULL,
-                            ctx->http_cb, OSSL_CMP_CTX_get_http_cb_arg(ctx),
-                            headers, content_type_pkix, (const ASN1_VALUE *)req,
-                            ASN1_ITEM_rptr(OSSL_CMP_MSG),
-                            0, 0, ctx->msg_timeout, content_type_pkix,
-                            ASN1_ITEM_rptr(OSSL_CMP_MSG));
+    rsp = OSSL_HTTP_transfer(NULL, ctx->server, server_port,
+                             ctx->serverPath, tls_used,
+                             ctx->proxy, ctx->no_proxy,
+                             NULL /* bio */, NULL /* rbio */,
+                             ctx->http_cb, OSSL_CMP_CTX_get_http_cb_arg(ctx),
+                             0 /* buf_size */, headers,
+                             content_type_pkix, req_mem,
+                             content_type_pkix, 1 /* expect_asn1 */,
+                             HTTP_DEFAULT_MAX_RESP_LEN,
+                             ctx->msg_timeout, 0 /* keep_alive */);
+    BIO_free(req_mem);
+    res = (OSSL_CMP_MSG *)ASN1_item_d2i_bio(it, rsp, NULL);
+    BIO_free(rsp);
     ossl_cmp_debug(ctx, "disconnected from CMP server");
+ err:
     sk_CONF_VALUE_pop_free(headers, X509V3_conf_free);
     return res;
 }
diff --git a/crypto/http/http_client.c b/crypto/http/http_client.c
index bf2e3b54c7..c32b352137 100644
--- a/crypto/http/http_client.c
+++ b/crypto/http/http_client.c
@@ -48,12 +48,14 @@ struct ossl_http_req_ctx_st {
     BIO *rbio;                  /* BIO to read response from */
     BIO *mem;                   /* Memory BIO response is built into */
     int method_POST;            /* HTTP method is "POST" (else "GET") */
-    const char *expected_ct;    /* expected Content-Type, or NULL */
+    char *expected_ct;          /* expected Content-Type, or NULL */
     int expect_asn1;            /* response must be ASN.1-encoded */
     long len_to_send;           /* number of bytes in request still to send */
     unsigned long resp_len;     /* length of response */
     unsigned long max_resp_len; /* Maximum length of response */
-    time_t max_time;            /* Maximum end time of the transfer, or 0 */
+    int keep_alive;             /* Persistent conn. 0=no, 1=prefer, 2=require */
+    time_t max_time;            /* Maximum end time of current transfer, or 0 */
+    time_t max_total_time;      /* Maximum end time of total transfer, or 0 */
     char *redirection_url;      /* Location given with HTTP status 301/302 */
 };
 
@@ -72,10 +74,7 @@ struct ossl_http_req_ctx_st {
 #define OHS_DONE           (8 | OHS_NOREAD) /* Completed */
 #define OHS_HTTP_HEADER    (9 | OHS_NOREAD) /* Headers set, w/o final \r\n */
 
-OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio,
-                                         int maxline, unsigned long max_resp_len,
-                                         int timeout, const char *expected_ct,
-                                         int expect_asn1)
+OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int maxline)
 {
     OSSL_HTTP_REQ_CTX *rctx;
 
@@ -95,11 +94,8 @@ OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio,
         OPENSSL_free(rctx);
         return NULL;
     }
-    rctx->expected_ct = expected_ct;
-    rctx->expect_asn1 = expect_asn1;
     rctx->resp_len = 0;
-    OSSL_HTTP_REQ_CTX_set_max_response_length(rctx, max_resp_len);
-    rctx->max_time = timeout > 0 ? time(NULL) + timeout : 0;
+    rctx->max_resp_len = HTTP_DEFAULT_MAX_RESP_LEN;
     /* everything else is 0, e.g. rctx->len_to_send, or NULL, e.g. rctx->mem  */
     return rctx;
 }
@@ -110,6 +106,7 @@ void OSSL_HTTP_REQ_CTX_free(OSSL_HTTP_REQ_CTX *rctx)
         return;
     BIO_free(rctx->mem); /* this may indirectly call ERR_clear_error() */
     OPENSSL_free(rctx->readbuf);
+    OPENSSL_free(rctx->expected_ct);
     OPENSSL_free(rctx);
 }
 
@@ -122,6 +119,15 @@ BIO *OSSL_HTTP_REQ_CTX_get0_mem_bio(const OSSL_HTTP_REQ_CTX *rctx)
     return rctx->mem;
 }
 
+size_t OSSL_HTTP_REQ_CTX_get_resp_len(const OSSL_HTTP_REQ_CTX *rctx)
+{
+    if (rctx == NULL) {
+        ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    return rctx->resp_len;
+}
+
 void OSSL_HTTP_REQ_CTX_set_max_response_length(OSSL_HTTP_REQ_CTX *rctx,
                                                unsigned long len)
 {
@@ -201,6 +207,36 @@ int OSSL_HTTP_REQ_CTX_add1_header(OSSL_HTTP_REQ_CTX *rctx,
     return 1;
 }
 
+int OSSL_HTTP_REQ_CTX_set_expected(OSSL_HTTP_REQ_CTX *rctx,
+                                   const char *content_type, int asn1,
+                                   int timeout, int keep_alive)
+{
+    if (rctx == NULL) {
+        ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if (keep_alive != 0
+            && rctx->state != OHS_ERROR && rctx->state != OHS_HEADERS) {
+        /* Cannot anymore set keep-alive in request header */
+        ERR_raise(ERR_LIB_HTTP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+
+    OPENSSL_free(rctx->expected_ct);
+    rctx->expected_ct = NULL;
+    if (content_type != NULL
+            && (rctx->expected_ct = OPENSSL_strdup(content_type)) == NULL)
+        return 0;
+
+    rctx->expect_asn1 = asn1;
+    if (timeout >= 0)
+        rctx->max_time = timeout > 0 ? time(NULL) + timeout : 0;
+    else
+        rctx->max_time = rctx->max_total_time;
+    rctx->keep_alive = keep_alive;
+    return 1;
+}
+
 static int ossl_http_req_ctx_set_content(OSSL_HTTP_REQ_CTX *rctx,
                                          const char *content_type, BIO *req_mem)
 {
@@ -228,26 +264,8 @@ static int ossl_http_req_ctx_set_content(OSSL_HTTP_REQ_CTX *rctx,
         && BIO_write(rctx->mem, req, req_len) == (int)req_len;
 }
 
-BIO *ossl_http_asn1_item2bio(const ASN1_ITEM *it, const ASN1_VALUE *val)
-{
-    BIO *res;
-
-    if (it == NULL || val == NULL) {
-        ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
-        return NULL;
-    }
-
-    if ((res = BIO_new(BIO_s_mem())) == NULL)
-        return NULL;
-    if (ASN1_item_i2d_bio(it, res, val) <= 0) {
-        BIO_free(res);
-        res = NULL;
-    }
-    return res;
-}
-
 int OSSL_HTTP_REQ_CTX_set1_req(OSSL_HTTP_REQ_CTX *rctx, const char *content_type,
-                               const ASN1_ITEM *it, ASN1_VALUE *req)
+                               const ASN1_ITEM *it, const ASN1_VALUE *req)
 {
     BIO *mem;
     int res;
@@ -257,7 +275,7 @@ int OSSL_HTTP_REQ_CTX_set1_req(OSSL_HTTP_REQ_CTX *rctx, const char *content_type
         return 0;
     }
 
-    res = (mem = ossl_http_asn1_item2bio(it, req)) != NULL
+    res = (mem = ASN1_item_i2d_mem_bio(it, req)) != NULL
         && ossl_http_req_ctx_set_content(rctx, content_type, mem);
     BIO_free(mem);
     return res;
@@ -289,14 +307,13 @@ static int OSSL_HTTP_REQ_CTX_add1_headers(OSSL_HTTP_REQ_CTX *rctx,
  * If !use_http_proxy then the 'server' and 'port' parameters are ignored.
  * If req_mem == NULL then use GET and ignore content_type, else POST.
  */
-OSSL_HTTP_REQ_CTX
+static OSSL_HTTP_REQ_CTX
 *ossl_http_req_ctx_new(BIO *wbio, BIO *rbio, int use_http_proxy,
                        const char *server, const char *port,
                        const char *path,
                        const STACK_OF(CONF_VALUE) *headers,
                        const char *content_type, BIO *req_mem,
-                       int maxline, unsigned long max_resp_len,
-                       int timeout,
+                       int maxline, int timeout,
                        const char *expected_ct, int expect_asn1)
 {
     OSSL_HTTP_REQ_CTX *rctx;
@@ -307,14 +324,14 @@ OSSL_HTTP_REQ_CTX
     }
     /* remaining parameters are checked indirectly by the functions called */
 
-    if ((rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, maxline, max_resp_len, timeout,
-                                      expected_ct, expect_asn1))
+    if ((rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, maxline))
         == NULL)
         return NULL;
-
     if (OSSL_HTTP_REQ_CTX_set_request_line(rctx, req_mem != NULL,
                                            use_http_proxy ? server : NULL, port,
                                            path)
+        && OSSL_HTTP_REQ_CTX_set_expected(rctx, expected_ct, expect_asn1,
+                                          timeout, 0)
         && OSSL_HTTP_REQ_CTX_add1_headers(rctx, headers, server)
         && (req_mem == NULL
             || ossl_http_req_ctx_set_content(rctx, content_type, req_mem)))
@@ -588,6 +605,7 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
                                    rctx->expected_ct, value);
                     return 0;
                 }
+                OPENSSL_free(rctx->expected_ct);
                 rctx->expected_ct = NULL; /* content-type has been found */
             }
             if (strcasecmp(key, "Content-Length") == 0) {
@@ -688,6 +706,20 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
     }
 }
 
+int OSSL_HTTP_REQ_CTX_nbio_d2i(OSSL_HTTP_REQ_CTX *rctx,
+                               ASN1_VALUE **pval, const ASN1_ITEM *it)
+{
+    const unsigned char *p;
+    int rv;
+
+    *pval = NULL;
+    if ((rv = OSSL_HTTP_REQ_CTX_nbio(rctx)) != 1)
+        return rv;
+    *pval = ASN1_item_d2i(NULL, &p, BIO_get_mem_data(rctx->mem, &p), it);
+    return *pval != NULL;
+
+}
+
 #ifndef OPENSSL_NO_SOCK
 
 /* set up a new connection BIO, to HTTP server or to HTTP(S) proxy if given */
@@ -723,20 +755,12 @@ static BIO *HTTP_new_bio(const char *server /* optionally includes ":port" */,
 }
 #endif /* OPENSSL_NO_SOCK */
 
-static ASN1_VALUE *BIO_mem_d2i(BIO *mem, const ASN1_ITEM *it)
+int OSSL_HTTP_is_alive(const OSSL_HTTP_REQ_CTX *rctx)
 {
-    const unsigned char *p;
-    ASN1_VALUE *resp;
-
-    if (mem == NULL)
-        return NULL;
-
-    if ((resp = ASN1_item_d2i(NULL, &p, BIO_get_mem_data(mem, &p), it)) == NULL)
-        ERR_raise(ERR_LIB_HTTP, HTTP_R_RESPONSE_PARSE_ERROR);
-    return resp;
+    return rctx != NULL && rctx->keep_alive != 0;
 }
 
-static BIO *ossl_http_req_ctx_transfer(OSSL_HTTP_REQ_CTX *rctx)
+BIO *OSSL_HTTP_REQ_CTX_exchange(OSSL_HTTP_REQ_CTX *rctx)
 {
     int rv;
 
@@ -767,17 +791,6 @@ static BIO *ossl_http_req_ctx_transfer(OSSL_HTTP_REQ_CTX *rctx)
     return rctx->mem;
 }
 
-/* Exchange ASN.1-encoded request and response via HTTP on (non-)blocking BIO */
-ASN1_VALUE *OSSL_HTTP_REQ_CTX_sendreq_d2i(OSSL_HTTP_REQ_CTX *rctx,
-                                          const ASN1_ITEM *it)
-{
-    if (rctx == NULL || it == NULL) {
-        ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
-        return NULL;
-    }
-    return BIO_mem_d2i(ossl_http_req_ctx_transfer(rctx), it);
-}
-
 static int update_timeout(int timeout, time_t start_time)
 {
     long elapsed_time;
@@ -788,6 +801,15 @@ static int update_timeout(int timeout, time_t start_time)
     return timeout <= elapsed_time ? -1 : timeout - elapsed_time;
 }
 
+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)
+{
+    return NULL; /* TODO(3.0) expand */
+}
+
 /*-
  * Exchange HTTP request and response with the given server.
  * If req_mem == NULL then use GET and ignore content_type, else POST.
@@ -815,16 +837,31 @@ static int update_timeout(int timeout, time_t start_time)
  * The function should return NULL to indicate failure.
  * After disconnect the modified BIO will be deallocated using BIO_free_all().
  */
-BIO *OSSL_HTTP_transfer(const char *server, const char *port, const char *path,
+int OSSL_HTTP_set_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)
+{
+    return 0; /* TODO(3.0) expand */
+}
+
+BIO *OSSL_HTTP_exchange(OSSL_HTTP_REQ_CTX *rctx, char **redirection_url)
+{
+    return NULL; /* TODO(3.0) expand */
+}
+
+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,
-                        const STACK_OF(CONF_VALUE) *headers,
+                        int maxline, const STACK_OF(CONF_VALUE) *headers,
                         const char *content_type, BIO *req_mem,
-                        int maxline, unsigned long max_resp_len, int timeout,
                         const char *expected_ct, int expect_asn1,
-                        char **redirection_url)
+                        size_t max_resp_len, int timeout, int keep_alive)
 {
+    char **redirection_url = (char **)prctx; /* TODO(3.0) fix when API approved */
     time_t start_time = timeout > 0 ? time(NULL) : 0;
     BIO *cbio; /* = bio if present, used as connection BIO if rbio is NULL */
     OSSL_HTTP_REQ_CTX *rctx;
@@ -892,12 +929,12 @@ BIO *OSSL_HTTP_transfer(const char *server, const char *port, const char *path,
     rctx = ossl_http_req_ctx_new(cbio, rbio != NULL ? rbio : cbio,
                                  !use_ssl && proxy != NULL, server, port, path,
                                  headers, content_type, req_mem, maxline,
-                                 max_resp_len, update_timeout(timeout, start_time),
+                                 update_timeout(timeout, start_time),
                                  expected_ct, expect_asn1);
     if (rctx == NULL)
         goto end;
 
-    resp = ossl_http_req_ctx_transfer(rctx);
+    resp = OSSL_HTTP_REQ_CTX_exchange(rctx);
     if (resp == NULL) {
         if (rctx->redirection_url != NULL) {
             if (redirection_url == NULL)
@@ -981,12 +1018,12 @@ static int redirection_ok(int n_redir, const char *old_url, const char *new_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,
-                   const STACK_OF(CONF_VALUE) *headers,
-                   int maxline, unsigned long max_resp_len, int timeout,
-                   const char *expected_ct, int expect_asn1)
+                   int maxline, const STACK_OF(CONF_VALUE) *headers,
+                   const char *expected_ct, int expect_asn1,
+                   unsigned long max_resp_len, int timeout)
 {
     time_t start_time = timeout > 0 ? time(NULL) : 0;
-    char *current_url, *redirection_url;
+    char *current_url, *redirection_url = NULL;
     int n_redirs = 0;
     char *host;
     char *port;
@@ -1007,13 +1044,13 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
             break;
 
      new_rpath:
-        resp = OSSL_HTTP_transfer(host, port, path, use_ssl, proxy, no_proxy,
+        resp = OSSL_HTTP_transfer((OSSL_HTTP_REQ_CTX **)&redirection_url, /* TODO(3.0) fix when API approved */
+                                  host, port, path, use_ssl, proxy, no_proxy,
                                   bio, rbio,
-                                  bio_update_fn, arg, headers, NULL, NULL,
-                                  maxline, max_resp_len,
-                                  update_timeout(timeout, start_time),
+                                  bio_update_fn, arg, maxline, headers, NULL, NULL,
                                   expected_ct, expect_asn1,
-                                  &redirection_url);
+                                  max_resp_len,
+                                  update_timeout(timeout, start_time), 0);
         OPENSSL_free(path);
         if (resp == NULL && redirection_url != NULL) {
             if (redirection_ok(++n_redirs, current_url, redirection_url)) {
@@ -1038,65 +1075,9 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
     return resp;
 }
 
-/* Get ASN.1-encoded data via HTTP from server at given URL */
-ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url,
-                               const char *proxy, const char *no_proxy,
-                               BIO *bio, BIO *rbio,
-                               OSSL_HTTP_bio_cb_t bio_update_fn, void *arg,
-                               const STACK_OF(CONF_VALUE) *headers,
-                               int maxline, unsigned long max_resp_len,
-                               int timeout, const char *expected_ct,
-                               const ASN1_ITEM *rsp_it)
+int OSSL_HTTP_close(OSSL_HTTP_REQ_CTX *rctx, int ok)
 {
-    BIO *mem;
-    ASN1_VALUE *resp = NULL;
-
-    if (url == NULL || rsp_it == NULL) {
-        ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
-        return NULL;
-    }
-    mem = OSSL_HTTP_get(url, proxy, no_proxy, bio, rbio, bio_update_fn,
-                        arg, headers, maxline, max_resp_len, timeout,
-                        expected_ct, 1 /* expect_asn1 */);
-    resp = BIO_mem_d2i(mem /* may be NULL */, rsp_it);
-    BIO_free(mem);
-    return resp;
-}
-
-/* Post ASN.1-encoded request via HTTP to server return ASN.1 response */
-ASN1_VALUE *OSSL_HTTP_post_asn1(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,
-                                const STACK_OF(CONF_VALUE) *headers,
-                                const char *content_type,
-                                const ASN1_VALUE *req, const ASN1_ITEM *req_it,
-                                int maxline, unsigned long max_resp_len,
-                                int timeout, const char *expected_ct,
-                                const ASN1_ITEM *rsp_it)
-{
-    BIO *req_mem;
-    BIO *res_mem;
-    ASN1_VALUE *resp = NULL;
-
-    if (req == NULL) {
-        ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
-        return NULL;
-    }
-    /* remaining parameters are checked indirectly */
-
-    req_mem = ossl_http_asn1_item2bio(req_it, req);
-    res_mem = OSSL_HTTP_transfer(server, port, path, use_ssl, proxy, no_proxy,
-                                 bio, rbio,
-                                 bio_update_fn, arg, headers, content_type,
-                                 req_mem /* may be NULL */, maxline,
-                                 max_resp_len, timeout,
-                                 expected_ct, 1 /* expect_asn1 */, NULL);
-    BIO_free(req_mem);
-    resp = BIO_mem_d2i(res_mem /* may be NULL */, rsp_it);
-    BIO_free(res_mem);
-    return resp;
+    return 0; /* TODO(3.0) expand */
 }
 
 /* BASE64 encoder used for encoding basic proxy authentication credentials */
diff --git a/crypto/http/http_local.h b/crypto/http/http_local.h
index 3164f62a77..16f7f7c8a5 100644
--- a/crypto/http/http_local.h
+++ b/crypto/http/http_local.h
@@ -11,21 +11,6 @@
 #ifndef OSSL_CRYPTO_HTTP_LOCAL_H
 # define OSSL_CRYPTO_HTTP_LOCAL_H
 
-# include <openssl/ocsp.h>
-
-BIO *ossl_http_asn1_item2bio(const ASN1_ITEM *it, const ASN1_VALUE *val);
-
-OSSL_HTTP_REQ_CTX
-*ossl_http_req_ctx_new(BIO *wbio, BIO *rbio, int use_http_proxy,
-                       const char *server, const char *port,
-                       const char *path,
-                       const STACK_OF(CONF_VALUE) *headers,
-                       const char *content_type, BIO *req_mem,
-                       int maxline, unsigned long max_resp_len,
-                       int timeout,
-                       const char *expected_content_type,
-                       int expect_asn1);
-
 int ossl_http_use_proxy(const char *no_proxy, const char *server);
 const char *ossl_http_adapt_proxy(const char *proxy, const char *no_proxy,
                                   const char *server, int use_ssl);
diff --git a/crypto/ocsp/ocsp_http.c b/crypto/ocsp/ocsp_http.c
index 7a3c19c860..8cf816e53f 100644
--- a/crypto/ocsp/ocsp_http.c
+++ b/crypto/ocsp/ocsp_http.c
@@ -16,17 +16,18 @@
 OSSL_HTTP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path,
                                     const OCSP_REQUEST *req, int maxline)
 {
-    OSSL_HTTP_REQ_CTX *rctx = NULL;
+    OSSL_HTTP_REQ_CTX *rctx = OSSL_HTTP_REQ_CTX_new(io, io, maxline);
 
-    if ((rctx = OSSL_HTTP_REQ_CTX_new(io, io,
-                                      maxline, 0 /* default max_resp_len */,
-                                      0 /* no timeout, blocking indefinitely */,
-                                      NULL, 1 /* expect_asn1 */)) == NULL)
+    if (rctx == NULL)
         return NULL;
 
     if (!OSSL_HTTP_REQ_CTX_set_request_line(rctx, 1 /* POST */, NULL, NULL, path))
         goto err;
 
+    if (!OSSL_HTTP_REQ_CTX_set_expected(rctx,
+                                        NULL /* content_type */, 1 /* asn1 */,
+                                        0 /* timeout */, 0 /* keep_alive */))
+        goto err;
     if (req != NULL
         && !OSSL_HTTP_REQ_CTX_set1_req(rctx, "application/ocsp-request",
                                        ASN1_ITEM_rptr(OCSP_REQUEST),
@@ -40,23 +41,19 @@ OSSL_HTTP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path,
     return NULL;
 }
 
-int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OSSL_HTTP_REQ_CTX *rctx)
-{
-    *presp = (OCSP_RESPONSE *)
-        OSSL_HTTP_REQ_CTX_sendreq_d2i(rctx, ASN1_ITEM_rptr(OCSP_RESPONSE));
-    return *presp != NULL;
-}
-
 OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req)
 {
     OCSP_RESPONSE *resp = NULL;
     OSSL_HTTP_REQ_CTX *ctx;
+    BIO *mem;
 
     ctx = OCSP_sendreq_new(b, path, req, -1 /* default max resp line length */);
     if (ctx == NULL)
         return NULL;
-
-    OCSP_sendreq_nbio(&resp, ctx);
+    mem = OSSL_HTTP_REQ_CTX_exchange(ctx);
+    resp = (OCSP_RESPONSE *)
+        ASN1_item_d2i_bio(ASN1_ITEM_rptr(OCSP_RESPONSE), mem, NULL);
+    BIO_free(mem);
 
     /* this indirectly calls ERR_clear_error(): */
     OSSL_HTTP_REQ_CTX_free(ctx);
diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c
index 9733597d37..1bd47ce654 100644
--- a/crypto/x509/x_all.c
+++ b/crypto/x509/x_all.c
@@ -75,11 +75,15 @@ int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx)
 static ASN1_VALUE *simple_get_asn1(const char *url, BIO *bio, BIO *rbio,
                                    int timeout, const ASN1_ITEM *it)
 {
-    return OSSL_HTTP_get_asn1(url, NULL, NULL /* no proxy used */, bio,
-                              rbio, NULL /* no callback for SSL/TLS */, NULL,
-                              NULL /* headers */, 1024 /* maxline */,
-                              0 /* max_resp_len */, timeout,
-                              NULL /* expected_content_type */, it);
+    BIO *mem = OSSL_HTTP_get(url, NULL /* proxy */, NULL /* no_proxy */,
+                             bio, rbio, NULL /* cb */ , NULL /* arg */,
+                             1024 /* buf_size */, NULL /* headers */,
+                             NULL /* expected_ct */, 1 /* expect_asn1 */,
+                             HTTP_DEFAULT_MAX_RESP_LEN, timeout);
+    ASN1_VALUE *res = ASN1_item_d2i_bio(it, mem, NULL);
+
+    BIO_free(mem);
+    return res;
 }
 
 X509 *X509_load_http(const char *url, BIO *bio, BIO *rbio, int timeout)
diff --git a/doc/build.info b/doc/build.info
index ec3baa2373..af0e0e0539 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -514,6 +514,10 @@ DEPEND[html/man3/ASN1_generate_nconf.html]=man3/ASN1_generate_nconf.pod
 GENERATE[html/man3/ASN1_generate_nconf.html]=man3/ASN1_generate_nconf.pod
 DEPEND[man/man3/ASN1_generate_nconf.3]=man3/ASN1_generate_nconf.pod
 GENERATE[man/man3/ASN1_generate_nconf.3]=man3/ASN1_generate_nconf.pod
+DEPEND[html/man3/ASN1_item_d2i_bio.html]=man3/ASN1_item_d2i_bio.pod
+GENERATE[html/man3/ASN1_item_d2i_bio.html]=man3/ASN1_item_d2i_bio.pod
+DEPEND[man/man3/ASN1_item_d2i_bio.3]=man3/ASN1_item_d2i_bio.pod
+GENERATE[man/man3/ASN1_item_d2i_bio.3]=man3/ASN1_item_d2i_bio.pod
 DEPEND[html/man3/ASN1_item_sign.html]=man3/ASN1_item_sign.pod
 GENERATE[html/man3/ASN1_item_sign.html]=man3/ASN1_item_sign.pod
 DEPEND[man/man3/ASN1_item_sign.3]=man3/ASN1_item_sign.pod
@@ -2826,6 +2830,7 @@ html/man3/ASN1_STRING_print_ex.html \
 html/man3/ASN1_TIME_set.html \
 html/man3/ASN1_TYPE_get.html \
 html/man3/ASN1_generate_nconf.html \
+html/man3/ASN1_item_d2i_bio.html \
 html/man3/ASN1_item_sign.html \
 html/man3/ASYNC_WAIT_CTX_new.html \
 html/man3/ASYNC_start_job.html \
@@ -3413,6 +3418,7 @@ man/man3/ASN1_STRING_print_ex.3 \
 man/man3/ASN1_TIME_set.3 \
 man/man3/ASN1_TYPE_get.3 \
 man/man3/ASN1_generate_nconf.3 \
+man/man3/ASN1_item_d2i_bio.3 \
 man/man3/ASN1_item_sign.3 \
 man/man3/ASYNC_WAIT_CTX_new.3 \
 man/man3/ASYNC_start_job.3 \
diff --git a/doc/man1/openssl-cmp.pod.in b/doc/man1/openssl-cmp.pod.in
index 28ea4ee6a5..49105ca315 100644
--- a/doc/man1/openssl-cmp.pod.in
+++ b/doc/man1/openssl-cmp.pod.in
@@ -52,6 +52,7 @@ Message transfer options:
 [B<-proxy> I<[http[s]://][userinfo@]host[:port][/path][?query][#fragment]>]
 [B<-no_proxy> I<addresses>]
 [B<-recipient> I<name>]
+[B<-keep_alive> I<value>]
 [B<-msg_timeout> I<seconds>]
 [B<-total_timeout> I<seconds>]
 
@@ -488,11 +489,20 @@ as far as any of those is present, else the NULL-DN as last resort.
 The argument must be formatted as I</type0=value0/type1=value1/type2=...>.
 For details see the description of the B<-subject> option.
 
+=item B<-keep_alive> I<value>
+
+If the given value is 0 then HTTP connections are not kept open
+after receiving a response, which is the default behavior for HTTP 1.0.
+If the value is 1 or 2 then persistent connections are requested.
+If the value is 2 then persistent connections are required,
+i.e., in case the server does not grant them an error occurs.
+The default value is 1, which means preferring to keep the connection open.
+
 =item B<-msg_timeout> I<seconds>
 
 Number of seconds (or 0 for infinite) a CMP request-response message round trip
 is allowed to take before a timeout error is returned.
-Default is 120.
+Default is to use the B<-total_timeout> setting.
 
 =item B<-total_timeout> I<seconds>
 
diff --git a/doc/man3/ASN1_item_d2i_bio.pod b/doc/man3/ASN1_item_d2i_bio.pod
new file mode 100644
index 0000000000..bd3c9b06c2
--- /dev/null
+++ b/doc/man3/ASN1_item_d2i_bio.pod
@@ -0,0 +1,45 @@
+=pod
+
+=head1 NAME
+
+ASN1_item_d2i_bio,
+ASN1_item_i2d_mem_bio
+- decode and encode DER-encoded ASN.1 structures
+
+=head1 SYNOPSIS
+
+ #include <openssl/asn1.h>
+
+ void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *pval);
+ BIO *ASN1_item_i2d_mem_bio(const ASN1_ITEM *it, const ASN1_VALUE *val);
+
+=head1 DESCRIPTION
+
+ASN1_item_d2i_bio() decodes the contents of its input BIO I<in>,
+which must be a DER-encoded ASN.1 structure, using the ASN.1 template I<it>
+and places the result in I<*pval> unless I<pval> is NULL.
+If I<in> is NULL it returns NULL, else a pointer to the parsed structure.
+
+ASN1_item_i2d_mem_bio() encodes the given ASN.1 value I<val>
+using the ASN.1 template I<it> and returns the result in a memory BIO.
+
+=head1 RETURN VALUES
+
+ASN1_item_d2i_bio() returns a pointer to an B<ASN1_VALUE> or NULL.
+
+ASN1_item_i2d_mem_bio() returns a pointer to a memory BIO or NULL on error.
+
+=head1 HISTORY
+
+ASN1_item_i2d_mem_bio() was added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2021 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
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/OCSP_sendreq_new.pod b/doc/man3/OCSP_sendreq_new.pod
index 10c6131f86..51469661de 100644
--- a/doc/man3/OCSP_sendreq_new.pod
+++ b/doc/man3/OCSP_sendreq_new.pod
@@ -18,10 +18,7 @@ OCSP_REQ_CTX_set1_req
  #include <openssl/ocsp.h>
 
  OSSL_HTTP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path,
-                                     const OCSP_REQUEST *req, int maxline);
-
- int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OSSL_HTTP_REQ_CTX *rctx);
-
+                                     const OCSP_REQUEST *req, int buf_size);
  OCSP_RESPONSE *OCSP_sendreq_bio(BIO *io, const char *path, OCSP_REQUEST *req);
 
 Deprecated since OpenSSL 3.0, can be hidden entirely by defining
@@ -29,12 +26,12 @@ B<OPENSSL_API_COMPAT> with a suitable version value, see
 L<openssl_user_macros(7)>:
 
  typedef OSSL_HTTP_REQ_CTX OCSP_REQ_CTX;
+ int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OSSL_HTTP_REQ_CTX *rctx);
  int OCSP_REQ_CTX_i2d(OCSP_REQ_CT *rctx, const ASN1_ITEM *it, ASN1_VALUE *req);
  int OCSP_REQ_CTX_add1_header(OCSP_REQ_CT *rctx,
                               const char *name, const char *value);
  void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx);
- void OCSP_set_max_response_length(OCSP_REQ_CT *rctx,
-                                   unsigned long len);
+ void OCSP_set_max_response_length(OCSP_REQ_CT *rctx, unsigned long len);
  int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, const OCSP_REQUEST *req);
 
 =head1 DESCRIPTION
@@ -42,28 +39,32 @@ L<openssl_user_macros(7)>:
 These functions perform an OCSP POST request / response transfer over HTTP,
 using the HTTP request functions described in L<OSSL_HTTP_REQ_CTX(3)>.
 
-The function OCSP_sendreq_new() builds a complete B<OSSL_HTTP_REQ_CTX>
-structure using connection B<BIO> I<io>, the URL path I<path>, the OCSP
-request I<req>, and with a response header maximum line length of I<maxline>.
-If I<maxline> is zero a default value of 4k is used.
+The function OCSP_sendreq_new() builds a complete B<OSSL_HTTP_REQ_CTX> structure
+with the B<BIO> I<io> to be used for requests and reponse, the URL path I<path>,
+optionally the OCSP request I<req>, and a response header maximum line length
+of I<buf_size>. If I<buf_size> is zero a default value of 4KiB is used.
 The I<req> may be set to NULL and provided later using OCSP_REQ_CTX_set1_req()
-or L<OSSL_HTTP_REQ_CTX_set1_req(3)> .
-
+or L<OSSL_HTTP_REQ_CTX_set1_req(3)>.
 The I<io> and I<path> arguments to OCSP_sendreq_new() correspond to the
 components of the URL.
 For example if the responder URL is C<http://example.com/ocspreq> the BIO
-I<io> should be connected to host C<example.com> on port 80 and I<path>
+I<io> should haven been connected to host C<example.com> on port 80 and I<path>
 should be set to C</ocspreq>.
 
-OCSP_sendreq_nbio() performs I/O on the OCSP request context I<rctx>.
-When the operation is complete it assigns the response, a pointer to a
-B<OCSP_RESPONSE> structure, in I<*presp>.
-
-OCSP_sendreq_bio() is the same as a call to OCSP_sendreq_new() followed by
-OCSP_sendreq_nbio() and then OCSP_REQ_CTX_free() in a single call, with a
+OCSP_sendreq_nbio() attempts to send the request prepared in I<rctx>
+and to gather the response via HTTP, using the BIO I<io> and I<path>
+that were given when calling OCSP_sendreq_new().
+If the operation gets completed it assigns the response,
+a pointer to a B<OCSP_RESPONSE> structure, in I<*presp>.
+The function may need to be called again if its result is -1, which indicates
+L<BIO_should_retry(3)>.  In such a case it is advisable to sleep a little in
+between, using L<BIO_wait(3)> on the read BIO to prevent a busy loop.
+
+OCSP_sendreq_bio() combines OCSP_sendreq_new() with as many calls of
+OCSP_sendreq_nbio() as needed and then OCSP_REQ_CTX_free(), with a
 response header maximum line length 4k. It waits indefinitely on a response.
 It does not support setting a timeout or adding headers and is retained
-for compatibility; use OCSP_sendreq_nbio() instead.
+for compatibility; use L<OSSL_HTTP_transfer(3)> instead.
 
 OCSP_REQ_CTX_i2d(rctx, it, req) is equivalent to the following:
 
@@ -88,15 +89,14 @@ L<OSSL_HTTP_REQ_CTX_set_max_response_length(3)>.
 OCSP_sendreq_new() returns a valid B<OSSL_HTTP_REQ_CTX> structure or NULL
 if an error occurred.
 
-OCSP_sendreq_nbio(), OCSP_REQ_CTX_i2d(), and OCSP_REQ_CTX_set1_req()
-return 1 for success and 0 for failure.
+OCSP_sendreq_nbio() returns 1 for success, 0 on error, -1 if retry is needed.
 
 OCSP_sendreq_bio() returns the B<OCSP_RESPONSE> structure sent by the
 responder or NULL if an error occurred.
 
 =head1 SEE ALSO
 
-L<OSSL_HTTP_REQ_CTX(3)>
+L<OSSL_HTTP_REQ_CTX(3)>, L<OSSL_HTTP_transfer(3)>,
 L<OCSP_cert_to_id(3)>,
 L<OCSP_request_add1_nonce(3)>,
 L<OCSP_REQUEST_new(3)>,
diff --git a/doc/man3/OSSL_HTTP_REQ_CTX.pod b/doc/man3/OSSL_HTTP_REQ_CTX.pod
index 8e928f19fa..a09b9b81a9 100644
--- a/doc/man3/OSSL_HTTP_REQ_CTX.pod
+++ b/doc/man3/OSSL_HTTP_REQ_CTX.pod
@@ -7,11 +7,15 @@ OSSL_HTTP_REQ_CTX_new,
 OSSL_HTTP_REQ_CTX_free,
 OSSL_HTTP_REQ_CTX_set_request_line,
 OSSL_HTTP_REQ_CTX_add1_header,
+OSSL_HTTP_REQ_CTX_set_expected,
 OSSL_HTTP_REQ_CTX_set1_req,
 OSSL_HTTP_REQ_CTX_nbio,
-OSSL_HTTP_REQ_CTX_sendreq_d2i,
+OSSL_HTTP_REQ_CTX_nbio_d2i,
+OSSL_HTTP_REQ_CTX_exchange,
 OSSL_HTTP_REQ_CTX_get0_mem_bio,
-OSSL_HTTP_REQ_CTX_set_max_response_length
+OSSL_HTTP_REQ_CTX_get_resp_len,
+OSSL_HTTP_REQ_CTX_set_max_response_length,
+OSSL_HTTP_is_alive
 - HTTP client low-level functions
 
 =head1 SYNOPSIS
@@ -20,11 +24,7 @@ OSSL_HTTP_REQ_CTX_set_max_response_length
 
  typedef struct ossl_http_req_ctx_st OSSL_HTTP_REQ_CTX;
 
- OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio,
-                                          int maxline, unsigned long max_resp_len,
-                                          int timeout,
-                                          const char *expected_content_type,
-                                          int expect_asn1);
+ OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int buf_size);
  void OSSL_HTTP_REQ_CTX_free(OSSL_HTTP_REQ_CTX *rctx);
 
  int OSSL_HTTP_REQ_CTX_set_request_line(OSSL_HTTP_REQ_CTX *rctx, int method_POST,
@@ -33,42 +33,41 @@ OSSL_HTTP_REQ_CTX_set_max_response_length
  int OSSL_HTTP_REQ_CTX_add1_header(OSSL_HTTP_REQ_CTX *rctx,
                                    const char *name, const char *value);
 
+ int OSSL_HTTP_REQ_CTX_set_expected(OSSL_HTTP_REQ_CTX *rctx,
+                                    const char *content_type, int asn1,
+                                    int timeout, int keep_alive);
  int OSSL_HTTP_REQ_CTX_set1_req(OSSL_HTTP_REQ_CTX *rctx, const char *content_type,
-                                const ASN1_ITEM *it, ASN1_VALUE *req);
+                                const ASN1_ITEM *it, const ASN1_VALUE *req);
  int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx);
- ASN1_VALUE *OSSL_HTTP_REQ_CTX_sendreq_d2i(OSSL_HTTP_REQ_CTX *rctx,
-                                           const ASN1_ITEM *it);
+ int OSSL_HTTP_REQ_CTX_nbio_d2i(OSSL_HTTP_REQ_CTX *rctx,
+                                ASN1_VALUE **pval, const ASN1_ITEM *it);
+ BIO *OSSL_HTTP_REQ_CTX_exchange(OSSL_HTTP_REQ_CTX *rctx);
 
  BIO *OSSL_HTTP_REQ_CTX_get0_mem_bio(const OSSL_HTTP_REQ_CTX *rctx);
+ size_t OSSL_HTTP_REQ_CTX_get_resp_len(const OSSL_HTTP_REQ_CTX *rctx);
  void OSSL_HTTP_REQ_CTX_set_max_response_length(OSSL_HTTP_REQ_CTX *rctx,
                                                 unsigned long len);
 
+ int OSSL_HTTP_is_alive(const OSSL_HTTP_REQ_CTX *rctx);
+
 =head1 DESCRIPTION
 
-B<OSSL_HTTP_REQ_CTX> is a context structure for an HTTP request, used to
-collect all the necessary data to perform that request.
+B<OSSL_HTTP_REQ_CTX> is a context structure for an HTTP request and response,
+used to collect all the necessary data to perform that request.
 
 This file documents low-level HTTP functions rarely used directly.  High-level
 HTTP client functions like L<OSSL_HTTP_get(3)> and L<OSSL_HTTP_transfer(3)>
 should be preferred.
 
 OSSL_HTTP_REQ_CTX_new() allocates a new HTTP request context structure,
-which gets populated with the B<BIO> to send the request to (I<wbio>),
-the B<BIO> to read the response from (I<rbio>, which may be equal to I<wbio>),
-the maximum expected response header line length (I<maxline>, where a value <= 0
-indicates that the B<HTTP_DEFAULT_MAX_LINE_LENGTH> of 4KiB should be used;
-this length is also used as the number of content bytes read at a time),
-the maximum allowed response content length (I<max_resp_len>, where 0 means
-that the B<HTTP_DEFAULT_MAX_RESP_LEN> is used, which currently is 100 KiB),
-a response timeout measure in seconds (I<timeout>,
-where 0 indicates no timeout, i.e., waiting indefinitely),
-the expected MIME content type of the response (I<expected_content_type>,
-which may be NULL for no expectation),
-and a flag indicating that the response is expected to be
-a DER encoded ASN.1 structure (I<expect_asn1>).
+which gets populated with the B<BIO> to write/send the request to (I<wbio>),
+the B<BIO> to read/receive the response from (I<rbio>, which may be equal to
+I<wbio>), and the maximum expected response header line length I<buf_size>.
+A value <= 0 indicates that
+the B<HTTP_DEFAULT_MAX_LINE_LENGTH> of 4KiB should be used.
+I<buf_size> is also used as the number of content bytes that are read at a time.
 The allocated context structure is also populated with an internal allocated
 memory B<BIO>, which collects the HTTP request and additional headers as text.
-The returned context should only be used for a single HTTP request/response.
 
 OSSL_HTTP_REQ_CTX_free() frees up the HTTP request context I<rctx>.
 The I<wbio> and I<rbio> are not free'd and it is up to the application
@@ -87,33 +86,71 @@ For example, to add a C<Host> header for C<example.com> you would call:
 
  OSSL_HTTP_REQ_CTX_add1_header(ctx, "Host", "example.com");
 
+OSSL_HTTP_REQ_CTX_set_expected() optionally sets in I<rctx> some expectations
+of the HTTP client on the response.
+Due to the structure of an HTTP request, if the I<keep_alive> argument is
+nonzero the function must be used before calling OSSL_HTTP_REQ_CTX_set1_req().
+If the I<content_type> parameter
+is not NULL then the client will check that the given content type string
+is included in the HTTP header of the response and return an error if not.
+If the I<asn1> parameter is nonzero a structure in ASN.1 encoding will be
+expected as the response content and input streaming is disabled.  This means
+that an ASN.1 sequence header is required, its length field is checked, and
+OSSL_HTTP_REQ_CTX_get0_mem_bio() should be used to get the buffered response.
+Else any form of input is allowed without length checks, which is the default.
+In this case the BIO given as I<rbio> argument to OSSL_HTTP_REQ_CTX_new() should
+be used directly to read the response contents, which may support streaming.
+If the I<timeout> parameter is > 0 this indicates the maximum number of seconds
+the subsequent HTTP transfer (sending the request and receiving a response)
+is allowed to take.
+A value <= 0 enables waiting indefinitely, i.e., no timeout can occur.
+This is the default.
+If the I<keep_alive> parameter is 0, which is the default, the connection is not
+kept open after receiving a response. This is the default behavior for HTTP 1.0.
+If the value is 1 or 2 then a persistent connection is requested.
+If the value is 2 then a persistent connection is required,
+i.e., an error occurs in case the server does not grant it.
+
 OSSL_HTTP_REQ_CTX_set1_req() is to be used if and only if the I<method_POST>
-parameter in the OSSL_HTTP_REQ_CTX_set_request_line() call was 1.
+parameter in the OSSL_HTTP_REQ_CTX_set_request_line() call was 1
+and an ASN.1-encoded request should be sent, which does not support streaming.
 It finalizes the HTTP request context by adding the DER encoding of I<req>,
 using the ASN.1 template I<it> to do the encoding.
 The HTTP header C<Content-Length> is filled out with the length of the request.
 If I<content_type> isn't NULL,
-the HTTP header C<Content-Type> is also added with its content as value.
+the HTTP header C<Content-Type> is also added with the given string value.
 All of this ends up in the internal memory B<BIO>.
 
-OSSL_HTTP_REQ_CTX_nbio() attempts to send the request prepared I<rctx>
-and gathering the response via HTTP, using the I<rbio> and I<wbio>
+OSSL_HTTP_REQ_CTX_nbio() attempts to send the request prepared in I<rctx>
+and to gather the response via HTTP, using the I<wbio> and I<rbio>
 that were given when calling OSSL_HTTP_REQ_CTX_new().
-When successful, the contents of the internal memory B<BIO> contains
-the contents of the HTTP response, without the response headers.
-It may need to be called again if its result is -1, which indicates
+The function may need to be called again if its result is -1, which indicates
 L<BIO_should_retry(3)>.  In such a case it is advisable to sleep a little in
-between using L<BIO_wait(3)> on the read BIO to prevent a busy loop.
-
-OSSL_HTTP_REQ_CTX_sendreq_d2i() calls OSSL_HTTP_REQ_CTX_nbio(), possibly
-several times until a timeout is reached, and DER decodes the received
-response using the ASN.1 template I<it>.
+between, using L<BIO_wait(3)> on the read BIO to prevent a busy loop.
+
+OSSL_HTTP_REQ_CTX_nbio_d2i() is like OSSL_HTTP_REQ_CTX_nbio() but on successs
+in addition parses the response, which must be a DER-encoded ASN.1 structure,
+using the ASN.1 template I<it> and places the result in I<*pval>.
+
+OSSL_HTTP_REQ_CTX_exchange() calls OSSL_HTTP_REQ_CTX_nbio() as often as needed
+in order to exchange a request and response or until a timeout is reached.
+If successful and an ASN.1-encoded response was expected, the response contents
+should be read via the BIO returned by OSSL_HTTP_REQ_CTX_get0_mem_bio().
+Else the I<rbio> that was given when calling OSSL_HTTP_REQ_CTX_new()
+represents the current state of reading the response.
+If OSSL_HTTP_REQ_CTX_exchange() was successful, this BIO has been read past the
+end of the response headers, such that the actual response contents can be read
+via this BIO, which may support streaming.
 
 OSSL_HTTP_REQ_CTX_get0_mem_bio() returns the internal memory B<BIO>.
 Before sending the request, this could used to modify the HTTP request text.
 I<Use with caution!>
-After receiving a response via HTTP, the BIO represents
-the current state of reading the response headers and contents.
+After receiving a response via HTTP, the BIO represents the current state of
+reading the response headers. If the response was expected to be ASN.1 encoded,
+its contents can be read via this BIO, which does not support streaming.
+
+OSSL_HTTP_REQ_CTX_get_resp_len() returns the size of the response contents
+in I<rctx> if provided by the server as <Content-Length> header field, else 0.
 
 OSSL_HTTP_REQ_CTX_set_max_response_length() sets the maximum allowed
 response content length for I<rctx> to I<len>. If not set or I<len> is 0
@@ -122,6 +159,18 @@ If the C<Content-Length> header is present and exceeds this value or
 the content is an ASN.1 encoded structure with a length exceeding this value
 or both length indications are present but disagree then an error occurs.
 
+OSSL_HTTP_is_alive() can be used to query if the HTTP connection
+given by I<rctx> is still alive, i.e., has not been closed.
+It returns 0 if I<rctx> is NULL.
+
+If the client application requested or required a persistent connection
+and this was granted by the server, it can keep I<rctx> as long as it wants
+to send further requests and OSSL_HTTP_is_alive() returns nonzero,
+else it should call I<OSSL_HTTP_REQ_CTX_free(rctx)> or L<OSSL_HTTP_close(3)>.
+In case the client application keeps I<rctx> but the connection then dies
+for any reason at the server side, it will notice this obtaining an
+I/O error when trying to send the next request via I<rctx>.
+
 =head1 WARNINGS
 
 The server's response may be unexpected if the hostname that was used to
@@ -155,7 +204,7 @@ and must be done exactly once in that case.
 =back
 
 When the request context is fully prepared, the HTTP exchange may be performed
-with OSSL_HTTP_REQ_CTX_nbio() or OSSL_HTTP_REQ_CTX_sendreq_d2i().
+with OSSL_HTTP_REQ_CTX_nbio() or OSSL_HTTP_REQ_CTX_exchange().
 
 =head1 RETURN VALUES
 
@@ -166,20 +215,36 @@ OSSL_HTTP_REQ_CTX_free() and OSSL_HTTP_REQ_CTX_set_max_response_length()
 do not return values.
 
 OSSL_HTTP_REQ_CTX_set_request_line(), OSSL_HTTP_REQ_CTX_add1_header(),
-OSSL_HTTP_REQ_CTX_set1_req() and OSSL_HTTP_REQ_CTX_nbio
+OSSL_HTTP_REQ_CTX_set1_req(), and OSSL_HTTP_REQ_CTX_set_expected()
 return 1 for success and 0 for failure.
 
-OSSL_HTTP_REQ_CTX_sendreq_d2i() returns a pointer to an B<ASN1_VALUE> for
-success and NULL for failure.
+OSSL_HTTP_REQ_CTX_nbio() and OSSL_HTTP_REQ_CTX_nbio_d2i()
+return 1 for success, 0 on error or redirection, -1 if retry is needed.
 
-OSSL_HTTP_REQ_CTX_get0_mem_bio() returns the internal memory B<BIO>.
+OSSL_HTTP_REQ_CTX_exchange() and OSSL_HTTP_REQ_CTX_get0_mem_bio()
+returns a pointer to a B<BIO> on success and NULL on failure.
+
+OSSL_HTTP_REQ_CTX_get_resp_len() returns the size of the response contents
+or 0 if not available or an error occurred.
+
+OSSL_HTTP_is_alive() returns 1 if its argument is non-NULL
+and the client requested a persistent connection
+and the server did not disagree on keeping the connection open, else 0.
 
 =head1 SEE ALSO
 
 L<BIO_should_retry(3)>,
 L<BIO_wait(3)>,
+L<ASN1_item_d2i_bio(3)>,
+L<ASN1_item_i2d_mem_bio(3)>,
+L<OSSL_HTTP_open(3)>,
 L<OSSL_HTTP_get(3)>,
-L<OSSL_HTTP_transfer(3)>
+L<OSSL_HTTP_transfer(3)>,
+L<OSSL_HTTP_close(3)>
+
+=head1 HISTORY
+
+The functions described here were added in OpenSSL 3.0.
 
 =head1 COPYRIGHT
 
diff --git a/doc/man3/OSSL_HTTP_transfer.pod b/doc/man3/OSSL_HTTP_transfer.pod
index d2ff8eeebc..da84789472 100644
--- a/doc/man3/OSSL_HTTP_transfer.pod
+++ b/doc/man3/OSSL_HTTP_transfer.pod
@@ -2,13 +2,15 @@
 
 =head1 NAME
 
+OSSL_HTTP_open,
+OSSL_HTTP_bio_cb_t,
+OSSL_HTTP_proxy_connect,
+OSSL_HTTP_set_request,
+OSSL_HTTP_exchange,
 OSSL_HTTP_get,
-OSSL_HTTP_get_asn1,
-OSSL_HTTP_post_asn1,
 OSSL_HTTP_transfer,
-OSSL_HTTP_bio_cb_t,
-OSSL_HTTP_proxy_connect
-- http client functions
+OSSL_HTTP_close
+-  HTTP client high-level functions
 
 =head1 SYNOPSIS
 
@@ -16,91 +18,53 @@ OSSL_HTTP_proxy_connect
 
  typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg,
                                     int connect, int detail);
+ 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_set_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,
-                    const STACK_OF(CONF_VALUE) *headers,
-                    int maxline, unsigned long max_resp_len, int timeout,
-                    const char *expected_ct, int expect_asn1);
- ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url,
-                                const char *proxy, const char *no_proxy,
-                                BIO *bio, BIO *rbio,
-                                OSSL_HTTP_bio_cb_t bio_update_fn, void *arg,
-                                const STACK_OF(CONF_VALUE) *headers,
-                                int maxline, unsigned long max_resp_len,
-                                int timeout, const char *expected_ct,
-                                const ASN1_ITEM *rsp_it);
- ASN1_VALUE *OSSL_HTTP_post_asn1(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,
-                                 const STACK_OF(CONF_VALUE) *headers,
-                                 const char *content_type,
-                                 const ASN1_VALUE *req, const ASN1_ITEM *req_it,
-                                 int maxline, unsigned long max_resp_len,
-                                 int timeout, const char *expected_ct,
-                                 const ASN1_ITEM *rsp_it);
- BIO *OSSL_HTTP_transfer(const char *server, const char *port, const char *path,
-                         int use_ssl, const char *proxy, const char *no_proxy,
+                    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,
-                         const STACK_OF(CONF_VALUE) *headers,
-                         const char *content_type, BIO *req_mem,
-                         int maxline, unsigned long max_resp_len, int timeout,
-                         const char *expected_ct, int expect_asn1,
-                         char **redirection_url);
- 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 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);
 
 =head1 DESCRIPTION
 
-OSSL_HTTP_get() uses HTTP GET to obtain data (of any type) from the given I<url>
-and returns it as a memory BIO.
-If the schema component of the I<url> is C<https> a TLS connection is requested
-and the I<bio_update_fn> parameter, described below, must be provided.
-Any userinfo and fragment components in the I<url> are ignored.
-Any query component is handled as part of the path component.
-
-OSSL_HTTP_get_asn1() is like OSSL_HTTP_get() but in addition
-parses the received contents (e.g., an X.509 certificate)
-as an ASN.1 DER encoded value with the expected structure specified by I<rsp_it>
-and returns it on success as a pointer to I<ASN1_VALUE>.
-
-OSSL_HTTP_post_asn1() is like OSSL_HTTP_get_asn1() but uses the HTTP POST method
-to send a request I<req> with the ASN.1 structure defined in I<req_it> and the
-given I<content_type> to the given I<server> and optional I<port> and I<path>.
-If I<use_ssl> is nonzero a TLS connection is requested and the I<bio_update_fn>
-parameter, described below, must be provided.
+OSSL_HTTP_open() initiates an HTTP session using the I<bio> argument if not
+NULL, else by connecting to a given I<server> optionally via a I<proxy>.
 
-OSSL_HTTP_transfer() exchanges any form of HTTP request and response.
-It implements the core of the functions described above.
-If I<path> parameter is NULL it defaults to "/".
-If I<use_ssl> is nonzero a TLS connection is requested
-and the I<bio_update_fn> parameter, described below, must be provided.
-If I<req_mem> is NULL it uses the HTTP GET method, else it uses HTTP POST to
-send a request with the contents of the memory BIO and optional I<content_type>.
-The optional list I<headers> may contain additional custom HTTP header lines.
-If I<req_mem> is NULL (i.e., the HTTP method is GET) and I<redirection_url>
-is not NULL the latter pointer is used to provide any new location that
-the server may return with HTTP code 301 (MOVED_PERMANENTLY) or 302 (FOUND).
-In this case the caller is responsible for deallocating this URL with
-L<OPENSSL_free(3)>.
-
-The above functions have the following parameters in common.
-
-Typically the OpenSSL build supports sockets
-and the I<bio> and I<rbio> parameters are both NULL.
-In this case the client creates a network BIO internally
-for connecting to the given I<server>
-at the specified I<port> (if any, defaulting to 80 for HTTP or 443 for HTTPS),
-optionally via a I<proxy> (respecting I<no_proxy>) as described below.
-Then the client uses this internal BIO for exchanging the request and response.
-If I<bio> is given and I<rbio> is NULL then the client uses this I<bio> instead.
+Typically the OpenSSL build supports sockets and the I<bio> parameter is NULL.
+In this case I<rbio> must be NULL as well, and the
+library creates a network BIO internally for connecting to the given I<server>
+at the specified I<port> if any, defaulting to 80 for HTTP or 443 for HTTPS.
+Then this internal BIO is used for setting up a connection
+and for exchanging one or more request and response.
+If I<bio> is given and I<rbio> is NULL then this I<bio> is used instead.
 If both I<bio> and I<rbio> are given (which may be memory BIOs for instance)
-then no explicit connection is attempted,
-I<bio> is used for writing the request, and I<rbio> for reading the response.
+then no explicit connection is set up, but
+I<bio> is used for writing requests and I<rbio> for reading responses.
 As soon as the client has flushed I<bio> the server must be ready to provide
 a response or indicate a waiting condition via I<rbio>.
 
@@ -121,33 +85,12 @@ Proxying plain HTTP is supported directly,
 while using a proxy for HTTPS connections requires a suitable callback function
 such as OSSL_HTTP_proxy_connect(), described below.
 
-The I<maxline> parameter specifies the response header maximum line length,
-where a value <= 0 indicates that the B<HTTP_DEFAULT_MAX_LINE_LENGTH> of 4KiB
-should be used.
-This length is also used as the number of content bytes that are read at a time.
-The I<max_resp_len> parameter specifies the maximum response length,
-where 0 indicates B<HTTP_DEFAULT_MAX_RESP_LEN>, which currently is 100 KiB.
-
-An ASN.1-encoded response is expected by OSSL_HTTP_get_asn1() and
-OSSL_HTTP_post_asn1(), while for OSSL_HTTP_get() or OSSL_HTTP_transfer()
-this is only the case if the I<expect_asn1> parameter is nonzero.
-If the response header contains one or more "Content-Length" header 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 the maximum response length.
-
-If the parameter I<expected_ct>
-is not NULL then the HTTP client checks that the given content type string
-is included in the HTTP header of the response and returns an error if not.
-
-If the I<timeout> parameter is > 0 this indicates the maximum number of seconds
-to wait until the transfer is complete.
-A value of 0 enables waiting indefinitely,
-while a value < 0 immediately leads to a timeout condition.
+If I<use_ssl> is nonzero a TLS connection is requested
+and the I<bio_update_fn> parameter must be provided.
 
-The optional parameter I<bio_update_fn> with its optional argument I<arg> may
-be used to modify the connection BIO used by the HTTP client (and cannot be
-used when both I<bio> and I<rbio> are given).
+The parameter I<bio_update_fn>, which is optional if I<use_ssl> is 0,
+may be used to modify the connection BIO used by the HTTP client,
+but cannot be used when both I<bio> and I<rbio> are given.
 I<bio_update_fn> is a BIO connect/disconnect callback function with prototype
 
  BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail)
@@ -157,7 +100,7 @@ whereby it may make use of a custom defined argument I<arg>,
 which may for instance refer to an I<SSL_CTX> structure.
 During connection establishment, just after calling BIO_do_connect_retry(),
 the function is invoked with the I<connect> argument being 1 and the I<detail>
-argument being 1 if HTTPS is requested, i.e., SSL/TLS should be enabled.
+argument being 1 if HTTPS is requested, i.e., SSL/TLS should be enabled, else 0.
 On disconnect I<connect> is 0 and I<detail> is 1 if no error occurred, else 0.
 For instance, on connect the function may prepend a TLS BIO to implement HTTPS;
 after disconnect it may do some diagnostic output and/or specific cleanup.
@@ -166,10 +109,10 @@ Here is a simple example that supports TLS connections (but not via a proxy):
 
  BIO *http_tls_cb(BIO *hbio, void *arg, int connect, int detail)
  {
-     SSL_CTX *ctx = (SSL_CTX *)arg;
-
      if (connect && detail) { /* connecting with TLS */
+         SSL_CTX *ctx = (SSL_CTX *)arg;
          BIO *sbio = BIO_new_ssl(ctx, 1);
+
          hbio = sbio != NULL ? BIO_push(sbio, hbio) : NULL;
      } else if (!connect && !detail) { /* disconnecting after error */
          /* optionally add diagnostics here */
@@ -179,6 +122,16 @@ Here is a simple example that supports TLS connections (but not via a proxy):
 
 After disconnect the modified BIO will be deallocated using BIO_free_all().
 
+The I<buf_size> parameter specifies the response header maximum line length.
+A value <= 0 indicates that
+the B<HTTP_DEFAULT_MAX_LINE_LENGTH> of 4KiB should be used.
+I<buf_size> is also used as the number of content bytes that are read at a time.
+
+If the I<overall_timeout> parameter is > 0 this 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.
+
 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 I<bio> representing a connection
@@ -186,11 +139,86 @@ pre-established with a TLS proxy using the HTTP CONNECT method,
 optionally using proxy client credentials I<proxyuser> and I<proxypass>,
 to connect with TLS protection ultimately to I<server> and I<port>.
 If the I<port> argument is NULL or the empty string it defaults to "443".
-The I<timeout> parameter is used as described above.
+If the I<timeout> parameter is > 0 this 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
 L<openssl-s_client(1)> it uses the I<bio_err> and I<prog> parameters (unless
 NULL) to print additional diagnostic information in a user-oriented way.
 
+OSSL_HTTP_set_request() sets up in I<rctx> the request header and content data
+and expectations on the response using the following parameters.
+If I<path> is NULL it defaults to "/".
+If I<req> is NULL the HTTP GET method will be used to send the request
+else HTTP POST with the contents of I<req> and optional I<content_type>, where
+the length of the data in I<req> does not need to be determined in advance: the
+BIO will be read on-the-fly while sending the request, which supports streaming.
+The optional list I<headers> may contain additional custom HTTP header lines.
+If the parameter I<expected_content_type>
+is not NULL then the client will check that the given content type string
+is included in the HTTP header of the response and return an error if not.
+If the I<expect_asn1> parameter is nonzero,
+a structure in ASN.1 encoding will be expected as response content.
+The I<max_resp_len> parameter specifies the maximum allowed
+response content length, where the value 0 indicates no limit.
+If the I<timeout> parameter is > 0 this indicates the maximum number of seconds
+the subsequent HTTP transfer (sending the request and receiving a response)
+is allowed to take.
+A value of 0 enables waiting indefinitely, i.e., no timeout.
+A value < 0 indicates that the I<overall_timeout> parameter value given
+when opening the HTTP transfer will be used instead.
+If I<keep_alive> is 0 the connection is not kept open
+after receiving a response, which is the default behavior for HTTP 1.0.
+If the value is 1 or 2 then a persistent connection is requested.
+If the value is 2 then a persistent connection is required,
+i.e., an error occurs in case the server does not grant it.
+
+OSSL_HTTP_exchange() exchanges any form of HTTP request and response
+as specified by I<rctx>, which must include both connection and request data,
+typically set up using OSSL_HTTP_open() and OSSL_HTTP_set_request().
+It implements the core of the functions described below.
+If the HTTP method is GET and I<redirection_url>
+is not NULL the latter pointer is used to provide any new location that
+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 L<OPENSSL_free(3)>.
+If the response header contains one or more "Content-Length" header 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.
+On receiving a response, the function returns the contents as a memory BIO,
+which does not support streaming, in case an ASN.1-encoded response is expected.
+Else it returns directly the read BIO that holds the response contents,
+which allows a response of indefinite length and may support streaming.
+
+OSSL_HTTP_get() uses HTTP GET to obtain data from I<bio> if non-NULL,
+else from the server contained in the I<url>, and returns it as a BIO.
+It supports redirection via HTTP status code 301 or 302.  It is meant for
+transfers with a single round trip, so does not support persistent connections.
+If I<bio> is non-NULL, any host and port components in the I<url> are not used
+for connecting but the hostname is used, as usual, for the C<Host> header.
+Any userinfo and fragment components in the I<url> are ignored.
+Any query component is handled as part of the path component.
+If the scheme component of the I<url> is C<https> a TLS connection is requested
+and the I<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_set_request(), respectively.
+
+OSSL_HTTP_transfer() exchanges an HTTP request and response
+over a connection managed via I<prctx> without supporting redirection.
+It combines OSSL_HTTP_open(), OSSL_HTTP_set_request(), OSSL_HTTP_exchange(),
+and OSSL_HTTP_close().
+If I<prctx> is not NULL it reuses any open connection represented by a non-NULL
+I<*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 I<*prctx>.
+The remaining parameters are interpreted as described for OSSL_HTTP_open()
+and OSSL_HTTP_set_request(), respectively.
+
+OSSL_HTTP_close() closes the connection and releases I<rctx>.
+The I<ok> parameter is passed to any BIO update function
+given during setup as described above for OSSL_HTTP_open().
+
 =head1 NOTES
 
 The names of the environment variables used by this implementation:
@@ -200,23 +228,29 @@ other HTTP client implementations such as wget, curl, and git.
 
 =head1 RETURN VALUES
 
-On success, OSSL_HTTP_get(), OSSL_HTTP_get_asn1(), OSSL_HTTP_post_asn1(), and
-OSSL_HTTP_transfer() return a memory BIO containing the data received via HTTP.
-This must be freed by the caller. On failure, NULL is returned.
+OSSL_HTTP_open() returns on success a B<OSSL_HTTP_REQ_CTX>, else NULL.
+
+OSSL_HTTP_proxy_connect() and OSSL_HTTP_set_request()
+return 1 on success, 0 on error.
+
+On success, OSSL_HTTP_exchange(), OSSL_HTTP_get(), and OSSL_HTTP_transfer()
+return a memory BIO containing the data received if an ASN.1-encoded response
+is expected, else a BIO that may support streaming.
+The BIO must be freed by the caller.
+On failure, they return NULL.
 Failure conditions include connection/transfer timeout, parse errors, etc.
 
-OSSL_HTTP_proxy_connect() returns 1 on success, 0 on error.
+OSSL_HTTP_close() returns 0 if anything went wrong while disconnecting, else 1.
 
 =head1 SEE ALSO
 
-L<OSSL_HTTP_parse_url(3)>
-L<BIO_set_conn_port(3)>
+L<OSSL_HTTP_parse_url(3)>, L<BIO_set_conn_port(3)>
+L<ASN1_item_i2d_mem_bio(3)>, L<ASN1_item_d2i_bio(3)>,
+L<OSSL_HTTP_is_alive(3)>
 
 =head1 HISTORY
 
-OSSL_HTTP_get(), OSSL_HTTP_get_asn1(), OSSL_HTTP_post_asn1(),
-OSSL_HTTP_transfer(), and OSSL_HTTP_proxy_connect()
-were added in OpenSSL 3.0.
+All the functions described here were added in OpenSSL 3.0.
 
 =head1 COPYRIGHT
 
diff --git a/doc/man3/X509_load_http.pod b/doc/man3/X509_load_http.pod
index 9e54d31c42..93a63c68cf 100644
--- a/doc/man3/X509_load_http.pod
+++ b/doc/man3/X509_load_http.pod
@@ -49,7 +49,7 @@ Error conditions include connection/transfer timeout, parse errors, etc.
 
 =head1 SEE ALSO
 
-L<OSSL_HTTP_get_asn1(3)>
+L<OSSL_HTTP_get(3)>
 
 =head1 HISTORY
 
diff --git a/include/crypto/httperr.h b/include/crypto/httperr.h
index 648f55c691..c68ca3b0c4 100644
--- a/include/crypto/httperr.h
+++ b/include/crypto/httperr.h
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2020-2021 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
diff --git a/include/openssl/asn1.h.in b/include/openssl/asn1.h.in
index 6a00b3e7f7..0ee82e7d58 100644
--- a/include/openssl/asn1.h.in
+++ b/include/openssl/asn1.h.in
@@ -784,7 +784,7 @@ void *ASN1_d2i_bio(void *(*xnew) (void), d2i_of_void *d2i, BIO *in, void **x);
                           in, \
                           CHECKED_PPTR_OF(type, x)))
 
-void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x);
+void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *pval);
 int ASN1_i2d_bio(i2d_of_void *i2d, BIO *out, const void *x);
 
 #  define ASN1_i2d_bio_of(type,i2d,out,x) \
@@ -793,6 +793,7 @@ int ASN1_i2d_bio(i2d_of_void *i2d, BIO *out, const void *x);
                   CHECKED_PTR_OF(const type, x)))
 
 int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, const void *x);
+BIO *ASN1_item_i2d_mem_bio(const ASN1_ITEM *it, const ASN1_VALUE *val);
 int ASN1_UTCTIME_print(BIO *fp, const ASN1_UTCTIME *a);
 int ASN1_GENERALIZEDTIME_print(BIO *fp, const ASN1_GENERALIZEDTIME *a);
 int ASN1_TIME_print(BIO *fp, const ASN1_TIME *a);
diff --git a/include/openssl/cmp.h.in b/include/openssl/cmp.h.in
index 2591963b6f..352ffcdb2f 100644
--- a/include/openssl/cmp.h.in
+++ b/include/openssl/cmp.h.in
@@ -262,25 +262,29 @@ void OSSL_CMP_MSG_free(OSSL_CMP_MSG *msg);
 OSSL_CMP_CTX *OSSL_CMP_CTX_new(OSSL_LIB_CTX *libctx, const char *propq);
 void OSSL_CMP_CTX_free(OSSL_CMP_CTX *ctx);
 int OSSL_CMP_CTX_reinit(OSSL_CMP_CTX *ctx);
-/* various CMP options: */
+/* CMP general options: */
 #  define OSSL_CMP_OPT_LOG_VERBOSITY 0
-#  define OSSL_CMP_OPT_MSG_TIMEOUT 1
-#  define OSSL_CMP_OPT_TOTAL_TIMEOUT 2
-#  define OSSL_CMP_OPT_VALIDITY_DAYS 3
-#  define OSSL_CMP_OPT_SUBJECTALTNAME_NODEFAULT 4
-#  define OSSL_CMP_OPT_SUBJECTALTNAME_CRITICAL 5
-#  define OSSL_CMP_OPT_POLICIES_CRITICAL 6
-#  define OSSL_CMP_OPT_POPO_METHOD 7
-#  define OSSL_CMP_OPT_DIGEST_ALGNID 8
-#  define OSSL_CMP_OPT_OWF_ALGNID 9
-#  define OSSL_CMP_OPT_MAC_ALGNID 10
-#  define OSSL_CMP_OPT_REVOCATION_REASON 11
-#  define OSSL_CMP_OPT_IMPLICIT_CONFIRM 12
-#  define OSSL_CMP_OPT_DISABLE_CONFIRM 13
-#  define OSSL_CMP_OPT_UNPROTECTED_SEND 14
-#  define OSSL_CMP_OPT_UNPROTECTED_ERRORS 15
-#  define OSSL_CMP_OPT_IGNORE_KEYUSAGE 16
-#  define OSSL_CMP_OPT_PERMIT_TA_IN_EXTRACERTS_FOR_IR 17
+/* CMP transfer options: */
+#  define OSSL_CMP_OPT_KEEP_ALIVE 10
+#  define OSSL_CMP_OPT_MSG_TIMEOUT 11
+#  define OSSL_CMP_OPT_TOTAL_TIMEOUT 12
+/* CMP request options: */
+#  define OSSL_CMP_OPT_VALIDITY_DAYS 20
+#  define OSSL_CMP_OPT_SUBJECTALTNAME_NODEFAULT 21
+#  define OSSL_CMP_OPT_SUBJECTALTNAME_CRITICAL 22
+#  define OSSL_CMP_OPT_POLICIES_CRITICAL 23
+#  define OSSL_CMP_OPT_POPO_METHOD 24
+#  define OSSL_CMP_OPT_IMPLICIT_CONFIRM 25
+#  define OSSL_CMP_OPT_DISABLE_CONFIRM 26
+#  define OSSL_CMP_OPT_REVOCATION_REASON 27
+/* CMP protection options: */
+#  define OSSL_CMP_OPT_UNPROTECTED_SEND 30
+#  define OSSL_CMP_OPT_UNPROTECTED_ERRORS 31
+#  define OSSL_CMP_OPT_OWF_ALGNID 32
+#  define OSSL_CMP_OPT_MAC_ALGNID 33
+#  define OSSL_CMP_OPT_DIGEST_ALGNID 34
+#  define OSSL_CMP_OPT_IGNORE_KEYUSAGE 35
+#  define OSSL_CMP_OPT_PERMIT_TA_IN_EXTRACERTS_FOR_IR 36
 int OSSL_CMP_CTX_set_option(OSSL_CMP_CTX *ctx, int opt, int val);
 int OSSL_CMP_CTX_get_option(const OSSL_CMP_CTX *ctx, int opt);
 /* CMP-specific callback for logging and outputting the error queue: */
diff --git a/include/openssl/http.h b/include/openssl/http.h
index 18d0f13b3e..2140d5d2f8 100644
--- a/include/openssl/http.h
+++ b/include/openssl/http.h
@@ -23,8 +23,6 @@
 extern "C" {
 # endif
 
-typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail);
-
 # define OSSL_HTTP_NAME "http"
 # define OSSL_HTTPS_NAME "https"
 # define OSSL_HTTP_PREFIX OSSL_HTTP_NAME"://"
@@ -38,63 +36,64 @@ typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail)
 #define HTTP_DEFAULT_MAX_LINE_LENGTH (4 * 1024)
 #define HTTP_DEFAULT_MAX_RESP_LEN (100 * 1024)
 
-OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio,
-                                         int maxline, unsigned long max_resp_len,
-                                         int timeout, const char *expected_ct,
-                                         int expect_asn1);
+/* Low-level HTTP API */
+OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int buf_size);
 void OSSL_HTTP_REQ_CTX_free(OSSL_HTTP_REQ_CTX *rctx);
 int OSSL_HTTP_REQ_CTX_set_request_line(OSSL_HTTP_REQ_CTX *rctx, int method_POST,
                                        const char *server, const char *port,
                                        const char *path);
 int OSSL_HTTP_REQ_CTX_add1_header(OSSL_HTTP_REQ_CTX *rctx,
                                   const char *name, const char *value);
+int OSSL_HTTP_REQ_CTX_set_expected(OSSL_HTTP_REQ_CTX *rctx,
+                                   const char *content_type, int asn1,
+                                   int timeout, int keep_alive);
 int OSSL_HTTP_REQ_CTX_set1_req(OSSL_HTTP_REQ_CTX *rctx, const char *content_type,
-                               const ASN1_ITEM *it, ASN1_VALUE *req);
+                               const ASN1_ITEM *it, const ASN1_VALUE *req);
 int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx);
-ASN1_VALUE *OSSL_HTTP_REQ_CTX_sendreq_d2i(OSSL_HTTP_REQ_CTX *rctx,
-                                          const ASN1_ITEM *it);
+int OSSL_HTTP_REQ_CTX_nbio_d2i(OSSL_HTTP_REQ_CTX *rctx,
+                               ASN1_VALUE **pval, const ASN1_ITEM *it);
+BIO *OSSL_HTTP_REQ_CTX_exchange(OSSL_HTTP_REQ_CTX *rctx);
 BIO *OSSL_HTTP_REQ_CTX_get0_mem_bio(const OSSL_HTTP_REQ_CTX *rctx);
+size_t OSSL_HTTP_REQ_CTX_get_resp_len(const OSSL_HTTP_REQ_CTX *rctx);
 void OSSL_HTTP_REQ_CTX_set_max_response_length(OSSL_HTTP_REQ_CTX *rctx,
                                                unsigned long len);
+int OSSL_HTTP_is_alive(const OSSL_HTTP_REQ_CTX *rctx);
 
+/* High-level HTTP API */
+typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail);
+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_set_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,
-                   const STACK_OF(CONF_VALUE) *headers,
-                   int maxline, unsigned long max_resp_len, int timeout,
-                   const char *expected_ct, int expect_asn1);
-ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url,
-                               const char *proxy, const char *no_proxy,
-                               BIO *bio, BIO *rbio,
-                               OSSL_HTTP_bio_cb_t bio_update_fn, void *arg,
-                               const STACK_OF(CONF_VALUE) *headers,
-                               int maxline, unsigned long max_resp_len,
-                               int timeout, const char *expected_ct,
-                               const ASN1_ITEM *rsp_it);
-ASN1_VALUE *OSSL_HTTP_post_asn1(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,
-                                const STACK_OF(CONF_VALUE) *headers,
-                                const char *content_type,
-                                const ASN1_VALUE *req, const ASN1_ITEM *req_it,
-                                int maxline, unsigned long max_resp_len,
-                                int timeout, const char *expected_ct,
-                                const ASN1_ITEM *rsp_it);
-BIO *OSSL_HTTP_transfer(const char *server, const char *port, const char *path,
-                        int use_ssl, const char *proxy, const char *no_proxy,
+                   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,
-                        const STACK_OF(CONF_VALUE) *headers,
-                        const char *content_type, BIO *req_mem,
-                        int maxline, unsigned long max_resp_len, int timeout,
-                        const char *expected_ct, int expect_asn1,
-                        char **redirection_url);
-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 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);
 
+/* Auxiliary functions */
 int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost,
                    char **pport, int *pport_num,
                    char **ppath, char **pquery, char **pfrag);
diff --git a/include/openssl/httperr.h b/include/openssl/httperr.h
index af5717d3dc..b639ef0051 100644
--- a/include/openssl/httperr.h
+++ b/include/openssl/httperr.h
@@ -29,6 +29,7 @@
 # define HTTP_R_ERROR_RECEIVING                           103
 # define HTTP_R_ERROR_SENDING                             102
 # define HTTP_R_FAILED_READING_DATA                       128
+# define HTTP_R_HEADER_PARSE_ERROR                        126
 # define HTTP_R_INCONSISTENT_CONTENT_LENGTH               120
 # define HTTP_R_INVALID_PORT_NUMBER                       123
 # define HTTP_R_INVALID_URL_PATH                          125
@@ -43,6 +44,7 @@
 # define HTTP_R_REDIRECTION_NOT_ENABLED                   116
 # define HTTP_R_RESPONSE_LINE_TOO_LONG                    113
 # define HTTP_R_RESPONSE_PARSE_ERROR                      104
+# define HTTP_R_SERVER_CANCELED_CONNECTION                127
 # define HTTP_R_SOCK_NOT_SUPPORTED                        122
 # define HTTP_R_STATUS_CODE_UNSUPPORTED                   114
 # define HTTP_R_TLS_NOT_ENABLED                           107
diff --git a/include/openssl/ocsp.h.in b/include/openssl/ocsp.h.in
index 83c8a175fe..869c3ad415 100644
--- a/include/openssl/ocsp.h.in
+++ b/include/openssl/ocsp.h.in
@@ -170,34 +170,30 @@ typedef struct ocsp_service_locator_st OCSP_SERVICELOC;
 
 DECLARE_ASN1_DUP_FUNCTION(OCSP_CERTID)
 
-OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req);
 OSSL_HTTP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path,
-                                    const OCSP_REQUEST *req, int maxline);
-int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OSSL_HTTP_REQ_CTX *rctx);
+                                    const OCSP_REQUEST *req, int buf_size);
+OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req);
 
 #  ifndef OPENSSL_NO_DEPRECATED_3_0
 typedef OSSL_HTTP_REQ_CTX OCSP_REQ_CTX;
-#   define OCSP_REQ_CTX_new(io, maxline) \
-        OSSL_HTTP_REQ_CTX_new(io, io, maxline, 0, 0, NULL, 1)
-#   define OCSP_REQ_CTX_free(r) \
-        OSSL_HTTP_REQ_CTX_free(r)
+#   define OCSP_REQ_CTX_new(io, buf_size) \
+        OSSL_HTTP_REQ_CTX_new(io, io, buf_size)
+#   define OCSP_REQ_CTX_free OSSL_HTTP_REQ_CTX_free
 #   define OCSP_REQ_CTX_http(rctx, op, path) \
         OSSL_HTTP_REQ_CTX_set_request_line(rctx, strcmp(op, "POST") == 0, \
                                            NULL, NULL, path)
-#   define OCSP_REQ_CTX_add1_header(r, n, v) \
-        OSSL_HTTP_REQ_CTX_add1_header(r, n, v)
+#   define OCSP_REQ_CTX_add1_header OSSL_HTTP_REQ_CTX_add1_header
 #   define OCSP_REQ_CTX_i2d(r, it, req) \
         OSSL_HTTP_REQ_CTX_set1_req(r, "application/ocsp-request", it, req)
 #   define OCSP_REQ_CTX_set1_req(r, req) \
         OCSP_REQ_CTX_i2d(r, ASN1_ITEM_rptr(OCSP_REQUEST), (ASN1_VALUE *)(req))
-#   define OCSP_REQ_CTX_nbio(r) \
-        OSSL_HTTP_REQ_CTX_nbio(r)
-#   define OCSP_REQ_CTX_nbio_d2i(r, p, i)        \
-        ((*(p) = OSSL_HTTP_REQ_CTX_sendreq_d2i(r, i)) != NULL)
-#   define OCSP_REQ_CTX_get0_mem_bio(r) \
-        OSSL_HTTP_REQ_CTX_get0_mem_bio(r)
-#   define OCSP_set_max_response_length(r, l) \
-        OSSL_HTTP_REQ_CTX_set_max_response_length(r, l)
+#   define OCSP_REQ_CTX_nbio OSSL_HTTP_REQ_CTX_nbio
+#   define OCSP_REQ_CTX_nbio_d2i OSSL_HTTP_REQ_CTX_nbio_d2i
+#   define OCSP_sendreq_nbio(r, p) \
+        OSSL_HTTP_REQ_CTX_nbio_d2i(r, (ASN1_VALUE **)(p), \
+                                   ASN1_ITEM_rptr(OCSP_RESPONSE))
+#   define OCSP_REQ_CTX_get0_mem_bio OSSL_HTTP_REQ_CTX_get0_mem_bio
+#   define OCSP_set_max_response_length OSSL_HTTP_REQ_CTX_set_max_response_length
 #  endif
 
 OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject,
diff --git a/test/cmp_ctx_test.c b/test/cmp_ctx_test.c
index 2ca2c26dd5..e25aa9ab43 100644
--- a/test/cmp_ctx_test.c
+++ b/test/cmp_ctx_test.c
@@ -717,9 +717,8 @@ void cleanup_tests(void)
     return;
 }
 
-DEFINE_SET_GET_ARG_FN(set, get, option, 16, int)
-/* option == OSSL_CMP_OPT_IGNORE_KEYUSAGE */
-DEFINE_SET_GET_BASE_TEST(OSSL_CMP_CTX, set, get, 0, option_16, int, -1, IS_0, \
+DEFINE_SET_GET_ARG_FN(set, get, option, 35, int) /* OPT_IGNORE_KEYUSAGE */
+DEFINE_SET_GET_BASE_TEST(OSSL_CMP_CTX, set, get, 0, option_35, int, -1, IS_0, \
                          1 /* true */, DROP)
 
 DEFINE_SET_CB_TEST(log_cb)
@@ -792,7 +791,7 @@ int setup_tests(void)
     ADD_TEST(test_CTX_reinit);
 
     /* various CMP options: */
-    ADD_TEST(test_CTX_set_get_option_16);
+    ADD_TEST(test_CTX_set_get_option_35);
     /* CMP-specific callback for logging and outputting the error queue: */
     ADD_TEST(test_CTX_set_get_log_cb);
     /*
diff --git a/test/http_test.c b/test/http_test.c
index 0a3389c15f..e4209a37c0 100644
--- a/test/http_test.c
+++ b/test/http_test.c
@@ -99,36 +99,38 @@ static int test_http_x509(int do_get)
     X509 *rcert = NULL;
     BIO *wbio = BIO_new(BIO_s_mem());
     BIO *rbio = BIO_new(BIO_s_mem());
+    BIO *rsp, *req = ASN1_item_i2d_mem_bio(x509_it, (ASN1_VALUE *)x509);
     STACK_OF(CONF_VALUE) *headers = NULL;
+    const char content_type[] = "application/x-x509-ca-cert";
     int res = 0;
 
-    if (wbio == NULL || rbio == NULL)
+    if (wbio == NULL || rbio == NULL || req == NULL)
         goto err;
     BIO_set_callback_ex(wbio, http_bio_cb_ex);
     BIO_set_callback_arg(wbio, (char *)rbio);
 
     rpath = RPATH;
-    rcert = (X509 *)
-        (do_get ?
-         OSSL_HTTP_get_asn1("http://"SERVER":"PORT"/"RPATH,
-                            NULL /* proxy */, NULL /* no_proxy */,
-                            wbio, rbio, NULL /* bio_update_fn */, NULL,
-                            headers, 0 /* maxline */,
-                            0 /* max_resp_len */, 0 /* timeout */,
-                            "application/x-x509-ca-cert", x509_it)
-         :
-         OSSL_HTTP_post_asn1(SERVER, PORT, RPATH, 0 /* use_ssl */,
-                             NULL /* proxy */, NULL /* no_proxy */,
-                             wbio, rbio, NULL /* bio_update_fn */, NULL,
-                             headers, "application/x-x509-ca-cert",
-                             (ASN1_VALUE *)x509, x509_it, 0 /* maxline */,
-                             0 /* max_resp_len */, 0 /* timeout */,
-                             "application/x-x509-ca-cert", x509_it)
-         );
+    rsp = do_get ?
+        OSSL_HTTP_get("http://"SERVER":"PORT"/"RPATH,
+                      NULL /* proxy */, NULL /* no_proxy */,
+                      wbio, rbio, NULL /* bio_fn */, NULL /* arg */,
+                      0 /* buf_size */, headers, content_type,
+                      1 /* expect_asn1 */,
+                      HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */)
+        : OSSL_HTTP_transfer(NULL, NULL /* host */, NULL /* port */, RPATH,
+                             0 /* use_ssl */,NULL /* proxy */, NULL /* no_pr */,
+                             wbio, rbio, NULL /* bio_fn */, NULL /* arg */,
+                             0 /* buf_size */, headers, content_type,
+                             req, content_type, 1 /* expect_asn1 */,
+                             HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */,
+                             0 /* keep_alive */);
+    rcert = d2i_X509_bio(rsp, NULL);
+    BIO_free(rsp);
     res = TEST_ptr(rcert) && TEST_int_eq(X509_cmp(x509, rcert), 0);
 
  err:
     X509_free(rcert);
+    BIO_free(req);
     BIO_free(wbio);
     BIO_free(rbio);
     sk_CONF_VALUE_pop_free(headers, X509V3_conf_free);
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 857ed43a52..69b8f63e32 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -614,7 +614,7 @@ UI_get0_result_string                   629	3_0_0	EXIST::FUNCTION:
 TS_RESP_CTX_add_policy                  630	3_0_0	EXIST::FUNCTION:TS
 X509_REQ_dup                            631	3_0_0	EXIST::FUNCTION:
 d2i_DSA_PUBKEY_fp                       633	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,DSA,STDIO
-OSSL_HTTP_REQ_CTX_sendreq_d2i           634	3_0_0	EXIST::FUNCTION:
+OSSL_HTTP_REQ_CTX_exchange              634	3_0_0	EXIST::FUNCTION:
 d2i_X509_REQ_fp                         635	3_0_0	EXIST::FUNCTION:STDIO
 DH_OpenSSL                              636	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,DH
 BN_get_rfc3526_prime_8192               637	3_0_0	EXIST::FUNCTION:
@@ -3612,7 +3612,7 @@ EVP_CIPHER_CTX_encrypting               3694	3_0_0	EXIST::FUNCTION:
 EC_KEY_can_sign                         3695	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,EC
 PEM_write_bio_RSAPublicKey              3696	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0
 X509_CRL_set1_lastUpdate                3697	3_0_0	EXIST::FUNCTION:
-OCSP_sendreq_nbio                       3698	3_0_0	EXIST::FUNCTION:OCSP
+OSSL_HTTP_REQ_CTX_nbio_d2i              3698	3_0_0	EXIST::FUNCTION:
 PKCS8_encrypt                           3699	3_0_0	EXIST::FUNCTION:
 i2d_PKCS7_fp                            3700	3_0_0	EXIST::FUNCTION:STDIO
 i2d_X509_REQ                            3701	3_0_0	EXIST::FUNCTION:
@@ -4882,11 +4882,17 @@ BIO_socket_wait                         ?	3_0_0	EXIST::FUNCTION:SOCK
 BIO_wait                                ?	3_0_0	EXIST::FUNCTION:
 BIO_do_connect_retry                    ?	3_0_0	EXIST::FUNCTION:
 OSSL_parse_url                          ?	3_0_0	EXIST::FUNCTION:
+OSSL_HTTP_REQ_CTX_get_resp_len          ?	3_0_0	EXIST::FUNCTION:
+OSSL_HTTP_REQ_CTX_set_expected          ?	3_0_0	EXIST::FUNCTION:
+OSSL_HTTP_is_alive                      ?	3_0_0	EXIST::FUNCTION:
+OSSL_HTTP_open                          ?	3_0_0	EXIST::FUNCTION:
+OSSL_HTTP_proxy_connect                 ?	3_0_0	EXIST::FUNCTION:
+OSSL_HTTP_set_request                   ?	3_0_0	EXIST::FUNCTION:
+OSSL_HTTP_exchange                      ?	3_0_0	EXIST::FUNCTION:
 OSSL_HTTP_get                           ?	3_0_0	EXIST::FUNCTION:
-OSSL_HTTP_get_asn1                      ?	3_0_0	EXIST::FUNCTION:
-OSSL_HTTP_post_asn1                     ?	3_0_0	EXIST::FUNCTION:
 OSSL_HTTP_transfer                      ?	3_0_0	EXIST::FUNCTION:
-OSSL_HTTP_proxy_connect                 ?	3_0_0	EXIST::FUNCTION:
+OSSL_HTTP_close                         ?	3_0_0	EXIST::FUNCTION:
+ASN1_item_i2d_mem_bio                   ?	3_0_0	EXIST::FUNCTION:
 ERR_add_error_txt                       ?	3_0_0	EXIST::FUNCTION:
 ERR_add_error_mem_bio                   ?	3_0_0	EXIST::FUNCTION:
 X509_STORE_CTX_print_verify_cb          ?	3_0_0	EXIST::FUNCTION:
diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt
index cb5a9eaa6f..9eefc090f8 100644
--- a/util/missingcrypto.txt
+++ b/util/missingcrypto.txt
@@ -781,7 +781,6 @@ OCSP_REQ_CTX_get0_mem_bio(3)
 OCSP_REQ_CTX_http(3)
 OCSP_REQ_CTX_new(3)
 OCSP_REQ_CTX_nbio(3)
-OCSP_REQ_CTX_nbio_d2i(3)
 OCSP_REQUEST_add1_ext_i2d(3)
 OCSP_REQUEST_add_ext(3)
 OCSP_REQUEST_delete_ext(3)
diff --git a/util/other.syms b/util/other.syms
index 0047905209..f8fb0d52e2 100644
--- a/util/other.syms
+++ b/util/other.syms
@@ -339,8 +339,9 @@ OCSP_REQ_CTX                            datatype deprecated 3.0.0
 OCSP_REQ_CTX_add1_header                define deprecated 3.0.0
 OCSP_REQ_CTX_free                       define deprecated 3.0.0
 OCSP_REQ_CTX_i2d                        define deprecated 3.0.0
-OCSP_set_max_response_length            define deprecated 3.0.0
 OCSP_REQ_CTX_set1_req                   define deprecated 3.0.0
+OCSP_sendreq_nbio                       define deprecated 3.0.0
+OCSP_set_max_response_length            define deprecated 3.0.0
 OPENSSL_FILE                            define
 OPENSSL_FUNC                            define
 OPENSSL_LINE                            define


More information about the openssl-commits mailing list