diff --git a/include/haproxy/applet.h b/include/haproxy/applet.h index 3a61dc3a1..74437401d 100644 --- a/include/haproxy/applet.h +++ b/include/haproxy/applet.h @@ -532,6 +532,283 @@ static inline int applet_putchr(struct appctx *appctx, char chr) return ret; } +static inline int applet_may_get(const struct appctx *appctx, size_t len) +{ + if (appctx->flags & APPCTX_FL_INOUT_BUFS) { + if (len > b_data(&appctx->inbuf)) { + if (se_fl_test(appctx->sedesc, SE_FL_SHW)) + return -1; + return 0; + } + } + else { + const struct stconn *sc = appctx_sc(appctx); + + if ((sc->flags & SC_FL_SHUT_DONE) || len > co_data(sc_oc(sc))) { + if (sc->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED)) + return -1; + return 0; + } + } + return 1; +} +/* Gets one char from the applet input buffer (see appet_get_inbuf), + * + * Return values : + * 1 : number of bytes read, equal to requested size. + * =0 : not enough data available. is left undefined. + * <0 : no more bytes readable because output is shut. + * + * The status of the corresponding buffer is not changed. The caller must call + * applet_skip_input() to update it. + */ +static inline int applet_getchar(const struct appctx *appctx, char *c) +{ + int ret; + + ret = applet_may_get(appctx, 1); + if (ret <= 0) + return ret; + *c = ((appctx->flags & APPCTX_FL_INOUT_BUFS) + ? *(b_head(&appctx->inbuf)) + : *(co_head(sc_oc(appctx_sc(appctx))))); + + return 1; +} + +/* Copies one full block of data from the applet input buffer (see + * appet_get_inbuf). + * + * bytes are capied, starting at the offset . + * + * Return values : + * >0 : number of bytes read, equal to requested size. + * =0 : not enough data available. is left undefined. + * <0 : no more bytes readable because output is shut. + * + * The status of the corresponding buffer is not changed. The caller must call + * applet_skip_input() to update it. + */ +static inline int applet_getblk(const struct appctx *appctx, char *blk, int len, int offset) +{ + const struct buffer *buf; + int ret; + + ret = applet_may_get(appctx, len+offset); + if (ret <= 0) + return ret; + + buf = ((appctx->flags & APPCTX_FL_INOUT_BUFS) + ? &appctx->inbuf + : sc_ob(appctx_sc(appctx))); + return b_getblk(buf, blk, len, offset); +} + +/* Gets one text block representing a word from the applet input buffer (see + * appet_get_inbuf). + * + * The separator is waited for as long as some data can still be received and the + * destination is not full. Otherwise, the string may be returned as is, without + * the separator. + * + * Return values : + * >0 : number of bytes read. Includes the separator if present before len or end. + * =0 : no separator before end found. is left undefined. + * <0 : no more bytes readable because output is shut. + * + * The status of the corresponding buffer is not changed. The caller must call + * applet_skip_input() to update it. + */ +static inline int applet_getword(const struct appctx *appctx, char *str, int len, char sep) +{ + const struct buffer *buf; + char *p; + size_t input, max = len; + int ret = 0; + + ret = applet_may_get(appctx, 1); + if (ret <= 0) + goto out; + + if (appctx->flags & APPCTX_FL_INOUT_BUFS) { + buf = &appctx->inbuf; + input = b_data(buf); + } + else { + struct stconn *sc = appctx_sc(appctx); + + buf = sc_ob(sc); + input = co_data(sc_oc(sc)); + } + + if (max > input) { + max = input; + str[max-1] = 0; + } + + p = b_head(buf); + + while (max) { + *str++ = *p; + ret++; + max--; + if (*p == sep) + goto out; + p = b_next(buf, p); + } + + if (appctx->flags & APPCTX_FL_INOUT_BUFS) { + if (ret < len && (ret < input || b_room(buf)) && + !se_fl_test(appctx->sedesc, SE_FL_SHW)) + ret = 0; + } + else { + struct stconn *sc = appctx_sc(appctx); + + if (ret < len && (ret < input || channel_may_recv(sc_oc(sc))) && + !(sc->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED))) + ret = 0; + } + out: + if (max) + *str = 0; + return ret; +} + +/* Gets one text block representing a line from the applet input buffer (see + * appet_get_inbuf). + * + * The '\n' is waited for as long as some data can still be received and the + * destination is not full. Otherwise, the string may be returned as is, without + * the '\n'. + * + * Return values : + * >0 : number of bytes read. Includes the \n if present before len or end. + * =0 : no '\n' before end found. is left undefined. + * <0 : no more bytes readable because output is shut. + * + * The status of the corresponding buffer is not changed. The caller must call + * applet_skip_input() to update it. + */ +static inline int applet_getline(const struct appctx *appctx, char *str, int len) +{ + return applet_getword(appctx, str, len, '\n'); +} + +/* Gets one or two blocks of data at once from the applet input buffer (see appet_get_inbuf), + * + * Data are not copied. + * + * Return values : + * >0 : number of blocks filled (1 or 2). blk1 is always filled before blk2. + * =0 : not enough data available. are left undefined. + * <0 : no more bytes readable because output is shut. + * + * The status of the corresponding buffer is not changed. The caller must call + * applet_skip_input() to update it. + */ +static inline int applet_getblk_nc(const struct appctx *appctx, const char **blk1, size_t *len1, const char **blk2, size_t *len2) +{ + const struct buffer *buf; + size_t max; + int ret; + + ret = applet_may_get(appctx, 1); + if (ret <= 0) + return ret; + + if (appctx->flags & APPCTX_FL_INOUT_BUFS) { + buf = &appctx->inbuf; + max = b_data(buf); + } + else { + struct stconn *sc = appctx_sc(appctx); + + buf = sc_ob(sc); + max = co_data(sc_oc(sc)); + } + + return b_getblk_nc(buf, blk1, len1, blk2, len2, 0, max); +} + +/* Gets one or two blocks of text representing a word from the applet input + * buffer (see appet_get_inbuf). + * + * Data are not copied. The separator is waited for as long as some data can + * still be received and the destination is not full. Otherwise, the string may + * be returned as is, without the separator. + * + * Return values : + * >0 : number of bytes read. Includes the separator if present before len or end. + * =0 : no separator before end found. is left undefined. + * <0 : no more bytes readable because output is shut. + * + * The status of the corresponding buffer is not changed. The caller must call + * applet_skip_input() to update it. + */ +static inline int applet_getword_nc(const struct appctx *appctx, const char **blk1, size_t *len1, const char **blk2, size_t *len2, char sep) +{ + int ret; + size_t l; + + ret = applet_getblk_nc(appctx, blk1, len1, blk2, len2); + if (unlikely(ret <= 0)) + return ret; + + for (l = 0; l < *len1 && (*blk1)[l] != sep; l++); + if (l < *len1 && (*blk1)[l] == sep) { + *len1 = l + 1; + return 1; + } + + if (ret >= 2) { + for (l = 0; l < *len2 && (*blk2)[l] != sep; l++); + if (l < *len2 && (*blk2)[l] == sep) { + *len2 = l + 1; + return 2; + } + } + + /* If we have found no LF and the buffer is full or the SC is shut, then + * the resulting string is made of the concatenation of the pending + * blocks (1 or 2). + */ + if (appctx->flags & APPCTX_FL_INOUT_BUFS) { + if (b_full(&appctx->inbuf) || se_fl_test(appctx->sedesc, SE_FL_SHW)) + return ret; + } + else { + struct stconn *sc = appctx_sc(appctx); + + if (!channel_may_recv(sc_oc(sc)) || sc->flags & (SC_FL_SHUT_DONE|SC_FL_SHUT_WANTED)) + return ret; + } + + /* No LF yet and not shut yet */ + return 0; +} + + +/* Gets one or two blocks of text representing a line from the applet input + * buffer (see appet_get_inbuf). + * + * Data are not copied. The '\n' is waited for as long as some data can still be + * received and the destination is not full. Otherwise, the string may be + * returned as is, without the '\n'. + * + * Return values : + * >0 : number of bytes read. Includes the \n if present before len or end. + * =0 : no '\n' before end found. is left undefined. + * <0 : no more bytes readable because output is shut. + * + * The status of the corresponding buffer is not changed. The caller must call + * applet_skip_input() to update it. + */ +static inline int applet_getline_nc(const struct appctx *appctx, const char **blk1, size_t *len1, const char **blk2, size_t *len2) +{ + return applet_getword_nc(appctx, blk1, len1, blk2, len2, '\n'); +} + #endif /* _HAPROXY_APPLET_H */ /*