/*
- include/proto/buffers.h
- Buffer management definitions, macros and inline functions.
-
- Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation, version 2.1
- exclusively.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*/
+ * include/proto/buffers.h
+ * Buffer management definitions, macros and inline functions.
+ *
+ * Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
#ifndef _PROTO_BUFFERS_H
#define _PROTO_BUFFERS_H
#include <common/time.h>
#include <types/buffers.h>
+#include <types/global.h>
extern struct pool_head *pool2_buffer;
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
int init_buffer();
-/* Initializes all fields in the buffer. The ->max_len field is initialized last
- * so that the compiler can optimize it away if changed immediately after the
- * call to this function. By default, it is set to the full size of the buffer.
- * This implies that buffer_init() must only be called once ->size is set !
- * The BF_OUT_EMPTY flags is set.
- */
+/* Initialize all fields in the buffer. The BF_OUT_EMPTY flags is set. */
static inline void buffer_init(struct buffer *buf)
{
buf->send_max = 0;
buf->cons = NULL;
buf->flags = BF_OUT_EMPTY;
buf->r = buf->lr = buf->w = buf->data;
- buf->max_len = buf->size;
+}
+
+/* Return the max number of bytes the buffer can contain so that once all the
+ * pending bytes are forwarded, the buffer still has global.tune.maxrewrite
+ * bytes free. The result sits between buf->size - maxrewrite and buf->size.
+ */
+static inline int buffer_max_len(struct buffer *buf)
+{
+ if (buf->to_forward == BUF_INFINITE_FORWARD ||
+ buf->to_forward + buf->send_max >= global.tune.maxrewrite)
+ return buf->size;
+ else
+ return buf->size - global.tune.maxrewrite + buf->to_forward + buf->send_max;
}
/* Check buffer timeouts, and set the corresponding flags. The
if (buf->send_max)
buf->flags &= ~BF_OUT_EMPTY;
- if (buf->to_forward == BUF_INFINITE_FORWARD)
- return;
+ if (buf->to_forward != BUF_INFINITE_FORWARD) {
+ buf->to_forward += bytes - data_left;
+ if (bytes == BUF_INFINITE_FORWARD)
+ buf->to_forward = bytes;
+ }
- buf->to_forward += bytes - data_left;
- if (bytes == BUF_INFINITE_FORWARD)
- buf->to_forward = bytes;
+ if (buf->l < buffer_max_len(buf))
+ buf->flags &= ~BF_FULL;
+ else
+ buf->flags |= BF_FULL;
}
/* Schedule all remaining buffer data to be sent. send_max is not touched if it
buf->flags &= ~(BF_FULL | BF_OUT_EMPTY);
if (!buf->pipe)
buf->flags |= BF_OUT_EMPTY;
- if (!buf->max_len)
- buf->flags |= BF_FULL;
}
/* Cut the "tail" of the buffer, which means strip it to the length of unsent
buf->r -= buf->size;
buf->lr = buf->r;
buf->flags &= ~BF_FULL;
- if (buf->l >= buf->max_len)
+ if (buf->l >= buffer_max_len(buf))
buf->flags |= BF_FULL;
}
return buf->w - buf->r;
}
-/* sets the buffer read limit to <size> bytes, and adjusts the FULL
- * flag accordingly.
- */
-static inline void buffer_set_rlim(struct buffer *buf, int size)
-{
- buf->max_len = size;
- if (buf->l < size)
- buf->flags &= ~BF_FULL;
- else
- buf->flags |= BF_FULL;
-}
-
/*
* Tries to realign the given buffer, and returns how many bytes can be written
* there at once without overwriting anything.
if (buf->l == 0) {
buf->r = buf->w = buf->lr = buf->data;
- ret = buf->max_len;
+ ret = buffer_max_len(buf);
}
else if (buf->r > buf->w) {
- ret = buf->data + buf->max_len - buf->r;
+ ret = buf->data + buffer_max_len(buf) - buf->r;
}
else {
ret = buf->w - buf->r;
- if (ret > buf->max_len)
- ret = buf->max_len;
+ if (ret > buffer_max_len(buf))
+ ret = buffer_max_len(buf);
}
return ret;
}
if (!buf->l)
buf->r = buf->w = buf->lr = buf->data;
- if (buf->l < buf->max_len)
+ if (buf->l < buffer_max_len(buf))
buf->flags &= ~BF_FULL;
buf->send_max -= len;
*buf->r = c;
buf->l++;
- if (buf->l >= buf->max_len)
+ if (buf->l >= buffer_max_len(buf))
buf->flags |= BF_FULL;
buf->r++;
/*
- include/types/buffers.h
- Buffer management definitions, macros and inline functions.
-
- Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation, version 2.1
- exclusively.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*/
+ * include/types/buffers.h
+ * Buffer management definitions, macros and inline functions.
+ *
+ * Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
#ifndef _TYPES_BUFFERS_H
#define _TYPES_BUFFERS_H
#define BF_READ_ERROR 0x000008 /* unrecoverable error on producer side */
#define BF_READ_ACTIVITY (BF_READ_NULL|BF_READ_PARTIAL|BF_READ_ERROR)
-#define BF_FULL 0x000010 /* buffer cannot accept any more data (l >= max_len) */
+#define BF_FULL 0x000010 /* buffer cannot accept any more data (l >= max len) */
#define BF_SHUTR 0x000020 /* producer has already shut down */
#define BF_SHUTR_NOW 0x000040 /* the producer must shut down for reads ASAP */
#define BF_READ_NOEXP 0x000080 /* producer should not expire */
unsigned int l; /* data length */
char *r, *w, *lr; /* read ptr, write ptr, last read */
unsigned int size; /* buffer size in bytes */
- unsigned int max_len; /* read limit, used to keep room for header rewriting */
unsigned int send_max; /* number of bytes the sender can consume om this buffer, <= l */
unsigned long to_forward; /* number of bytes to forward after send_max without a wake-up */
unsigned int analysers; /* bit field indicating what to do on the buffer */
whole buffer, and reduce ->to_forward to 8000. After that, the producer may
try to feed the additional data through the invisible buffer using a
platform-specific method such as splice().
+
+ The ->to_forward entry is also used to detect whether we can fill the buffer
+ or not. The idea is that we need to save some space for data manipulation
+ (mainly header rewriting in HTTP) so we don't want to have a full buffer on
+ input before processing a request or response. Thus, we ensure that there is
+ always global.maxrewrite bytes of free space. Since we don't want to forward
+ chunks without filling the buffer, we rely on ->to_forward. When ->to_forward
+ is null, we may have some processing to do so we don't want to fill the
+ buffer. When ->to_forward is non-null, we know we don't care for at least as
+ many bytes. In the end, we know that each of the ->to_forward bytes will
+ eventually leave the buffer. So as long as ->to_forward is larger than
+ global.maxrewrite, we can fill the buffer. If ->to_forward is smaller than
+ global.maxrewrite, then we don't want to fill the buffer with more than
+ ->size - global.maxrewrite + ->to_forward.
+
+ Note that this also means that anyone touching ->to_forward must also take
+ care of updating the BF_FULL flag. For this reason, it's really advised to
+ use buffer_forward() only.
*/
#endif /* _TYPES_BUFFERS_H */
buf->r = buf->data;
buf->flags &= ~(BF_OUT_EMPTY|BF_FULL);
- if (buf->l >= buf->max_len)
+ if (buf->l >= buffer_max_len(buf))
buf->flags |= BF_FULL;
return -1;
if (len == 0)
return -1;
- if (len > buf->max_len) {
+ if (len > buffer_max_len(buf)) {
/* we can't write this chunk and will never be able to, because
* it is larger than the buffer's current max size.
*/
buf->r = buf->data;
buf->flags &= ~BF_FULL;
- if (buf->l >= buf->max_len)
+ if (buf->l >= buffer_max_len(buf))
buf->flags |= BF_FULL;
/* notify that some data was read from the SI into the buffer */
b->flags &= ~BF_FULL;
if (b->l == 0)
b->r = b->w = b->lr = b->data;
- if (b->l >= b->max_len)
+ if (b->l >= buffer_max_len(b))
b->flags |= BF_FULL;
return delta;
b->flags &= ~BF_FULL;
if (b->l == 0)
b->r = b->w = b->lr = b->data;
- if (b->l >= b->max_len)
+ if (b->l >= buffer_max_len(b))
b->flags |= BF_FULL;
return delta;
b->l += delta;
b->flags &= ~BF_FULL;
- if (b->l >= b->max_len)
+ if (b->l >= buffer_max_len(b))
b->flags |= BF_FULL;
return delta;
s->req->flags |= BF_READ_ATTACHED; /* the producer is already connected */
- if (p->mode == PR_MODE_HTTP) { /* reserve some space for header rewriting */
- s->req->max_len -= global.tune.maxrewrite;
+ if (p->mode == PR_MODE_HTTP)
s->req->flags |= BF_READ_DONTWAIT; /* one read is usually enough */
- }
/* activate default analysers enabled for this listener */
s->req->analysers = l->analysers;
req->analyse_exp = TICK_ETERNITY;
req->analysers &= ~an_bit;
- buffer_set_rlim(req, req->size); /* no more rewrite needed */
s->logs.tv_request = now;
/* OK let's go on with the BODY now */
return 1;
* could. Let's switch to the DATA state. *
************************************************************/
- buffer_set_rlim(rep, rep->size); /* no more rewrite needed */
t->logs.t_data = tv_ms_elapsed(&t->logs.tv_accept, &now);
/* if the user wants to log as soon as possible, without counting
* all the part of the request which fits in a buffer is already
* there.
*/
- if (msg_len > l4->req->max_len + l4->req->data - l4->req->w)
- msg_len = l4->req->max_len + l4->req->data - l4->req->w;
+ if (msg_len > buffer_max_len(l4->req) + l4->req->data - l4->req->w)
+ msg_len = buffer_max_len(l4->req) + l4->req->data - l4->req->w;
if (bleft < msg_len)
goto too_short;
health_adjust(s->srv, HANA_STATUS_L4_OK);
if (s->be->mode == PR_MODE_TCP) { /* let's allow immediate data connection in this case */
- buffer_set_rlim(rep, rep->size); /* no rewrite needed */
-
/* if the user wants to log as soon as possible, without counting
* bytes from the server, then this is the right moment. */
if (s->fe->to_log && !(s->logs.logwait & LW_BYTES)) {
}
}
else {
- buffer_set_rlim(rep, req->size - global.tune.maxrewrite); /* rewrite needed */
s->txn.rsp.msg_state = HTTP_MSG_RPBEFORE;
/* reset hdr_idx which was already initialized by the request.
* right now, the http parser does it.
if (b->l == 0) {
/* let's realign the buffer to optimize I/O */
b->r = b->w = b->lr = b->data;
- max = b->max_len;
+ max = buffer_max_len(b);
}
else if (b->r > b->w) {
- max = b->data + b->max_len - b->r;
+ max = b->data + buffer_max_len(b) - b->r;
}
else {
max = b->w - b->r;
- if (max > b->max_len)
- max = b->max_len;
+ if (max > buffer_max_len(b))
+ max = buffer_max_len(b);
}
if (max == 0) {
b->total += ret;
- if (b->l >= b->max_len) {
+ if (b->l >= buffer_max_len(b)) {
/* The buffer is now full, there's no point in going through
* the loop again.
*/
b->w = b->data; /* wrap around the buffer */
b->l -= ret;
- if (likely(b->l < b->max_len))
+ if (likely(b->l < buffer_max_len(b)))
b->flags &= ~BF_FULL;
if (likely(!b->l))