]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: httpclient: request streaming with a callback
authorWilliam Lallemand <wlallemand@haproxy.org>
Thu, 28 Oct 2021 13:34:26 +0000 (15:34 +0200)
committerWilliam Lallemand <wlallemand@haproxy.org>
Thu, 28 Oct 2021 14:24:14 +0000 (16:24 +0200)
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().

include/haproxy/http_client-t.h
include/haproxy/http_client.h
src/http_client.c

index fa0f8fbdd422aed9e7d0f3d0d1e4794cf2e99c65..6e820c42b1997c6cbe521f0e4cfc7bd615693fb4 100644 (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,
index 13673e801bb6536fe2ebde70eb93587473a83556..1d99aa7d17ed0a418c5ba6f1180958b7ed66ab2b 100644 (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)
index 7756e821176cb1f7080f9f424cd50fb2b02ad2c7..a07cf9e26f4417e691dee0fb48c6500395a47d32 100644 (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 */