]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
HTTP client API: Generalize to arbitrary request and response contents
authorDr. David von Oheimb <David.von.Oheimb@siemens.com>
Mon, 3 May 2021 14:33:10 +0000 (16:33 +0200)
committerDr. David von Oheimb <dev@ddvo.net>
Fri, 14 May 2021 17:24:42 +0000 (19:24 +0200)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/15053)

NEWS.md
apps/include/apps.h
apps/lib/apps.c
apps/ocsp.c
crypto/http/http_client.c
doc/man3/OSSL_HTTP_REQ_CTX.pod
doc/man3/OSSL_HTTP_transfer.pod
include/openssl/http.h

diff --git a/NEWS.md b/NEWS.md
index 3193ce614984af5c1a641e0abac1d34b85a5b518..78d0772b9aec83357edefcb72d8e66006127330b 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -53,8 +53,10 @@ OpenSSL 3.0
     also covering CRMF (RFC 4211) and HTTP transfer (RFC 6712).
     It is part of the crypto lib and adds a 'cmp' app with a demo configuration.
     All widely used CMP features are supported for both clients and servers.
-  * Added a proper HTTP(S) client to libcrypto supporting GET and POST,
-    redirection, plain and ASN.1-encoded contents, proxies, and timeouts.
+  * Added a proper HTTP client supporting GET with optional redirection, POST,
+    arbitrary request and response content types, TLS, persistent connections,
+    connections via HTTP(s) proxies, connections and exchange via user-defined
+    BIOs (allowing implicit connections), and timeout checks.
   * Added util/check-format.pl for checking adherence to the coding guidelines.
   * Added OSSL_ENCODER, a generic encoder API.
   * Added OSSL_PARAM_BLD, an easier to use API to OSSL_PARAM.
index 207ed41bc7ab32e995cfb18c41b0e85f76751b90..41178a6e226bcb083c45c9cc53498167247a1f07 100644 (file)
@@ -285,6 +285,7 @@ ASN1_VALUE *app_http_post_asn1(const char *host, const char *port,
                                const STACK_OF(CONF_VALUE) *headers,
                                const char *content_type,
                                ASN1_VALUE *req, const ASN1_ITEM *req_it,
+                               const char *expected_content_type,
                                long timeout, const ASN1_ITEM *rsp_it);
 # endif
 
index dafcf419bf51f876a3b22c77ad86e225f4a015d8..d32f6c54909b764e72e964cb091059a698f3a3e6 100644 (file)
@@ -2521,6 +2521,7 @@ ASN1_VALUE *app_http_post_asn1(const char *host, const char *port,
                                const STACK_OF(CONF_VALUE) *headers,
                                const char *content_type,
                                ASN1_VALUE *req, const ASN1_ITEM *req_it,
+                               const char *expected_content_type,
                                long timeout, const ASN1_ITEM *rsp_it)
 {
     APP_HTTP_TLS_INFO info;
@@ -2538,7 +2539,7 @@ ASN1_VALUE *app_http_post_asn1(const char *host, const char *port,
                              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 */,
+                             expected_content_type, 1 /* expect_asn1 */,
                              HTTP_DEFAULT_MAX_RESP_LEN, timeout,
                              0 /* keep_alive */);
     BIO_free(req_mem);
index 694855fe0992efd52f1c84639e824ad87d22eebb..dd816c42214a7c63ae2078015788434294f73501 100644 (file)
@@ -1214,6 +1214,7 @@ OCSP_RESPONSE *process_responder(OCSP_REQUEST *req,
         app_http_post_asn1(host, port, path, NULL, NULL /* no proxy used */,
                            ctx, headers, "application/ocsp-request",
                            (ASN1_VALUE *)req, ASN1_ITEM_rptr(OCSP_REQUEST),
+                           "application/ocsp-response",
                            req_timeout, ASN1_ITEM_rptr(OCSP_RESPONSE));
 
     if (resp == NULL)
index ee97f64ef6797b497035c28adfdda8217c2fc1fa..077159cab6b43fa92f586256ebbf34a8e4552b0a 100644 (file)
@@ -796,6 +796,7 @@ static BIO *HTTP_new_bio(const char *server /* optionally includes ":port" */,
 }
 #endif /* OPENSSL_NO_SOCK */
 
+/* Exchange request and response via HTTP on (non-)blocking BIO */
 BIO *OSSL_HTTP_REQ_CTX_exchange(OSSL_HTTP_REQ_CTX *rctx)
 {
     int rv;
@@ -856,6 +857,10 @@ OSSL_HTTP_REQ_CTX *OSSL_HTTP_open(const char *server, const char *port,
 
     if (bio != NULL) {
         cbio = bio;
+        if (proxy != NULL || no_proxy != NULL) {
+            ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_INVALID_ARGUMENT);
+            return NULL;
+        }
     } else {
 #ifndef OPENSSL_NO_SOCK
         char *proxy_host = NULL, *proxy_port = NULL;
@@ -1064,7 +1069,7 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
                                        NULL /* content_type */,
                                        NULL /* req_mem */,
                                        expected_ct, expect_asn1,
-                                       -1 /* use same max time */,
+                                       -1 /* use same max time (timeout) */,
                                        0 /* no keep_alive */))
                 OSSL_HTTP_REQ_CTX_free(rctx);
             else
@@ -1100,6 +1105,7 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
     return resp;
 }
 
+/* Exchange request and response over a connection managed via |prctx| */
 BIO *OSSL_HTTP_transfer(OSSL_HTTP_REQ_CTX **prctx,
                         const char *server, const char *port,
                         const char *path, int use_ssl,
@@ -1122,11 +1128,14 @@ BIO *OSSL_HTTP_transfer(OSSL_HTTP_REQ_CTX **prctx,
     }
     if (rctx != NULL) {
         if (OSSL_HTTP_set_request(rctx, path, headers, content_type, req,
-                                  expected_ct, expect_asn1, timeout, keep_alive))
+                                  expected_ct, expect_asn1,
+                                  timeout, keep_alive))
             resp = OSSL_HTTP_exchange(rctx, NULL);
         if (resp == NULL || !OSSL_HTTP_is_alive(rctx)) {
-            if (!OSSL_HTTP_close(rctx, resp != NULL))
+            if (!OSSL_HTTP_close(rctx, resp != NULL)) {
+                BIO_free(resp);
                 resp = NULL;
+            }
             rctx = NULL;
         }
     }
index 52e5d441f369eeeae63971bf7684b4a53179fe3b..f5f70584df6766aa12a8761e98709448d5f307f8 100644 (file)
@@ -123,6 +123,7 @@ and to gather the response via HTTP, using the I<wbio> and I<rbio>
 that were given when calling OSSL_HTTP_REQ_CTX_new().
 If successful, the contents of the internal memory B<BIO> contains
 the contents of the HTTP response, without the response headers.
+This does not support streaming.
 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.
@@ -221,7 +222,7 @@ 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_exchange() and OSSL_HTTP_REQ_CTX_get0_mem_bio()
-returns a pointer to a B<BIO> on success and NULL on failure.
+return 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.
index 9745932e3765afd3671758be1cff7c1443975fd7..71294913e5d1d39d3ff5e8a1e4a64ca3f5ab2a91 100644 (file)
@@ -30,7 +30,7 @@ OSSL_HTTP_close
                            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 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,
@@ -52,8 +52,8 @@ OSSL_HTTP_close
 
 =head1 DESCRIPTION
 
-OSSL_HTTP_open() initiates an HTTP session using I<bio> if not NULL, else
-by connecting to a given I<server> optionally via a I<proxy>.
+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>.
 
 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
@@ -148,12 +148,12 @@ 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_mem> is NULL the HTTP GET method will be used to send the request
-else HTTP POST with the contents of I<req_mem> and optional I<content_type>.
+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
+I<req> must contain data with determined length; streaming is not supported.
 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
@@ -172,15 +172,15 @@ 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_transfer() exchanges any form of HTTP request and response
+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 caller is responsible for deallocating this URL with
-L<OPENSSL_free(3)>.
+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
@@ -203,6 +203,17 @@ 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().
@@ -222,9 +233,8 @@ 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.
+return a memory BIO containing the data received.
+This must be freed by the caller.
 On failure, they return NULL.
 Failure conditions include connection/transfer timeout, parse errors, etc.
 
index e3bd9d75792ebc181c994bd2ddfecb699b972447..7552b2f42e8ccae36f595721833b7b8537ed38cf 100644 (file)
@@ -72,7 +72,7 @@ int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port,
                             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_mem,
+                          const char *content_type, BIO *req,
                           const char *expected_content_type, int expect_asn1,
                           int timeout, int keep_alive);
 BIO *OSSL_HTTP_exchange(OSSL_HTTP_REQ_CTX *rctx, char **redirection_url);