static void stats_dump_json_schema(struct buffer *out);
-int stats_putchk(struct appctx *appctx, struct htx *htx)
+int stats_putchk(struct appctx *appctx, struct buffer *buf, struct htx *htx)
{
struct buffer *chk = &trash_chunk;
}
chk->data = 0;
}
- else {
+ else if (buf) {
+ if (b_data(chk) > b_room(buf))
+ return 0;
+ b_putblk(buf, b_head(chk), b_data(chk));
+ chk->data = 0;
+ }
+ else {
if (applet_putchk(appctx, chk) == -1)
return 0;
}
* both by the CLI and the HTTP entry points, and is able to dump the output
* in HTML or CSV formats.
*/
-int stats_dump_proxy_to_buffer(struct stconn *sc, struct htx *htx,
+int stats_dump_proxy_to_buffer(struct stconn *sc, struct buffer *buf, struct htx *htx,
struct proxy *px)
{
struct appctx *appctx = __sc_appctx(sc);
case STAT_PX_ST_TH:
if (ctx->flags & STAT_FMT_HTML) {
stats_dump_html_px_hdr(sc, px);
- if (!stats_putchk(appctx, htx))
+ if (!stats_putchk(appctx, buf, htx))
goto full;
}
case STAT_PX_ST_FE:
/* print the frontend */
if (stats_dump_fe_stats(sc, px)) {
- if (!stats_putchk(appctx, htx))
+ if (!stats_putchk(appctx, buf, htx))
goto full;
ctx->flags |= STAT_STARTED;
if (ctx->field)
goto full;
}
}
+ else if (buf) {
+ if (buffer_almost_full(buf))
+ goto full;
+ }
else {
struct channel *rep = sc_ic(appctx_sc(appctx));
/* print the frontend */
if (stats_dump_li_stats(sc, px, l)) {
- if (!stats_putchk(appctx, htx))
+ if (!stats_putchk(appctx, buf, htx))
goto full;
ctx->flags |= STAT_STARTED;
if (ctx->field)
goto full;
}
}
+ else if (buf) {
+ if (buffer_almost_full(buf))
+ goto full;
+ }
else {
struct channel *rep = sc_ic(appctx_sc(appctx));
}
if (stats_dump_sv_stats(sc, px, sv)) {
- if (!stats_putchk(appctx, htx))
+ if (!stats_putchk(appctx, buf, htx))
goto full;
ctx->flags |= STAT_STARTED;
if (ctx->field)
case STAT_PX_ST_BE:
/* print the backend */
if (stats_dump_be_stats(sc, px)) {
- if (!stats_putchk(appctx, htx))
+ if (!stats_putchk(appctx, buf, htx))
goto full;
ctx->flags |= STAT_STARTED;
if (ctx->field)
case STAT_PX_ST_END:
if (ctx->flags & STAT_FMT_HTML) {
stats_dump_html_px_end(sc, px);
- if (!stats_putchk(appctx, htx))
+ if (!stats_putchk(appctx, buf, htx))
goto full;
}
/* Uses <appctx.ctx.stats.obj1> as a pointer to the current proxy and <obj2> as
* a pointer to the current server/listener.
*/
-static int stats_dump_proxies(struct stconn *sc,
+static int stats_dump_proxies(struct stconn *sc, struct buffer *buf,
struct htx *htx)
{
struct appctx *appctx = __sc_appctx(sc);
goto full;
}
}
+ else if (buf) {
+ if (buffer_almost_full(buf))
+ goto full;
+ }
else {
struct channel *rep = sc_ic(appctx_sc(appctx));
*/
if (!(px->flags & PR_FL_DISABLED) && px->uuid > 0 &&
(px->cap & (PR_CAP_FE | PR_CAP_BE)) && !(px->cap & PR_CAP_INT)) {
- if (stats_dump_proxy_to_buffer(sc, htx, px) == 0)
+ if (stats_dump_proxy_to_buffer(sc, buf, htx, px) == 0)
return 0;
}
* or -1 in case of any error. This function is used by both the CLI and the
* HTTP handlers.
*/
-static int stats_dump_stat_to_buffer(struct stconn *sc, struct htx *htx)
+static int stats_dump_stat_to_buffer(struct stconn *sc, struct buffer *buf, struct htx *htx)
{
struct appctx *appctx = __sc_appctx(sc);
struct show_stat_ctx *ctx = appctx->svcctx;
else if (!(ctx->flags & STAT_FMT_TYPED))
stats_dump_csv_header(ctx->domain);
- if (!stats_putchk(appctx, htx))
+ if (!stats_putchk(appctx, buf, htx))
goto full;
if (ctx->flags & STAT_JSON_SCHM) {
case STAT_STATE_INFO:
if (ctx->flags & STAT_FMT_HTML) {
stats_dump_html_info(sc);
- if (!stats_putchk(appctx, htx))
+ if (!stats_putchk(appctx, buf, htx))
goto full;
}
case STATS_DOMAIN_PROXY:
default:
/* dump proxies */
- if (!stats_dump_proxies(sc, htx))
+ if (!stats_dump_proxies(sc, buf, htx))
return 0;
break;
}
stats_dump_html_end();
else
stats_dump_json_end();
- if (!stats_putchk(appctx, htx))
+ if (!stats_putchk(appctx, buf, htx))
goto full;
}
return 0;
}
+static size_t http_stats_fastfwd(struct appctx *appctx, struct buffer *buf, size_t count, unsigned int flags)
+{
+ struct stconn *sc = appctx_sc(appctx);
+ size_t ret = 0;
+
+ ret = b_data(buf);
+ if (stats_dump_stat_to_buffer(sc, buf, NULL)) {
+ se_fl_clr(appctx->sedesc, SE_FL_MAY_FASTFWD);
+ appctx->st0 = STAT_HTTP_DONE;
+ }
+
+ ret = b_data(buf) - ret;
+ return ret;
+}
/* This I/O handler runs as an applet embedded in a stream connector. It is
* used to send HTTP stats over a TCP socket. The mechanism is very simple.
if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC|APPCTX_FL_OUTBLK_FULL))
goto out;
+ if (se_fl_test(appctx->sedesc, SE_FL_MAY_FASTFWD))
+ goto out;
+
if (!appctx_get_buf(appctx, &appctx->outbuf)) {
appctx->flags |= APPCTX_FL_OUTBLK_ALLOC;
goto out;
if (find_http_meth(istptr(meth), istlen(meth)) == HTTP_METH_HEAD)
appctx->st0 = STAT_HTTP_DONE;
- else
+ else {
+ if (!(global.tune.no_zero_copy_fwd & NO_ZERO_COPY_FWD))
+ se_fl_set(appctx->sedesc, SE_FL_MAY_FASTFWD);
appctx->st0 = STAT_HTTP_DUMP;
+ }
}
}
* make sure to perform this call on an empty buffer
*/
trash_chunk.size = buf_room_for_htx_data(&trash_chunk);
- if (stats_dump_stat_to_buffer(sc, res_htx))
+ if (stats_dump_stat_to_buffer(sc, NULL, res_htx))
appctx->st0 = STAT_HTTP_DONE;
}
}
res_htx->flags |= HTX_FL_EOM;
applet_set_eoi(appctx);
+ se_fl_clr(appctx->sedesc, SE_FL_MAY_FASTFWD);
appctx->st0 = STAT_HTTP_END;
}
/* eat the whole request */
b_reset(&appctx->inbuf);
applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
+ appctx->sedesc->iobuf.flags &= ~IOBUF_FL_FF_BLOCKED;
}
else if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_FULL))
applet_wont_consume(appctx);
static int cli_io_handler_dump_stat(struct appctx *appctx)
{
trash_chunk = b_make(trash.area, trash.size, 0, 0);
- return stats_dump_stat_to_buffer(appctx_sc(appctx), NULL);
+ return stats_dump_stat_to_buffer(appctx_sc(appctx), NULL, NULL);
}
static int cli_io_handler_dump_json_schema(struct appctx *appctx)
.fct = http_stats_io_handler,
.rcv_buf = appctx_rcv_buf,
.snd_buf = appctx_snd_buf,
+ .fastfwd = http_stats_fastfwd,
.release = NULL,
};