Merge tag 'v1.5.19' into dev

HAProxy 1.5.19

Change-Id: I8e64d81c0750f07cc2a1d6c10f61843622587c2b
diff --git a/.gitignore b/.gitignore
index 762f5ad..940c2df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,52 @@
 dlmalloc.c
 00*.patch
 *.service
+*.bak
+contrib/base64/base64rev
+contrib/halog/halog
+contrib/ip6range/ip6range
+contrib/iprange/iprange
+tests/test_hashes
+/*.cfg
+/*.conf
+/*.diff
+/*.patch
+/*.c
+/*.o
+/*.so
+/*.txt
+/*.TXT
+/*.txt.*
+/*.prof
+/*.gprof
+/*.prof.*
+/*.gprof.*
+/*.tar
+/*.tar.gz
+/*.tgz
+/*.mbox
+/*.sh
+/bug*
+/TAGS
+# Below we forbid everything and only allow what we know, that's much easier
+# than blocking about 500 different test files and bug report outputs.
+/.*
+/*
+!/.gitignore
+!/CHANGELOG
+!/LICENSE
+!/Makefile
+!/README
+!/CONTRIBUTING
+!/ROADMAP
+!/SUBVERS
+!/VERDATE
+!/VERSION
+!/contrib
+!/doc
+!/ebtree
+!/examples
+!/include
+!/src
+!/tests
+!/debian
diff --git a/CHANGELOG b/CHANGELOG
index c2ec538..885b531 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,170 @@
 ChangeLog :
 ===========
 
+2016/12/25 : 1.5.19
+    - BUG/MAJOR: fix listening IP address storage for frontends
+    - CLEANUP: connection: fix double negation on memcmp()
+    - BUG/MEDIUM: sticktables: segfault in some configuration error cases
+    - BUG/MINOR: http: add-header: header name copied twice
+    - BUG/MINOR: ssl: fix potential memory leak in ssl_sock_load_dh_params()
+    - BUG/MINOR: http: url32+src should use the big endian version of url32
+    - BUG/MINOR: http: url32+src should check cli_conn before using it
+    - DOC: http: add documentation for url32 and url32+src
+    - MINOR: systemd: Use variable for config and pidfile paths
+    - MINOR: systemd: Perform sanity check on config before reload
+    - BUG/MINOR: init: always ensure that global.rlimit_nofile matches actual limits
+    - BUG/MINOR: init: ensure that FD limit is raised to the max allowed
+    - Revert "BUG/MINOR: ssl: fix potential memory leak in ssl_sock_load_dh_params()"
+    - BUG/MEDIUM: stream-int: completely detach connection on connect error
+    - DOC: minor typo fixes to improve HTML parsing by haproxy-dconv
+    - BUG/MAJOR: compression: initialize avail_in/next_in even during flush
+    - BUG/MAJOR: stick-counters: possible crash when using sc_trackers with wrong table
+    - BUG/MAJOR: stream: properly mark the server address as unset on connect retry
+    - BUG/MINOR: payload: fix SSLv2 version parser
+    - MINOR: cli: allow the semi-colon to be escaped on the CLI
+    - BUG/MINOR: displayed PCRE version is running release
+    - MINOR: show Built with PCRE version
+    - MINOR: show Running on zlib version
+    - BUG/MINOR: ssl: Check malloc return code
+    - BUG/MINOR: ssl: prevent multiple entries for the same certificate
+    - BUG/MINOR: systemd: make the wrapper return a non-null status code on error
+    - BUILD/CLEANUP: systemd: avoid a warning due to mixed code and declaration
+    - BUG/MINOR: systemd: always restore signals before execve()
+    - BUG/MINOR: systemd: check return value of calloc()
+    - MINOR: systemd: report it when execve() fails
+    - BUG/MEDIUM: systemd: let the wrapper know that haproxy has completed or failed
+    - BUILD: poll: remove unused hap_fd_isset() which causes a warning with clang
+    - DOC: Fix typo in description of `-st` parameter in man page
+    - BUG/MEDIUM: peers: fix use after free in peer_session_create()
+    - BUG/MEDIUM: systemd-wrapper: return correct exit codes
+    - BUG/MINOR: stick-table: handle out-of-memory condition gracefully
+    - BUG/MEDIUM: connection: check the control layer before stopping polling
+    - BUG/MEDIUM: stick-table: fix regression caused by recent fix for out-of-memory
+    - BUG/MINOR: cli: properly decrement ref count on tables during failed dumps
+    - BUG/MINOR: cli: fix pointer size when reporting data/transport layer name
+    - BUG/MINOR: cli: dequeue from the proxy when changing a maxconn
+    - BUG/MEDIUM: proxy: return "none" and "unknown" for unknown LB algos
+    - BUG/MINOR: http: don't send an extra CRLF after a Set-Cookie in a redirect
+    - DOC: fix small typo in fe_id (backend instead of frontend)
+    - BUG/MEDIUM: ssl: properly reset the reused_sess during a forced handshake
+    - BUG/MINOR: backend: nbsrv() should return 0 if backend is disabled
+    - BUG/MINOR: systemd: potential zombie processes
+
+2016/05/10 : 1.5.18
+    - DOC: Clarify IPv4 address / mask notation rules
+    - CLEANUP: fix inconsistency between fd->iocb, proto->accept and accept()
+    - BUG/MEDIUM: fix maxaccept computation on per-process listeners
+    - BUG/MINOR: listener: stop unbound listeners on startup
+    - BUG/MINOR: fix maxaccept computation according to the frontend process range
+    - MEDIUM: unblock signals on startup.
+    - BUG/MEDIUM: channel: don't allow to overwrite the reserve until connected
+    - BUG/MEDIUM: channel: incorrect polling condition may delay event delivery
+    - BUG/MEDIUM: channel: fix miscalculation of available buffer space (3rd try)
+    - MINOR: channel: add new function channel_congested()
+    - BUG/MEDIUM: http: fix risk of CPU spikes with pipelined requests from dead client
+    - BUG/MAJOR: channel: fix miscalculation of available buffer space (4th try)
+    - BUG/MEDIUM: stream: ensure the SI_FL_DONT_WAKE flag is properly cleared
+    - BUG/MEDIUM: channel: fix inconsistent handling of 4GB-1 transfers
+    - MINOR: stats: fix typo in help messages
+
+2016/04/13 : 1.5.17
+    - BUG/MINOR: log: Don't use strftime() which can clobber timezone if chrooted
+    - BUG/MINOR: conf: "listener id" expects integer, but its not checked
+    - BUG/MAJOR: Fix crash in http_get_fhdr with exactly MAX_HDR_HISTORY headers
+    - DOC: "addr" parameter applies to both health and agent checks
+    - DOC: timeout client: pointers to timeout http-request
+    - DOC: typo on stick-store response
+    - DOC: typo: ACL subdir match
+    - DOC: typo: maxconn paragraph is wrong due to a wrong buffer size
+    - DOC: typo: req.uri is now replaced by capture.req.uri
+    - DOC: fix "needed" typo
+    - BUG/MINOR : allow to log cookie for tarpit and denied request
+    - BUG/MAJOR: channel: fix miscalculation of available buffer space (2nd try)
+    - DOC: fix discrepancy in the example for http-request redirect
+
+2016/03/14 : 1.5.16
+    - BUG/BUILD: replace haproxy-systemd-wrapper with $(EXTRA) in install-bin.
+    - BUG/MINOR: acl: don't use record layer in req_ssl_ver
+    - BUG: http: do not abort keep-alive connections on server timeout
+    - BUG/MEDIUM: http: switch the request channel to no-delay once done.
+    - MINOR: config: extend the default max hostname length to 64 and beyond
+    - BUG/MEDIUM: http: don't enable auto-close on the response side
+    - BUG/MEDIUM: stream: fix half-closed timeout handling
+    - BUG/MEDIUM: cli: changing compression rate-limiting must require admin level
+    - BUILD: freebsd: double declaration
+    - BUG/MEDIUM: sample: urlp can't match an empty value
+    - BUG/MEDIUM: peers: table entries learned from a remote are pushed to others after a random delay.
+    - BUG/MEDIUM: peers: old stick table updates could be repushed.
+    - CLEANUP: haproxy: using _GNU_SOURCE instead of __USE_GNU macro.
+    - BUG/MINOR: chunk: make chunk_dup() always check and set dst->size
+    - MINOR: chunks: ensure that chunk_strcpy() adds a trailing zero
+    - MINOR: chunks: add chunk_strcat() and chunk_newstr()
+    - MINOR: chunk: make chunk_initstr() take a const string
+    - BUG/MEDIUM: config: Adding validation to stick-table expire value.
+    - BUG/MEDIUM: sample: http_date() doesn't provide the right day of the week
+    - BUG/MEDIUM: channel: fix miscalculation of available buffer space.
+    - BUG/MINOR: stream: don't force retries if the server is DOWN
+    - MINOR: unix: don't mention free ports on EAGAIN
+    - BUG/CLEANUP: CLI: report the proper field states in "show sess"
+    - MINOR: stats: send content-length with the redirect to allow keep-alive
+    - BUG: stream_interface: Reuse connection even if the output channel is empty
+    - DOC: remove old tunnel mode assumptions
+    - DOC: add server name at rate-limit sessions example
+    - BUG/MEDIUM: ssl: fix off-by-one in ALPN list allocation
+    - BUG/MEDIUM: ssl: fix off-by-one in NPN list allocation
+    - BUG/MEDIUM: stats: stats bind-process doesn't propagate the process mask correctly
+    - BUG/MINOR: http: Be sure to process all the data received from a server
+    - BUG/MEDIUM: chunks: always reject negative-length chunks
+    - BUG/MINOR: systemd: ensure we don't miss signals
+    - BUG/MINOR: systemd: report the correct signal in debug message output
+    - BUG/MINOR: systemd: propagate the correct signal to haproxy
+    - MINOR: systemd: ensure a reload doesn't mask a stop
+    - CLEANUP: stats: Avoid computation with uninitialized bits.
+    - CLEANUP: pattern: Ignore unknown samples in pat_match_ip().
+    - CLEANUP: map: Avoid memory leak in out-of-memory condition.
+    - BUG/MINOR: tcpcheck: conf parsing error when no port configured on server and last rule is a CONNECT with no port
+    - BUG/MINOR: tcpcheck: fix incorrect list usage resulting in failure to load certain configs
+    - MINOR: cfgparse: warn when uid parameter is not a number
+    - MINOR: cfgparse: warn when gid parameter is not a number
+    - BUG/MINOR: standard: Avoid free of non-allocated pointer
+    - BUG/MINOR: pattern: Avoid memory leak on out-of-memory condition
+    - CLEANUP: http: fix a build warning introduced by a recent fix
+    - BUG/MINOR: log: GMT offset not updated when entering/leaving DST
+
+2015/11/01 : 1.5.15
+    - BUG/MINOR: log: missing some ARGC_* entries in fmt_directives()
+    - DOC: usesrc root privileges requirements
+    - BUILD: ssl: Allow building against libssl without SSLv3.
+    - DOC/MINOR: fix OpenBSD versions where haproxy works
+    - BUG/MINOR: http/sample: gmtime/localtime can fail
+    - DOC: typo in 'redirect', 302 code meaning
+    - DOC: mention that %ms is left-padded with zeroes.
+    - CLEANUP: .gitignore: ignore more test files
+    - CLEANUP: .gitignore: finally ignore everything but what is known.
+    - MEDIUM: config: emit a warning on a frontend without listener
+    - BUG/MEDIUM: counters: ensure that src_{inc,clr}_gpc0 creates a missing entry
+    - DOC: ssl: missing LF
+    - DOC: fix example of http-request using ssl_fc_session_id
+    - BUG/MINOR: http: remove stupid HTTP_METH_NONE entry
+    - BUG/MAJOR: http: don't call http_send_name_header() after an error
+    - BUG/MINOR: tools: make str2sa_range() report unresolvable addresses
+    - BUG/MEDIUM: acl: always accept match "found"
+    - DOC: clarify how to make use of abstract sockets in socat
+    - CLEANUP: config: make the errorloc/errorfile messages less confusing
+    - BUG/MINOR: config: check that tune.bufsize is always positive
+    - BUG/MEDIUM: proxy: ignore stopped peers
+    - BUG/MEDIUM: proxy: do not wake stopped proxies' tasks during soft_stop()
+    - BUG/MINOR: http: Add OPTIONS in supported http methods (found by find_http_meth)
+    - BUILD: enable build on Linux/s390x
+    - DOC: backend section missing parameters
+    - DOC: stats paramaters available in frontend
+    - BUG/MINOR: config: make the stats socket pass the correct proxy to the parsers
+    - BUG/MEDIUM: pattern: fixup use_after_free in the pat_ref_delete_by_id
+    - CLEANUP: don't ignore debian/ directory if present
+    - FIX: small typo in an example using the "Referer" header
+    - BUG/MEDIUM: config: count memory limits on 64 bits, not 32
+    - DOC: add a CONTRIBUTING file
+
 2015/07/03 : 1.5.14
     - BUILD/MINOR: tools: rename popcount to my_popcountl
     - BUG/MAJOR: buffers: make the buffer_slow_realign() function respect output data
diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100644
index 0000000..968da4f
--- /dev/null
+++ b/CONTRIBUTING
@@ -0,0 +1,654 @@
+                      HOW TO GET YOUR CODE ACCEPTED IN HAPROXY
+                     READ THIS CAREFULLY BEFORE SUBMITTING CODE
+
+THIS DOCUMENT PROVIDES SOME RULES TO FOLLOW WHEN SENDING CONTRIBUTIONS. PATCHES
+NOT FOLLOWING THESE RULES WILL SIMPLY BE REJECTED IN ORDER TO PROTECT ALL OTHER
+RESPECTFUL CONTRIBUTORS' VALUABLE TIME.
+
+
+Background
+----------
+
+During the development cycle of version 1.6, much more time was spent reviewing
+poor quality submissions, fixing them and troubleshooting the bugs they
+introduced than doing any development work. This is not acceptable as it ends
+up with people actually slowing down the project for the features they're the
+only ones interested in. On the other end of the scale, there are people who
+make the effort of polishing their work to contribute excellent quality work
+which doesn't even require a review. Contrary to what newcomers may think, it's
+very easy to reach that level of quality and get your changes accepted quickly,
+even late in the development cycle. It only requires that you make your homework
+and not rely on others to do it for you. The most important point is that
+HAProxy is a community-driven project, all involved participants must respect
+all other ones' time and work.
+
+
+Preparation
+-----------
+
+It is possible that you'll want to add a specific feature to satisfy your needs
+or one of your customers'. Contributions are welcome, however maintainers are
+often very picky about changes. Patches that change massive parts of the code,
+or that touch the core parts without any good reason will generally be rejected
+if those changes have not been discussed first.
+
+The proper place to discuss your changes is the HAProxy Mailing List. There are
+enough skilled readers to catch hazardous mistakes and to suggest improvements.
+There is no other place where you'll find as many skilled people on the project,
+and these people can help you get your code integrated quickly. You can
+subscribe to it by sending an empty e-mail at the following address :
+
+                        haproxy+subscribe@formilux.org
+
+If you have an idea about something to implement, *please* discuss it on the
+list first. It has already happened several times that two persons did the same
+thing simultaneously. This is a waste of time for both of them. It's also very
+common to see some changes rejected because they're done in a way that will
+conflict with future evolutions, or that does not leave a good feeling. It's
+always unpleasant for the person who did the work, and it is unpleasant in
+general because value people's time and efforts are valuable and would be better
+spent working on something else. That would not happen if these were discussed
+first. There is no problem posting work in progress to the list, it happens
+quite often in fact. Also, don't waste your time with the doc when submitting
+patches for review, only add the doc with the patch you consider ready to merge.
+
+Another important point concerns code portability. Haproxy requires gcc as the
+C compiler, and may or may not work with other compilers. However it's known to
+build using gcc 2.95 or any later version. As such, it is important to keep in
+mind that certain facilities offered by recent versions must not be used in the
+code :
+
+  - declarations mixed in the code (requires gcc >= 3.x and is a bad practice)
+  - GCC builtins without checking for their availability based on version and
+    architecture ;
+  - assembly code without any alternate portable form for other platforms
+  - use of stdbool.h, "bool", "false", "true" : simply use "int", "0", "1"
+  - in general, anything which requires C99 (such as declaring variables in
+    "for" statements)
+
+Since most of these restrictions are just a matter of coding style, it is
+normally not a problem to comply.
+
+If your work is very confidential and you can't publicly discuss it, you can
+also mail willy@haproxy.org directly about it, but your mail may be waiting
+several days in the queue before you get a response. Retransmit if you don't
+get a response by one week.
+
+If you'd like a feature to be added but you think you don't have the skills to
+implement it yourself, you should follow these steps :
+
+    1. discuss the feature on the mailing list. It is possible that someone
+       else has already implemented it, or that someone will tell you how to
+       proceed without it, or even why not to do it. It is also possible that
+       in fact it's quite easy to implement and people will guide you through
+       the process. That way you'll finally have YOUR patch merged, providing
+       the feature YOU need.
+
+    2. if you really can't code it yourself after discussing it, then you may
+       consider contacting someone to do the job for you. Some people on the
+       list might sometimes be OK with trying to do it.
+
+
+Rules : the 12 laws of patch contribution
+-----------------------------------------
+
+People contributing patches must apply the following rules. That may sound heavy
+at the beginning but it's common sense more than anything else and contributors
+do not think about them anymore after a few patches.
+
+1) Before modifying some code, you have read the LICENSE file ("main license")
+   coming with the sources, and all the files this file references. Certain
+   files may be covered by different licenses, in which case it will be
+   indicated in the files themselves. In any case, you agree to respect these
+   licenses and to contribute your changes under the same licenses. If you want
+   to create new files, they will be under the main license, or any license of
+   your choice that you have verified to be compatible with the main license,
+   and that will be explicitly mentionned in the affected files. The project's
+   maintainers are free to reject contributions proposing license changes they
+   feel are not appropriate or could cause future trouble.
+
+2) Your work may only be based on the latest development version. No development
+   is made on a stable branch. If your work needs to be applied to a stable
+   branch, it will first be applied to the development branch and only then will
+   be backported to the stable branch. You are responsible for ensuring that
+   your work correctly applies to the development version. If at any moment you
+   are going to work on restructuring something important which may impact other
+   contributors, the rule that applies is that the first sent is the first
+   served. However it is considered good practice and politeness to warn others
+   in advance if you know you're going to make changes that may force them to
+   re-adapt their code, because they did probably not expect to have to spend
+   more time discovering your changes and rebasing their work.
+
+3) You have read and understood "doc/codingstyle.txt", and you're actively
+   determined to respect it and to enforce it on your coworkers if you're going
+   to submit a team's work. We don't care what text editor you use, whether it's
+   an hex editor, cat, vi, emacs, Notepad, Word, or even Eclipse. The editor is
+   only the interface between you and the text file. What matters is what is in
+   the text file in the end. The editor is not an excuse for submitting poorly
+   indented code, which only proves that the person has no consideration for
+   quality and/or has done it in a hurry (probably worse). Please note that most
+   bugs were found in low-quality code. Reviewers know this and tend to be much
+   more reluctant to accept poorly formated code because by experience they
+   won't trust their author's ability to write correct code. It is also worth
+   noting that poor quality code is painful to read and may result in nobody
+   willing to waste their time even reviewing your work.
+
+4) The time it takes for you to polish your code is always much smaller than the
+   time it takes others to do it for you, because they always have to wonder if
+   what they see is intended (meaning they didn't understand something) or if it
+   is a mistake that needs to be fixed. And since there are less reviewers than
+   submitters, it is vital to spread the effort closer to where the code is
+   written and not closer to where it gets merged. For example if you have to
+   write a report for a customer that your boss wants to review before you send
+   it to the customer, will you throw on his desk a pile of paper with stains,
+   typos and copy-pastes everywhere ? Will you say "come on, OK I made a mistake
+   in the company's name but they will find it by themselves, it's obvious it
+   comes from us" ? No. When in doubt, simply ask for help on the mailing list.
+
+5) There are four levels of importance of quality in the project :
+
+   - The most important one, and by far, is the quality of the user-facing
+     documentation. This is the first contact for most users and it immediately
+     gives them an accurate idea of how the project is maintained. Dirty docs
+     necessarily belong to a dirty project. Be careful to the way the text you
+     add is presented and indented. Be very careful about typos, usual mistakes
+     such as double consonants when only one is needed or "it's" instead of
+     "its", don't mix US english and UK english in the same paragraph, etc.
+     When in doubt, check in a dictionary. Fixes for existing typos in the doc
+     are always welcome and chasing them is a good way to become familiar with
+     the project and to get other participants' respect and consideration.
+
+   - The second most important level is user-facing messages emitted by the
+     code. You must try to see all the messages your code produces to ensure
+     they are understandable outside of the context where you wrote them,
+     because the user often doesn't expect them. That's true for warnings, and
+     that's even more important for errors which prevent the program from
+     working and which require an immediate and well understood fix in the
+     configuration. It's much better to say "line 35: compression level must be
+     an integer between 1 and 9" than "invalid argument at line 35". In HAProxy,
+     error handling roughly represents half of the code, and that's about 3/4 of
+     the configuration parser. Take the time to do something you're proud of. A
+     good rule of thumb is to keep in mind that your code talks to a human and
+     tries to teach him/her how to proceed. It must then speak like a human.
+
+   - The third most important level is the code and its accompanying comments,
+     including the commit message which is a complement to your code and
+     comments. It's important for all other contributors that the code is
+     readable, fluid, understandable and that the commit message describes what
+     was done, the choices made, the possible alternatives you thought about,
+     the reason for picking this one and its limits if any. Comments should be
+     written where it's easy to have a doubt or after some error cases have been
+     wiped out and you want to explain what possibilities remain. All functions
+     must have a comment indicating what they take on input and what they
+     provide on output. Please adjust the comments when you copy-paste a
+     function or change its prototype, this type of lazy mistake is too common
+     and very confusing when reading code later to debug an issue. Do not forget
+     that others will feel really angry at you when they have to dig into your
+     code for a bug that your code caused and they feel like this code is dirty
+     or confusing, that the commit message doesn't explain anything useful and
+     that the patch should never have been accepted in the first place. That
+     will strongly impact your reputation and will definitely affect your
+     chances to contribute again!
+
+   - The fourth level of importance is in the technical documentation that you
+     may want to add with your code. Technical documentation is always welcome
+     as it helps others make the best use of your work and to go exactly in the
+     direction you thought about during the design. This is also what reduces
+     the risk that your design gets changed in the near future due to a misuse
+     and/or a poor understanding. All such documentation is actually considered
+     as a bonus. It is more important that this documentation exists than that
+     it looks clean. Sometimes just copy-pasting your draft notes in a file to
+     keep a record of design ideas is better than losing them. Please do your
+     best so that other ones can read your doc. If these docs require a special
+     tool such as a graphics utility, ensure that the file name makes it
+     unambiguous how to process it. So there are no rules here for the contents,
+     except one.  Please write the date in your file. Design docs tend to stay
+     forever and to remain long after they become obsolete. At this point that
+     can cause harm more than it can help. Writing the date in the document
+     helps developers guess the degree of validity and/or compare them with the
+     date of certain commits touching the same area.
+
+6) All text files and commit messages are written using the US-ASCII charset.
+   Please be careful that your contributions do not contain any character not
+   printable using this charset, as they will render differently in different
+   editors and/or terminals. Avoid latin1 and more importantly UTF-8 which some
+   editors tend to abuse to replace some US-ASCII characters with their
+   typographic equivalent which aren't readable anymore in other editors. The
+   only place where alternative charsets are tolerated is in your name in the
+   commit message, but it's at your own risk as it can be mangled during the
+   merge. Anyway if you have an e-mail address, you probably have a valid
+   US-ASCII representation for it as well.
+
+7) Be careful about comments when you move code around. It's not acceptable that
+   a block of code is moved to another place leaving irrelevant comments at the
+   old place, just like it's not acceptable that a function is duplicated without
+   the comments being adjusted. The example below started to become quite common
+   during the 1.6 cycle, it is not acceptable and wastes everyone's time :
+
+      /* Parse switching <str> to build rule <rule>. Returns 0 on error. */
+      int parse_switching_rule(const char *str, struct rule *rule)
+      {
+       ...
+      }
+
+      /* Parse switching <str> to build rule <rule>. Returns 0 on error. */
+      void execute_switching_rule(struct rule *rule)
+      {
+       ...
+      }
+
+   This patch is not acceptable either (and it's unfortunately not that rare) :
+
+    +   if (!session || !arg || list_is_empty(&session->rules->head))
+    +         return 0;
+    +
+        /* Check if session->rules is valid before dereferencing it */
+        if (!session->rules_allocated)
+              return 0;
+
+    -   if (!arg || list_is_empty(&session->rules->head))
+    -         return 0;
+    -
+
+8) Limit the length of your identifiers in the code. When your identifiers start
+   to sound like sentences, it's very hard for the reader to keep on track with
+   what operation they are observing. Also long names force expressions to fit
+   on several lines which also cause some difficulties to the reader. See the
+   example below :
+
+       int file_name_len_including_global_path;
+       int file_name_len_without_global_path;
+       int global_path_len_or_zero_if_default;
+
+       if (global_path)
+               global_path_len_or_zero_if_default = strlen(global_path);
+       else
+               global_path_len_or_zero_if_default = 0;
+
+       file_name_len_without_global_path = strlen(file_name);
+       file_name_len_including_global_path =
+               file_name_len_without_global_path + 1 + /* for '/' */
+               global_path_len_or_zero_if_default ?
+                       global_path_len_or_zero_if_default : default_path_len;
+
+   Compare it to this one :
+
+       int f, p;
+
+       p = global_path ? strlen(global_path) : default_path_len;
+       f = p + 1 + strlen(file_name);  /* 1 for '/' */
+
+   A good rule of thumb is that if your identifiers start to contain more than
+   3 words or more than 15 characters, they can become confusing. For function
+   names it's less important especially if these functions are rarely used or
+   are used in a complex context where it is important to differenciate between
+   their multiple variants.
+
+9) Your patches should be sent in "diff -up" format, which is also the format
+   used by Git. This means the "unified" diff format must be used exclusively,
+   and with the function name printed in the diff header of each block. That
+   significantly helps during reviews. Keep in mind that most reviews are done
+   on the patch and not on the code after applying the patch. Your diff must
+   keep some context (3 lines above and 3 lines below) so that there's no doubt
+   where the code has to be applied. Don't change code outside of the context of
+   your patch (eg: take care of not adding/removing empty lines once you remove
+   your debugging code). If you are using Git (which is strongly recommended),
+   please produce your patches using "git format-patch" and not "git diff", and
+   always use "git show" after doing a commit to ensure it looks good, and
+   enable syntax coloring that will automatically report in red the trailing
+   spaces or tabs that your patch added to the code and that must absolutely be
+   removed. These ones cause a real pain to apply patches later because they
+   mangle the context in an invisible way. Such patches with trailing spaces at
+   end of lines will be rejected.
+
+10) Please cut your work in series of patches that can be independantly reviewed
+    and merged. Each patch must do something on its own that you can explain to
+    someone without being ashamed of what you did. For example, you must not say
+    "This is the patch that implements SSL, it was tricky". There's clearly
+    something wrong there, your patch will be huge, will definitely break things
+    and nobody will be able to figure what exactly introduced the bug. However
+    it's much better to say "I needed to add some fields in the session to store
+    the SSL context so this patch does this and doesn't touch anything else, so
+    it's safe". Also when dealing with series, you will sometimes fix a bug that
+    one of your patches introduced. Please do merge these fixes (eg: using git
+    rebase -i and squash or fixup), as it is not acceptable to see patches which
+    introduce known bugs even if they're fixed later. Another benefit of cleanly
+    splitting patches is that if some of your patches need to be reworked after
+    a review, the other ones can still be merged so that you don't need to care
+    about them anymore. When sending multiple patches for review, prefer to send
+    one e-mail per patch than all patches in a single e-mail. The reason is that
+    not everyone is skilled in all areas nor has the time to review everything
+    at once. With one patch per e-mail, it's easy to comment on a single patch
+    without giving an opinion on the other ones, especially if a long thread
+    starts about one specific patch on the mailing list. "git send-email" does
+    that for you though it requires a few trials before getting it right.
+
+11) Please properly format your commit messages. While it's not strictly
+    required to use Git, it is strongly recommended because it helps you do the
+    cleanest job with the least effort. Patches always have the format of an
+    e-mail made of a subject, a description and the actual patch. If you're
+    sending a patch as an e-mail formated this way, it can quickly be applied
+    with limited effort so that's acceptable. But in any case, it is important
+    that there is a clean description of what the patch does, the motivation for
+    what it does, why it's the best way to do it, its impacts, and what it does
+    not yet cover. Also, in HAProxy, like many projects which take a great care
+    of maintaining stable branches, patches are reviewed later so that some of
+    them can be backported to stable releases. While reviewing hundreds of
+    patches can seem cumbersome, with a proper formating of the subject line it
+    actually becomes very easy. For example, here's how one can find patches
+    that need to be reviewed for backports (bugs and doc) between since commit
+    ID 827752e :
+
+        $ git log --oneline 827752e.. | grep 'BUG\|DOC'
+        0d79cf6 DOC: fix function name
+        bc96534 DOC: ssl: missing LF
+        10ec214 BUG/MEDIUM: lua: the lua fucntion Channel:close() causes a segf
+        bdc97a8 BUG/MEDIUM: lua: outgoing connection was broken since 1.6-dev2
+        ba56d9c DOC: mention support for RFC 5077 TLS Ticket extension in start
+        f1650a8 DOC: clarify some points about SSL and the proxy protocol
+        b157d73 BUG/MAJOR: peers: fix current table pointer not re-initialized
+        e1ab808 BUG/MEDIUM: peers: fix wrong message id on stick table updates
+        cc79b00 BUG/MINOR: ssl: TLS Ticket Key rotation broken via socket comma
+        d8e42b6 DOC: add new file intro.txt
+        c7d7607 BUG/MEDIUM: lua: bad error processing
+        386a127 DOC: match several lua configuration option names to those impl
+        0f4eadd BUG/MEDIUM: counters: ensure that src_{inc,clr}_gpc0 creates a
+
+    It is made possible by the fact that subject lines are properly formated and
+    always respect the same principle : one part indicating the nature and
+    severity of the patch, another one to indicate which subsystem is affected,
+    and the last one is a succint description of the change, with the important
+    part at the beginning so that it's obvious what it does even when lines are
+    truncated like above. The whole stable maintenance process relies on this.
+    For this reason, it is mandatory to respect some easy rules regarding the
+    way the subject is built. Please see the section below for more information
+    regarding this formating.
+
+12) When submitting changes, please always CC the mailing list address so that
+    everyone gets a chance to spot any issue in your code. It will also serve
+    as an advertisement for your work, you'll get more testers quicker and
+    you'll feel better knowing that people really use your work. It is also
+    important to CC any author mentionned in the file you change, or a subsystem
+    maintainers whose address is mentionned in a MAINTAINERS file. Not everyone
+    reads the list on a daily basis so it's very easy to miss some changes.
+    Don't consider it as a failure when a reviewer tells you you have to modify
+    your patch, actually it's a success because now you know what is missing for
+    your work to get accepted.  That's why you should not hesitate to CC enough
+    people. Don't copy people who have no deal with your work area just because
+    you found their address on the list. That's the best way to appear careless
+    about their time and make them reject your changes in the future.
+
+
+Patch classifying rules
+-----------------------
+
+There are 3 criteria of particular importance in any patch :
+  - its nature (is it a fix for a bug, a new feature, an optimization, ...)
+  - its importance, which generally reflects the risk of merging/not merging it
+  - what area it applies to (eg: http, stats, startup, config, doc, ...)
+
+It's important to make these 3 criteria easy to spot in the patch's subject,
+because it's the first (and sometimes the only) thing which is read when
+reviewing patches to find which ones need to be backported to older versions.
+It also helps when trying to find which patch is the most likely to have caused
+a regression.
+
+Specifically, bugs must be clearly easy to spot so that they're never missed.
+Any patch fixing a bug must have the "BUG" tag in its subject. Most common
+patch types include :
+
+  - BUG      fix for a bug. The severity of the bug should also be indicated
+             when known. Similarly, if a backport is needed to older versions,
+             it should be indicated on the last line of the commit message. If
+             the bug has been identified as a regression brought by a specific
+             patch or version, this indication will be appreciated too. New
+             maintenance releases are generally emitted when a few of these
+             patches are merged. If the bug is a vulnerability for which a CVE
+             identifier was assigned before you publish the fix, you can mention
+             it in the commit message, it will help distro maintainers.
+
+  - CLEANUP  code cleanup, silence of warnings, etc... theorically no impact.
+             These patches will rarely be seen in stable branches, though they
+             may appear when they remove some annoyance or when they make
+             backporting easier. By nature, a cleanup is always of minor
+             importance and it's not needed to mention it.
+
+  - DOC      updates to any of the documentation files, including README. Many
+             documentation updates are backported since they don't impact the
+             product's stability and may help users avoid bugs. So please
+             indicate in the commit message if a backport is desired. When a
+             feature gets documented, it's preferred that the doc patch appears
+             in the same patch or after the feature patch, but not before, as it
+             becomes confusing when someone working on a code base including
+             only the doc patch won't understand why a documented feature does
+             not work as documented.
+
+  - REORG    code reorganization. Some blocks may be moved to other places,
+             some important checks might be swapped, etc... These changes
+             always present a risk of regression. For this reason, they should
+             never be mixed with any bug fix nor functional change. Code is
+             only moved as-is. Indicating the risk of breakage is highly
+             recommended. Minor breakage is tolerated in such patches if trying
+             to fix it at once makes the whole change even more confusing. That
+             may happen for example when some #ifdefs need to be propagated in
+             every file consecutive to the change.
+
+  - BUILD    updates or fixes for build issues. Changes to makefiles also fall
+             into this category. The risk of breakage should be indicated if
+             known. It is also appreciated to indicate what platforms and/or
+             configurations were tested after the change.
+
+  - OPTIM    some code was optimised. Sometimes if the regression risk is very
+             low and the gains significant, such patches may be merged in the
+             stable branch. Depending on the amount of code changed or replaced
+             and the level of trust the author has in the change, the risk of
+             regression should be indicated.
+
+  - RELEASE  release of a new version (development or stable).
+
+  - LICENSE  licensing updates (may impact distro packagers).
+
+
+When the patch cannot be categorized, it's best not to put any type tag. This is
+commonly the case for new features, which development versions are mostly made
+of.
+
+Additionally, the importance of the patch or severity of the bug it fixes must
+be indicated when relevant. A single upper-case word is preferred, among :
+
+  - MINOR    minor change, very low risk of impact. It is often the case for
+             code additions that don't touch live code. As a rule of thumb, a
+             patch tagged "MINOR" is safe enough to be backported to stable
+             branches. For a bug, it generally indicates an annoyance, nothing
+             more.
+
+  - MEDIUM   medium risk, may cause unexpected regressions of low importance or
+             which may quickly be discovered. In short, the patch is safe but
+             touches working areas and it is always possible that you missed
+             something you didn't know existed (eg: adding a "case" entry or
+             an error message after adding an error code to an enum). For a bug,
+             it generally indicates something odd which requires changing the
+             configuration in an undesired way to work around the issue.
+
+  - MAJOR    major risk of hidden regression. This happens when large parts of
+             the code are rearranged, when new timeouts are introduced, when
+             sensitive parts of the session scheduling are touched, etc... We
+             should only exceptionally find such patches in stable branches when
+             there is no other option to fix a design issue. For a bug, it
+             indicates severe reliability issues for which workarounds are
+             identified with or without performance impacts.
+
+  - CRITICAL medium-term reliability or security is at risk and workarounds,
+             if they exist, might not always be acceptable. An upgrade is
+             absolutely required. A maintenance release may be emitted even if
+             only one of these bugs are fixed. Note that this tag is only used
+             with bugs. Such patches must indicate what is the first version
+             affected, and if known, the commit ID which introduced the issue.
+
+The expected length of the commit message grows with the importance of the
+change. While a MINOR patch may sometimes be described in 1 or 2 lines, MAJOR
+or CRITICAL patches cannot have less than 10-15 lines to describe exactly the
+impacts otherwise the submitter's work will be considered as rough sabotage.
+
+For BUILD, DOC and CLEANUP types, this tag is not always relevant and may be
+omitted.
+
+The area the patch applies to is quite important, because some areas are known
+to be similar in older versions, suggesting a backport might be desirable, and
+conversely, some areas are known to be specific to one version. The area is a
+single-word lowercase name the contributor find clear enough to describe what
+part is being touched. The following tags are suggested but not limitative :
+
+  - examples  example files. Be careful, sometimes these files are packaged.
+
+  - tests     regression test files. No code is affected, no need to upgrade.
+
+  - init      initialization code, arguments parsing, etc...
+
+  - config    configuration parser, mostly used when adding new config keywords
+
+  - http      the HTTP engine
+
+  - stats     the stats reporting engine
+
+  - cli       the stats socket CLI
+
+  - checks    the health checks engine (eg: when adding new checks)
+
+  - sample    the sample fetch system (new fetch or converter functions)
+
+  - acl       the ACL processing core or some ACLs from other areas
+
+  - peers     the peer synchronization engine
+
+  - lua       the Lua scripting engine
+
+  - listeners everything related to incoming connection settings
+
+  - frontend  everything related to incoming connection processing
+
+  - backend   everything related to LB algorithms and server farm
+
+  - session   session processing and flags (very sensible, be careful)
+
+  - server    server connection management, queueing
+
+  - ssl       the SSL/TLS interface
+
+  - proxy     proxy maintenance (start/stop)
+
+  - log       log management
+
+  - poll      any of the pollers
+
+  - halog     the halog sub-component in the contrib directory
+
+  - contrib   any addition to the contrib directory
+
+Other names may be invented when more precise indications are meaningful, for
+instance : "cookie" which indicates cookie processing in the HTTP core. Last,
+indicating the name of the affected file is also a good way to quickly spot
+changes. Many commits were already tagged with "stream_sock" or "cfgparse" for
+instance.
+
+It is required that the type of change and the severity when relevant are
+indicated, as well as the touched area when relevant as well in the patch
+subject. Normally, we would have the 3 most often. The two first criteria should
+be present before a first colon (':'). If both are present, then they should be
+delimited with a slash ('/'). The 3rd criterion (area) should appear next, also
+followed by a colon. Thus, all of the following messages are valid :
+
+Examples of messages :
+  - DOC: document options forwardfor to logasap
+  - DOC/MAJOR: reorganize the whole document and change indenting
+  - BUG: stats: connection reset counters must be plain ascii, not HTML
+  - BUG/MINOR: stats: connection reset counters must be plain ascii, not HTML
+  - MEDIUM: checks: support multi-packet health check responses
+  - RELEASE: Released version 1.4.2
+  - BUILD: stats: stdint is not present on solaris
+  - OPTIM/MINOR: halog: make fgets parse more bytes by blocks
+  - REORG/MEDIUM: move syscall redefinition to specific places
+
+Please do not use square brackets anymore around the tags, because they induce
+more work when merging patches, which need to be hand-edited not to lose the
+enclosed part.
+
+In fact, one of the only square bracket tags that still makes sense is '[RFC]'
+at the beginning of the subject, when you're asking for someone to review your
+change before getting it merged. If the patch is OK to be merged, then it can
+be merge as-is and the '[RFC]' tag will automatically be removed. If you don't
+want it to be merged at all, you can simply state it in the message, or use an
+alternate 'WIP/' prefix in front of your tag tag ("work in progress").
+
+The tags are not rigid, follow your intuition first, and they may be readjusted
+when your patch is merged. It may happen that a same patch has a different tag
+in two distinct branches. The reason is that a bug in one branch may just be a
+cleanup or safety measure in the other one because the code cannot be triggered.
+
+
+Working with Git
+----------------
+
+For a more efficient interaction between the mainline code and your code, you
+are strongly encouraged to try the Git version control system :
+
+                        http://git-scm.com/
+
+It's very fast, lightweight and lets you undo/redo your work as often as you
+want, without making your mistakes visible to the rest of the world. It will
+definitely help you contribute quality code and take other people's feedback
+in consideration. In order to clone the HAProxy Git repository :
+
+   $ git clone http://git.haproxy.org/git/haproxy.git/       (development)
+
+If you decide to use Git for your developments, then your commit messages will
+have the subject line in the format described above, then the whole description
+of your work (mainly why you did it) will be in the body. You can directly send
+your commits to the mailing list, the format is convenient to read and process.
+
+It is recommended to create a branch for your work that is based on the master
+branch :
+
+   $ git checkout -b 20150920-fix-stats master
+
+You can then do your work and even experiment with multiple alternatives if you
+are not completely sure that your solution is the best one :
+
+   $ git checkout -b 20150920-fix-stats-v2
+
+Then reorder/merge/edit your patches :
+
+   $ git rebase -i master
+
+When you think you're ready, reread your whole patchset to ensure there is no
+formating or style issue :
+
+   $ git show master..
+
+And once you're satisfied, you should update your master branch to be sure that
+nothing changed during your work (only needed if you left it unattended for days
+or weeks) :
+
+   $ git checkout -b 20150920-fix-stats-rebased
+   $ git fetch origin master:master
+   $ git rebase master
+
+You can build a list of patches ready for submission like this :
+
+   $ git format-patch master
+
+The output files are the patches ready to be sent over e-mail, either via a
+regular e-mail or via git send-email (carefully check the man page). Don't
+destroy your other work branches until your patches get merged, it may happen
+that earlier designs will be preferred for various reasons. Patches should be
+sent to the mailing list : haproxy@formilux.org and CCed to relevant subsystem
+maintainers or authors of the modified files if their address appears at the
+top of the file.
+
+Please don't send pull-requests, they are really unconvenient. First, a pull
+implies a merge operation and the code doesn't move fast enough to justify the
+use of merges. Second, pull requests are not easily commented on by the
+project's participants, contrary to e-mails where anyone is allowed to have an
+opinion and to express it.
+
+-- end
diff --git a/Makefile b/Makefile
index 9556069..e3199b2 100644
--- a/Makefile
+++ b/Makefile
@@ -719,10 +719,9 @@
 		install -m 644 doc/$$x.txt "$(DESTDIR)$(DOCDIR)" ; \
 	done
 
-install-bin: haproxy haproxy-systemd-wrapper
+install-bin: haproxy $(EXTRA)
 	install -d "$(DESTDIR)$(SBINDIR)"
-	install haproxy "$(DESTDIR)$(SBINDIR)"
-	install haproxy-systemd-wrapper "$(DESTDIR)$(SBINDIR)"
+	install haproxy $(EXTRA) "$(DESTDIR)$(SBINDIR)"
 
 install: install-bin install-man install-doc
 
diff --git a/README b/README
index add7f06..52fc31a 100644
--- a/README
+++ b/README
@@ -1,9 +1,9 @@
                          ----------------------
                              HAProxy how-to
                          ----------------------
-                             version 1.5.14
+                               version 1.5
                              willy tarreau
-                               2015/07/02
+                               2016/12/25
 
 
 1) How to build it
@@ -39,7 +39,7 @@
   - solaris     for Solaris 8 or 10 (others untested)
   - freebsd     for FreeBSD 5 to 10 (others untested)
   - osx         for Mac OS/X
-  - openbsd     for OpenBSD 3.1 to 5.2 (others untested)
+  - openbsd     for OpenBSD 3.1 and above
   - aix51       for AIX 5.1
   - aix52       for AIX 5.2
   - cygwin      for Cygwin
@@ -281,259 +281,7 @@
 5) How to contribute
 --------------------
 
-It is possible that you'll want to add a specific feature to satisfy your needs
-or one of your customers'. Contributions are welcome, however I'm often very
-picky about changes. I will generally reject patches that change massive parts
-of the code, or that touch the core parts without any good reason if those
-changes have not been discussed first.
-
-The proper place to discuss your changes is the HAProxy Mailing List. There are
-enough skilled readers to catch hazardous mistakes and to suggest improvements.
-I trust a number of them enough to merge a patch if they say it's OK, so using
-the list is the fastest way to get your code reviewed and merged. You can
-subscribe to it by sending an empty e-mail at the following address :
-
-                        haproxy+subscribe@formilux.org
-
-If you have an idea about something to implement, *please* discuss it on the
-list first. It has already happened several times that two persons did the same
-thing simultaneously. This is a waste of time for both of them. It's also very
-common to see some changes rejected because they're done in a way that will
-conflict with future evolutions, or that does not leave a good feeling. It's
-always unpleasant for the person who did the work, and it is unpleasant for me
-too because I value people's time and efforts. That would not happen if these
-were discussed first. There is no problem posting work in progress to the list,
-it happens quite often in fact. Also, don't waste your time with the doc when
-submitting patches for review, only add the doc with the patch you consider
-ready to merge.
-
-Another important point concerns code portability. Haproxy requires gcc as the
-C compiler, and may or may not work with other compilers. However it's known
-to build using gcc 2.95 or any later version. As such, it is important to keep
-in mind that certain facilities offered by recent versions must not be used in
-the code :
-
-  - declarations mixed in the code (requires gcc >= 3.x)
-  - GCC builtins without checking for their availability based on version and
-    architecture ;
-  - assembly code without any alternate portable form for other platforms
-  - use of stdbool.h, "bool", "false", "true" : simply use "int", "0", "1"
-  - in general, anything which requires C99 (such as declaring variables in
-    "for" statements)
-
-Since most of these restrictions are just a matter of coding style, it is
-normally not a problem to comply.
-
-If your work is very confidential and you can't publicly discuss it, you can
-also mail me directly about it, but your mail may be waiting several days in
-the queue before you get a response.
-
-If you'd like a feature to be added but you think you don't have the skills to
-implement it yourself, you should follow these steps :
-
-    1. discuss the feature on the mailing list. It is possible that someone
-       else has already implemented it, or that someone will tell you how to
-       proceed without it, or even why not to do it. It is also possible that
-       in fact it's quite easy to implement and people will guide you through
-       the process. That way you'll finally have YOUR patch merged, providing
-       the feature YOU need.
-
-    2. if you really can't code it yourself after discussing it, then you may
-       consider contacting someone to do the job for you. Some people on the
-       list might sometimes be OK with trying to do it.
-
-Note to contributors: it's very handy when patches comes with a properly
-formated subject. There are 3 criteria of particular importance in any patch :
-
-  - its nature (is it a fix for a bug, a new feature, an optimization, ...)
-  - its importance, which generally reflects the risk of merging/not merging it
-  - what area it applies to (eg: http, stats, startup, config, doc, ...)
-
-It's important to make these 3 criteria easy to spot in the patch's subject,
-because it's the first (and sometimes the only) thing which is read when
-reviewing patches to find which ones need to be backported to older versions.
-
-Specifically, bugs must be clearly easy to spot so that they're never missed.
-Any patch fixing a bug must have the "BUG" tag in its subject. Most common
-patch types include :
-
-  - BUG      fix for a bug. The severity of the bug should also be indicated
-             when known. Similarly, if a backport is needed to older versions,
-             it should be indicated on the last line of the commit message. If
-             the bug has been identified as a regression brought by a specific
-             patch or version, this indication will be appreciated too. New
-             maintenance releases are generally emitted when a few of these
-             patches are merged.
-
-  - CLEANUP  code cleanup, silence of warnings, etc... theorically no impact.
-             These patches will rarely be seen in stable branches, though they
-             may appear when they remove some annoyance or when they make
-             backporting easier. By nature, a cleanup is always minor.
-
-  - REORG    code reorganization. Some blocks may be moved to other places,
-             some important checks might be swapped, etc... These changes
-             always present a risk of regression. For this reason, they should
-             never be mixed with any bug fix nor functional change. Code is
-             only moved as-is. Indicating the risk of breakage is highly
-             recommended.
-
-  - BUILD    updates or fixes for build issues. Changes to makefiles also fall
-             into this category. The risk of breakage should be indicated if
-             known. It is also appreciated to indicate what platforms and/or
-             configurations were tested after the change.
-
-  - OPTIM    some code was optimised. Sometimes if the regression risk is very
-             low and the gains significant, such patches may be merged in the
-             stable branch. Depending on the amount of code changed or replaced
-             and the level of trust the author has in the change, the risk of
-             regression should be indicated.
-
-  - RELEASE  release of a new version (development or stable).
-
-  - LICENSE  licensing updates (may impact distro packagers).
-
-
-When the patch cannot be categorized, it's best not to put any tag. This is
-commonly the case for new features, which development versions are mostly made
-of.
-
-Additionally, the importance of the patch should be indicated when known. A
-single upper-case word is preferred, among :
-
-  - MINOR    minor change, very low risk of impact. It is often the case for
-             code additions that don't touch live code. For a bug, it generally
-             indicates an annoyance, nothing more.
-
-  - MEDIUM   medium risk, may cause unexpected regressions of low importance or
-             which may quickly be discovered. For a bug, it generally indicates
-             something odd which requires changing the configuration in an
-             undesired way to work around the issue.
-
-  - MAJOR    major risk of hidden regression. This happens when I rearrange
-             large parts of code, when I play with timeouts, with variable
-             initializations, etc... We should only exceptionally find such
-             patches in stable branches. For a bug, it indicates severe
-             reliability issues for which workarounds are identified with or
-             without performance impacts.
-
-  - CRITICAL medium-term reliability or security is at risk and workarounds,
-             if they exist, might not always be acceptable. An upgrade is
-             absolutely required. A maintenance release may be emitted even if
-             only one of these bugs are fixed. Note that this tag is only used
-             with bugs. Such patches must indicate what is the first version
-             affected, and if known, the commit ID which introduced the issue.
-
-If this criterion doesn't apply, it's best not to put it. For instance, most
-doc updates and most examples or test files are just added or updated without
-any need to qualify a level of importance.
-
-The area the patch applies to is quite important, because some areas are known
-to be similar in older versions, suggesting a backport might be desirable, and
-conversely, some areas are known to be specific to one version. When the tag is
-used alone, uppercase is preferred for readability, otherwise lowercase is fine
-too. The following tags are suggested but not limitative :
-
- - doc       documentation updates or fixes. No code is affected, no need to
-             upgrade. These patches can also be sent right after a new feature,
-             to document it.
-
- - examples  example files. Be careful, sometimes these files are packaged.
-
- - tests     regression test files. No code is affected, no need to upgrade.
-
- - init      initialization code, arguments parsing, etc...
-
- - config    configuration parser, mostly used when adding new config keywords
-
- - http      the HTTP engine
-
- - stats     the stats reporting engine as well as the stats socket CLI
-
- - checks    the health checks engine (eg: when adding new checks)
-
- - acl       the ACL processing core or some ACLs from other areas
-
- - peers     the peer synchronization engine
-
- - listeners everything related to incoming connection settings
-
- - frontend  everything related to incoming connection processing
-
- - backend   everything related to LB algorithms and server farm
-
- - session   session processing and flags (very sensible, be careful)
-
- - server    server connection management, queueing
-
- - proxy     proxy maintenance (start/stop)
-
- - log       log management
-
- - poll      any of the pollers
-
- - halog     the halog sub-component in the contrib directory
-
- - contrib   any addition to the contrib directory
-
-Other names may be invented when more precise indications are meaningful, for
-instance : "cookie" which indicates cookie processing in the HTTP core. Last,
-indicating the name of the affected file is also a good way to quickly spot
-changes. Many commits were already tagged with "stream_sock" or "cfgparse" for
-instance.
-
-It is desired that AT LEAST one of the 3 criteria tags is reported in the patch
-subject. Ideally, we would have the 3 most often. The two first criteria should
-be present before a first colon (':'). If both are present, then they should be
-delimited with a slash ('/'). The 3rd criterion (area) should appear next, also
-followed by a colon. Thus, all of the following messages are valid :
-
-Examples of messages :
-  - DOC: document options forwardfor to logasap
-  - DOC/MAJOR: reorganize the whole document and change indenting
-  - BUG: stats: connection reset counters must be plain ascii, not HTML
-  - BUG/MINOR: stats: connection reset counters must be plain ascii, not HTML
-  - MEDIUM: checks: support multi-packet health check responses
-  - RELEASE: Released version 1.4.2
-  - BUILD: stats: stdint is not present on solaris
-  - OPTIM/MINOR: halog: make fgets parse more bytes by blocks
-  - REORG/MEDIUM: move syscall redefinition to specific places
-
-Please do not use square brackets anymore around the tags, because they give me
-more work when merging patches. By default I'm asking Git to keep them but this
-causes trouble when patches are prefixed with the [PATCH] tag because in order
-not to store it, I have to hand-edit the patches. So as of now, I will ask Git
-to remove whatever is located between square brackets, which implies that any
-subject formatted the old way will have its tag stripped out.
-
-In fact, one of the only square bracket tags that still makes sense is '[RFC]'
-at the beginning of the subject, when you're asking for someone to review your
-change before getting it merged. If the patch is OK to be merged, then I can
-merge it as-is and the '[RFC]' tag will automatically be removed. If you don't
-want it to be merged at all, you can simply state it in the message, or use an
-alternate '[WIP]' tag ("work in progress").
-
-The tags are not rigid, follow your intuition first, anyway I reserve the right
-to change them when merging the patch. It may happen that a same patch has a
-different tag in two distinct branches. The reason is that a bug in one branch
-may just be a cleanup in the other one because the code cannot be triggered.
-
-
-For a more efficient interaction between the mainline code and your code, I can
-only strongly encourage you to try the Git version control system :
-
-                        http://git-scm.com/
-
-It's very fast, lightweight and lets you undo/redo your work as often as you
-want, without making your mistakes visible to the rest of the world. It will
-definitely help you contribute quality code and take other people's feedback
-in consideration. In order to clone the HAProxy Git repository :
-
-    $ git clone http://git.haproxy.org/git/haproxy-1.5.git    (stable 1.5)
-    $ git clone http://git.haproxy.org/git/haproxy.git/       (development)
-
-If you decide to use Git for your developments, then your commit messages will
-have the subject line in the format described above, then the whole description
-of your work (mainly why you did it) will be in the body. You can directly send
-your commits to the mailing list, the format is convenient to read and process.
+Please carefully read the CONTRIBUTING file that comes with the sources. It is
+mandatory.
 
 -- end
diff --git a/VERDATE b/VERDATE
index 27c7b79..f54ea95 100644
--- a/VERDATE
+++ b/VERDATE
@@ -1,2 +1,2 @@
 $Format:%ci$
-2015/07/02
+2016/12/25
diff --git a/VERSION b/VERSION
index 8df6b88..e7a15ce 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.5.14
+1.5.19
diff --git a/contrib/systemd/haproxy.service.in b/contrib/systemd/haproxy.service.in
index 85937e4..dca81a2 100644
--- a/contrib/systemd/haproxy.service.in
+++ b/contrib/systemd/haproxy.service.in
@@ -3,8 +3,10 @@
 After=network.target
 
 [Service]
-ExecStartPre=@SBINDIR@/haproxy -f /etc/haproxy/haproxy.cfg -c -q
-ExecStart=@SBINDIR@/haproxy-systemd-wrapper -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
+Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid"
+ExecStartPre=@SBINDIR@/haproxy -f $CONFIG -c -q
+ExecStart=@SBINDIR@/haproxy-systemd-wrapper -f $CONFIG -p $PIDFILE
+ExecReload=@SBINDIR@/haproxy -f $CONFIG -c -q
 ExecReload=/bin/kill -USR2 $MAINPID
 KillMode=mixed
 Restart=always
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 6714afb..b246bde 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -2,9 +2,9 @@
                                 HAProxy
                           Configuration Manual
                          ----------------------
-                             version 1.5.14
+                               version 1.5
                              willy tarreau
-                               2015/07/02
+                               2016/12/25
 
 
 This document covers the configuration language as implemented in the version
@@ -1367,10 +1367,11 @@
 option log-separate-errors           (*)  X          X         X         -
 option logasap                       (*)  X          X         X         -
 option mysql-check                        X          -         X         X
-option pgsql-check                        X          -         X         X
 option nolinger                      (*)  X          X         X         X
 option originalto                         X          X         X         X
 option persist                       (*)  X          -         X         X
+option pgsql-check                        X          -         X         X
+option prefer-last-server            (*)  X          -         X         X
 option redispatch                    (*)  X          -         X         X
 option redis-check                        X          -         X         X
 option smtpchk                            X          -         X         X
@@ -1419,18 +1420,18 @@
 server                                    -          -         X         X
 source                                    X          -         X         X
 srvtimeout                  (deprecated)  X          -         X         X
-stats admin                               -          -         X         X
-stats auth                                X          -         X         X
-stats enable                              X          -         X         X
-stats hide-version                        X          -         X         X
-stats http-request                        -          -         X         X
-stats realm                               X          -         X         X
-stats refresh                             X          -         X         X
-stats scope                               X          -         X         X
-stats show-desc                           X          -         X         X
-stats show-legends                        X          -         X         X
-stats show-node                           X          -         X         X
-stats uri                                 X          -         X         X
+stats admin                               -          X         X         X
+stats auth                                X          X         X         X
+stats enable                              X          X         X         X
+stats hide-version                        X          X         X         X
+stats http-request                        -          X         X         X
+stats realm                               X          X         X         X
+stats refresh                             X          X         X         X
+stats scope                               X          X         X         X
+stats show-desc                           X          X         X         X
+stats show-legends                        X          X         X         X
+stats show-node                           X          X         X         X
+stats uri                                 X          X         X         X
 -- keyword -------------------------- defaults - frontend - listen -- backend -
 stick match                               -          -         X         X
 stick on                                  -          -         X         X
@@ -1888,6 +1889,12 @@
         listen external_bind_app1
             bind fd@${FD_APP1}
 
+  Note: regarding Linux's abstract namespace sockets, HAProxy uses the whole
+        sun_path length is used for the address length. Some other programs
+        such as socat use the string length only by default. Pass the option
+        ",unix-tightsocklen=0" to any abstract socket definition in socat to
+        make it compatible with HAProxy's.
+
   See also : "source", "option forwardfor", "unix-bind" and the PROXY protocol
              documentation, and section 5 about bind options.
 
@@ -2055,7 +2062,7 @@
   Example:
         capture request header Host len 15
         capture request header X-Forwarded-For len 15
-        capture request header Referrer len 15
+        capture request header Referer len 15
 
   See also : "capture cookie", "capture response header" as well as section 8
              about logging.
@@ -2260,11 +2267,11 @@
               of complex combinations of "Set-cookie" and "Cache-control"
               headers is left to the application. The application can then
               decide whether or not it is appropriate to emit a persistence
-              cookie. Since all responses should be monitored, this mode only
-              works in HTTP close mode. Unless the application behaviour is
-              very complex and/or broken, it is advised not to start with this
-              mode for new deployments. This keyword is incompatible with
-              "insert" and "prefix".
+              cookie. Since all responses should be monitored, this mode
+              doesn't work in HTTP tunnel mode. Unless the application
+              behaviour is very complex and/or broken, it is advised not to
+              start with this mode for new deployments. This keyword is
+              incompatible with "insert" and "prefix".
 
     insert    This keyword indicates that the persistence cookie will have to
               be inserted by haproxy in server responses if the client did not
@@ -2290,7 +2297,7 @@
               and a delimiter. The prefix will be removed from all client
               requests so that the server still finds the cookie it emitted.
               Since all requests and responses are subject to being modified,
-              this mode requires the HTTP close mode. The "prefix" keyword is
+              this mode doesn't work with tunnel mode. The "prefix" keyword is
               not compatible with "rewrite" and "insert". Note: it is highly
               recommended not to use "indirect" with "prefix", otherwise server
               cookie updates would not be sent to clients.
@@ -3135,7 +3142,7 @@
   Example:
         http-request set-header X-Haproxy-Current-Date %T
         http-request set-header X-SSL                  %[ssl_fc]
-        http-request set-header X-SSL-Session_ID       %[ssl_fc_session_id]
+        http-request set-header X-SSL-Session_ID       %[ssl_fc_session_id,hex]
         http-request set-header X-SSL-Client-Verify    %[ssl_c_verify]
         http-request set-header X-SSL-Client-DN        %{+Q}[ssl_c_s_dn]
         http-request set-header X-SSL-Client-CN        %{+Q}[ssl_c_s_dn(cn)]
@@ -3543,10 +3550,10 @@
   very high so that haproxy manages connection queues, instead of leaving the
   clients with unanswered connection attempts. This value should not exceed the
   global maxconn. Also, keep in mind that a connection contains two buffers
-  of 8kB each, as well as some other data resulting in about 17 kB of RAM being
-  consumed per established connection. That means that a medium system equipped
-  with 1GB of RAM can withstand around 40000-50000 concurrent connections if
-  properly tuned.
+  of tune.bufsize (16kB by default) each, as well as some other data resulting
+  in about 33 kB of RAM being consumed per established connection. That means
+  that a medium system equipped with 1GB of RAM can withstand around
+  20000-25000 concurrent connections if properly tuned.
 
   Also, when <conns> is set to large values, it is possible that the servers
   are not sized to accept such loads, and for this reason it is generally wise
@@ -4076,7 +4083,7 @@
   the frontend or the backend does not specify it, it wants the addition to be
   mandatory, so it wins.
 
-  Examples :
+  Example :
     # Public HTTP address also used by stunnel on the same machine
     frontend www
         mode http
@@ -4356,11 +4363,9 @@
 
   By setting this option in a frontend, haproxy can automatically switch to use
   that non-standard header if it sees proxied requests. A proxied request is
-  defined here as one where the URI begins with neither a '/' nor a '*'. The
-  choice of header only affects requests passing through proxies making use of
-  one of the "httpclose", "forceclose" and "http-server-close" options. Note
-  that this option can only be specified in a frontend and will affect the
-  request along its whole life.
+  defined here as one where the URI begins with neither a '/' nor a '*'. This
+  is incompatible with the HTTP tunnel mode. Note that this option can only be
+  specified in a frontend and will affect the request along its whole life.
 
   Also, when this option is set, a request which requires authentication will
   automatically switch to use proxy authentication headers if it is itself a
@@ -4509,10 +4514,8 @@
 
   No host address resolution is performed, so this only works when pure IP
   addresses are passed. Since this option's usage perimeter is rather limited,
-  it will probably be used only by experts who know they need exactly it. Last,
-  if the clients are susceptible of sending keep-alive requests, it will be
-  needed to add "option httpclose" to ensure that all requests will correctly
-  be analyzed.
+  it will probably be used only by experts who know they need exactly it. This
+  is incompatible with the HTTP tunnel mode.
 
   If this option has been enabled in a "defaults" section, it can be disabled
   in a specific instance by prepending the "no" keyword before it.
@@ -5392,7 +5395,7 @@
             mode tcp
             bind :25
             rate-limit sessions 10
-            server 127.0.0.1:1025
+            server smtp1 127.0.0.1:1025
 
   Note : when the maximum rate is reached, the frontend's status is not changed
          but its sockets appear as "WAITING" in the statistics if the
@@ -5443,7 +5446,7 @@
               is desired. Only codes 301, 302, 303, 307 and 308 are supported,
               with 302 used by default if no code is specified. 301 means
               "Moved permanently", and a browser may cache the Location. 302
-              means "Moved permanently" and means that the browser should not
+              means "Moved temporarily" and means that the browser should not
               cache the redirection. 303 is equivalent to 302 except that the
               browser will fetch the location with a GET method. 307 is just
               like 302 but makes it clear that the same method must be reused.
@@ -5502,7 +5505,8 @@
         redirect scheme https if !{ ssl_fc }
 
   Example: append 'www.' prefix in front of all hosts not having it
-        http-request redirect code 301 location www.%[hdr(host)]%[req.uri] \
+        http-request redirect code 301 location      \
+          http://www.%[hdr(host)]%[capture.req.uri]  \
           unless { hdr_beg(host) -i www }
 
   See section 7 about ACL usage.
@@ -5996,6 +6000,12 @@
         server www1_dc1 ${LAN_DC1}.101:80
         server www1_dc2 ${LAN_DC2}.101:80
 
+  Note: regarding Linux's abstract namespace sockets, HAProxy uses the whole
+        sun_path length is used for the address length. Some other programs
+        such as socat use the string length only by default. Pass the option
+        ",unix-tightsocklen=0" to any abstract socket definition in socat to
+        make it compatible with HAProxy's.
+
   See also: "default-server", "http-send-name-header" and section 5 about
              server options
 
@@ -6117,6 +6127,8 @@
   is possible at the server level using the "source" server option. Refer to
   section 5 for more information.
 
+  In order to work, "usesrc" requires root privileges.
+
   Examples :
         backend private
             # Connect to the servers using our 192.168.1.200 source address
@@ -7070,7 +7082,7 @@
 
 
 stick store-response <pattern> [table <table>] [{if | unless} <condition>]
-  Define a request pattern used to create an entry in a stickiness table
+  Define a response pattern used to create an entry in a stickiness table
   May be used in sections :   defaults | frontend | listen | backend
                                  no    |    no    |   yes  |   yes
 
@@ -7774,8 +7786,10 @@
   The inactivity timeout applies when the client is expected to acknowledge or
   send data. In HTTP mode, this timeout is particularly important to consider
   during the first phase, when the client sends the request, and during the
-  response while it is reading data sent by the server. The value is specified
-  in milliseconds by default, but can be in any other unit if the number is
+  response while it is reading data sent by the server. That said, for the
+  first phase, it is preferable to set the "timeout http-request" to better
+  protect HAProxy from Slowloris like attacks. The value is specified in
+  milliseconds by default, but can be in any other unit if the number is
   suffixed by the unit, as specified at the top of this document. In TCP mode
   (and to a lesser extent, in HTTP mode), it is highly recommended that the
   client timeout remains equal to the server timeout in order to avoid complex
@@ -7797,7 +7811,8 @@
   to use it to write new configurations. The form "timeout clitimeout" is
   provided only by backwards compatibility but its use is strongly discouraged.
 
-  See also : "clitimeout", "timeout server", "timeout tunnel".
+  See also : "clitimeout", "timeout server", "timeout tunnel",
+             "timeout http-request".
 
 
 timeout client-fin <timeout>
@@ -8719,10 +8734,11 @@
 
 addr <ipv4|ipv6>
   Using the "addr" parameter, it becomes possible to use a different IP address
-  to send health-checks. On some servers, it may be desirable to dedicate an IP
-  address to specific component able to perform complex tests which are more
-  suitable to health-checks than the application. This parameter is ignored if
-  the "check" parameter is not set. See also the "port" parameter.
+  to send health-checks or to probe the agent-check. On some servers, it may be
+  desirable to dedicate an IP address to specific component able to perform
+  complex tests which are more suitable to health-checks than the application.
+  This parameter is ignored if the "check" parameter is not set. See also the
+  "port" parameter.
 
   Supported in default-server: No
 
@@ -9780,7 +9796,7 @@
   - suffix match    (-m end) : the patterns are compared with the end of the
     extracted string, and the ACL matches if any of them matches.
 
-  - subdir match    (-m sub) : the patterns are looked up inside the extracted
+  - subdir match    (-m dir) : the patterns are looked up inside the extracted
     string, delimited with slashes ("/"), and the ACL matches if any of them
     matches.
 
@@ -9834,6 +9850,21 @@
 does not depend on any random DNS match at the moment the configuration is
 parsed.
 
+The dotted IPv4 address notation is supported in both regular as well as the
+abbreviated form with all-0-octets omitted:
+
+    +------------------+------------------+------------------+
+    |   Example 1      |     Example 2    |     Example 3    |
+    +------------------+------------------+------------------+
+    |  192.168.0.1     |   10.0.0.12      |   127.0.0.1      |
+    |  192.168.1       |   10.12          |   127.1          |
+    |  192.168.0.1/22  |   10.0.0.12/8    |   127.0.0.1/8    |
+    |  192.168.1/22    |   10.12/8        |   127.1/8        |
+    +------------------+------------------+------------------+
+
+Notice that this is different from RFC 4632 CIDR address notation in which
+192.168.42/24 would be equivalent to 192.168.42.0/24.
+
 IPv6 may be entered in their usual form, with or without a netmask appended.
 Only bit counts are accepted for IPv6 netmasks. In order to avoid any risk of
 trouble with randomly resolved IP addresses, host names are never allowed in
@@ -10372,7 +10403,7 @@
 
 fe_id : integer
   Returns an integer containing the current frontend's id. It can be used in
-  backends to check from which backend it was called, or to stick all users
+  backends to check from which frontend it was called, or to stick all users
   coming via a same frontend to the same server.
 
 sc_bytes_in_rate(<ctr>[,<table>]) : integer
@@ -11711,6 +11742,18 @@
   and converts it to an integer value. This can be used for session stickiness
   based on a user ID for example, or with ACLs to match a page number or price.
 
+url32 : integer
+  This returns a 32-bit hash of the value obtained by concatenating the first
+  Host header and the whole URL including parameters (not only the path part of
+  the request, as in the "base32" fetch above). This is useful to track per-URL
+  activity. A shorter hash is stored, saving a lot of memory. The output type
+  is an unsigned integer.
+
+url32+src : binary
+  This returns the concatenation of the "url32" fetch and the "src" fetch. The
+  resulting type is of type binary, with a size of 8 or 20 bytes depending on
+  the source address family. This can be used to track per-IP, per-URL counters.
+
 
 7.4. Pre-defined ACLs
 ---------------------
@@ -12379,7 +12422,7 @@
   |   | %hrl | captured_request_headers CLF style            | string list |
   |   | %hs  | captured_response_headers default style       | string      |
   |   | %hsl | captured_response_headers CLF style           | string list |
-  |   | %ms  | accept date milliseconds                      | numeric     |
+  |   | %ms  | accept date milliseconds (left-padded with 0) | numeric     |
   |   | %pid | PID                                           | numeric     |
   | H | %r   | http_request                                  | string      |
   |   | %rc  | retries                                       | numeric     |
@@ -12573,7 +12616,7 @@
     spent accepting these connections will inevitably slightly delay processing
     of other connections, and it can happen that request times in the order of
     a few tens of milliseconds are measured after a few thousands of new
-    connections have been accepted at once. Setting "option http-server-close"
+    connections have been accepted at once. Using one of the keep-alive modes
     may display larger request times since "Tq" also measures the time spent
     waiting for additional requests.
 
@@ -13368,6 +13411,9 @@
 
     # echo "show info;show stat;show table" | socat /var/run/haproxy stdio
 
+If a command needs to use a semi-colon (eg: in a value), it must be preceeded
+by a backslash ('\').
+
 The interactive mode displays a prompt ('>') and waits for commands to be
 entered on the line, then processes them, and displays the prompt again to wait
 for a new command. This mode is entered via the "prompt" command which must be
diff --git a/doc/haproxy.1 b/doc/haproxy.1
index a836d5d..20c9343 100644
--- a/doc/haproxy.1
+++ b/doc/haproxy.1
@@ -149,9 +149,9 @@
 .TP
 \fB\-st <pidlist>\fP
 Send TERMINATE signal to the pids in pidlist after startup. The processes
-which receive this signal will wait immediately terminate, closing all
-active sessions. This option must be specified last, followed by any number
-of PIDs. Technically speaking, \fBSIGTTOU\fP and \fBSIGTERM\fP are sent.
+which receive this signal will terminate immediately, closing all active
+sessions. This option must be specified last, followed by any number of
+PIDs. Technically speaking, \fBSIGTTOU\fP and \fBSIGTERM\fP are sent.
 
 .SH LOGGING
 Since HAProxy can run inside a chroot, it cannot reliably access /dev/log.
diff --git a/examples/haproxy.spec b/examples/haproxy.spec
index 1144bb1..4dac27b 100644
--- a/examples/haproxy.spec
+++ b/examples/haproxy.spec
@@ -1,6 +1,6 @@
 Summary: HA-Proxy is a TCP/HTTP reverse proxy for high availability environments
 Name: haproxy
-Version: 1.5.14
+Version: 1.5.19
 Release: 1
 License: GPL
 Group: System Environment/Daemons
@@ -76,6 +76,21 @@
 %attr(0755,root,root) %config %{_sysconfdir}/rc.d/init.d/%{name}
 
 %changelog
+* Sun Dec 25 2016 Willy Tarreau <w@1wt.eu>
+- updated to 1.5.19
+
+* Tue May 10 2016 Willy Tarreau <w@1wt.eu>
+- updated to 1.5.18
+
+* Wed Apr 13 2016 Willy Tarreau <w@1wt.eu>
+- updated to 1.5.17
+
+* Mon Mar 14 2016 Willy Tarreau <w@1wt.eu>
+- updated to 1.5.16
+
+* Sun Nov  1 2015 Willy Tarreau <w@1wt.eu>
+- updated to 1.5.15
+
 * Fri Jul  3 2015 Willy Tarreau <w@1wt.eu>
 - updated to 1.5.14
 
diff --git a/include/common/chunk.h b/include/common/chunk.h
index 18f41af..397bd24 100644
--- a/include/common/chunk.h
+++ b/include/common/chunk.h
@@ -66,7 +66,7 @@
 static inline int chunk_initlen(struct chunk *chk, char *str, size_t size, int len)
 {
 
-	if (size && len > size)
+	if (len < 0 || (size && len > size))
 		return 0;
 
 	chk->str  = str;
@@ -76,28 +76,70 @@
 	return 1;
 }
 
-static inline void chunk_initstr(struct chunk *chk, char *str)
+/* this is only for temporary manipulation, the chunk is read-only */
+static inline void chunk_initstr(struct chunk *chk, const char *str)
 {
-	chk->str = str;
+	chk->str = (char *)str;
 	chk->len = strlen(str);
 	chk->size = 0;			/* mark it read-only */
 }
 
+/* copies str into <chk> followed by a trailing zero. Returns 0 in
+ * case of failure.
+ */
 static inline int chunk_strcpy(struct chunk *chk, const char *str)
 {
 	size_t len;
 
 	len = strlen(str);
 
-	if (unlikely(len > chk->size))
+	if (unlikely(len >= chk->size))
 		return 0;
 
 	chk->len  = len;
-	memcpy(chk->str, str, len);
+	memcpy(chk->str, str, len + 1);
 
 	return 1;
 }
 
+/* appends str after <chk> followed by a trailing zero. Returns 0 in
+ * case of failure.
+ */
+static inline int chunk_strcat(struct chunk *chk, const char *str)
+{
+	size_t len;
+
+	len = strlen(str);
+
+	if (unlikely(chk->len < 0 || chk->len + len >= chk->size))
+		return 0;
+
+	memcpy(chk->str + chk->len, str, len + 1);
+	chk->len += len;
+	return 1;
+}
+
+/* Adds a trailing zero to the current chunk and returns the pointer to the
+ * following part. The purpose is to be able to use a chunk as a series of
+ * short independant strings with chunk_* functions, which do not need to be
+ * released. Returns NULL if no space is available to ensure that the new
+ * string will have its own trailing zero. For example :
+ *   chunk_init(&trash);
+ *   pid = chunk_newstr(&trash);
+ *   chunk_appendf(&trash, "%d", getpid()));
+ *   name = chunk_newstr(&trash);
+ *   chunk_appendf(&trash, "%s", gethosname());
+ *   printf("hostname=<%s>, pid=<%d>\n", name, pid);
+ */
+static inline char *chunk_newstr(struct chunk *chk)
+{
+	if (chk->len < 0 || chk->len + 1 >= chk->size)
+		return NULL;
+
+	chk->str[chk->len++] = 0;
+	return chk->str + chk->len;
+}
+
 static inline void chunk_drop(struct chunk *chk)
 {
 	chk->str  = NULL;
@@ -116,18 +158,28 @@
 
 /*
  * frees the destination chunk if already allocated, allocates a new string,
- * and copies the source into it. The pointer to the destination string is
- * returned, or NULL if the allocation fails or if any pointer is NULL..
+ * and copies the source into it. The new chunk will have extra room for a
+ * trailing zero unless the source chunk was actually full. The pointer to
+ * the destination string is returned, or NULL if the allocation fails or if
+ * any pointer is NULL.
  */
 static inline char *chunk_dup(struct chunk *dst, const struct chunk *src)
 {
-	if (!dst || !src || !src->str)
+	if (!dst || !src || src->len < 0 || !src->str)
 		return NULL;
-	if (dst->str)
+
+	if (dst->size)
 		free(dst->str);
 	dst->len = src->len;
-	dst->str = (char *)malloc(dst->len);
+	dst->size = src->len;
+	if (dst->size < src->size || !src->size)
+		dst->size++;
+
+	dst->str = (char *)malloc(dst->size);
 	memcpy(dst->str, src->str, dst->len);
+	if (dst->len < dst->size)
+		dst->str[dst->len] = 0;
+
 	return dst->str;
 }
 
diff --git a/include/common/defaults.h b/include/common/defaults.h
index 0075509..a191b8a 100644
--- a/include/common/defaults.h
+++ b/include/common/defaults.h
@@ -190,8 +190,12 @@
 
 /* Maximum host name length */
 #ifndef MAX_HOSTNAME_LEN
-#define MAX_HOSTNAME_LEN	32
-#endif
+#if MAXHOSTNAMELEN
+#define MAX_HOSTNAME_LEN	MAXHOSTNAMELEN
+#else
+#define MAX_HOSTNAME_LEN	64
+#endif // MAXHOSTNAMELEN
+#endif // MAX_HOSTNAME_LEN
 
 /* Maximum health check description length */
 #ifndef HCHK_DESC_LEN
diff --git a/include/common/mini-clist.h b/include/common/mini-clist.h
index 3c3f001..404b6fa 100644
--- a/include/common/mini-clist.h
+++ b/include/common/mini-clist.h
@@ -144,6 +144,7 @@
  * which contains list head <lh>, which is known as element <el> in
  * struct pt.
  */
+#undef LIST_PREV
 #define LIST_PREV(lh, pt, el) (LIST_ELEM((lh)->p, pt, el))
 
 /*
diff --git a/include/common/standard.h b/include/common/standard.h
index d6976ca..0d8e0e7 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -768,9 +768,6 @@
 
 extern const char *monthname[];
 
-/* numeric timezone (that is, the hour and minute offset from UTC) */
-char localtimezone[6];
-
 /* date2str_log: write a date in the format :
  * 	sprintf(str, "%02d/%s/%04d:%02d:%02d:%02d.%03d",
  *		tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
@@ -781,6 +778,13 @@
  */
 char *date2str_log(char *dest, struct tm *tm, struct timeval *date, size_t size);
 
+/* Return the GMT offset for a specific local time.
+ * Both t and tm must represent the same time.
+ * The string returned has the same format as returned by strftime(... "%z", tm).
+ * Offsets are kept in an internal cache for better performances.
+ */
+const char *get_gmt_offset(time_t t, struct tm *tm);
+
 /* gmt2str_log: write a date in the format :
  * "%02d/%s/%04d:%02d:%02d:%02d +0000" without using snprintf
  * return a pointer to the last char written (\0) or
@@ -790,10 +794,11 @@
 
 /* localdate2str_log: write a date in the format :
  * "%02d/%s/%04d:%02d:%02d:%02d +0000(local timezone)" without using snprintf
+ * Both t and tm must represent the same time.
  * return a pointer to the last char written (\0) or
  * NULL if there isn't enough space.
  */
-char *localdate2str_log(char *dst, struct tm *tm, size_t size);
+char *localdate2str_log(char *dst, time_t t, struct tm *tm, size_t size);
 
 /* Dynamically allocates a string of the proper length to hold the formatted
  * output. NULL is returned on error. The caller is responsible for freeing the
@@ -914,4 +919,13 @@
 	return caddr & ~(unsigned long)(data & 3);
 }
 
+/* HAP_STRING() makes a string from a literal while HAP_XSTRING() first
+ * evaluates the argument and is suited to pass macros.
+ *
+ * They allow macros like PCRE_MAJOR to be defined without quotes, which
+ * is convenient for applications that want to test its value.
+ */
+#define HAP_STRING(...) #__VA_ARGS__
+#define HAP_XSTRING(...) HAP_STRING(__VA_ARGS__)
+
 #endif /* _COMMON_STANDARD_H */
diff --git a/include/common/syscall.h b/include/common/syscall.h
index 7c9a456..9554d5a 100644
--- a/include/common/syscall.h
+++ b/include/common/syscall.h
@@ -104,6 +104,10 @@
 #define __NR_epoll_create 254
 #define __NR_epoll_ctl    255
 #define __NR_epoll_wait   256
+#elif defined (__s390__) || defined(__s390x__)
+#define __NR_epoll_create 249
+#define __NR_epoll_ctl    250
+#define __NR_epoll_wait   251
 #endif /* $arch */
 #endif /* __NR_epoll_ctl */
 
@@ -121,6 +125,8 @@
 #define __NR_splice             468
 #elif defined (__i386__)
 #define __NR_splice             313
+#elif defined(__s390__) || defined(__s390x__)
+#define __NR_splace		306
 #endif /* $arch */
 #endif /* __NR_splice */
 
diff --git a/include/proto/channel.h b/include/proto/channel.h
index 3c835b8..3620178 100644
--- a/include/proto/channel.h
+++ b/include/proto/channel.h
@@ -90,6 +90,13 @@
 	return __channel_forward(chn, bytes);
 }
 
+/* Forwards any input data and marks the channel for permanent forwarding */
+static inline void channel_forward_forever(struct channel *chn)
+{
+	b_adv(chn->buf, chn->buf->i);
+	chn->to_forward = CHN_INFINITE_FORWARD;
+}
+
 /*********************************************************************/
 /* These functions are used to compute various channel content sizes */
 /*********************************************************************/
@@ -118,6 +125,27 @@
 	return rem >= 0;
 }
 
+/* Returns non-zero if the channel is congested with data in transit waiting
+ * for leaving, indicating to the caller that it should wait for the reserve to
+ * be released before starting to process new data in case it needs the ability
+ * to append data. This is meant to be used while waiting for a clean response
+ * buffer before processing a request.
+ */
+static inline int channel_congested(const struct channel *chn)
+{
+	if (!chn->buf->o)
+		return 0;
+
+	if (!channel_reserved(chn))
+		return 1;
+
+	if (chn->buf->p + chn->buf->i >
+	    chn->buf->data + chn->buf->size - global.tune.maxrewrite)
+		return 1;
+
+	return 0;
+}
+
 /* Tells whether data are likely to leave the buffer. This is used to know when
  * we can safely ignore the reserve since we know we cannot retry a connection.
  * It returns zero if data are blocked, non-zero otherwise.
@@ -127,32 +155,6 @@
 	return chn->cons->state == SI_ST_EST;
 }
 
-/* Returns the amount of bytes from the channel that are already scheduled for
- * leaving (buf->o) or that are still part of the input and expected to be sent
- * soon as covered by to_forward. This is useful to know by how much we can
- * shrink the rewrite reserve during forwards. Buffer data are not considered
- * in transit until the channel is connected, so that the reserve remains
- * protected.
- */
-static inline int channel_in_transit(const struct channel *chn)
-{
-	int ret;
-
-	if (!channel_may_send(chn))
-		return 0;
-
-	/* below, this is min(i, to_forward) optimized for the fast case */
-	if (chn->to_forward >= chn->buf->i ||
-	    (CHN_INFINITE_FORWARD < MAX_RANGE(typeof(chn->buf->i)) &&
-	     chn->to_forward == CHN_INFINITE_FORWARD))
-		ret = chn->buf->i;
-	else
-		ret = chn->to_forward;
-
-	ret += chn->buf->o;
-	return ret;
-}
-
 /* Returns non-zero if the buffer input is considered full. This is used to
  * decide when to stop reading into a buffer when we want to ensure that we
  * leave the reserve untouched after all pending outgoing data are forwarded.
@@ -160,7 +162,9 @@
  * end of transfer is close to happen. Note that both ->buf->o and ->to_forward
  * are considered as available since they're supposed to leave the buffer. The
  * test is optimized to avoid as many operations as possible for the fast case
- * and to be used as an "if" condition.
+ * and to be used as an "if" condition. Just like channel_recv_limit(), we
+ * never allow to overwrite the reserve until the output stream interface is
+ * connected, otherwise we could spin on a POST with http-send-name-header.
  */
 static inline int channel_full(const struct channel *chn)
 {
@@ -171,18 +175,14 @@
 	if (!rem)
 		return 1; /* buffer already full */
 
-	/* now we know there's some room left, verify if we're touching
-	 * the reserve with some permanent input data.
-	 */
-	if (chn->to_forward >= chn->buf->i ||
-	    (CHN_INFINITE_FORWARD < MAX_RANGE(typeof(chn->buf->i)) && // just there to ensure gcc
-	     chn->to_forward == CHN_INFINITE_FORWARD))                // avoids the useless second
-		return 0;                                             // test whenever possible
+	if (rem > global.tune.maxrewrite)
+		return 0;
 
-	rem -= global.tune.maxrewrite;
-	rem += chn->buf->o;
-	rem += chn->to_forward;
-	return rem <= 0;
+	if (!channel_may_send(chn))
+		return 1;
+
+	rem = chn->buf->i + global.tune.maxrewrite - chn->buf->size;
+	return rem >= 0 && (unsigned int)rem >= chn->to_forward;
 }
 
 /* Returns true if the channel's input is already closed */
@@ -292,30 +292,91 @@
 /*************************************************/
 
 
-/* Return the number of reserved bytes in the channel's visible
- * buffer, which ensures that once all pending data are forwarded, the
- * buffer still has global.tune.maxrewrite bytes free. The result is
- * between 0 and global.tune.maxrewrite, which is itself smaller than
- * any chn->size. Special care is taken to avoid any possible integer
- * overflow in the operations.
- */
-static inline int buffer_reserved(const struct channel *chn)
-{
-	int reserved;
-
-	reserved = global.tune.maxrewrite - channel_in_transit(chn);
-	if (reserved < 0)
-		reserved = 0;
-	return reserved;
-}
-
 /* 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 chn->size - maxrewrite and chn->size.
+ * It is important to mention that if buf->i is already larger than size-maxrw
+ * the condition above cannot be satisfied and the lowest size will be returned
+ * anyway. The principles are the following :
+ *   1) a non-connected buffer cannot touch the reserve
+ *   2) infinite forward can always fill the buffer since all data will leave
+ *   3) all output bytes are considered in transit since they're leaving
+ *   4) all input bytes covered by to_forward are considered in transit since
+ *      they'll be converted to output bytes.
+ *   5) all input bytes not covered by to_forward as considered remaining
+ *   6) all bytes scheduled to be forwarded minus what is already in the input
+ *      buffer will be in transit during future rounds.
+ *   7) 4+5+6 imply that the amount of input bytes (i) is irrelevant to the max
+ *      usable length, only to_forward and output count. The difference is
+ *      visible when to_forward > i.
+ *   8) the reserve may be covered up to the amount of bytes in transit since
+ *      these bytes will only take temporary space.
+ *
+ * A typical buffer looks like this :
+ *
+ *      <-------------- max_len ----------->
+ *      <---- o ----><----- i ----->        <--- 0..maxrewrite --->
+ *      +------------+--------------+-------+----------------------+
+ *      |////////////|\\\\\\\\\\\\\\|xxxxxxx|        reserve       |
+ *      +------------+--------+-----+-------+----------------------+
+ *                   <- fwd ->      <-avail->
+ *
+ * Or when to_forward > i :
+ *
+ *      <-------------- max_len ----------->
+ *      <---- o ----><----- i ----->        <--- 0..maxrewrite --->
+ *      +------------+--------------+-------+----------------------+
+ *      |////////////|\\\\\\\\\\\\\\|xxxxxxx|        reserve       |
+ *      +------------+--------+-----+-------+----------------------+
+ *                                  <-avail->
+ *                   <------------------ fwd ---------------->
+ *
+ * - the amount of buffer bytes in transit is : min(i, fwd) + o
+ * - some scheduled bytes may be in transit (up to fwd - i)
+ * - the reserve is max(0, maxrewrite - transit)
+ * - the maximum usable buffer length is size - reserve.
+ * - the available space is max_len - i - o
+ *
+ * So the formula to compute the buffer's maximum length to protect the reserve
+ * when reading new data is :
+ *
+ *    max = size - maxrewrite + min(maxrewrite, transit)
+ *        = size - max(maxrewrite - transit, 0)
+ *
+ * But WARNING! The conditions might change during the transfer and it could
+ * very well happen that a buffer would contain more bytes than max_len due to
+ * i+o already walking over the reserve (eg: after a header rewrite), including
+ * i or o alone hitting the limit. So it is critical to always consider that
+ * bounds may have already been crossed and that available space may be negative
+ * for example. Due to this it is perfectly possible for this function to return
+ * a value that is lower than current i+o.
  */
 static inline int buffer_max_len(const struct channel *chn)
 {
-	return chn->buf->size - buffer_reserved(chn);
+	unsigned int transit;
+	int reserve;
+
+	/* return size - maxrewrite if we can't send */
+	reserve = global.tune.maxrewrite;
+	if (unlikely(!channel_may_send(chn)))
+		goto end;
+
+	/* We need to check what remains of the reserve after o and to_forward
+	 * have been transmitted, but they can overflow together and they can
+	 * cause an integer underflow in the comparison since both are unsigned
+	 * while maxrewrite is signed.
+	 * The code below has been verified for being a valid check for this :
+	 *   - if (o + to_forward) overflow => return size  [ large enough ]
+	 *   - if o + to_forward >= maxrw   => return size  [ large enough ]
+	 *   - otherwise return size - (maxrw - (o + to_forward))
+	 */
+	transit = chn->buf->o + chn->to_forward;
+	reserve -= transit;
+	if (transit < chn->to_forward ||                 // addition overflow
+	    transit >= (unsigned)global.tune.maxrewrite) // enough transit data
+		return chn->buf->size;
+ end:
+	return chn->buf->size - reserve;
 }
 
 /* Returns the amount of space available at the input of the buffer, taking the
diff --git a/include/proto/connection.h b/include/proto/connection.h
index c9972b1..9673078 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -35,9 +35,9 @@
 int init_connection();
 
 /* I/O callback for fd-based connections. It calls the read/write handlers
- * provided by the connection's sock_ops. Returns 0.
+ * provided by the connection's sock_ops.
  */
-int conn_fd_handler(int fd);
+void conn_fd_handler(int fd);
 
 /* receive a PROXY protocol header over a connection */
 int conn_recv_proxy(struct connection *conn, int flag);
@@ -252,7 +252,8 @@
 	c->flags &= ~(CO_FL_CURR_RD_ENA | CO_FL_CURR_WR_ENA |
 		      CO_FL_SOCK_RD_ENA | CO_FL_SOCK_WR_ENA |
 		      CO_FL_DATA_RD_ENA | CO_FL_DATA_WR_ENA);
-	fd_stop_both(c->t.sock.fd);
+	if (conn_ctrl_ready(c))
+		fd_stop_both(c->t.sock.fd);
 }
 
 /* Automatically update polling on connection <c> depending on the DATA and
diff --git a/include/proto/listener.h b/include/proto/listener.h
index 1473bfd..75bae86 100644
--- a/include/proto/listener.h
+++ b/include/proto/listener.h
@@ -105,7 +105,7 @@
  * to an accept. It tries to accept as many connections as possible, and for each
  * calls the listener's accept handler (generally the frontend's accept handler).
  */
-int listener_accept(int fd);
+void listener_accept(int fd);
 
 /*
  * Registers the bind keyword list <kwl> as a list of valid keywords for next
diff --git a/include/proto/stream_interface.h b/include/proto/stream_interface.h
index 9b457c6..b413a98 100644
--- a/include/proto/stream_interface.h
+++ b/include/proto/stream_interface.h
@@ -331,11 +331,12 @@
 		/* we're in the process of establishing a connection */
 		si->state = SI_ST_CON;
 	}
-	else if (!channel_is_empty(si->ob)) {
-		/* reuse the existing connection, we'll have to send a
-		 * request there.
-		 */
-		conn_data_want_send(conn);
+	else {
+		/* reuse the existing connection */
+		if (!channel_is_empty(si->ob)) {
+			/* we'll have to send a request there. */
+			conn_data_want_send(conn);
+		}
 
 		/* the connection is established */
 		si->state = SI_ST_EST;
diff --git a/include/types/fd.h b/include/types/fd.h
index 057d968..8299a02 100644
--- a/include/types/fd.h
+++ b/include/types/fd.h
@@ -78,7 +78,7 @@
 
 /* info about one given fd */
 struct fdtab {
-	int (*iocb)(int fd);                 /* I/O handler, returns FD_WAIT_* */
+	void (*iocb)(int fd);                /* I/O handler */
 	void *owner;                         /* the connection or listener associated with this fd, NULL if closed */
 	unsigned int  cache;                 /* position+1 in the FD cache. 0=not in cache. */
 	unsigned char state;                 /* FD state for read and write directions (2*3 bits) */
diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index a5a5d31..dbce972 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -219,7 +219,6 @@
 
 /* Known HTTP methods */
 enum http_meth_t {
-	HTTP_METH_NONE = 0,
 	HTTP_METH_OPTIONS,
 	HTTP_METH_GET,
 	HTTP_METH_HEAD,
diff --git a/include/types/protocol.h b/include/types/protocol.h
index 74b20e8..6a4986f 100644
--- a/include/types/protocol.h
+++ b/include/types/protocol.h
@@ -50,7 +50,7 @@
 	sa_family_t sock_family;			/* socket family, for sockaddr */
 	socklen_t sock_addrlen;				/* socket address length, used by bind() */
 	int l3_addrlen;					/* layer3 address length, used by hashes */
-	int (*accept)(int fd);				/* generic accept function */
+	void (*accept)(int fd);				/* generic accept function */
 	int (*bind)(struct listener *l, char *errmsg, int errlen); /* bind a listener */
 	int (*bind_all)(struct protocol *proto, char *errmsg, int errlen); /* bind all unbound listeners */
 	int (*unbind_all)(struct protocol *proto);	/* unbind all bound listeners */
diff --git a/src/acl.c b/src/acl.c
index d8b3000..eb87910 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -488,7 +488,7 @@
 			}
 
 			/* Note: -m found is always valid, bool/int are compatible, str/bin/reg/len are compatible */
-			if (!sample_casts[cur_type][pat_match_types[idx]]) {
+			if (idx != PAT_MATCH_FOUND && !sample_casts[cur_type][pat_match_types[idx]]) {
 				memprintf(err, "matching method '%s' cannot be used with fetch keyword '%s'", args[1], expr->kw);
 				goto out_free_expr;
 			}
diff --git a/src/backend.c b/src/backend.c
index 6b9a1c5..8f95f5a 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1417,8 +1417,10 @@
 		return "hdr";
 	else if (algo == BE_LB_ALGO_RCH)
 		return "rdp-cookie";
+	else if (algo == BE_LB_ALGO_NONE)
+		return "none";
 	else
-		return NULL;
+		return "unknown";
 }
 
 /* This function parses a "balance" statement in a backend section describing
@@ -1604,7 +1606,9 @@
 	smp->type = SMP_T_UINT;
 	px = args->data.prx;
 
-	if (px->srv_act)
+	if (px->state == PR_STSTOPPED)
+		smp->data.uint = 0;
+	else if (px->srv_act)
 		smp->data.uint = px->srv_act;
 	else if (px->lbprm.fbck)
 		smp->data.uint = 1;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 260804c..8d04950 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -280,7 +280,7 @@
 		}
 
 		/* OK the address looks correct */
-		ss = *ss2;
+		memcpy(&ss, ss2, sizeof(ss));
 
 		for (; port <= end; port++) {
 			l = (struct listener *)calloc(1, sizeof(struct listener));
@@ -291,7 +291,7 @@
 			l->bind_conf = bind_conf;
 
 			l->fd = fd;
-			l->addr = ss;
+			memcpy(&l->addr, &ss, sizeof(ss));
 			l->xprt = &raw_sock;
 			l->state = LI_INIT;
 
@@ -719,6 +719,11 @@
 			goto out;
 		}
 		global.tune.bufsize = atol(args[1]);
+		if (global.tune.bufsize <= 0) {
+			Alert("parsing [%s:%d] : '%s' expects a positive integer argument.\n", file, linenum, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
 		if (global.tune.maxrewrite >= global.tune.bufsize / 2)
 			global.tune.maxrewrite = global.tune.bufsize / 2;
 		chunk_init(&trash, realloc(trash.str, global.tune.bufsize), global.tune.bufsize);
@@ -906,7 +911,12 @@
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
-		global.uid = atol(args[1]);
+		if (strl2irc(args[1], strlen(args[1]), &global.uid) != 0) {
+			Warning("parsing [%s:%d] :  uid: string '%s' is not a number.\n   | You might want to use the 'user' parameter to use a system user name.\n", file, linenum, args[1]);
+			err_code |= ERR_WARN;
+			goto out;
+		}
+
 	}
 	else if (!strcmp(args[0], "gid")) {
 		if (global.gid != 0) {
@@ -919,7 +929,11 @@
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
-		global.gid = atol(args[1]);
+		if (strl2irc(args[1], strlen(args[1]), &global.gid) != 0) {
+			Warning("parsing [%s:%d] :  gid: string '%s' is not a number.\n   | You might want to use the 'group' parameter to use a system group name.\n", file, linenum, args[1]);
+			err_code |= ERR_WARN;
+			goto out;
+		}
 	}
 	/* user/group name handling */
 	else if (!strcmp(args[0], "user")) {
@@ -3224,6 +3238,12 @@
 					err_code |= ERR_ALERT | ERR_FATAL;
 					goto out;
 				}
+				if (val > INT_MAX) {
+					Alert("parsing [%s:%d] : Expire value [%u]ms exceeds maxmimum value of 24.85 days.\n",
+					      file, linenum, val);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
+				}
 				curproxy->table.expire = val;
 				myidx++;
 			}
@@ -5450,8 +5470,8 @@
 		}
 
 		if (rc >= HTTP_ERR_SIZE) {
-			Warning("parsing [%s:%d] : status code %d not handled, error relocation will be ignored.\n",
-				file, linenum, errnum);
+			Warning("parsing [%s:%d] : status code %d not handled by '%s', error relocation will be ignored.\n",
+				file, linenum, errnum, args[0]);
 			free(err);
 		}
 	}
@@ -5510,8 +5530,8 @@
 		}
 
 		if (rc >= HTTP_ERR_SIZE) {
-			Warning("parsing [%s:%d] : status code %d not handled, error customization will be ignored.\n",
-				file, linenum, errnum);
+			Warning("parsing [%s:%d] : status code %d not handled by '%s', error customization will be ignored.\n",
+				file, linenum, errnum, args[0]);
 			err_code |= ERR_WARN;
 			free(err);
 		}
@@ -6209,6 +6229,12 @@
 			break;
 		}
 
+		if ((curproxy->cap & PR_CAP_FE) && LIST_ISEMPTY(&curproxy->conf.listeners)) {
+			Warning("config : %s '%s' has no 'bind' directive. Please declare it as a backend if this was intended.\n",
+			        proxy_type_str(curproxy), curproxy->id);
+			err_code |= ERR_WARN;
+		}
+
 		if ((curproxy->cap & PR_CAP_BE) && (curproxy->mode != PR_MODE_HEALTH)) {
 			if (curproxy->lbprm.algo & BE_LB_KIND) {
 				if (curproxy->options & PR_O_TRANSP) {
@@ -7136,7 +7162,7 @@
 		list_for_each_entry(bind_conf, &global.stats_fe->conf.bind, by_fe) {
 			unsigned long mask;
 
-			mask = bind_conf->bind_proc ? bind_conf->bind_proc : nbits(global.nbproc);
+			mask = bind_conf->bind_proc ? bind_conf->bind_proc : 0;
 			global.stats_fe->bind_proc |= mask;
 		}
 		if (!global.stats_fe->bind_proc)
@@ -7169,9 +7195,6 @@
 	for (curproxy = proxy; curproxy; curproxy = curproxy->next) {
 		struct listener *listener;
 		unsigned int next_id;
-		int nbproc;
-
-		nbproc = my_popcountl(curproxy->bind_proc & nbits(global.nbproc));
 
 #ifdef USE_OPENSSL
 		/* Configure SSL for each bind line.
@@ -7213,6 +7236,15 @@
 		/* adjust this proxy's listeners */
 		next_id = 1;
 		list_for_each_entry(listener, &curproxy->conf.listeners, by_fe) {
+			int nbproc;
+
+			nbproc = my_popcountl(curproxy->bind_proc &
+			                      (listener->bind_conf->bind_proc ? listener->bind_conf->bind_proc : curproxy->bind_proc) &
+			                      nbits(global.nbproc));
+
+			if (!nbproc) /* no intersection between listener and frontend */
+				nbproc = 1;
+
 			if (!listener->luid) {
 				/* listener ID not set, use automatic numbering with first
 				 * spare entry starting with next_luid.
@@ -7283,7 +7315,7 @@
 #endif /* USE_OPENSSL */
 		}
 
-		if (nbproc > 1) {
+		if (my_popcountl(curproxy->bind_proc & nbits(global.nbproc)) > 1) {
 			if (curproxy->uri_auth) {
 				int count, maxproc = 0;
 
diff --git a/src/channel.c b/src/channel.c
index 2f98396..57428d2 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -33,44 +33,49 @@
 
 /* Schedule up to <bytes> more bytes to be forwarded via the channel without
  * notifying the owner task. Any data pending in the buffer are scheduled to be
- * sent as well, in the limit of the number of bytes to forward. This must be
- * the only method to use to schedule bytes to be forwarded. If the requested
+ * sent as well, within the limit of the number of bytes to forward. This must
+ * be the only method to use to schedule bytes to be forwarded. If the requested
  * number is too large, it is automatically adjusted. The number of bytes taken
  * into account is returned. Directly touching ->to_forward will cause lockups
  * when buf->o goes down to zero if nobody is ready to push the remaining data.
  */
 unsigned long long __channel_forward(struct channel *chn, unsigned long long bytes)
 {
-	unsigned int new_forward;
+	unsigned int budget;
 	unsigned int forwarded;
 
-	forwarded = chn->buf->i;
-	b_adv(chn->buf, chn->buf->i);
-
-	/* Note: the case below is the only case where we may return
-	 * a byte count that does not fit into a 32-bit number.
+	/* This is more of a safety measure as it's not supposed to happen in
+	 * regular code paths.
 	 */
-	if (likely(chn->to_forward == CHN_INFINITE_FORWARD))
-		return bytes;
-
-	if (likely(bytes == CHN_INFINITE_FORWARD)) {
-		chn->to_forward = bytes;
+	if (unlikely(chn->to_forward == CHN_INFINITE_FORWARD)) {
+		b_adv(chn->buf, chn->buf->i);
 		return bytes;
 	}
 
-	new_forward = chn->to_forward + bytes - forwarded;
-	bytes = forwarded; /* at least those bytes were scheduled */
+	/* Bound the transferred size to a 32-bit count since all our values
+	 * are 32-bit, and we don't want to reach CHN_INFINITE_FORWARD.
+	 */
+	budget = MIN(bytes, CHN_INFINITE_FORWARD - 1);
 
-	if (new_forward <= chn->to_forward) {
-		/* integer overflow detected, let's assume no more than 2G at once */
-		new_forward = MID_RANGE(new_forward);
-	}
+	/* transfer as much as we can of buf->i */
+	forwarded = MIN(chn->buf->i, budget);
+	b_adv(chn->buf, forwarded);
+	budget -= forwarded;
 
-	if (new_forward > chn->to_forward) {
-		bytes += new_forward - chn->to_forward;
-		chn->to_forward = new_forward;
-	}
-	return bytes;
+	if (!budget)
+		return forwarded;
+
+	/* Now we must ensure chn->to_forward sats below CHN_INFINITE_FORWARD,
+	 * which also implies it won't overflow. It's less operations in 64-bit.
+	 */
+	bytes = (unsigned long long)chn->to_forward + budget;
+	if (bytes >= CHN_INFINITE_FORWARD)
+		bytes = CHN_INFINITE_FORWARD - 1;
+	budget = bytes - chn->to_forward;
+
+	chn->to_forward += budget;
+	forwarded += budget;
+	return forwarded;
 }
 
 /* writes <len> bytes from message <msg> to the channel's buffer. Returns -1 in
@@ -87,7 +92,7 @@
 	if (len == 0)
 		return -1;
 
-	if (len > chn->buf->size) {
+	if (len < 0 || len > chn->buf->size) {
 		/* we can't write this chunk and will never be able to, because
 		 * it is larger than the buffer. This must be reported as an
 		 * error. Then we return -2 so that writers that don't care can
@@ -157,6 +162,9 @@
 	if (unlikely(channel_input_closed(chn)))
 		return -2;
 
+	if (len < 0)
+		return -3;
+
 	max = buffer_max_len(chn);
 	if (unlikely(len > max - buffer_len(chn->buf))) {
 		/* we can't write this chunk right now because the buffer is
diff --git a/src/chunk.c b/src/chunk.c
index 3c1cfdf..fbba6d5 100644
--- a/src/chunk.c
+++ b/src/chunk.c
@@ -100,7 +100,7 @@
 	va_list argp;
 	int ret;
 
-	if (!chk->str || !chk->size)
+	if (chk->len < 0 || !chk->str || !chk->size)
 		return 0;
 
 	va_start(argp, fmt);
@@ -125,6 +125,9 @@
 	int olen, free;
 	char c;
 
+	if (dst->len < 0)
+		return dst->len;
+
 	olen = dst->len;
 
 	for (i = 0; i < src->len; i++) {
@@ -166,6 +169,9 @@
 	int olen, free;
 	char c;
 
+	if (dst->len < 0)
+		return dst->len;
+
 	olen = dst->len;
 
 	for (i = 0; i < src->len; i++) {
diff --git a/src/compression.c b/src/compression.c
index d55f14e..8f066bb 100644
--- a/src/compression.c
+++ b/src/compression.c
@@ -571,6 +571,8 @@
 	int out_len = 0;
 	z_stream *strm = &comp_ctx->strm;
 
+	strm->next_in = NULL;
+	strm->avail_in = 0;
 	strm->next_out = (unsigned char *)bi_end(out);
 	strm->avail_out = out->size - buffer_len(out);
 
diff --git a/src/connection.c b/src/connection.c
index b9f5c42..af18878 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -36,15 +36,15 @@
 }
 
 /* I/O callback for fd-based connections. It calls the read/write handlers
- * provided by the connection's sock_ops, which must be valid. It returns 0.
+ * provided by the connection's sock_ops, which must be valid.
  */
-int conn_fd_handler(int fd)
+void conn_fd_handler(int fd)
 {
 	struct connection *conn = fdtab[fd].owner;
 	unsigned int flags;
 
 	if (unlikely(!conn))
-		return 0;
+		return;
 
 	conn_refresh_polling_flags(conn);
 	flags = conn->flags & ~CO_FL_ERROR; /* ensure to call the wake handler upon error */
@@ -86,7 +86,7 @@
 	 * we must not use it anymore and should immediately leave instead.
 	 */
 	if ((conn->flags & CO_FL_INIT_DATA) && conn->data->init(conn) < 0)
-		return 0;
+		return;
 
 	/* The data transfer starts here and stops on error and handshakes. Note
 	 * that we must absolutely test conn->xprt at each step in case it suddenly
@@ -133,7 +133,7 @@
 	if ((conn->flags & CO_FL_WAKE_DATA) &&
 	    ((conn->flags ^ flags) & CO_FL_CONN_STATE) &&
 	    conn->data->wake(conn) < 0)
-		return 0;
+		return;
 
 	/* Last check, verify if the connection just established */
 	if (unlikely(!(conn->flags & (CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN | CO_FL_CONNECTED))))
@@ -144,7 +144,7 @@
 
 	/* commit polling changes */
 	conn_cond_update_polling(conn);
-	return 0;
+	return;
 }
 
 /* Update polling on connection <c>'s file descriptor depending on its current
@@ -289,7 +289,7 @@
 	if (trash.len < 9) /* shortest possible line */
 		goto missing;
 
-	if (!memcmp(line, "TCP4 ", 5) != 0) {
+	if (memcmp(line, "TCP4 ", 5) == 0) {
 		u32 src3, dst3, sport, dport;
 
 		line += 5;
@@ -330,7 +330,7 @@
 		((struct sockaddr_in *)&conn->addr.to)->sin_port          = htons(dport);
 		conn->flags |= CO_FL_ADDR_FROM_SET | CO_FL_ADDR_TO_SET;
 	}
-	else if (!memcmp(line, "TCP6 ", 5) != 0) {
+	else if (memcmp(line, "TCP6 ", 5) == 0) {
 		u32 sport, dport;
 		char *src_s;
 		char *dst_s, *sport_s, *dport_s;
@@ -620,7 +620,7 @@
 	const char pp2_signature[] = PP2_SIGNATURE;
 	int ret = 0;
 	struct proxy_hdr_v2 *hdr = (struct proxy_hdr_v2 *)buf;
-	struct sockaddr_storage null_addr = {0};
+	struct sockaddr_storage null_addr = { .ss_family = 0 };
 	struct sockaddr_storage *src = &null_addr;
 	struct sockaddr_storage *dst = &null_addr;
 #ifdef USE_OPENSSL
diff --git a/src/dumpstats.c b/src/dumpstats.c
index ca084ac..e34cb72 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -81,7 +81,7 @@
 	STAT_CLI_O_CLR,      /* clear tables */
 	STAT_CLI_O_SET,      /* set entries in tables */
 	STAT_CLI_O_STAT,     /* dump stats */
-	STAT_CLI_O_PATS,     /* list all pattern reference avalaible */
+	STAT_CLI_O_PATS,     /* list all pattern reference available */
 	STAT_CLI_O_PAT,      /* list all entries of a pattern */
 	STAT_CLI_O_MLOOK,    /* lookup a map entry */
 	STAT_CLI_O_POOLS,    /* dump memory pools */
@@ -186,12 +186,12 @@
 	"  disable        : put a server or frontend in maintenance mode\n"
 	"  enable         : re-enable a server or frontend which is in maintenance mode\n"
 	"  shutdown       : kill a session or a frontend (eg:to release listening ports)\n"
-	"  show acl [id]  : report avalaible acls or dump an acl's contents\n"
+	"  show acl [id]  : report available acls or dump an acl's contents\n"
 	"  get acl        : reports the patterns matching a sample for an ACL\n"
 	"  add acl        : add acl entry\n"
 	"  del acl        : delete acl entry\n"
 	"  clear acl <id> : clear the content of this acl\n"
-	"  show map [id]  : report avalaible maps or dump a map's contents\n"
+	"  show map [id]  : report available maps or dump a map's contents\n"
 	"  get map        : reports the keys and values matching a sample for a map\n"
 	"  set map        : modify map entry\n"
 	"  add map        : add map entry\n"
@@ -334,7 +334,7 @@
 					return -1;
 				}
 
-				if (kw->parse(args, cur_arg, curpx, bind_conf, err) != 0) {
+				if (kw->parse(args, cur_arg, global.stats_fe, bind_conf, err) != 0) {
 					if (err && *err)
 						memprintf(err, "'%s %s' : '%s'", args[0], args[1], *err);
 					else
@@ -1175,7 +1175,7 @@
 			else
 				appctx->ctx.map.display_flags = PAT_REF_ACL;
 
-			/* no parameter: display all map avalaible */
+			/* no parameter: display all map available */
 			if (!*args[2]) {
 				appctx->st2 = STAT_ST_INIT;
 				appctx->st0 = STAT_CLI_O_PATS;
@@ -1532,8 +1532,8 @@
 						resume_listener(l);
 				}
 
-				if (px->maxconn > px->feconn && !LIST_ISEMPTY(&s->fe->listener_queue))
-					dequeue_all_listeners(&s->fe->listener_queue);
+				if (px->maxconn > px->feconn && !LIST_ISEMPTY(&px->listener_queue))
+					dequeue_all_listeners(&px->listener_queue);
 
 				return 1;
 			}
@@ -1695,6 +1695,12 @@
 				if (strcmp(args[3], "global") == 0) {
 					int v;
 
+					if (s->listener->bind_conf->level < ACCESS_LVL_ADMIN) {
+						appctx->ctx.cli.msg = stats_permission_denied_msg;
+						appctx->st0 = STAT_CLI_PRINT;
+						return 1;
+					}
+
 					if (!*args[4]) {
 						appctx->ctx.cli.msg = "Expects a maximum input byte rate in kB/s.\n";
 						appctx->st0 = STAT_CLI_PRINT;
@@ -2278,15 +2284,20 @@
 				continue;
 			}
 
-			/* seek for a possible semi-colon. If we find one, we
-			 * replace it with an LF and skip only this part.
+			/* seek for a possible unescaped semi-colon. If we find
+			 * one, we replace it with an LF and skip only this part.
 			 */
-			for (len = 0; len < reql; len++)
+			for (len = 0; len < reql; len++) {
+				if (trash.str[len] == '\\') {
+					len++;
+					continue;
+				}
 				if (trash.str[len] == ';') {
 					trash.str[len] = '\n';
 					reql = len + 1;
 					break;
 				}
+			}
 
 			/* now it is time to check that we have a full line,
 			 * remove the trailing \n and possibly \r, then cut the
@@ -4772,6 +4783,7 @@
 		     "Content-Type: text/plain\r\n"
 		     "Connection: close\r\n"
 		     "Location: %s;st=%s%s%s%s\r\n"
+		     "Content-length: 0\r\n"
 		     "\r\n",
 		     uri->uri_prefix,
 		     ((appctx->ctx.stats.st_code > STAT_STATUS_INIT) &&
@@ -4930,7 +4942,7 @@
 
 static inline const char *get_conn_xprt_name(const struct connection *conn)
 {
-	static char ptr[17];
+	static char ptr[19];
 
 	if (!conn_xprt_ready(conn))
 		return "NONE";
@@ -4948,7 +4960,7 @@
 
 static inline const char *get_conn_data_name(const struct connection *conn)
 {
-	static char ptr[17];
+	static char ptr[19];
 
 	if (!conn->data)
 		return "NONE";
@@ -5190,7 +5202,7 @@
 			              obj_base_ptr(conn->target));
 
 			chunk_appendf(&trash,
-			              "      flags=0x%08x fd=%d fd_spec_e=%02x fd_spec_p=%d updt=%d\n",
+			              "      flags=0x%08x fd=%d fd.state=%02x fd.cache=%d updt=%d\n",
 			              conn->flags,
 			              conn->t.sock.fd,
 			              conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].state : 0,
@@ -5293,8 +5305,8 @@
 
 		/* Now, we start the browsing of the references lists.
 		 * Note that the following call to LIST_ELEM return bad pointer. The only
-		 * avalaible field of this pointer is <list>. It is used with the function
-		 * pat_list_get_next() for retruning the first avalaible entry
+		 * available field of this pointer is <list>. It is used with the function
+		 * pat_list_get_next() for retruning the first available entry
 		 */
 		appctx->ctx.map.ref = LIST_ELEM(&pattern_reference, struct pat_ref *, list);
 		appctx->ctx.map.ref = pat_list_get_next(appctx->ctx.map.ref, &pattern_reference,
@@ -5358,7 +5370,7 @@
 
 			/* execute pattern matching */
 			sample.type = SMP_T_STR;
-			sample.flags |= SMP_F_CONST;
+			sample.flags = SMP_F_CONST;
 			sample.data.str.len = appctx->ctx.map.chunk.len;
 			sample.data.str.str = appctx->ctx.map.chunk.str;
 			if (appctx->ctx.map.expr->pat_head->match &&
@@ -5733,6 +5745,11 @@
 		if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users))
 			LIST_DEL(&appctx->ctx.sess.bref.users);
 	}
+	else if ((appctx->st0 == STAT_CLI_O_TAB || appctx->st0 == STAT_CLI_O_CLR) &&
+		 appctx->st2 == STAT_ST_LIST) {
+		appctx->ctx.table.entry->ref_cnt--;
+		stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
+	}
 	else if (appctx->st0 == STAT_CLI_PRINT_FREE) {
 		free(appctx->ctx.cli.err);
 	}
diff --git a/src/ev_poll.c b/src/ev_poll.c
index 2f6e56d..c4ac193 100644
--- a/src/ev_poll.c
+++ b/src/ev_poll.c
@@ -33,11 +33,6 @@
 static struct pollfd *poll_events = NULL;
 
 
-static inline unsigned int hap_fd_isset(int fd, unsigned int *evts)
-{
-	return evts[fd / (8*sizeof(*evts))] & (1U << (fd & (8*sizeof(*evts) - 1)));
-}
-
 static inline void hap_fd_set(int fd, unsigned int *evts)
 {
 	evts[fd / (8*sizeof(*evts))] |= 1U << (fd & (8*sizeof(*evts) - 1));
diff --git a/src/haproxy-systemd-wrapper.c b/src/haproxy-systemd-wrapper.c
index 8602881..f6a9c85 100644
--- a/src/haproxy-systemd-wrapper.c
+++ b/src/haproxy-systemd-wrapper.c
@@ -28,6 +28,11 @@
 static int wrapper_argc;
 static char **wrapper_argv;
 
+static void setup_signal_handler();
+static void pause_signal_handler();
+static void reset_signal_handler();
+
+
 /* returns the path to the haproxy binary into <buffer>, whose size indicated
  * in <buffer_size> must be at least 1 byte long.
  */
@@ -60,22 +65,53 @@
 	return;
 }
 
+/* Note: this function must not exit in case of error (except in the child), as
+ * it is only dedicated the starting a new haproxy process. By keeping the
+ * process alive it will ensure that future signal delivery may get rid of
+ * the issue. If the first startup fails, the wrapper will notice it and
+ * return an error thanks to wait() returning ECHILD.
+ */
 static void spawn_haproxy(char **pid_strv, int nb_pid)
 {
 	char haproxy_bin[512];
 	pid_t pid;
 	int main_argc;
 	char **main_argv;
+	int pipefd[2];
+	char fdstr[20];
+	int ret;
 
 	main_argc = wrapper_argc - 1;
 	main_argv = wrapper_argv + 1;
 
+	if (pipe(pipefd) != 0) {
+		fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: failed to create a pipe, please try again later.\n");
+		return;
+	}
+
 	pid = fork();
 	if (!pid) {
-		/* 3 for "haproxy -Ds -sf" */
-		char **argv = calloc(4 + main_argc + nb_pid + 1, sizeof(char *));
+		char **argv;
 		int i;
 		int argno = 0;
+
+		/* 3 for "haproxy -Ds -sf" */
+		argv = calloc(4 + main_argc + nb_pid + 1, sizeof(char *));
+		if (!argv) {
+			fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: failed to calloc(), please try again later.\n");
+			exit(1);
+		}
+
+		reset_signal_handler();
+
+		close(pipefd[0]); /* close the read side */
+
+		snprintf(fdstr, sizeof(fdstr), "%d", pipefd[1]);
+		if (setenv("HAPROXY_WRAPPER_FD", fdstr, 1) != 0) {
+			fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: failed to setenv(), please try again later.\n");
+			exit(1);
+		}
+
 		locate_haproxy(haproxy_bin, 512);
 		argv[argno++] = haproxy_bin;
 		for (i = 0; i < main_argc; ++i)
@@ -94,8 +130,25 @@
 		fprintf(stderr, "\n");
 
 		execv(argv[0], argv);
-		exit(0);
+		fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: execv(%s) failed, please try again later.\n", argv[0]);
+		exit(1);
 	}
+	else if (pid == -1) {
+		fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: failed to fork(), please try again later.\n");
+	}
+
+	/* The parent closes the write side and waits for the child to close it
+	 * as well. Also deal the case where the fd would unexpectedly be 1 or 2
+	 * by silently draining all data.
+	 */
+	close(pipefd[1]);
+
+	do {
+		char c;
+		ret = read(pipefd[0], &c, sizeof(c));
+	} while ((ret > 0) || (ret == -1 && errno == EINTR));
+	/* the child has finished starting up */
+	close(pipefd[0]);
 }
 
 static int read_pids(char ***pid_strv)
@@ -123,18 +176,55 @@
 
 static void signal_handler(int signum)
 {
+	if (caught_signal != SIGINT && caught_signal != SIGTERM)
+		caught_signal = signum;
+}
+
+static void setup_signal_handler()
+{
+	struct sigaction sa;
+
+	memset(&sa, 0, sizeof(struct sigaction));
+	sa.sa_handler = &signal_handler;
+	sigaction(SIGUSR2, &sa, NULL);
+	sigaction(SIGHUP, &sa, NULL);
+	sigaction(SIGINT, &sa, NULL);
+	sigaction(SIGTERM, &sa, NULL);
+}
+
+static void pause_signal_handler()
+{
+	signal(SIGUSR2, SIG_IGN);
+	signal(SIGHUP,  SIG_IGN);
+	signal(SIGINT,  SIG_DFL);
+	signal(SIGTERM, SIG_DFL);
+}
+
+static void reset_signal_handler()
+{
-	caught_signal = signum;
+	signal(SIGUSR2, SIG_DFL);
+	signal(SIGHUP,  SIG_DFL);
+	signal(SIGINT,  SIG_DFL);
+	signal(SIGTERM, SIG_DFL);
 }
 
-static void do_restart(void)
+/* handles SIGUSR2 and SIGHUP only */
+static void do_restart(int sig)
 {
 	setenv(REEXEC_FLAG, "1", 1);
-	fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: re-executing\n");
+	fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: re-executing on %s.\n",
+	        sig == SIGUSR2 ? "SIGUSR2" : "SIGHUP");
 
+	/* don't let the other process take one of those signals by accident */
+	pause_signal_handler();
 	execv(wrapper_argv[0], wrapper_argv);
+	/* failed, let's reinstall the signal handler and continue */
+	setup_signal_handler();
+	fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: re-exec(%s) failed.\n", wrapper_argv[0]);
 }
 
-static void do_shutdown(void)
+/* handles SIGTERM and SIGINT only */
+static void do_shutdown(int sig)
 {
 	int i, pid;
 	char **pid_strv = NULL;
@@ -142,8 +232,9 @@
 	for (i = 0; i < nb_pid; ++i) {
 		pid = atoi(pid_strv[i]);
 		if (pid > 0) {
-			fprintf(stderr, SD_DEBUG "haproxy-systemd-wrapper: SIGINT -> %d\n", pid);
-			kill(pid, SIGINT);
+			fprintf(stderr, SD_DEBUG "haproxy-systemd-wrapper: %s -> %d.\n",
+			        sig == SIGTERM ? "SIGTERM" : "SIGINT", pid);
+			kill(pid, sig);
 			free(pid_strv[i]);
 		}
 	}
@@ -164,20 +255,14 @@
 {
 	int status;
 
+	setup_signal_handler();
+
 	wrapper_argc = argc;
 	wrapper_argv = argv;
 
 	--argc; ++argv;
 	init(argc, argv);
 
-	struct sigaction sa;
-	memset(&sa, 0, sizeof(struct sigaction));
-	sa.sa_handler = &signal_handler;
-	sigaction(SIGUSR2, &sa, NULL);
-	sigaction(SIGHUP, &sa, NULL);
-	sigaction(SIGINT, &sa, NULL);
-	sigaction(SIGTERM, &sa, NULL);
-
 	if (getenv(REEXEC_FLAG) != NULL) {
 		/* We are being re-executed: restart HAProxy gracefully */
 		int i;
@@ -197,17 +282,29 @@
 	}
 
 	status = -1;
-	while (-1 != wait(&status) || errno == EINTR) {
+	while (caught_signal || wait(&status) != -1 || errno == EINTR) {
+		int sig = caught_signal;
+
 		if (caught_signal == SIGUSR2 || caught_signal == SIGHUP) {
 			caught_signal = 0;
-			do_restart();
+			do_restart(sig);
 		}
 		else if (caught_signal == SIGINT || caught_signal == SIGTERM) {
 			caught_signal = 0;
-			do_shutdown();
+			do_shutdown(sig);
 		}
 	}
 
+	/* return either exit code or signal+128 */
+	if (WIFEXITED(status))
+		status = WEXITSTATUS(status);
+	else if (WIFSIGNALED(status))
+		status = 128 + WTERMSIG(status);
+	else if (WIFSTOPPED(status))
+		status = 128 + WSTOPSIG(status);
+	else
+		status = 255;
+
 	fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: exit, haproxy RC=%d\n",
 			status);
 	return status;
diff --git a/src/haproxy.c b/src/haproxy.c
index 125f082..53d9bec 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1,6 +1,6 @@
 /*
  * HA-Proxy : High Availability-enabled HTTP/TCP proxy
- * Copyright 2000-2014  Willy Tarreau <w@1wt.eu>.
+ * Copyright 2000-2016 Willy Tarreau <willy@haproxy.org>.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -25,6 +25,7 @@
  *
  */
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -47,9 +48,7 @@
 #include <syslog.h>
 #include <grp.h>
 #ifdef USE_CPU_AFFINITY
-#define __USE_GNU
 #include <sched.h>
-#undef __USE_GNU
 #endif
 
 #ifdef DEBUG_FULL
@@ -216,7 +215,7 @@
 void display_version()
 {
 	printf("HA-Proxy version " HAPROXY_VERSION " " HAPROXY_DATE"\n");
-	printf("Copyright 2000-2015 Willy Tarreau <willy@haproxy.org>\n\n");
+	printf("Copyright 2000-2016 Willy Tarreau <willy@haproxy.org>\n\n");
 }
 
 void display_build_opts()
@@ -252,6 +251,7 @@
 
 #ifdef USE_ZLIB
 	printf("Built with zlib version : " ZLIB_VERSION "\n");
+	printf("Running on zlib version : %s\n", zlibVersion());
 #else /* USE_ZLIB */
 	printf("Built without zlib support (USE_ZLIB not set)\n");
 #endif
@@ -305,7 +305,10 @@
 #endif
 
 #ifdef USE_PCRE
-	printf("Built with PCRE version : %s", pcre_version());
+	printf("Built with PCRE version : %s\n", (HAP_XSTRING(Z PCRE_PRERELEASE)[1] == 0)?
+		HAP_XSTRING(PCRE_MAJOR.PCRE_MINOR PCRE_DATE) :
+		HAP_XSTRING(PCRE_MAJOR.PCRE_MINOR) HAP_XSTRING(PCRE_PRERELEASE PCRE_DATE));
+	printf("Running on PCRE version : %s", pcre_version());
 	printf("\nPCRE library supports JIT : ");
 #ifdef USE_PCRE_JIT
 	{
@@ -503,7 +506,6 @@
 	struct wordlist *wl;
 	char *progname;
 	char *change_dir = NULL;
-	struct tm curtime;
 
 	chunk_init(&trash, malloc(global.tune.bufsize), global.tune.bufsize);
 	alloc_trash_buffers(global.tune.bufsize);
@@ -528,15 +530,12 @@
 	global.rlimit_memmax = HAPROXY_MEMMAX;
 #endif
 
+	tzset();
 	tv_update_date(-1,-1);
 	start_date = now;
 
 	srandom(now_ms - getpid());
 
-	/* Get the numeric timezone. */
-	get_localtime(start_date.tv_sec, &curtime);
-	strftime(localtimezone, 6, "%z", &curtime);
-
 	signal_init();
 	if (init_acl() != 0)
 		exit(1);
@@ -1364,13 +1363,20 @@
 	if (global.rlimit_nofile) {
 		limit.rlim_cur = limit.rlim_max = global.rlimit_nofile;
 		if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
-			Warning("[%s.main()] Cannot raise FD limit to %d.\n", argv[0], global.rlimit_nofile);
+			/* try to set it to the max possible at least */
+			getrlimit(RLIMIT_NOFILE, &limit);
+			limit.rlim_cur = limit.rlim_max;
+			if (setrlimit(RLIMIT_NOFILE, &limit) != -1)
+				getrlimit(RLIMIT_NOFILE, &limit);
+
+			Warning("[%s.main()] Cannot raise FD limit to %d, limit is %d.\n", argv[0], global.rlimit_nofile, (int)limit.rlim_cur);
+			global.rlimit_nofile = limit.rlim_cur;
 		}
 	}
 
 	if (global.rlimit_memmax) {
 		limit.rlim_cur = limit.rlim_max =
-			global.rlimit_memmax * 1048576 / global.nbproc;
+			global.rlimit_memmax * 1048576ULL / global.nbproc;
 #ifdef RLIMIT_AS
 		if (setrlimit(RLIMIT_AS, &limit) == -1) {
 			Warning("[%s.main()] Cannot fix MEM limit to %d megs.\n",
@@ -1552,6 +1558,7 @@
 		int ret = 0;
 		int *children = calloc(global.nbproc, sizeof(int));
 		int proc;
+		char *wrapper_fd;
 
 		/* the father launches the required number of processes */
 		for (proc = 0; proc < global.nbproc; proc++) {
@@ -1584,6 +1591,15 @@
 			close(pidfd);
 		}
 
+		/* each child must notify the wrapper that it's ready by closing the requested fd */
+		wrapper_fd = getenv("HAPROXY_WRAPPER_FD");
+		if (wrapper_fd) {
+			int pipe_fd = atoi(wrapper_fd);
+
+			if (pipe_fd >= 0)
+				close(pipe_fd);
+		}
+
 		/* We won't ever use this anymore */
 		free(oldpids);        oldpids = NULL;
 		free(global.chroot);  global.chroot = NULL;
@@ -1593,7 +1609,7 @@
 			if (global.mode & MODE_SYSTEMD) {
 				protocol_unbind_all();
 				for (proc = 0; proc < global.nbproc; proc++)
-					while (waitpid(children[proc], NULL, 0) == -1 && errno == EINTR);
+					while (waitpid(-1, NULL, 0) == -1 && errno == EINTR);
 			}
 			exit(0); /* parent must leave */
 		}
diff --git a/src/listener.c b/src/listener.c
index 21eba52..58ca894 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -32,6 +32,7 @@
 #include <proto/fd.h>
 #include <proto/freq_ctr.h>
 #include <proto/log.h>
+#include <proto/listener.h>
 #include <proto/sample.h>
 #include <proto/task.h>
 
@@ -55,8 +56,7 @@
 			/* we don't want to enable this listener and don't
 			 * want any fd event to reach it.
 			 */
-			fd_stop_recv(listener->fd);
-			listener->state = LI_PAUSED;
+			unbind_listener(listener);
 		}
 		else if (listener->nbconn < listener->maxconn) {
 			fd_want_recv(listener->fd);
@@ -647,6 +647,7 @@
 {
 	struct eb32_node *node;
 	struct listener *l, *new;
+	char *error;
 
 	if (conf->listeners.n != conf->listeners.p) {
 		memprintf(err, "'%s' can only be used with a single socket", args[cur_arg]);
@@ -659,7 +660,11 @@
 	}
 
 	new = LIST_NEXT(&conf->listeners, struct listener *, by_bind);
-	new->luid = atol(args[cur_arg + 1]);
+	new->luid = strtol(args[cur_arg + 1], &error, 10);
+	if (*error != '\0') {
+		memprintf(err, "'%s' : expects an integer argument, found '%s'", args[cur_arg], args[cur_arg + 1]);
+		return ERR_ALERT | ERR_FATAL;
+	}
 	new->conf.id.key = new->luid;
 
 	if (new->luid <= 0) {
diff --git a/src/log.c b/src/log.c
index 7dc844d..888deb7 100644
--- a/src/log.c
+++ b/src/log.c
@@ -167,22 +167,26 @@
 static inline const char *fmt_directive(const struct proxy *curproxy)
 {
 	switch (curproxy->conf.args.ctx) {
-	case ARGC_UIF:
-		return "unique-id-format";
+	case ARGC_ACL:
+		return "acl";
+	case ARGC_STK:
+		return "stick";
+	case ARGC_TRK:
+		return "track-sc";
+	case ARGC_LOG:
+		return "log-format";
 	case ARGC_HRQ:
 		return "http-request";
 	case ARGC_HRS:
 		return "http-response";
-	case ARGC_STK:
-		return "stick";
-	case ARGC_TRK:
-		return "track-sc"; break;
+	case ARGC_UIF:
+		return "unique-id-format";
 	case ARGC_RDR:
-		return "redirect"; break;
-	case ARGC_ACL:
-		return "acl"; break;
+		return "redirect";
+	case ARGC_CAP:
+		return "capture";
 	default:
-		return "log-format";
+		return "undefined(please report this bug)"; /* must never happen */
 	}
 }
 
@@ -1178,7 +1182,7 @@
 
 			case LOG_FMT_DATELOCAL: // %Tl
 				get_localtime(s->logs.accept_date.tv_sec, &tm);
-				ret = localdate2str_log(tmplog, &tm, dst + maxsize - tmplog);
+				ret = localdate2str_log(tmplog, s->logs.accept_date.tv_sec, &tm, dst + maxsize - tmplog);
 				if (ret == NULL)
 					goto out;
 				tmplog = ret;
diff --git a/src/map.c b/src/map.c
index a4fd54b..e3b0f2a 100644
--- a/src/map.c
+++ b/src/map.c
@@ -149,6 +149,7 @@
 	default:
 		memprintf(err, "map: internal haproxy error: no default parse case for the input type <%d>.",
 		          conv->out_type);
+		free(desc);
 		return 0;
 	}
 
diff --git a/src/pattern.c b/src/pattern.c
index b19ffe2..1804b05 100644
--- a/src/pattern.c
+++ b/src/pattern.c
@@ -887,6 +887,9 @@
 			}
 			else
 				continue;
+		} else {
+		  /* impossible */
+		  continue;
 		}
 
 		/* Check if the input sample match the current pattern. */
@@ -1423,14 +1426,13 @@
 	/* delete pattern from reference */
 	list_for_each_entry_safe(elt, safe, &ref->head, list) {
 		if (elt == refelt) {
+			list_for_each_entry(expr, &ref->pat, list)
+				pattern_delete(expr, elt);
+
 			LIST_DEL(&elt->list);
 			free(elt->sample);
 			free(elt->pattern);
 			free(elt);
-
-			list_for_each_entry(expr, &ref->pat, list)
-				pattern_delete(expr, elt);
-
 			return 1;
 		}
 	}
@@ -1902,6 +1904,7 @@
 		/* Get a lot of memory for the expr struct. */
 		expr = malloc(sizeof(*expr));
 		if (!expr) {
+			free(list);
 			memprintf(err, "out of memory");
 			return NULL;
 		}
diff --git a/src/payload.c b/src/payload.c
index f62163c..b548bd7 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -148,21 +148,24 @@
 	data = (const unsigned char *)s->req->buf->p;
 	if ((*data >= 0x14 && *data <= 0x17) || (*data == 0xFF)) {
 		/* SSLv3 header format */
-		if (bleft < 5)
+		if (bleft < 11)
 			goto too_short;
 
-		version = (data[1] << 16) + data[2]; /* version: major, minor */
+		version = (data[1] << 16) + data[2]; /* record layer version: major, minor */
 		msg_len = (data[3] <<  8) + data[4]; /* record length */
 
 		/* format introduced with SSLv3 */
 		if (version < 0x00030000)
 			goto not_ssl;
 
-		/* message length between 1 and 2^14 + 2048 */
-		if (msg_len < 1 || msg_len > ((1<<14) + 2048))
+		/* message length between 6 and 2^14 + 2048 */
+		if (msg_len < 6 || msg_len > ((1<<14) + 2048))
 			goto not_ssl;
 
 		bleft -= 5; data += 5;
+
+		/* return the client hello client version, not the record layer version */
+		version = (data[4] << 16) + data[5]; /* client hello version: major, minor */
 	} else {
 		/* SSLv2 header format, only supported for hello (msg type 1) */
 		int rlen, plen, cilen, silen, chlen;
@@ -180,7 +183,7 @@
 			/* long header format : 14 bits for length + pad length */
 			rlen = ((data[0] & 0x3F) << 8) | data[1];
 			plen = data[2];
-			bleft -= 3; data += 2;
+			bleft -= 3; data += 3;
 		}
 
 		if (*data != 0x01)
diff --git a/src/peers.c b/src/peers.c
index 0564d3d..1bd08c1 100644
--- a/src/peers.c
+++ b/src/peers.c
@@ -720,7 +720,7 @@
 							ts = stktable_store(ps->table->table, newts, 0);
 							newts = NULL; /* don't reuse it */
 
-							ts->upd.key= (++ps->table->table->update)+(2^31);
+							ts->upd.key= (++ps->table->table->update)+(2147483648U);
 							eb = eb32_insert(&ps->table->table->updates, &ts->upd);
 							if (eb != &ts->upd) {
 								eb32_delete(eb);
@@ -1323,7 +1323,7 @@
 	LIST_DEL(&s->list);
 	pool_free2(pool2_session, s);
  out_close:
-	return s;
+	return NULL;
 }
 
 /*
diff --git a/src/proto_http.c b/src/proto_http.c
index 5db64b5..35c8e99 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -353,6 +353,9 @@
 	['H' - 'A'] = {
 		[0] = {	.meth = HTTP_METH_HEAD    , .len=4, .text="HEAD"    },
 	},
+	['O' - 'A'] = {
+		[0] = {	.meth = HTTP_METH_OPTIONS , .len=7, .text="OPTIONS" },
+	},
 	['P' - 'A'] = {
 		[0] = {	.meth = HTTP_METH_POST    , .len=4, .text="POST"    },
 		[1] = {	.meth = HTTP_METH_PUT     , .len=3, .text="PUT"     },
@@ -361,12 +364,11 @@
 		[0] = {	.meth = HTTP_METH_TRACE   , .len=5, .text="TRACE"   },
 	},
 	/* rest is empty like this :
-	 *      [1] = {	.meth = HTTP_METH_NONE    , .len=0, .text=""        },
+	 *      [0] = {	.meth = HTTP_METH_OTHER   , .len=0, .text=""        },
 	 */
 };
 
 const struct http_method_name http_known_methods[HTTP_METH_OTHER] = {
-	[HTTP_METH_NONE]    = { "",         0 },
 	[HTTP_METH_OPTIONS] = { "OPTIONS",  7 },
 	[HTTP_METH_GET]     = { "GET",      3 },
 	[HTTP_METH_HEAD]    = { "HEAD",     4 },
@@ -793,8 +795,8 @@
 }
 
 /*
- * returns HTTP_METH_NONE if there is nothing valid to read (empty or non-text
- * string), HTTP_METH_OTHER for unknown methods, or the identified method.
+ * returns a known method among HTTP_METH_* or HTTP_METH_OTHER for all unknown
+ * ones.
  */
 enum http_meth_t find_http_meth(const char *str, const int len)
 {
@@ -810,10 +812,8 @@
 			if (likely(memcmp(str, h->text, h->len) == 0))
 				return h->meth;
 		};
-		return HTTP_METH_OTHER;
 	}
-	return HTTP_METH_NONE;
-
+	return HTTP_METH_OTHER;
 }
 
 /* Parse the URI from the given transaction (which is assumed to be in request
@@ -1402,11 +1402,12 @@
 	h = ctx.line + ctx.val;
 
 	p = memchr(h, ' ', ctx.vlen);
-	if (!p || p == h)
+	len = p - h;
+	if (!p || len <= 0)
 		return 0;
 
-	chunk_initlen(&auth_method, h, 0, p-h);
-	chunk_initlen(&txn->auth.method_data, p+1, 0, ctx.vlen-(p-h)-1);
+	chunk_initlen(&auth_method, h, 0, len);
+	chunk_initlen(&txn->auth.method_data, p + 1, 0, ctx.vlen - len - 1);
 
 	if (!strncasecmp("Basic", auth_method.str, auth_method.len)) {
 
@@ -3219,15 +3220,15 @@
 /* Sets the TOS header in IPv4 and the traffic class header in IPv6 packets
  * (as per RFC3260 #4 and BCP37 #4.2 and #5.2).
  */
-static inline void inet_set_tos(int fd, struct sockaddr_storage from, int tos)
+static inline void inet_set_tos(int fd, struct sockaddr_storage *from, int tos)
 {
 #ifdef IP_TOS
-	if (from.ss_family == AF_INET)
+	if (from->ss_family == AF_INET)
 		setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
 #endif
 #ifdef IPV6_TCLASS
-	if (from.ss_family == AF_INET6) {
-		if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr))
+	if (from->ss_family == AF_INET6) {
+		if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)from)->sin6_addr))
 			/* v4-mapped addresses need IP_TOS */
 			setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
 		else
@@ -3365,7 +3366,7 @@
 
 		case HTTP_REQ_ACT_SET_TOS:
 			if ((cli_conn = objt_conn(s->req->prod->end)) && conn_ctrl_ready(cli_conn))
-				inet_set_tos(cli_conn->t.sock.fd, cli_conn->addr.from, rule->arg.tos);
+				inet_set_tos(cli_conn->t.sock.fd, &cli_conn->addr.from, rule->arg.tos);
 			break;
 
 		case HTTP_REQ_ACT_SET_MARK:
@@ -3398,7 +3399,6 @@
 
 		case HTTP_REQ_ACT_SET_HDR:
 		case HTTP_REQ_ACT_ADD_HDR:
-			chunk_printf(&trash, "%s: ", rule->arg.hdr_add.name);
 			memcpy(trash.str, rule->arg.hdr_add.name, rule->arg.hdr_add.name_len);
 			trash.len = rule->arg.hdr_add.name_len;
 			trash.str[trash.len++] = ':';
@@ -3562,7 +3562,7 @@
 
 		case HTTP_RES_ACT_SET_TOS:
 			if ((cli_conn = objt_conn(s->req->prod->end)) && conn_ctrl_ready(cli_conn))
-				inet_set_tos(cli_conn->t.sock.fd, cli_conn->addr.from, rule->arg.tos);
+				inet_set_tos(cli_conn->t.sock.fd, &cli_conn->addr.from, rule->arg.tos);
 			break;
 
 		case HTTP_RES_ACT_SET_MARK:
@@ -3908,8 +3908,6 @@
 		trash.len += 14;
 		memcpy(trash.str + trash.len, rule->cookie_str, rule->cookie_len);
 		trash.len += rule->cookie_len;
-		memcpy(trash.str + trash.len, "\r\n", 2);
-		trash.len += 2;
 	}
 
 	/* add end of headers and the keep-alive/close status.
@@ -4151,6 +4149,12 @@
 	 */
 	channel_dont_connect(req);
 	req->analysers = 0; /* remove switching rules etc... */
+
+	/* Allow cookie logging
+	 */
+	if (s->be->cookie_name || s->fe->capture_name)
+		manage_client_side_cookies(s, req);
+
 	req->analysers |= AN_REQ_HTTP_TARPIT;
 	req->analyse_exp = tick_add_ifset(now_ms,  s->be->timeout.tarpit);
 	if (!req->analyse_exp)
@@ -4164,6 +4168,12 @@
 	goto done_without_exp;
 
  deny:	/* this request was blocked (denied) */
+
+	/* Allow cookie logging
+	 */
+	if (s->be->cookie_name || s->fe->capture_name)
+		manage_client_side_cookies(s, req);
+
 	txn->flags |= TX_CLDENY;
 	txn->status = 403;
 	s->logs.tv_request = now;
@@ -4304,8 +4314,7 @@
 	 * the fields will stay coherent and the URI will not move.
 	 * This should only be performed in the backend.
 	 */
-	if ((s->be->cookie_name || s->be->appsession_name || s->fe->capture_name)
-	    && !(txn->flags & (TX_CLDENY|TX_CLTARPIT)))
+	if (s->be->cookie_name || s->be->appsession_name || s->fe->capture_name)
 		manage_client_side_cookies(s, req);
 
 	/*
@@ -4946,11 +4955,13 @@
 			s->rep->flags |= CF_EXPECT_MORE;
 	}
 
-	/* we're removing the analysers, we MUST re-enable events detection */
+	/* we're removing the analysers, we MUST re-enable events detection.
+	 * We don't enable close on the response channel since it's either
+	 * already closed, or in keep-alive with an idle connection handler.
+	 */
 	channel_auto_read(s->req);
 	channel_auto_close(s->req);
 	channel_auto_read(s->rep);
-	channel_auto_close(s->rep);
 
 	/* we're in keep-alive with an idle connection, monitor it */
 	si_idle_conn(s->req->cons);
@@ -5001,6 +5012,13 @@
 		 */
 		chn->cons->flags |= SI_FL_NOHALF;
 
+		/* In any case we've finished parsing the request so we must
+		 * disable Nagle when sending data because 1) we're not going
+		 * to shut this side, and 2) the server is waiting for us to
+		 * send pending data.
+		 */
+		chn->flags |= CF_NEVER_WAIT;
+
 		if (txn->rsp.msg_state == HTTP_MSG_ERROR)
 			goto wait_other_side;
 
@@ -5015,7 +5033,6 @@
 			/* if any side switches to tunnel mode, the other one does too */
 			channel_auto_read(chn);
 			txn->req.msg_state = HTTP_MSG_TUNNEL;
-			chn->flags |= CF_NEVER_WAIT;
 			goto wait_other_side;
 		}
 
@@ -5048,7 +5065,6 @@
 			if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_TUN) {
 				channel_auto_read(chn);
 				txn->req.msg_state = HTTP_MSG_TUNNEL;
-				chn->flags |= CF_NEVER_WAIT;
 			}
 		}
 
@@ -5291,9 +5307,18 @@
 		  (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL)) {
 		/* server-close/keep-alive: terminate this transaction,
 		 * possibly killing the server connection and reinitialize
-		 * a fresh-new transaction.
+		 * a fresh-new transaction, but only once we're sure there's
+		 * enough room in the request and response buffer to process
+		 * another request. The request buffer must not hold any
+		 * pending output data and the request buffer must not have
+		 * output data occupying the reserve.
 		 */
-		http_end_txn_clean_session(s);
+		if (s->req->buf->o)
+			s->req->flags |= CF_WAKE_WRITE;
+		else if (channel_congested(s->rep))
+			s->rep->flags |= CF_WAKE_WRITE;
+		else
+			http_end_txn_clean_session(s);
 	}
 
 	return txn->req.msg_state != old_req_state ||
@@ -5782,8 +5807,6 @@
 		else if (rep->flags & CF_READ_TIMEOUT) {
 			if (msg->err_pos >= 0)
 				http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->msg_state, s->fe);
-			else if (txn->flags & TX_NOT_FIRST)
-				goto abort_keep_alive;
 
 			s->be->be_counters.failed_resp++;
 			if (objt_server(s->target)) {
@@ -6770,12 +6793,15 @@
 	if (res->flags & CF_SHUTR) {
 		if ((s->req->flags & (CF_SHUTR|CF_SHUTW)) == (CF_SHUTR|CF_SHUTW))
 			goto aborted_xfer;
-		if (!(s->flags & SN_ERR_MASK))
-			s->flags |= SN_ERR_SRVCL;
-		s->be->be_counters.srv_aborts++;
-		if (objt_server(s->target))
-			objt_server(s->target)->counters.srv_aborts++;
-		goto return_bad_res_stats_ok;
+		/* If we have some pending data, we continue the processing */
+		if (!buffer_pending(res->buf)) {
+			if (!(s->flags & SN_ERR_MASK))
+				s->flags |= SN_ERR_SRVCL;
+			s->be->be_counters.srv_aborts++;
+			if (objt_server(s->target))
+				objt_server(s->target)->counters.srv_aborts++;
+			goto return_bad_res_stats_ok;
+		}
 	}
 
 	/* we need to obey the req analyser, so if it leaves, we must too */
@@ -8673,10 +8699,13 @@
 	}
 	if (-occ > found)
 		return 0;
+
 	/* OK now we have the last occurrence in [hist_ptr-1], and we need to
-	 * find occurrence -occ, so we have to check [hist_ptr+occ].
+	 * find occurrence -occ. 0 <= hist_ptr < MAX_HDR_HISTORY, and we have
+	 * -10 <= occ <= -1. So we have to check [hist_ptr%MAX_HDR_HISTORY+occ]
+	 * to remain in the 0..9 range.
 	 */
-	hist_ptr += occ;
+	hist_ptr += occ + MAX_HDR_HISTORY;
 	if (hist_ptr >= MAX_HDR_HISTORY)
 		hist_ptr -= MAX_HDR_HISTORY;
 	*vptr = ptr_hist[hist_ptr];
@@ -11045,9 +11074,11 @@
 }
 
 /*
- * Given a url parameter name, returns its value and size into *value and
- * *value_l respectively, and returns non-zero. If the parameter is not found,
- * zero is returned and value/value_l are not touched.
+ * Given a url parameter name and a query string, find the next value.
+ * An empty url_param_name matches the first available parameter.
+ * If the parameter is found, 1 is returned and *value / *value_l are updated
+ * to respectively provide a pointer to the value and its length.
+ * Otherwise, 0 is returned and value/value_l are not modified.
  */
 static int
 find_url_param_value(char* path, size_t path_l,
@@ -11077,7 +11108,7 @@
 
 	*value = value_start;
 	*value_l = value_end - value_start;
-	return value_end != value_start;
+	return 1;
 }
 
 static int
@@ -11190,12 +11221,15 @@
 	struct chunk *temp;
 	struct connection *cli_conn = objt_conn(l4->si[0].end);
 
+	if (!cli_conn)
+		return 0;
+
 	if (!smp_fetch_url32(px, l4, l7, opt, args, smp, kw))
 		return 0;
 
 	temp = get_trash_chunk();
-	memcpy(temp->str + temp->len, &smp->data.uint, sizeof(smp->data.uint));
-	temp->len += sizeof(smp->data.uint);
+	*(unsigned int *)temp->str = htonl(smp->data.sint);
+	temp->len += sizeof(unsigned int);
 
 	switch (cli_conn->addr.from.ss_family) {
 	case AF_INET:
@@ -11238,7 +11272,7 @@
  */
 static int sample_conv_http_date(const struct arg *args, struct sample *smp)
 {
-	const char day[7][4] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
+	const char day[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
 	const char mon[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
 	struct chunk *temp;
 	struct tm *tm;
@@ -11249,6 +11283,8 @@
 		curr_date += args[0].data.sint;
 
 	tm = gmtime(&curr_date);
+	if (!tm)
+		return 0;
 
 	temp = get_trash_chunk();
 	temp->len = snprintf(temp->str, temp->size - temp->len,
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index cfa62f7..8c06441 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -394,7 +394,7 @@
 			struct sockaddr_storage sa;
 
 			ret = 1;
-			sa = src->source_addr;
+			memcpy(&sa, &src->source_addr, sizeof(sa));
 
 			do {
 				/* note: in case of retry, we may have to release a previously
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index adc1b46..8e3393d 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -504,7 +504,7 @@
 		else if (errno == EAGAIN || errno == EADDRINUSE || errno == EADDRNOTAVAIL) {
 			char *msg;
 			if (errno == EAGAIN || errno == EADDRNOTAVAIL) {
-				msg = "no free ports";
+				msg = "can't connect to destination unix socket, check backlog size on the server";
 				conn->err_code = CO_ER_FREE_PORTS;
 			}
 			else {
diff --git a/src/proxy.c b/src/proxy.c
index 405c4c4..01c8c0b 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -727,19 +727,23 @@
 			Warning("Stopping %s %s in %d ms.\n", proxy_cap_str(p->cap), p->id, p->grace);
 			send_log(p, LOG_WARNING, "Stopping %s %s in %d ms.\n", proxy_cap_str(p->cap), p->id, p->grace);
 			p->stop_time = tick_add(now_ms, p->grace);
-		}
-		if (p->table.size && p->table.sync_task)
-			 task_wakeup(p->table.sync_task, TASK_WOKEN_MSG);
 
-		/* wake every proxy task up so that they can handle the stopping */
-		if (p->task)
-			task_wakeup(p->task, TASK_WOKEN_MSG);
+			/* Note: do not wake up stopped proxies' task nor their tables'
+			 * tasks as these ones might point to already released entries.
+			 */
+			if (p->table.size && p->table.sync_task)
+				task_wakeup(p->table.sync_task, TASK_WOKEN_MSG);
+
+			if (p->task)
+				task_wakeup(p->task, TASK_WOKEN_MSG);
+		}
 		p = p->next;
 	}
 
 	prs = peers;
 	while (prs) {
-		stop_proxy((struct proxy *)prs->peers_fe);
+		if (prs->peers_fe)
+			stop_proxy(prs->peers_fe);
 		prs = prs->next;
 	}
 	/* signal zero is used to broadcast the "stopping" event */
@@ -873,8 +877,8 @@
 
 	prs = peers;
 	while (prs) {
-		p = prs->peers_fe;
-		err |= !pause_proxy(p);
+		if (prs->peers_fe)
+			err |= !pause_proxy(prs->peers_fe);
 		prs = prs->next;
         }
 
@@ -907,8 +911,8 @@
 
 	prs = peers;
 	while (prs) {
-		p = prs->peers_fe;
-		err |= !resume_proxy(p);
+		if (prs->peers_fe)
+			err |= !resume_proxy(prs->peers_fe);
 		prs = prs->next;
         }
 
diff --git a/src/server.c b/src/server.c
index aa9d7df..5f63f0d 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1615,7 +1615,7 @@
 			if (!newsrv->check.port &&
 			    (is_inet_addr(&newsrv->check_common.addr) ||
 			     (!is_addr(&newsrv->check_common.addr) && is_inet_addr(&newsrv->addr)))) {
-				struct tcpcheck_rule *n = NULL, *r = NULL;
+				struct tcpcheck_rule *r = NULL;
 				struct list *l;
 
 				r = (struct tcpcheck_rule *)newsrv->proxy->tcpcheck_rules.n;
@@ -1634,8 +1634,7 @@
 				else {
 					/* scan the tcp-check ruleset to ensure a port has been configured */
 					l = &newsrv->proxy->tcpcheck_rules;
-					list_for_each_entry(n, l, list) {
-						r = (struct tcpcheck_rule *)n->list.p;
+					list_for_each_entry(r, l, list) {
 						if ((r->action == TCPCHK_ACT_CONNECT) && (!r->port)) {
 							Alert("parsing [%s:%d] : server %s has neither service port nor check port, and a tcp_check rule 'connect' with no port information. Check has been disabled.\n",
 							      file, linenum, newsrv->id);
diff --git a/src/session.c b/src/session.c
index 5b9e407..6e4c075 100644
--- a/src/session.c
+++ b/src/session.c
@@ -887,6 +887,7 @@
 	 */
 	if (objt_server(s->target) &&
 	    (si->conn_retries == 0 ||
+	     (__objt_server(s->target)->state < SRV_ST_RUNNING) ||
 	     (!(s->flags & SN_DIRECT) && s->be->srv_act > 1 &&
 	      ((s->be->lbprm.algo & BE_LB_KIND) == BE_LB_KIND_RR))) &&
 	    s->be->options & PR_O_REDISP && !(s->flags & SN_FORCE_PRST)) {
@@ -1706,8 +1707,11 @@
 		      (CF_SHUTR|CF_READ_ACTIVITY|CF_READ_TIMEOUT|CF_SHUTW|
 		       CF_WRITE_ACTIVITY|CF_WRITE_TIMEOUT|CF_ANA_TIMEOUT)) &&
 		    !((s->si[0].flags | s->si[1].flags) & (SI_FL_EXP|SI_FL_ERR)) &&
-		    ((t->state & TASK_WOKEN_ANY) == TASK_WOKEN_TIMER))
+		    ((t->state & TASK_WOKEN_ANY) == TASK_WOKEN_TIMER)) {
+			s->si[0].flags &= ~SI_FL_DONT_WAKE;
+			s->si[1].flags &= ~SI_FL_DONT_WAKE;
 			goto update_exp_and_leave;
+		}
 	}
 
 	/* 1b: check for low-level errors reported at the stream interface.
@@ -2182,7 +2186,7 @@
 		 * to the consumer (which might possibly not be connected yet).
 		 */
 		if (!(s->req->flags & (CF_SHUTR|CF_SHUTW_NOW)))
-			channel_forward(s->req, CHN_INFINITE_FORWARD);
+			channel_forward_forever(s->req);
 	}
 
 	/* check if it is wise to enable kernel splicing to forward request data */
@@ -2213,10 +2217,6 @@
 	if (unlikely((s->req->flags & (CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CLOSE|CF_SHUTR)) ==
 		     (CF_AUTO_CLOSE|CF_SHUTR))) {
 		channel_shutw_now(s->req);
-		if (tick_isset(s->fe->timeout.clientfin)) {
-			s->rep->wto = s->fe->timeout.clientfin;
-			s->rep->wex = tick_add(now_ms, s->rep->wto);
-		}
 	}
 
 	/* shutdown(write) pending */
@@ -2241,10 +2241,6 @@
 		if (s->req->prod->flags & SI_FL_NOHALF)
 			s->req->prod->flags |= SI_FL_NOLINGER;
 		si_shutr(s->req->prod);
-		if (tick_isset(s->fe->timeout.clientfin)) {
-			s->rep->wto = s->fe->timeout.clientfin;
-			s->rep->wex = tick_add(now_ms, s->rep->wto);
-		}
 	}
 
 	/* it's possible that an upper layer has requested a connection setup or abort.
@@ -2293,7 +2289,7 @@
 
 			/* Now we can add the server name to a header (if requested) */
 			/* check for HTTP mode and proxy server_name_hdr_name != NULL */
-			if ((s->si[1].state >= SI_ST_CON) &&
+			if ((s->si[1].state >= SI_ST_CON) && (s->si[1].state < SI_ST_CLO) &&
 			    (s->be->server_id_hdr_name != NULL) &&
 			    (s->be->mode == PR_MODE_HTTP) &&
 			    objt_server(s->target)) {
@@ -2337,7 +2333,7 @@
 		 * to the consumer.
 		 */
 		if (!(s->rep->flags & (CF_SHUTR|CF_SHUTW_NOW)))
-			channel_forward(s->rep, CHN_INFINITE_FORWARD);
+			channel_forward_forever(s->rep);
 
 		/* if we have no analyser anymore in any direction and have a
 		 * tunnel timeout set, use it now. Note that we must respect
@@ -2391,10 +2387,6 @@
 	if (unlikely((s->rep->flags & (CF_SHUTW|CF_SHUTW_NOW|CF_AUTO_CLOSE|CF_SHUTR)) ==
 		     (CF_AUTO_CLOSE|CF_SHUTR))) {
 		channel_shutw_now(s->rep);
-		if (tick_isset(s->be->timeout.serverfin)) {
-			s->req->wto = s->be->timeout.serverfin;
-			s->req->wex = tick_add(now_ms, s->req->wto);
-		}
 	}
 
 	/* shutdown(write) pending */
@@ -2417,10 +2409,6 @@
 		if (s->rep->prod->flags & SI_FL_NOHALF)
 			s->rep->prod->flags |= SI_FL_NOLINGER;
 		si_shutr(s->rep->prod);
-		if (tick_isset(s->be->timeout.serverfin)) {
-			s->req->wto = s->be->timeout.serverfin;
-			s->req->wex = tick_add(now_ms, s->req->wto);
-		}
 	}
 
 	if (s->req->prod->state == SI_ST_DIS || s->req->cons->state == SI_ST_DIS)
@@ -2806,6 +2794,33 @@
 	return &l4->stkctr[num];
 }
 
+/* same as smp_fetch_sc_stkctr() but dedicated to src_* and can create
+ * the entry if it doesn't exist yet. This is needed for a few fetch
+ * functions which need to create an entry, such as src_inc_gpc* and
+ * src_clr_gpc*.
+ */
+struct stkctr *
+smp_create_src_stkctr(struct session *sess, const struct arg *args, const char *kw)
+{
+	static struct stkctr stkctr;
+	struct stktable_key *key;
+	struct connection *conn = objt_conn(sess->si[0].end);
+
+	if (strncmp(kw, "src_", 4) != 0)
+		return NULL;
+
+	if (!conn)
+		return NULL;
+
+	key = addr_to_stktable_key(&conn->addr.from, args->data.prx->table.type);
+	if (!key)
+		return NULL;
+
+	stkctr.table = &args->data.prx->table;
+	stkctr_set_entry(&stkctr, stktable_update_key(stkctr.table, key));
+	return &stkctr;
+}
+
 /* set return a boolean indicating if the requested session counter is
  * currently being tracked or not.
  * Supports being called as "sc[0-9]_tracked" only.
@@ -2887,10 +2902,13 @@
 	if (!stkctr)
 		return 0;
 
+	if (stkctr_entry(stkctr) == NULL)
+		stkctr = smp_create_src_stkctr(l4, args, kw);
+
 	smp->flags = SMP_F_VOL_TEST;
 	smp->type = SMP_T_UINT;
 	smp->data.uint = 0;
-	if (stkctr_entry(stkctr) != NULL) {
+	if (stkctr && stkctr_entry(stkctr)) {
 		void *ptr;
 
 		/* First, update gpc0_rate if it's tracked. Second, update its
@@ -2924,6 +2942,9 @@
 	if (!stkctr)
 		return 0;
 
+	if (stkctr_entry(stkctr) == NULL)
+		stkctr = smp_create_src_stkctr(l4, args, kw);
+
 	smp->flags = SMP_F_VOL_TEST;
 	smp->type = SMP_T_UINT;
 	smp->data.uint = 0;
@@ -3317,7 +3338,7 @@
 
 	smp->flags = SMP_F_VOL_TEST;
 	smp->type = SMP_T_UINT;
-	smp->data.uint = stkctr_entry(stkctr)->ref_cnt;
+	smp->data.uint = stkctr_entry(stkctr) ? stkctr_entry(stkctr)->ref_cnt : 0;
 	return 1;
 }
 
diff --git a/src/signal.c b/src/signal.c
index e9301ed..7b72622 100644
--- a/src/signal.c
+++ b/src/signal.c
@@ -105,6 +105,14 @@
 	signal_queue_len = 0;
 	memset(signal_queue, 0, sizeof(signal_queue));
 	memset(signal_state, 0, sizeof(signal_state));
+
+	/* Ensure signals are not blocked. Some shells or service managers may
+	 * accidently block all of our signals unfortunately, causing lots of
+	 * zombie processes to remain in the background during reloads.
+	 */
+	sigemptyset(&blocked_sig);
+	sigprocmask(SIG_SETMASK, &blocked_sig, NULL);
+
 	sigfillset(&blocked_sig);
 	sigdelset(&blocked_sig, SIGPROF);
 	for (sig = 0; sig < MAX_SIGNAL; sig++)
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 7d77d36..e3c21a4 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -994,6 +994,7 @@
 {
 	struct sni_ctx *sc;
 	int wild = 0, neg = 0;
+	struct ebmb_node *node;
 
 	if (*name == '!') {
 		neg = 1;
@@ -1009,10 +1010,27 @@
 	if (*name) {
 		int j, len;
 		len = strlen(name);
+		for (j = 0; j < len && j < trash.size; j++)
+			trash.str[j] = tolower(name[j]);
+		if (j >= trash.size)
+			return order;
+		trash.str[j] = 0;
+
+		/* Check for duplicates. */
+		if (wild)
+			node = ebst_lookup(&s->sni_w_ctx, trash.str);
+		else
+			node = ebst_lookup(&s->sni_ctx, trash.str);
+		for (; node; node = ebmb_next_dup(node)) {
+			sc = ebmb_entry(node, struct sni_ctx, name);
+			if (sc->ctx == ctx && sc->neg == neg)
+				return order;
+		}
+
 		sc = malloc(sizeof(struct sni_ctx) + len + 1);
-		for (j = 0; j < len; j++)
-			sc->name.key[j] = tolower(name[j]);
-		sc->name.key[len] = 0;
+		if (!sc)
+			return order;
+		memcpy(sc->name.key, trash.str, len + 1);
 		sc->ctx = ctx;
 		sc->order = order++;
 		sc->neg = neg;
@@ -1405,8 +1423,14 @@
 		ssloptions |= SSL_OP_NO_TLSv1_2;
 	if (bind_conf->ssl_options & BC_SSL_O_NO_TLS_TICKETS)
 		ssloptions |= SSL_OP_NO_TICKET;
-	if (bind_conf->ssl_options & BC_SSL_O_USE_SSLV3)
+	if (bind_conf->ssl_options & BC_SSL_O_USE_SSLV3) {
+#ifndef OPENSSL_NO_SSL3
 		SSL_CTX_set_ssl_version(ctx, SSLv3_server_method());
+#else
+		Alert("SSLv3 support requested but unavailable.\n");
+		cfgerr++;
+#endif
+	}
 	if (bind_conf->ssl_options & BC_SSL_O_USE_TLSV10)
 		SSL_CTX_set_ssl_version(ctx, TLSv1_server_method());
 #if SSL_OP_NO_TLSv1_1
@@ -1750,8 +1774,14 @@
 		options |= SSL_OP_NO_TLSv1_2;
 	if (srv->ssl_ctx.options & SRV_SSL_O_NO_TLS_TICKETS)
 		options |= SSL_OP_NO_TICKET;
-	if (srv->ssl_ctx.options & SRV_SSL_O_USE_SSLV3)
+	if (srv->ssl_ctx.options & SRV_SSL_O_USE_SSLV3) {
+#ifndef OPENSSL_NO_SSL3
 		SSL_CTX_set_ssl_version(srv->ssl_ctx.ctx, SSLv3_client_method());
+#else
+		Alert("SSLv3 support requested but unavailable.\n");
+		cfgerr++;
+#endif
+	}
 	if (srv->ssl_ctx.options & SRV_SSL_O_USE_TLSV10)
 		SSL_CTX_set_ssl_version(srv->ssl_ctx.ctx, TLSv1_client_method());
 #if SSL_OP_NO_TLSv1_1
@@ -2208,8 +2238,10 @@
 				global.ssl_be_keys_max = global.ssl_be_keys_per_sec.curr_ctr;
 
 			/* check if session was reused, if not store current session on server for reuse */
-			if (objt_server(conn->target)->ssl_ctx.reused_sess)
+			if (objt_server(conn->target)->ssl_ctx.reused_sess) {
 				SSL_SESSION_free(objt_server(conn->target)->ssl_ctx.reused_sess);
+				objt_server(conn->target)->ssl_ctx.reused_sess = NULL;
+			}
 
 			objt_server(conn->target)->ssl_ctx.reused_sess = SSL_get1_session(conn->xprt_ctx);
 		}
@@ -3976,9 +4008,12 @@
 
 	free(conf->npn_str);
 
-	/* the NPN string is built as a suite of (<len> <name>)* */
+	/* the NPN string is built as a suite of (<len> <name>)*,
+	 * so we reuse each comma to store the next <len> and need
+	 * one more for the end of the string.
+	 */
 	conf->npn_len = strlen(args[cur_arg + 1]) + 1;
-	conf->npn_str = calloc(1, conf->npn_len);
+	conf->npn_str = calloc(1, conf->npn_len + 1);
 	memcpy(conf->npn_str + 1, args[cur_arg + 1], conf->npn_len);
 
 	/* replace commas with the name length */
@@ -4024,9 +4059,12 @@
 
 	free(conf->alpn_str);
 
-	/* the ALPN string is built as a suite of (<len> <name>)* */
+	/* the ALPN string is built as a suite of (<len> <name>)*,
+	 * so we reuse each comma to store the next <len> and need
+	 * one more for the end of the string.
+	 */
 	conf->alpn_len = strlen(args[cur_arg + 1]) + 1;
-	conf->alpn_str = calloc(1, conf->alpn_len);
+	conf->alpn_str = calloc(1, conf->alpn_len + 1);
 	memcpy(conf->alpn_str + 1, args[cur_arg + 1], conf->alpn_len);
 
 	/* replace commas with the name length */
diff --git a/src/standard.c b/src/standard.c
index f57724c..28c2b9a 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -709,6 +709,11 @@
 		goto out;
 	}
 
+	if (!*str2) {
+		memprintf(err, "'%s' resolves to an empty address (environment variable missing?)\n", str);
+		goto out;
+	}
+
 	memset(&ss, 0, sizeof(ss));
 
 	if (strncmp(str2, "unix@", 5) == 0) {
@@ -1699,8 +1704,10 @@
 
 bad_input:
 	memprintf(err, "an hex digit is expected (found '%c')", p[i-1]);
-	if (alloc)
-		free(binstr);
+	if (alloc) {
+		free(*binstr);
+		*binstr = NULL;
+	}
 	return 0;
 }
 
@@ -2197,6 +2204,70 @@
 	return dst;
 }
 
+/* Base year used to compute leap years */
+#define TM_YEAR_BASE 1900
+
+/* Return the difference in seconds between two times (leap seconds are ignored).
+ * Retrieved from glibc 2.18 source code.
+ */
+static int my_tm_diff(const struct tm *a, const struct tm *b)
+{
+	/* Compute intervening leap days correctly even if year is negative.
+	 * Take care to avoid int overflow in leap day calculations,
+	 * but it's OK to assume that A and B are close to each other.
+	 */
+	int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
+	int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
+	int a100 = a4 / 25 - (a4 % 25 < 0);
+	int b100 = b4 / 25 - (b4 % 25 < 0);
+	int a400 = a100 >> 2;
+	int b400 = b100 >> 2;
+	int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
+	int years = a->tm_year - b->tm_year;
+	int days = (365 * years + intervening_leap_days
+	         + (a->tm_yday - b->tm_yday));
+	return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
+	       + (a->tm_min - b->tm_min))
+	       + (a->tm_sec - b->tm_sec));
+}
+
+/* Return the GMT offset for a specific local time.
+ * Both t and tm must represent the same time.
+ * The string returned has the same format as returned by strftime(... "%z", tm).
+ * Offsets are kept in an internal cache for better performances.
+ */
+const char *get_gmt_offset(time_t t, struct tm *tm)
+{
+	/* Cache offsets from GMT (depending on whether DST is active or not) */
+	static char gmt_offsets[2][5+1] = { "", "" };
+
+	char *gmt_offset;
+	struct tm tm_gmt;
+	int diff;
+	int isdst = tm->tm_isdst;
+
+	/* Pretend DST not active if its status is unknown */
+	if (isdst < 0)
+		isdst = 0;
+
+	/* Fetch the offset and initialize it if needed */
+	gmt_offset = gmt_offsets[isdst & 0x01];
+	if (unlikely(!*gmt_offset)) {
+		get_gmtime(t, &tm_gmt);
+		diff = my_tm_diff(tm, &tm_gmt);
+		if (diff < 0) {
+			diff = -diff;
+			*gmt_offset = '-';
+		} else {
+			*gmt_offset = '+';
+		}
+		diff /= 60; /* Convert to minutes */
+		snprintf(gmt_offset+1, 4+1, "%02d%02d", diff/60, diff%60);
+	}
+
+    return gmt_offset;
+}
+
 /* gmt2str_log: write a date in the format :
  * "%02d/%s/%04d:%02d:%02d:%02d +0000" without using snprintf
  * return a pointer to the last char written (\0) or
@@ -2232,14 +2303,18 @@
 
 /* localdate2str_log: write a date in the format :
  * "%02d/%s/%04d:%02d:%02d:%02d +0000(local timezone)" without using snprintf
- * * return a pointer to the last char written (\0) or
- * * NULL if there isn't enough space.
+ * Both t and tm must represent the same time.
+ * return a pointer to the last char written (\0) or
+ * NULL if there isn't enough space.
  */
-char *localdate2str_log(char *dst, struct tm *tm, size_t size)
+char *localdate2str_log(char *dst, time_t t, struct tm *tm, size_t size)
 {
+	const char *gmt_offset;
 	if (size < 27) /* the size is fixed: 26 chars + \0 */
 		return NULL;
 
+	gmt_offset = get_gmt_offset(t, tm);
+
 	dst = utoa_pad((unsigned int)tm->tm_mday, dst, 3); // day
 	*dst++ = '/';
 	memcpy(dst, monthname[tm->tm_mon], 3); // month
@@ -2253,7 +2328,7 @@
 	*dst++ = ':';
 	dst = utoa_pad((unsigned int)tm->tm_sec, dst, 3); // secondes
 	*dst++ = ' ';
-	memcpy(dst, localtimezone, 5); // timezone
+	memcpy(dst, gmt_offset, 5); // Offset from local time to GMT
 	dst += 5;
 	*dst = '\0';
 
diff --git a/src/stick_table.c b/src/stick_table.c
index 48d5710..2ab56fd 100644
--- a/src/stick_table.c
+++ b/src/stick_table.c
@@ -167,9 +167,10 @@
 			return NULL;
 	}
 
-	ts = pool_alloc2(t->pool) + t->data_size;
+	ts = pool_alloc2(t->pool);
 	if (ts) {
 		t->current++;
+		ts = (void *)ts + t->data_size;
 		stksess_init(t, ts);
 		if (key)
 			stksess_setkey(t, ts, key);
@@ -385,6 +386,7 @@
 	if (t->size) {
 		memset(&t->keys, 0, sizeof(t->keys));
 		memset(&t->exps, 0, sizeof(t->exps));
+		t->updates = EB_ROOT_UNIQUE;
 
 		t->pool = create_pool("sticktables", sizeof(struct stksess) + t->data_size + t->key_size, MEM_F_SHARED);