void httpclient_applet_io_handler(struct appctx *appctx)
{
struct httpclient *hc = appctx->svcctx;
- struct stconn *sc = appctx_sc(appctx);
- struct stream *s = __sc_strm(sc);
- struct channel *req = &s->req;
- struct channel *res = &s->res;
+ struct buffer *outbuf, *inbuf;
struct htx_blk *blk = NULL;
struct htx *htx;
struct htx_sl *sl = NULL;
uint32_t hdr_num;
- uint32_t sz;
- int count = 0;
int ret;
- if (unlikely(se_fl_test(appctx->sedesc, (SE_FL_EOS|SE_FL_ERROR)))) {
- if (co_data(res)) {
- htx = htx_from_buf(&res->buf);
- co_htx_skip(res, htx, co_data(res));
- htx_to_buf(htx, &res->buf);
- }
+ if (unlikely(applet_fl_test(appctx, APPCTX_FL_EOS|APPCTX_FL_ERROR))) {
+ applet_reset_input(appctx);
goto out;
}
+
/* The IO handler could be called after the release, so we need to
* check if hc is still there to run the IO handler */
if (!hc)
goto error;
switch(appctx->st0) {
-
case HTTPCLIENT_S_REQ:
+ outbuf = applet_get_outbuf(appctx);
+ if (outbuf == NULL) {
+ applet_have_more_data(appctx);
+ goto out;
+ }
+
/* we know that the buffer is empty here, since
* it's the first call, we can freely copy the
* request from the httpclient buffer */
- ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
+ ret = b_xfer(outbuf, &hc->req.buf, b_data(&hc->req.buf));
if (!ret) {
- sc_need_room(sc, 0);
+ applet_have_more_data(appctx);
goto out;
}
if (!b_data(&hc->req.buf))
b_free(&hc->req.buf);
- htx = htx_from_buf(&req->buf);
- if (!htx) {
- sc_need_room(sc, 0);
+ htx = htxbuf(outbuf);
+ if (htx_is_empty(htx)) {
+ applet_have_more_data(appctx);
goto out;
}
- channel_add_input(req, htx->data);
-
if (htx->flags & HTX_FL_EOM) { /* check if a body need to be added */
appctx->st0 = HTTPCLIENT_S_RES_STLINE;
- se_fl_set(appctx->sedesc, SE_FL_EOI);
+ applet_set_eoi(appctx);
goto out; /* we need to leave the IO handler once we wrote the request */
}
__fallthrough;
case HTTPCLIENT_S_REQ_BODY:
+ outbuf = applet_get_outbuf(appctx);
+ if (outbuf == NULL) {
+ applet_have_more_data(appctx);
+ goto out;
+ }
+
/* call the payload callback */
if (hc->ops.req_payload) {
struct htx *hc_htx;
- if (co_data(res)) {
+ if (applet_input_data(appctx)) {
/* A response was received but we are still process the request.
* It is unexpected and not really supported with the current API.
* So lets drain the request to avoid any issue.
*/
- b_reset(&hc->req.buf);
+ b_reset(outbuf);
hc->flags |= HTTPCLIENT_FA_DRAIN_REQ;
appctx->st0 = HTTPCLIENT_S_RES_STLINE;
break;
if (htx_is_empty(hc_htx))
goto out;
- htx = htx_from_buf(&req->buf);
+ htx = htx_from_buf(outbuf);
if (htx_is_empty(htx)) {
- size_t data = hc_htx->data;
-
/* Here htx_to_buf() will set buffer data to 0 because
* the HTX is empty, and allow us to do an xfer.
*/
htx_to_buf(hc_htx, &hc->req.buf);
- htx_to_buf(htx, &req->buf);
- b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
- channel_add_input(req, data);
+ htx_to_buf(htx, outbuf);
+ b_xfer(outbuf, &hc->req.buf, b_data(&hc->req.buf));
} else {
struct htx_ret ret;
- size_t data = htx->data;
ret = htx_xfer_blks(htx, hc_htx, htx_used_space(hc_htx), HTX_BLK_UNUSED);
if (!ret.ret) {
- sc_need_room(sc, channel_htx_recv_max(req, htx) + 1);
+ applet_have_more_data(appctx);
goto out;
}
- data = htx->data - data;
- channel_add_input(req, data);
/* we must copy the EOM if we empty the buffer */
if (htx_is_empty(hc_htx)) {
htx->flags |= (hc_htx->flags & HTX_FL_EOM);
}
- htx_to_buf(htx, &req->buf);
+ htx_to_buf(htx, outbuf);
htx_to_buf(hc_htx, &hc->req.buf);
}
-
if (!b_data(&hc->req.buf))
b_free(&hc->req.buf);
}
- htx = htxbuf(&req->buf);
+ htx = htxbuf(outbuf);
/* 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;
- se_fl_set(appctx->sedesc, SE_FL_EOI);
- goto out; /* we need to leave the IO handler once we wrote the request */
+ applet_set_eoi(appctx);
+ goto out; /* we need to leave the IO handler once we wrote the request */
}
applet_have_more_data(appctx);
- goto process_data;
+ goto out;
case HTTPCLIENT_S_RES_STLINE:
+ applet_will_consume(appctx);
+ inbuf = applet_get_inbuf(appctx);
+ if (inbuf == NULL || !applet_input_data(appctx)) {
+ applet_need_more_data(appctx);
+ goto out;
+ }
+
/* in HTX mode, don't try to copy the stline
* alone, we must copy the headers with it */
if (hc->options & HTTPCLIENT_O_RES_HTX) {
appctx->st0 = HTTPCLIENT_S_RES_HDR;
- goto out;
+ break;
}
/* copy the start line in the hc structure,then remove the htx block */
- if (!co_data(res))
- goto out;
- htx = htxbuf(&res->buf);
+ htx = htxbuf(inbuf);
if (htx_get_first_type(htx) != HTX_BLK_RES_SL)
goto error;
blk = DISGUISE(htx_get_head_blk(htx));
- sl = htx_get_blk_ptr(htx, blk);
+ sl = htx_get_blk_ptr(htx, blk);
/* Skipp any 1XX interim responses */
if (sl->info.res.status < 200) {
while (blk) {
enum htx_blk_type type = htx_get_blk_type(blk);
- uint32_t sz = htx_get_blksz(blk);
- c_rew(res, sz);
blk = htx_remove_blk(htx, blk);
if (type == HTX_BLK_EOH) {
- htx_to_buf(htx, &res->buf);
+ htx_to_buf(htx, inbuf);
break;
}
}
hc->res.status = sl->info.res.status;
hc->res.vsn = istdup(htx_sl_res_vsn(sl));
hc->res.reason = istdup(htx_sl_res_reason(sl));
- sz = htx_get_blksz(blk);
- c_rew(res, sz);
htx_remove_blk(htx, blk);
+
/* caller callback */
if (hc->ops.res_stline)
hc->ops.res_stline(hc);
else
appctx->st0 = HTTPCLIENT_S_RES_HDR;
- htx_to_buf(htx, &res->buf);
+ applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
+ htx_to_buf(htx, inbuf);
break;
case HTTPCLIENT_S_RES_HDR:
- if (!co_data(res))
- goto out;
- htx = htxbuf(&res->buf);
- if (htx_is_empty(htx))
+ applet_will_consume(appctx);
+ inbuf = applet_get_inbuf(appctx);
+ if (inbuf == NULL || !applet_input_data(appctx)) {
+ applet_need_more_data(appctx);
goto out;
+ }
+
+ htx = htxbuf(inbuf);
+ BUG_ON(htx_is_empty(htx));
if (hc->options & HTTPCLIENT_O_RES_HTX) {
/* HTX mode transfers the header to the hc buffer */
struct htx *hc_htx;
struct htx_ret ret;
- if (!b_alloc(&hc->res.buf, DB_MUX_TX))
+ if (!b_alloc(&hc->res.buf, DB_MUX_TX)) {
+ applet_wont_consume(appctx);
goto out;
+ }
hc_htx = htxbuf(&hc->res.buf);
/* xfer the headers */
- count = htx->data;
ret = htx_xfer_blks(hc_htx, htx, htx_used_space(htx), HTX_BLK_EOH);
- if (ret.ret <= 0)
+ if (!ret.ret) {
+ applet_need_more_data(appctx);
goto out;
- count -= htx->data;
- c_rew(res, count);
+ }
+ else
+ applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
if (htx->flags & HTX_FL_EOM)
hc_htx->flags |= HTX_FL_EOM;
blk = htx_get_head_blk(htx);
while (blk) {
enum htx_blk_type type = htx_get_blk_type(blk);
- uint32_t sz = htx_get_blksz(blk);
-
- c_rew(res, sz);
if (type == HTX_BLK_HDR) {
hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
if (!hc->res.hdrs)
goto error;
memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
+ applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
}
}
/* caller callback */
appctx->st0 = HTTPCLIENT_S_RES_BODY;
}
- htx_to_buf(htx, &res->buf);
+ htx_to_buf(htx, inbuf);
break;
case HTTPCLIENT_S_RES_BODY:
+ applet_will_consume(appctx);
+ inbuf = applet_get_inbuf(appctx);
+ if (inbuf == NULL || !applet_input_data(appctx)) {
+ applet_need_more_data(appctx);
+ goto out;
+ }
+
/*
* The IO handler removes the htx blocks in the response buffer and
* push them in the hc->res.buf buffer in a raw format.
*/
- if (!co_data(res))
- goto out;
-
- htx = htxbuf(&res->buf);
- if (htx_is_empty(htx))
+ htx = htxbuf(inbuf);
+ if (htx_is_empty(htx)) {
+ applet_need_more_data(appctx);
goto out;
+ }
- if (!b_alloc(&hc->res.buf, DB_MUX_TX))
+ if (!b_alloc(&hc->res.buf, DB_MUX_TX)) {
+ applet_wont_consume(appctx);
goto out;
-
- if (b_full(&hc->res.buf))
- goto process_data;
+ }
if (hc->options & HTTPCLIENT_O_RES_HTX) {
/* HTX mode transfers the header to the hc buffer */
hc_htx = htxbuf(&hc->res.buf);
- /* xfer the status line to the res buffer */
- count = htx->data;
ret = htx_xfer_blks(hc_htx, htx, htx_used_space(htx), HTX_BLK_UNUSED);
- if (ret.ret <= 0)
- goto process_data;
- count -= htx->data;
- c_rew(res, count);
+ if (!ret.ret)
+ applet_wont_consume(appctx);
+ else
+ applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
- if (htx->flags & HTX_FL_EOM)
+ if (htx_is_empty(htx) && (htx->flags & HTX_FL_EOM))
hc_htx->flags |= HTX_FL_EOM;
htx_to_buf(hc_htx, &hc->res.buf);
blk = htx_get_head_blk(htx);
while (blk) {
enum htx_blk_type type = htx_get_blk_type(blk);
- size_t count = co_data(res);
- uint32_t blksz = htx_get_blksz(blk);
- uint32_t room = b_room(&hc->res.buf);
- uint32_t vlen;
/* we should try to copy the maximum output data in a block, which fit
* the destination buffer */
- vlen = MIN(count, blksz);
- vlen = MIN(vlen, room);
-
- if (vlen == 0) {
- htx_to_buf(htx, &res->buf);
- goto process_data;
- }
-
if (type == HTX_BLK_DATA) {
struct ist v = htx_get_blk_value(htx, blk);
+ uint32_t room = b_room(&hc->res.buf);
+ uint32_t vlen;
+ vlen = MIN(v.len, room);
__b_putblk(&hc->res.buf, v.ptr, vlen);
- c_rew(res, vlen);
- if (vlen == blksz)
+ if (vlen == v.len)
blk = htx_remove_blk(htx, blk);
- else
+ else {
htx_cut_data_blk(htx, blk, vlen);
-
- /* cannot copy everything, need to process */
- if (vlen != blksz) {
- htx_to_buf(htx, &res->buf);
- goto process_data;
+ /* cannot copy everything, need to process */
+ applet_wont_consume(appctx);
+ break;
}
} else {
- if (vlen != blksz) {
- htx_to_buf(htx, &res->buf);
- goto process_data;
- }
-
/* remove any block which is not a data block */
- c_rew(res, blksz);
blk = htx_remove_blk(htx, blk);
}
}
}
- /* if not finished, should be called again */
- if ((htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)))
- appctx->st0 = HTTPCLIENT_S_RES_END;
-
- htx_to_buf(htx, &res->buf);
+ applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
/* the data must be processed by the caller in the receive phase */
if (hc->ops.res_payload)
hc->ops.res_payload(hc);
- break;
+ /* if not finished, should be called again */
+ if ((htx_is_empty(htx) && (htx->flags & HTX_FL_EOM))) {
+ appctx->st0 = HTTPCLIENT_S_RES_END;
+ htx_to_buf(htx, inbuf);
+ break;
+ }
+
+ htx_to_buf(htx, inbuf);
+ applet_need_more_data(appctx);
+ goto out;
case HTTPCLIENT_S_RES_END:
- se_fl_set(appctx->sedesc, SE_FL_EOS);
+ applet_set_eos(appctx);
goto out;
- break;
}
}
out:
return;
-process_data:
- sc_will_read(sc);
- goto out;
-
error:
- se_fl_set(appctx->sedesc, SE_FL_ERROR);
+ applet_set_eos(appctx);
+ applet_set_error(appctx);
goto out;
}
appctx->st0 = HTTPCLIENT_S_REQ_BODY;
else {
appctx->st0 = HTTPCLIENT_S_RES_STLINE;
- se_fl_set(appctx->sedesc, SE_FL_EOI);
+ applet_set_eoi(appctx);
}
return 0;
.obj_type = OBJ_TYPE_APPLET,
.name = "<HTTPCLIENT>",
.fct = httpclient_applet_io_handler,
+ .rcv_buf = appctx_htx_rcv_buf,
+ .snd_buf = appctx_htx_snd_buf,
.init = httpclient_applet_init,
.release = httpclient_applet_release,
};