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().
diff --git a/include/haproxy/http_client-t.h b/include/haproxy/http_client-t.h
index fa0f8fb..6e820c4 100644
--- a/include/haproxy/http_client-t.h
+++ b/include/haproxy/http_client-t.h
@@ -7,16 +7,19 @@
 	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 @@
 /* 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,
diff --git a/include/haproxy/http_client.h b/include/haproxy/http_client.h
index 13673e8..1d99aa7 100644
--- a/include/haproxy/http_client.h
+++ b/include/haproxy/http_client.h
@@ -10,7 +10,7 @@
 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)
diff --git a/src/http_client.c b/src/http_client.c
index 7756e82..a07cf9e 100644
--- a/src/http_client.c
+++ b/src/http_client.c
@@ -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 @@
 			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);
 
@@ -331,6 +335,44 @@
 }
 
 /*
+ * 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 @@
 				 * 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 */