MINOR: httpclient: request streaming with a callback

This patch add a way to handle HTTP requests streaming using a
callback.

The end of the data must be specified by using the "end" parameter in
httpclient_req_xfer().
This commit is contained in:
William Lallemand 2021-10-28 15:34:26 +02:00
parent 04065b87ce
commit 0da616ee18
3 changed files with 76 additions and 5 deletions

View File

@ -7,16 +7,19 @@ struct httpclient {
struct {
struct ist url; /* URL of the request */
enum http_meth_t meth; /* method of the request */
struct buffer buf; /* output buffer */
struct buffer buf; /* output buffer, HTX */
} req;
struct {
struct ist vsn;
uint16_t status;
struct ist reason;
struct http_hdr *hdrs; /* headers */
struct buffer buf; /* input buffer */
struct buffer buf; /* input buffer, raw HTTP */
} res;
struct {
/* callbacks used to send the request, */
void (*req_payload)(struct httpclient *hc); /* send a payload */
/* callbacks used to receive the response, if not set, the IO
* handler will consume the data without doing anything */
void (*res_stline)(struct httpclient *hc); /* start line received */
@ -41,6 +44,7 @@ struct httpclient {
/* States of the HTTP Client Appctx */
enum {
HTTPCLIENT_S_REQ = 0,
HTTPCLIENT_S_REQ_BODY,
HTTPCLIENT_S_RES_STLINE,
HTTPCLIENT_S_RES_HDR,
HTTPCLIENT_S_RES_BODY,

View File

@ -10,7 +10,7 @@ struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct is
struct appctx *httpclient_start(struct httpclient *hc);
int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst);
int httpclient_req_gen(struct httpclient *hc, const struct ist url, enum http_meth_t meth, const struct http_hdr *hdrs, const struct ist payload);
int httpclient_req_xfer(struct httpclient *hc, struct ist src, int end);
/* Return the amount of data available in the httpclient response buffer */
static inline int httpclient_data(struct httpclient *hc)

View File

@ -19,6 +19,7 @@
#include <haproxy/cfgparse.h>
#include <haproxy/connection.h>
#include <haproxy/global.h>
#include <haproxy/istbuf.h>
#include <haproxy/h1_htx.h>
#include <haproxy/http.h>
#include <haproxy/http_client.h>
@ -303,7 +304,10 @@ int httpclient_req_gen(struct httpclient *hc, const struct ist url, enum http_me
goto error;
}
htx->flags |= HTX_FL_EOM;
/* If req.payload was set, does not set the end of stream which *MUST*
* be set in the callback */
if (!hc->ops.req_payload)
htx->flags |= HTX_FL_EOM;
htx_to_buf(htx, &hc->req.buf);
@ -330,6 +334,44 @@ int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
return ret;
}
/*
* Transfer raw HTTP payload from src, and insert it into HTX format in the
* httpclient.
*
* Must be used to transfer the request body.
* Then wakeup the httpclient so it can transfer it.
*
* <end> tries to add the ending data flag if it succeed to copy all data.
*
* Return the number of bytes copied from src.
*/
int httpclient_req_xfer(struct httpclient *hc, struct ist src, int end)
{
int ret = 0;
struct htx *htx;
htx = htx_from_buf(&hc->req.buf);
if (!htx)
goto error;
if (hc->appctx)
appctx_wakeup(hc->appctx);
ret += htx_add_data(htx, src);
/* if we copied all the data and the end flag is set */
if ((istlen(src) == ret) && end) {
htx->flags |= HTX_FL_EOM;
}
htx_to_buf(htx, &hc->req.buf);
error:
return ret;
}
/*
* Start the HTTP client
* Create the appctx, session, stream and wakeup the applet
@ -532,9 +574,34 @@ static void httpclient_applet_io_handler(struct appctx *appctx)
* just push this entirely */
b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
channel_add_input(req, b_data(&req->buf));
appctx->st0 = HTTPCLIENT_S_RES_STLINE;
appctx->st0 = HTTPCLIENT_S_REQ_BODY;
goto more; /* we need to leave the IO handler once we wrote the request */
break;
case HTTPCLIENT_S_REQ_BODY:
/* call the payload callback */
{
if (hc->ops.req_payload) {
int ret;
ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
if (ret)
channel_add_input(req, b_data(&req->buf));
/* call the request callback */
hc->ops.req_payload(hc);
}
htx = htxbuf(&req->buf);
if (!htx)
goto more;
/* if the request contains the HTX_FL_EOM, we finished the request part. */
if (htx->flags & HTX_FL_EOM)
appctx->st0 = HTTPCLIENT_S_RES_STLINE;
goto more; /* we need to leave the IO handler once we wrote the request */
}
break;
case HTTPCLIENT_S_RES_STLINE:
/* copy the start line in the hc structure,then remove the htx block */