]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: buffer: add a new buf_wanted dummy buffer to report failed allocations
authorWilly Tarreau <w@1wt.eu>
Mon, 24 Nov 2014 10:55:08 +0000 (11:55 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 24 Dec 2014 22:47:32 +0000 (23:47 +0100)
Doing so ensures that even when no memory is available, we leave the
channel in a sane condition. There's a special case in proto_http.c
regarding the compression, we simply pre-allocate the tmpbuf to point
to the dummy buffer. Not reusing &buf_empty for this allows the rest
of the code to differenciate an empty buffer that's not used from an
empty buffer that results from a failed allocation which has the same
semantics as a buffer full.

include/common/buffer.h
src/buffer.c
src/proto_http.c

index 5ea8fab4117001a0572f780629a50547cef5a1bf..4e55285e7d09ae33ce3ec5f55f3989586d6fbd89 100644 (file)
@@ -41,6 +41,7 @@ struct buffer {
 
 extern struct pool_head *pool2_buffer;
 extern struct buffer buf_empty;
+extern struct buffer buf_wanted;
 
 int init_buffer();
 int buffer_replace2(struct buffer *b, char *pos, char *end, const char *str, int len);
@@ -396,18 +397,23 @@ static inline void b_reset(struct buffer *buf)
        buf->p = buf->data;
 }
 
-/* Allocates a buffer and replaces *buf with this buffer. No control is made
- * to check if *buf already pointed to another buffer. The allocated buffer
- * is returned, or NULL in case no memory is available.
+/* Allocates a buffer and replaces *buf with this buffer. If no memory is
+ * available, &buf_wanted is used instead. No control is made to check if *buf
+ * already pointed to another buffer. The allocated buffer is returned, or
+ * NULL in case no memory is available.
  */
 static inline struct buffer *b_alloc(struct buffer **buf)
 {
-       *buf = pool_alloc_dirty(pool2_buffer);
-       if (likely(*buf)) {
-               (*buf)->size = pool2_buffer->size - sizeof(struct buffer);
-               b_reset(*buf);
+       struct buffer *b;
+
+       *buf = &buf_wanted;
+       b = pool_alloc_dirty(pool2_buffer);
+       if (likely(b)) {
+               b->size = pool2_buffer->size - sizeof(struct buffer);
+               b_reset(b);
+               *buf = b;
        }
-       return *buf;
+       return b;
 }
 
 /* Releases buffer *buf (no check of emptiness) */
index 769102639f8f728ef07b3812fc34b050c1c51e23..d9301bf92d68ef5bfa12c3c310012230e8ed02e3 100644 (file)
 
 struct pool_head *pool2_buffer;
 
-/* this buffer is used to have a valid pointer to an empty buffer in channels
- * which convey no more data.
+/* These buffers are used to always have a valid pointer to an empty buffer in
+ * channels. The first buffer is set once a buffer is empty. The second one is
+ * set when a buffer is desired but no more are available. It helps knowing
+ * what channel wants a buffer. They can reliably be exchanged, the split
+ * between the two is only an optimization.
  */
 struct buffer buf_empty  = { .p = buf_empty.data };
+struct buffer buf_wanted = { .p = buf_wanted.data };
 
 /* perform minimal intializations, report 0 in case of error, 1 if OK. */
 int init_buffer()
index ee1a812c8d30d5d91185c732213c7c50ddd6ad34..b4861cea77a4306c63b335561da3905d26394753 100644 (file)
@@ -6516,7 +6516,7 @@ int http_response_forward_body(struct session *s, struct channel *res, int an_bi
 {
        struct http_txn *txn = &s->txn;
        struct http_msg *msg = &s->txn.rsp;
-       static struct buffer *tmpbuf = NULL;
+       static struct buffer *tmpbuf = &buf_empty;
        int compressing = 0;
        int ret;
 
@@ -6570,7 +6570,7 @@ int http_response_forward_body(struct session *s, struct channel *res, int an_bi
                 * output of compressed data, and in CRLF state to let the
                 * TRAILERS state finish the job of removing the trailing CRLF.
                 */
-               if (unlikely(tmpbuf == NULL)) {
+               if (unlikely(!tmpbuf->size)) {
                        /* this is the first time we need the compression buffer */
                        if (b_alloc(&tmpbuf) == NULL)
                                goto aborted_xfer; /* no memory */