diff options
author | Dr. David von Oheimb <David.von.Oheimb@siemens.com> | 2021-05-04 11:15:36 +0200 |
---|---|---|
committer | Dr. David von Oheimb <dev@ddvo.net> | 2021-05-14 19:24:42 +0200 |
commit | 8b5ca5111ed9d7907e2de91a5af5b5407a46eaf1 (patch) | |
tree | 7942ee91430b799e4d1e4bb6ce7eab3838ff45c6 /crypto/http/http_client.c | |
parent | 829902879eb7ba1260a9444f6b6b91d84ca61037 (diff) |
HTTP client: Allow streaming of request data (for POST method)
Also clean up OSSL_HTTP_REQ_CTX_nbio() states and make it more efficient.
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/15053)
Diffstat (limited to 'crypto/http/http_client.c')
-rw-r--r-- | crypto/http/http_client.c | 111 |
1 files changed, 63 insertions, 48 deletions
diff --git a/crypto/http/http_client.c b/crypto/http/http_client.c index 077159cab6..944e432252 100644 --- a/crypto/http/http_client.c +++ b/crypto/http/http_client.c @@ -44,7 +44,7 @@ struct ossl_http_req_ctx_st { int state; /* Current I/O state */ - unsigned char *readbuf; /* Buffer for reading response by line */ + unsigned char *readbuf; /* Buffer for reading request or response */ int readbuflen; /* Buffer length, equals buf_size */ int free_wbio; /* wbio allocated internally, free with ctx */ BIO *wbio; /* BIO to write/send request to */ @@ -55,11 +55,13 @@ struct ossl_http_req_ctx_st { char *proxy; /* Optional proxy name or URI */ char *server; /* Optional server host name */ char *port; /* Optional server port */ - BIO *mem; /* Memory BIO holding request and response */ + BIO *mem; /* Memory BIO holding request/response header */ + BIO *req; /* BIO holding the request provided by caller */ int method_POST; /* HTTP method is POST (else GET) */ char *expected_ct; /* Optional expected Content-Type */ int expect_asn1; /* Response must be ASN.1-encoded */ - long len_to_send; /* Number of bytes in request still to send */ + unsigned char *pos; /* Current position sending data */ + long len_to_send; /* Number of bytes still to send */ unsigned long resp_len; /* Length of response */ size_t max_resp_len; /* Maximum length of response */ int keep_alive; /* Persistent conn. 0=no, 1=prefer, 2=require */ @@ -72,16 +74,17 @@ struct ossl_http_req_ctx_st { #define OHS_NOREAD 0x1000 /* If set no reading should be performed */ #define OHS_ERROR (0 | OHS_NOREAD) /* Error condition */ -#define OHS_FIRSTLINE 1 /* First line being read */ -#define OHS_REDIRECT 0xa /* Looking for redirection location */ -#define OHS_HEADERS 2 /* MIME headers being read */ -#define OHS_ASN1_HEADER 3 /* HTTP initial header (tag+length) being read */ -#define OHS_CONTENT 4 /* HTTP content octets being read */ -#define OHS_WRITE_INIT (5 | OHS_NOREAD) /* 1st call: ready to start send */ -#define OHS_WRITE (6 | OHS_NOREAD) /* Request being sent */ -#define OHS_FLUSH (7 | OHS_NOREAD) /* Request being flushed */ -#define OHS_DONE (8 | OHS_NOREAD) /* Completed */ -#define OHS_HTTP_HEADER (9 | OHS_NOREAD) /* Headers set, w/o final \r\n */ +#define OHS_FIRSTLINE 1 /* First line of response being read */ +#define OHS_HEADERS 2 /* MIME headers of response being read */ +#define OHS_REDIRECT 3 /* MIME headers being read, expecting Location */ +#define OHS_ASN1_HEADER 4 /* HTTP initial header (tag+length) being read */ +#define OHS_CONTENT 5 /* HTTP content octets being read */ +#define OHS_ADD_HEADERS (1 | OHS_NOREAD) /* Adding header lines to request */ +#define OHS_WRITE_INIT (2 | OHS_NOREAD) /* 1st call: ready to start send */ +#define OHS_WRITE_HDR (3 | OHS_NOREAD) /* Request header being sent */ +#define OHS_WRITE_REQ (4 | OHS_NOREAD) /* Request contents being sent */ +#define OHS_FLUSH (5 | OHS_NOREAD) /* Request being flushed */ +#define OHS_DONE (6 | OHS_NOREAD) /* Completed */ /* Low-level HTTP API implementation */ @@ -199,7 +202,7 @@ int OSSL_HTTP_REQ_CTX_set_request_line(OSSL_HTTP_REQ_CTX *rctx, int method_POST, if (BIO_printf(rctx->mem, "%s "HTTP_1_0"\r\n", path) <= 0) return 0; rctx->resp_len = 0; - rctx->state = OHS_HTTP_HEADER; + rctx->state = OHS_ADD_HEADERS; return 1; } @@ -235,7 +238,7 @@ int OSSL_HTTP_REQ_CTX_set_expected(OSSL_HTTP_REQ_CTX *rctx, return 0; } if (keep_alive != 0 - && rctx->state != OHS_ERROR && rctx->state != OHS_HTTP_HEADER) { + && rctx->state != OHS_ERROR && rctx->state != OHS_ADD_HEADERS) { /* Cannot anymore set keep-alive in request header */ ERR_raise(ERR_LIB_HTTP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return 0; @@ -257,12 +260,11 @@ int OSSL_HTTP_REQ_CTX_set_expected(OSSL_HTTP_REQ_CTX *rctx, } static int ossl_http_req_ctx_set_content(OSSL_HTTP_REQ_CTX *rctx, - const char *content_type, BIO *req_mem) + const char *content_type, BIO *req) { - const unsigned char *req; long req_len; - if (rctx == NULL || (req_mem == NULL && content_type != NULL)) { + if (rctx == NULL || (req == NULL && content_type != NULL)) { ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER); return 0; } @@ -270,9 +272,8 @@ static int ossl_http_req_ctx_set_content(OSSL_HTTP_REQ_CTX *rctx, if (rctx->keep_alive != 0 && !OSSL_HTTP_REQ_CTX_add1_header(rctx, "Connection", "keep-alive")) return 0; - rctx->state = OHS_WRITE_INIT; - if (req_mem == NULL) + if (req == NULL) return 1; if (!rctx->method_POST) { ERR_raise(ERR_LIB_HTTP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); @@ -283,11 +284,10 @@ static int ossl_http_req_ctx_set_content(OSSL_HTTP_REQ_CTX *rctx, && BIO_printf(rctx->mem, "Content-Type: %s\r\n", content_type) <= 0) return 0; - if ((req_len = BIO_get_mem_data(req_mem, &req)) <= 0) - return 0; - - return BIO_printf(rctx->mem, "Content-Length: %ld\r\n\r\n", req_len) > 0 - && BIO_write(rctx->mem, req, req_len) == (int)req_len; + rctx->req = req; + if ((req_len = BIO_ctrl(req, BIO_CTRL_INFO, 0, NULL)) <= 0) + return 1; /* streaming BIO may not support querying size */ + return BIO_printf(rctx->mem, "Content-Length: %ld\r\n", req_len) > 0; } int OSSL_HTTP_REQ_CTX_set1_req(OSSL_HTTP_REQ_CTX *rctx, const char *content_type, @@ -495,7 +495,7 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx) } switch (rctx->state) { - case OHS_HTTP_HEADER: + case OHS_ADD_HEADERS: /* Last operation was adding headers: need a final \r\n */ if (BIO_write(rctx->mem, "\r\n", 2) != 2) { rctx->state = OHS_ERROR; @@ -505,30 +505,45 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx) /* fall thru */ case OHS_WRITE_INIT: - rctx->len_to_send = BIO_get_mem_data(rctx->mem, NULL); - rctx->state = OHS_WRITE; + rctx->len_to_send = BIO_get_mem_data(rctx->mem, &rctx->pos); + rctx->state = OHS_WRITE_HDR; /* fall thru */ - case OHS_WRITE: - n = BIO_get_mem_data(rctx->mem, &p) - rctx->len_to_send; - i = BIO_write(rctx->wbio, p + n, rctx->len_to_send); - - if (i <= 0) { - if (BIO_should_retry(rctx->wbio)) - return -1; - rctx->state = OHS_ERROR; - return 0; + case OHS_WRITE_HDR: + /* Copy some chunk of data from rctx->mem to rctx->wbio */ + case OHS_WRITE_REQ: + /* Copy some chunk of data from rctx->req to rctx->wbio */ + + if (rctx->len_to_send > 0) { + i = BIO_write(rctx->wbio, rctx->pos, rctx->len_to_send); + if (i <= 0) { + if (BIO_should_retry(rctx->wbio)) + return -1; + rctx->state = OHS_ERROR; + return 0; + } + rctx->pos += i; + rctx->len_to_send -= i; + goto next_io; } - - rctx->len_to_send -= i; - - if (rctx->len_to_send > 0) + if (rctx->state == OHS_WRITE_HDR) { + (void)BIO_reset(rctx->mem); + rctx->state = OHS_WRITE_REQ; + } + if (rctx->req != NULL && !BIO_eof(rctx->req)) { + n = BIO_read(rctx->req, rctx->readbuf, rctx->readbuflen); + if (n <= 0) { + if (BIO_should_retry(rctx->rbio)) + return -1; + ERR_raise(ERR_LIB_HTTP, HTTP_R_FAILED_READING_DATA); + return 0; + } + rctx->pos = rctx->readbuf; + rctx->len_to_send = n; goto next_io; - + } rctx->state = OHS_FLUSH; - (void)BIO_reset(rctx->mem); - /* fall thru */ case OHS_FLUSH: @@ -925,7 +940,7 @@ OSSL_HTTP_REQ_CTX *OSSL_HTTP_open(const char *server, const char *port, 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) { @@ -941,13 +956,13 @@ int OSSL_HTTP_set_request(OSSL_HTTP_REQ_CTX *rctx, const char *path, return 0; } - return OSSL_HTTP_REQ_CTX_set_request_line(rctx, req_mem != NULL, + return OSSL_HTTP_REQ_CTX_set_request_line(rctx, req != NULL, use_http_proxy ? rctx->server : NULL, rctx->port, path) && OSSL_HTTP_REQ_CTX_add1_headers(rctx, headers, rctx->server) && OSSL_HTTP_REQ_CTX_set_expected(rctx, expected_content_type, expect_asn1, timeout, keep_alive) - && ossl_http_req_ctx_set_content(rctx, content_type, req_mem); + && ossl_http_req_ctx_set_content(rctx, content_type, req); } /*- @@ -1067,7 +1082,7 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy, if (rctx != NULL) { if (!OSSL_HTTP_set_request(rctx, path, headers, NULL /* content_type */, - NULL /* req_mem */, + NULL /* req */, expected_ct, expect_asn1, -1 /* use same max time (timeout) */, 0 /* no keep_alive */)) |