diff -Nru haproxy-1.5.4/CHANGELOG haproxy-1.5.5/CHANGELOG --- haproxy-1.5.4/CHANGELOG 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/CHANGELOG 2014-10-08 08:07:23.000000000 +0000 @@ -1,6 +1,30 @@ ChangeLog : =========== +2014/10/08 : 1.5.5 + - DOC: Address issue where documentation is excluded due to a gitignore rule. + - MEDIUM: Improve signal handling in systemd wrapper. + - BUG/MINOR: config: don't propagate process binding for dynamic use_backend + - MINOR: Also accept SIGHUP/SIGTERM in systemd-wrapper + - DOC: clearly state that the "show sess" output format is not fixed + - MINOR: stats: fix minor typo fix in stats_dump_errors_to_buffer() + - DOC: indicate in the doc that track-sc* can wait if data are missing + - MEDIUM: http: enable header manipulation for 101 responses + - BUG/MEDIUM: config: propagate frontend to backend process binding again. + - MEDIUM: config: properly propagate process binding between proxies + - MEDIUM: config: make the frontends automatically bind to the listeners' processes + - MEDIUM: config: compute the exact bind-process before listener's maxaccept + - MEDIUM: config: only warn if stats are attached to multi-process bind directives + - MEDIUM: config: report it when tcp-request rules are misplaced + - MINOR: config: detect the case where a tcp-request content rule has no inspect-delay + - MEDIUM: systemd-wrapper: support multiple executable versions and names + - BUG/MEDIUM: remove debugging code from systemd-wrapper + - BUG/MEDIUM: http: adjust close mode when switching to backend + - BUG/MINOR: config: don't propagate process binding on fatal errors. + - BUG/MEDIUM: check: rule-less tcp-check must detect connect failures + - BUG/MINOR: tcp-check: report the correct failed step in the status + - DOC: indicate that weight zero is reported as DRAIN + 2014/09/02 : 1.5.4 - BUG: config: error in http-response replace-header number of arguments - BUG/MINOR: Fix search for -p argument in systemd wrapper. diff -Nru haproxy-1.5.4/debian/changelog haproxy-1.5.5/debian/changelog --- haproxy-1.5.4/debian/changelog 2014-09-02 17:44:47.000000000 +0000 +++ haproxy-1.5.5/debian/changelog 2014-10-10 06:21:25.000000000 +0000 @@ -1,3 +1,56 @@ +haproxy (1.5.5-1ppa1~lucid) lucid; urgency=medium + + * Rebuild for Lucid (PPA). + + -- Vincent Bernat Fri, 10 Oct 2014 08:21:19 +0200 + +haproxy (1.5.5-1) unstable; urgency=medium + + [ Vincent Bernat ] + * initscript: use start-stop-daemon to reliably terminate all haproxy + processes. Also treat stopping a non-running haproxy as success. + (Closes: #762608, LP: #1038139) + + [ Apollon Oikonomopoulos ] + * New upstream stable release including the following fixes: + + DOC: Address issue where documentation is excluded due to a gitignore + rule. + + MEDIUM: Improve signal handling in systemd wrapper. + + BUG/MINOR: config: don't propagate process binding for dynamic + use_backend + + MINOR: Also accept SIGHUP/SIGTERM in systemd-wrapper + + DOC: clearly state that the "show sess" output format is not fixed + + MINOR: stats: fix minor typo fix in stats_dump_errors_to_buffer() + + DOC: indicate in the doc that track-sc* can wait if data are missing + + MEDIUM: http: enable header manipulation for 101 responses + + BUG/MEDIUM: config: propagate frontend to backend process binding again. + + MEDIUM: config: properly propagate process binding between proxies + + MEDIUM: config: make the frontends automatically bind to the listeners' + processes + + MEDIUM: config: compute the exact bind-process before listener's + maxaccept + + MEDIUM: config: only warn if stats are attached to multi-process bind + directives + + MEDIUM: config: report it when tcp-request rules are misplaced + + MINOR: config: detect the case where a tcp-request content rule has no + inspect-delay + + MEDIUM: systemd-wrapper: support multiple executable versions and names + + BUG/MEDIUM: remove debugging code from systemd-wrapper + + BUG/MEDIUM: http: adjust close mode when switching to backend + + BUG/MINOR: config: don't propagate process binding on fatal errors. + + BUG/MEDIUM: check: rule-less tcp-check must detect connect failures + + BUG/MINOR: tcp-check: report the correct failed step in the status + + DOC: indicate that weight zero is reported as DRAIN + * Add a new patch (haproxy.service-set-killmode-to-mixed.patch) to fix the + systemctl stop action conflicting with the systemd wrapper now catching + SIGTERM. + * Bump standards to 3.9.6; no changes needed. + * haproxy-doc: link to tracker.debian.org instead of packages.qa.debian.org. + * d/copyright: move debian/dconv/* paragraph after debian/*, so that it + actually matches the files it is supposed to. + + -- Apollon Oikonomopoulos Wed, 08 Oct 2014 12:34:53 +0300 + haproxy (1.5.4-1ppa1~lucid) lucid; urgency=high * Rebuild for Lucid (PPA). diff -Nru haproxy-1.5.4/debian/control haproxy-1.5.5/debian/control --- haproxy-1.5.4/debian/control 2014-09-02 17:44:47.000000000 +0000 +++ haproxy-1.5.5/debian/control 2014-10-10 06:21:25.000000000 +0000 @@ -5,7 +5,7 @@ Uploaders: Apollon Oikonomopoulos , Prach Pongpanich , Vincent Bernat -Standards-Version: 3.9.5 +Standards-Version: 3.9.6 Build-Depends: debhelper (>= 7.0.50~), libpcre3-dev, libssl-dev Homepage: http://haproxy.1wt.eu/ Vcs-Git: git://anonscm.debian.org/pkg-haproxy/haproxy.git diff -Nru haproxy-1.5.4/debian/copyright haproxy-1.5.5/debian/copyright --- haproxy-1.5.4/debian/copyright 2014-09-02 17:44:47.000000000 +0000 +++ haproxy-1.5.5/debian/copyright 2014-10-10 06:21:25.000000000 +0000 @@ -130,10 +130,6 @@ Copyright: Copyright 2007 Aleksandar Lazic License: GPL-2+ -Files: debian/dconv/* -Copyright: Copyright (C) 2012 Cyril Bonté -License: Apache-2.0 - Files: debian/* Copyright: Copyright (C) 2007-2011, Arnaud Cornet Copyright (C) 2011, Christo Buschek @@ -141,6 +137,10 @@ Copyright (C) 2013-2014, Apollon Oikonomopoulos Copyright (C) 2013, Vincent Bernat License: GPL-2 + +Files: debian/dconv/* +Copyright: Copyright (C) 2012 Cyril Bonté +License: Apache-2.0 License: GPL-2+ This program is free software; you can redistribute it diff -Nru haproxy-1.5.4/debian/haproxy.init haproxy-1.5.5/debian/haproxy.init --- haproxy-1.5.4/debian/haproxy.init 2014-09-02 17:44:47.000000000 +0000 +++ haproxy-1.5.5/debian/haproxy.init 2014-10-10 06:21:25.000000000 +0000 @@ -59,11 +59,16 @@ # This is a success according to LSB return 0 fi - for pid in $(cat $PIDFILE) ; do - /bin/kill $pid || return 4 + + ret=0 + for pid in $(cat $PIDFILE); do + start-stop-daemon --quiet --oknodo --stop \ + --retry 5 --pid $pid --exec $HAPROXY || ret=$? done - rm -f $PIDFILE - return 0 + + [ $ret -eq 0 ] && rm -f $PIDFILE + + return $ret } haproxy_reload() diff -Nru haproxy-1.5.4/debian/patches/debianize-dconv.patch haproxy-1.5.5/debian/patches/debianize-dconv.patch --- haproxy-1.5.4/debian/patches/debianize-dconv.patch 2014-09-02 17:44:47.000000000 +0000 +++ haproxy-1.5.5/debian/patches/debianize-dconv.patch 2014-10-10 06:21:25.000000000 +0000 @@ -144,7 +144,7 @@ - +
  • Bug Tracking System
  • +
  • Package page
  • -+
  • Package Tracking System
  • ++
  • Package Tracking System
  • +
  • +
  • Package Git Repository
  • diff -Nru haproxy-1.5.4/debian/patches/haproxy.service-set-killmode-to-mixed.patch haproxy-1.5.5/debian/patches/haproxy.service-set-killmode-to-mixed.patch --- haproxy-1.5.4/debian/patches/haproxy.service-set-killmode-to-mixed.patch 1970-01-01 00:00:00.000000000 +0000 +++ haproxy-1.5.5/debian/patches/haproxy.service-set-killmode-to-mixed.patch 2014-10-10 06:21:25.000000000 +0000 @@ -0,0 +1,28 @@ +Author: Apollon Oikonomopoulos +Description: Set KillMode to "mixed" + Since 1.5.5, the systemd wrapper handles SIGTERM by dispatching SIGINT to the + workers (which are its grand-children). By default, systemd sends the TERM + signal to all processes in the control group, including the wrapper that used + to die as well. However, now the wrapper lives on and gets a SIGCHLD from the + master haproxy process getting killed by systemd and assumes this is an error. + . + To work around this behaviour, we set KillMode to "mixed", which means that + systemd will dispatch the initial SIGTERM to the wrapper only (as it's the + main process) and if this doesn't work, it will send the SIGKILL to all + processes in the group, thus making sure no runaway children are left behind. + See systemd.kill(5) for more information on this behavior. + +Last-Update: 2014-10-08 +Forwarded: no +--- a/contrib/systemd/haproxy.service.in ++++ b/contrib/systemd/haproxy.service.in +@@ -13,6 +13,9 @@ + ExecReload=@SBINDIR@/haproxy -c -f ${CONFIG} + ExecReload=/bin/kill -USR2 $MAINPID + Restart=always ++# Send SIGTERM to the wrapper, let it propagate it to the children. Send ++# SIGKILL, if needed, to everyone. ++KillMode=mixed + + [Install] + WantedBy=multi-user.target diff -Nru haproxy-1.5.4/debian/patches/series haproxy-1.5.5/debian/patches/series --- haproxy-1.5.4/debian/patches/series 2014-09-02 17:44:47.000000000 +0000 +++ haproxy-1.5.5/debian/patches/series 2014-10-10 06:21:25.000000000 +0000 @@ -4,3 +4,4 @@ haproxy.service-check-config-before-reload.patch haproxy.service-use-environment-variables.patch haproxy.service-also-check-on-start.patch +haproxy.service-set-killmode-to-mixed.patch diff -Nru haproxy-1.5.4/doc/configuration.txt haproxy-1.5.5/doc/configuration.txt --- haproxy-1.5.4/doc/configuration.txt 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/doc/configuration.txt 2014-10-08 08:07:23.000000000 +0000 @@ -1905,6 +1905,10 @@ Each "bind" line may further be limited to a subset of the proxy's processes, please consult the "process" bind keyword in section 5.1. + When a frontend has no explicit "bind-process" line, it tries to bind to all + the processes referenced by its "bind" lines. That means that frontends can + easily adapt to their listeners' processes. + If some backends are referenced by frontends bound to other processes, the backend automatically inherits the frontend's processes. @@ -7470,9 +7474,9 @@ contents will always be immediately present when the rule is evaluated first. Tracking layer7 information is also possible provided that the information - are present when the rule is processed. The current solution for making the - rule engine wait for such information is to set an inspect delay and to - condition its execution with an ACL relying on such information. + are present when the rule is processed. The rule processing engine is able to + wait until the inspect delay expires when the data to be tracked is not yet + available. Example: # Accept HTTP requests containing a Host header saying "example.com" @@ -7497,12 +7501,12 @@ Example: # Track the last IP from X-Forwarded-For tcp-request inspect-delay 10s - tcp-request content track-sc0 hdr(x-forwarded-for,-1) if HTTP + tcp-request content track-sc0 hdr(x-forwarded-for,-1) Example: # track request counts per "base" (concatenation of Host+URL) tcp-request inspect-delay 10s - tcp-request content track-sc0 base table req-rate if HTTP + tcp-request content track-sc0 base table req-rate Example: track per-frontend and per-backend counters, block abusers at the frontend when the backend detects abuse. @@ -8645,7 +8649,9 @@ - An ASCII representation of a positive integer percentage, e.g. "75%". Values in this format will set the weight proportional to the initial - weight of a server as configured when haproxy starts. + weight of a server as configured when haproxy starts. Note that a zero + weight is reported on the stats page as "DRAIN" since it has the same + effect on the server (it's removed from the LB farm). - The word "ready". This will turn the server's administrative state to the READY mode, thus cancelling any DRAIN or MAINT state @@ -13734,9 +13740,11 @@ of "show sess" (it corresponds to the session pointer). Those information are useless to most users but may be used by haproxy developers to troubleshoot a complex bug. The output format is intentionally not documented so that it can - freely evolve depending on demands. The special id "all" dumps the states of - all sessions, which can be avoided as much as possible as it is highly CPU - intensive and can take a lot of time. + freely evolve depending on demands. You may find a description of all fields + returned in src/dumpstats.c + + The special id "all" dumps the states of all sessions, which must be avoided + as much as possible as it is highly CPU intensive and can take a lot of time. show stat [ ] Dump statistics in the CSV format. By passing , and , it is diff -Nru haproxy-1.5.4/examples/haproxy.spec haproxy-1.5.5/examples/haproxy.spec --- haproxy-1.5.4/examples/haproxy.spec 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/examples/haproxy.spec 2014-10-08 08:07:23.000000000 +0000 @@ -1,6 +1,6 @@ Summary: HA-Proxy is a TCP/HTTP reverse proxy for high availability environments Name: haproxy -Version: 1.5.4 +Version: 1.5.5 Release: 1 License: GPL Group: System Environment/Daemons @@ -76,6 +76,9 @@ %attr(0755,root,root) %config %{_sysconfdir}/rc.d/init.d/%{name} %changelog +* Wed Oct 8 2014 Willy Tarreau +- updated to 1.5.5 + * Tue Sep 2 2014 Willy Tarreau - updated to 1.5.4 diff -Nru haproxy-1.5.4/.gitignore haproxy-1.5.5/.gitignore --- haproxy-1.5.4/.gitignore 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/.gitignore 2014-10-08 08:07:23.000000000 +0000 @@ -12,6 +12,7 @@ *.log* *.trace* haproxy-* +!doc/haproxy-*.txt !src/*.c make-* dlmalloc.c diff -Nru haproxy-1.5.4/include/common/cfgparse.h haproxy-1.5.5/include/common/cfgparse.h --- haproxy-1.5.4/include/common/cfgparse.h 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/include/common/cfgparse.h 2014-10-08 08:07:23.000000000 +0000 @@ -73,6 +73,8 @@ int str2listener(char *str, struct proxy *curproxy, struct bind_conf *bind_conf, const char *file, int line, char **err); int cfg_register_section(char *section_name, int (*section_parser)(const char *, int, char **, int)); +int warnif_misplaced_tcp_conn(struct proxy *proxy, const char *file, int line, const char *arg); +int warnif_misplaced_tcp_cont(struct proxy *proxy, const char *file, int line, const char *arg); /* * Sends a warning if proxy does not have at least one of the diff -Nru haproxy-1.5.4/include/proto/proto_http.h haproxy-1.5.5/include/proto/proto_http.h --- haproxy-1.5.4/include/proto/proto_http.h 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/include/proto/proto_http.h 2014-10-08 08:07:23.000000000 +0000 @@ -112,6 +112,7 @@ void http_init_txn(struct session *s); void http_end_txn(struct session *s); void http_reset_txn(struct session *s); +void http_adjust_conn_mode(struct session *s, struct http_txn *txn, struct http_msg *msg); struct http_req_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy); struct http_res_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy); diff -Nru haproxy-1.5.4/src/cfgparse.c haproxy-1.5.5/src/cfgparse.c --- haproxy-1.5.4/src/cfgparse.c 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/src/cfgparse.c 2014-10-08 08:07:23.000000000 +0000 @@ -317,6 +317,19 @@ return 0; } +/* Report a warning if a rule is placed after a 'tcp-request content' rule. + * Return 1 if the warning has been emitted, otherwise 0. + */ +int warnif_rule_after_tcp_cont(struct proxy *proxy, const char *file, int line, const char *arg) +{ + if (!LIST_ISEMPTY(&proxy->tcp_req.inspect_rules)) { + Warning("parsing [%s:%d] : a '%s' rule placed after a 'tcp-request content' rule will still be processed before.\n", + file, line, arg); + return 1; + } + return 0; +} + /* Report a warning if a rule is placed after a 'block' rule. * Return 1 if the warning has been emitted, otherwise 0. */ @@ -408,6 +421,31 @@ return 0; } +/* report a warning if a "tcp request connection" rule is dangerously placed */ +int warnif_misplaced_tcp_conn(struct proxy *proxy, const char *file, int line, const char *arg) +{ + return warnif_rule_after_tcp_cont(proxy, file, line, arg) || + warnif_rule_after_block(proxy, file, line, arg) || + warnif_rule_after_http_req(proxy, file, line, arg) || + warnif_rule_after_reqxxx(proxy, file, line, arg) || + warnif_rule_after_reqadd(proxy, file, line, arg) || + warnif_rule_after_redirect(proxy, file, line, arg) || + warnif_rule_after_use_backend(proxy, file, line, arg) || + warnif_rule_after_use_server(proxy, file, line, arg); +} + +/* report a warning if a "tcp request content" rule is dangerously placed */ +int warnif_misplaced_tcp_cont(struct proxy *proxy, const char *file, int line, const char *arg) +{ + return warnif_rule_after_block(proxy, file, line, arg) || + warnif_rule_after_http_req(proxy, file, line, arg) || + warnif_rule_after_reqxxx(proxy, file, line, arg) || + warnif_rule_after_reqadd(proxy, file, line, arg) || + warnif_rule_after_redirect(proxy, file, line, arg) || + warnif_rule_after_use_backend(proxy, file, line, arg) || + warnif_rule_after_use_server(proxy, file, line, arg); +} + /* report a warning if a block rule is dangerously placed */ int warnif_misplaced_block(struct proxy *proxy, const char *file, int line, const char *arg) { @@ -5932,6 +5970,66 @@ return err_code; } +/* This function propagates processes from frontend to backend so + * that it is always guaranteed that a backend pointed to by a frontend is + * bound to all of its processes. After that, if the target is a "listen" + * instance, the function recursively descends the target's own targets along + * default_backend, use_backend rules, and reqsetbe rules. Since the bits are + * checked first to ensure that is already bound to all processes of + * , there is no risk of looping and we ensure to follow the shortest + * path to the destination. + * + * It is possible to set to NULL for the first call so that the function + * takes care of visiting the initial frontend in . + * + * It is important to note that the function relies on the fact that all names + * have already been resolved. + */ +void propagate_processes(struct proxy *from, struct proxy *to) +{ + struct switching_rule *rule; + struct hdr_exp *exp; + + if (to) { + /* check whether we need to go down */ + if (from->bind_proc && + (from->bind_proc & to->bind_proc) == from->bind_proc) + return; + + if (!from->bind_proc && !to->bind_proc) + return; + + to->bind_proc = from->bind_proc ? + (to->bind_proc | from->bind_proc) : 0; + + /* now propagate down */ + from = to; + } + + if (!from->cap & PR_CAP_FE) + return; + + /* default_backend */ + if (from->defbe.be) + propagate_processes(from, from->defbe.be); + + /* use_backend */ + list_for_each_entry(rule, &from->switching_rules, list) { + if (rule->dynamic) + continue; + to = rule->be.backend; + propagate_processes(from, to); + } + + /* reqsetbe */ + for (exp = from->req_exp; exp != NULL; exp = exp->next) { + if (exp->action != ACT_SETBE) + continue; + to = (struct proxy *)exp->replace; + propagate_processes(from, to); + } +} + /* * Returns the error code, 0 if OK, or any combination of : * - ERR_ABORT: must abort ASAP @@ -5984,12 +6082,11 @@ proxy = next; } - while (curproxy != NULL) { + for (curproxy = proxy; curproxy; curproxy = curproxy->next) { struct switching_rule *rule; struct server_rule *srule; struct sticking_rule *mrule; struct tcp_rule *trule; - struct listener *listener; unsigned int next_id; int nbproc; @@ -6057,14 +6154,6 @@ } } - /* here, if bind_proc is null, it means no limit, otherwise it's explicit. - * We now check how many processes the proxy will effectively run on. - */ - - nbproc = global.nbproc; - if (curproxy->bind_proc) - nbproc = popcount(curproxy->bind_proc & nbits(global.nbproc)); - if (global.nbproc > 1 && curproxy->table.peers.name) { Alert("Proxy '%s': peers can't be used in multi-process mode (nbproc > 1).\n", curproxy->id); @@ -6162,12 +6251,6 @@ } else { free(curproxy->defbe.name); curproxy->defbe.be = target; - /* we force the backend to be present on at least all of - * the frontend's processes. - */ - if (target->bind_proc) - target->bind_proc = curproxy->bind_proc ? - (target->bind_proc | curproxy->bind_proc) : 0; /* Emit a warning if this proxy also has some servers */ if (curproxy->srv) { @@ -6200,12 +6283,6 @@ } else { free((void *)exp->replace); exp->replace = (const char *)target; - /* we force the backend to be present on at least all of - * the frontend's processes. - */ - if (target->bind_proc) - target->bind_proc = curproxy->bind_proc ? - (target->bind_proc | curproxy->bind_proc) : 0; } } } @@ -6254,16 +6331,10 @@ } else { free((void *)rule->be.name); rule->be.backend = target; - /* we force the backend to be present on at least all of - * the frontend's processes. - */ - if (target->bind_proc) - target->bind_proc = curproxy->bind_proc ? - (target->bind_proc | curproxy->bind_proc) : 0; } } - /* find the target proxy for 'use_backend' rules */ + /* find the target server for 'use_server' rules */ list_for_each_entry(srule, &curproxy->server_rules, list) { struct server *target = findserver(curproxy, srule->srv.name); @@ -6929,6 +7000,29 @@ newsrv = newsrv->next; } + /* check if we have a frontend with "tcp-request content" looking at L7 + * with no inspect-delay + */ + if ((curproxy->cap & PR_CAP_FE) && !curproxy->tcp_req.inspect_delay) { + list_for_each_entry(trule, &curproxy->tcp_req.inspect_rules, list) { + if (trule->action == TCP_ACT_CAPTURE && + !(trule->act_prm.cap.expr->fetch->val & SMP_VAL_FE_SES_ACC)) + break; + if ((trule->action >= TCP_ACT_TRK_SC0 && trule->action <= TCP_ACT_TRK_SCMAX) && + !(trule->act_prm.trk_ctr.expr->fetch->val & SMP_VAL_FE_SES_ACC)) + break; + } + + if (&trule->list != &curproxy->tcp_req.inspect_rules) { + Warning("config : %s '%s' : some 'tcp-request content' rules explicitly depending on request" + " contents were found in a frontend without any 'tcp-request inspect-delay' setting." + " This means that these rules will randomly find their contents. This can be fixed by" + " setting the tcp-request inspect-delay.\n", + proxy_type_str(curproxy), curproxy->id); + err_code |= ERR_WARN; + } + } + if (curproxy->cap & PR_CAP_FE) { if (!curproxy->accept) curproxy->accept = frontend_accept; @@ -6965,6 +7059,90 @@ if (curproxy->options2 & PR_O2_RDPC_PRST) curproxy->be_req_ana |= AN_REQ_PRST_RDP_COOKIE; } + } + + /***********************************************************/ + /* At this point, target names have already been resolved. */ + /***********************************************************/ + + /* Check multi-process mode compatibility */ + + if (global.nbproc > 1 && global.stats_fe) { + list_for_each_entry(bind_conf, &global.stats_fe->conf.bind, by_fe) { + unsigned long mask; + + mask = nbits(global.nbproc); + if (global.stats_fe->bind_proc) + mask &= global.stats_fe->bind_proc; + + if (bind_conf->bind_proc) + mask &= bind_conf->bind_proc; + + /* stop here if more than one process is used */ + if (popcount(mask) > 1) + break; + } + if (&bind_conf->by_fe != &global.stats_fe->conf.bind) { + Warning("stats socket will not work as expected in multi-process mode (nbproc > 1), you should force process binding globally using 'stats bind-process' or per socket using the 'process' attribute.\n"); + } + } + + /* Make each frontend inherit bind-process from its listeners when not specified. */ + for (curproxy = proxy; curproxy; curproxy = curproxy->next) { + if (curproxy->bind_proc) + continue; + + list_for_each_entry(bind_conf, &curproxy->conf.bind, by_fe) { + unsigned long mask; + + mask = bind_conf->bind_proc ? bind_conf->bind_proc : ~0UL; + curproxy->bind_proc |= mask; + } + + if (!curproxy->bind_proc) + curproxy->bind_proc = ~0UL; + } + + if (global.stats_fe) { + 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 : ~0UL; + global.stats_fe->bind_proc |= mask; + } + if (!global.stats_fe->bind_proc) + global.stats_fe->bind_proc = ~0UL; + } + + /* propagate bindings from frontends to backends. Don't do it if there + * are any fatal errors as we must not call it with unresolved proxies. + */ + if (!cfgerr) { + for (curproxy = proxy; curproxy; curproxy = curproxy->next) { + if (curproxy->cap & PR_CAP_FE) + propagate_processes(curproxy, NULL); + } + } + + /* Bind each unbound backend to all processes when not specified. */ + for (curproxy = proxy; curproxy; curproxy = curproxy->next) { + if (curproxy->bind_proc) + continue; + curproxy->bind_proc = ~0UL; + } + + /*******************************************************/ + /* At this step, all proxies have a non-null bind_proc */ + /*******************************************************/ + + /* perform the final checks before creating tasks */ + + for (curproxy = proxy; curproxy; curproxy = curproxy->next) { + struct listener *listener; + unsigned int next_id; + int nbproc; + + nbproc = popcount(curproxy->bind_proc & nbits(global.nbproc)); #ifdef USE_OPENSSL /* Configure SSL for each bind line. @@ -7078,8 +7256,19 @@ if (nbproc > 1) { if (curproxy->uri_auth) { - Warning("Proxy '%s': in multi-process mode, stats will be limited to process assigned to the current request.\n", - curproxy->id); + int count, maxproc = 0; + + list_for_each_entry(bind_conf, &curproxy->conf.bind, by_fe) { + count = popcount(bind_conf->bind_proc); + if (count > maxproc) + maxproc = count; + } + /* backends have 0, frontends have 1 or more */ + if (maxproc != 1) + Warning("Proxy '%s': in multi-process mode, stats will be" + " limited to process assigned to the current request.\n", + curproxy->id); + if (!LIST_ISEMPTY(&curproxy->uri_auth->admin_rules)) { Warning("Proxy '%s': stats admin will not work correctly in multi-process mode.\n", curproxy->id); @@ -7109,29 +7298,6 @@ curproxy->id); cfgerr++; } - - curproxy = curproxy->next; - } - - /* Check multi-process mode compatibility */ - if (global.nbproc > 1 && global.stats_fe) { - list_for_each_entry(bind_conf, &global.stats_fe->conf.bind, by_fe) { - unsigned long mask; - - mask = nbits(global.nbproc); - if (global.stats_fe->bind_proc) - mask &= global.stats_fe->bind_proc; - - if (bind_conf->bind_proc) - mask &= bind_conf->bind_proc; - - /* stop here if more than one process is used */ - if (popcount(mask) > 1) - break; - } - if (&bind_conf->by_fe != &global.stats_fe->conf.bind) { - Warning("stats socket will not work as expected in multi-process mode (nbproc > 1), you should force process binding globally using 'stats bind-process' or per socket using the 'process' attribute.\n"); - } } /* automatically compute fullconn if not set. We must not do it in the diff -Nru haproxy-1.5.4/src/checks.c haproxy-1.5.5/src/checks.c --- haproxy-1.5.4/src/checks.c 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/src/checks.c 2014-10-08 08:07:23.000000000 +0000 @@ -580,6 +580,7 @@ struct check *check = conn->owner; const char *err_msg; struct chunk *chk; + int step; if (check->result != CHK_RES_UNKNOWN) return; @@ -599,19 +600,27 @@ chk = get_trash_chunk(); if (check->type == PR_O2_TCPCHK_CHK) { - chunk_printf(chk, " at step %d of tcp-check", tcpcheck_get_step_id(check->server)); - /* we were looking for a string */ - if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) { - chunk_appendf(chk, " (connect)"); - } - else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) { - if (check->current_step->string) - chunk_appendf(chk, " (string '%s')", check->current_step->string); - else if (check->current_step->expect_regex) - chunk_appendf(chk, " (expect regex)"); - } - else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) { - chunk_appendf(chk, " (send)"); + step = tcpcheck_get_step_id(check->server); + if (!step) + chunk_printf(chk, " at initial connection step of tcp-check"); + else { + chunk_printf(chk, " at step %d of tcp-check", step); + /* we were looking for a string */ + if (check->last_started_step && check->last_started_step->action == TCPCHK_ACT_CONNECT) { + if (check->last_started_step->port) + chunk_appendf(chk, " (connect port %d)" ,check->last_started_step->port); + else + chunk_appendf(chk, " (connect)"); + } + else if (check->last_started_step && check->last_started_step->action == TCPCHK_ACT_EXPECT) { + if (check->last_started_step->string) + chunk_appendf(chk, " (string '%s')", check->last_started_step->string); + else if (check->last_started_step->expect_regex) + chunk_appendf(chk, " (expect regex)"); + } + else if (check->last_started_step && check->last_started_step->action == TCPCHK_ACT_SEND) { + chunk_appendf(chk, " (send)"); + } } } @@ -1818,6 +1827,10 @@ struct tcpcheck_rule *cur = NULL, *next = NULL; int i = 0; + /* not even started anything yet => step 0 = initial connect */ + if (!s->check.current_step) + return 0; + cur = s->check.last_started_step; /* no step => first step */ @@ -1837,20 +1850,34 @@ static void tcpcheck_main(struct connection *conn) { char *contentptr; - struct list *head = NULL; struct tcpcheck_rule *cur = NULL; int done = 0, ret = 0; - struct check *check = conn->owner; struct server *s = check->server; struct task *t = check->task; + struct list *head = &s->proxy->tcpcheck_rules; - /* - * don't do anything until the connection is established but if we're running - * first step which must be a connect + /* here, we know that the check is complete or that it failed */ + if (check->result != CHK_RES_UNKNOWN) + goto out_end_tcpcheck; + + /* We have 4 possibilities here : + * 1. we've not yet attempted step 1, and step 1 is a connect, so no + * connection attempt was made yet ; + * 2. we've not yet attempted step 1, and step 1 is a not connect or + * does not exist (no rule), so a connection attempt was made + * before coming here. + * 3. we're coming back after having started with step 1, so we may + * be waiting for a connection attempt to complete. + * 4. the connection + handshake are complete + * + * #2 and #3 are quite similar, we want both the connection and the + * handshake to complete before going any further. Thus we must always + * wait for a connection to complete unless we're before and existing + * step 1. */ - if (check->current_step && (!(conn->flags & CO_FL_CONNECTED))) { - /* update expire time, should be done by process_chk */ + if ((!(conn->flags & CO_FL_CONNECTED) || (conn->flags & CO_FL_HANDSHAKE)) && + (check->current_step || LIST_ISEMPTY(head))) { /* we allow up to min(inter, timeout.connect) for a connection * to establish but only when timeout.check is set * as it may be to short for a full check otherwise @@ -1867,16 +1894,15 @@ return; } - /* here, we know that the connection is established */ - if (check->result != CHK_RES_UNKNOWN) + /* special case: option tcp-check with no rule, a connect is enough */ + if (LIST_ISEMPTY(head)) { + set_server_check_status(check, HCHK_STATUS_L4OK, NULL); goto out_end_tcpcheck; + } - /* head is be the first element of the double chained list */ - head = &s->proxy->tcpcheck_rules; - - /* no step means first step - * initialisation */ + /* no step means first step initialisation */ if (check->current_step == NULL) { + check->last_started_step = NULL; check->bo->p = check->bo->data; check->bo->o = 0; check->bi->p = check->bi->data; @@ -1891,9 +1917,6 @@ cur = check->current_step; } - if (conn->flags & CO_FL_HANDSHAKE) - return; - /* It's only the rules which will enable send/recv */ __conn_data_stop_both(conn); diff -Nru haproxy-1.5.4/src/dumpstats.c haproxy-1.5.5/src/dumpstats.c --- haproxy-1.5.4/src/dumpstats.c 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/src/dumpstats.c 2014-10-08 08:07:23.000000000 +0000 @@ -6045,7 +6045,7 @@ break; case 1: chunk_appendf(&trash, - " backend %s (#%d) : invalid response\n" + " backend %s (#%d): invalid response\n" " frontend %s (#%d)", appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid, es->oe->id, es->oe->uuid); diff -Nru haproxy-1.5.4/src/haproxy-systemd-wrapper.c haproxy-1.5.5/src/haproxy-systemd-wrapper.c --- haproxy-1.5.4/src/haproxy-systemd-wrapper.c 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/src/haproxy-systemd-wrapper.c 2014-10-08 08:07:23.000000000 +0000 @@ -22,24 +22,42 @@ #define SD_DEBUG "<7>" #define SD_NOTICE "<5>" +static volatile sig_atomic_t caught_signal; + static char *pid_file = "/run/haproxy.pid"; static int wrapper_argc; static char **wrapper_argv; +/* returns the path to the haproxy binary into , whose size indicated + * in must be at least 1 byte long. + */ static void locate_haproxy(char *buffer, size_t buffer_size) { char *end = NULL; + int len; - if (readlink("/proc/self/exe", buffer, buffer_size) > 0) - end = strrchr(buffer, '/'); + len = readlink("/proc/self/exe", buffer, buffer_size - 1); + if (len == -1) + goto fail; + + buffer[len] = 0; + end = strrchr(buffer, '/'); + if (end == NULL) + goto fail; - if (end == NULL) { - strncpy(buffer, "/usr/sbin/haproxy", buffer_size); + if (strcmp(end + strlen(end) - 16, "-systemd-wrapper") == 0) { + end[strlen(end) - 16] = '\0'; return; } + end[1] = '\0'; strncpy(end + 1, "haproxy", buffer + buffer_size - (end + 1)); buffer[buffer_size - 1] = '\0'; + return; + fail: + strncpy(buffer, "/usr/sbin/haproxy", buffer_size); + buffer[buffer_size - 1] = '\0'; + return; } static void spawn_haproxy(char **pid_strv, int nb_pid) @@ -103,7 +121,12 @@ return read; } -static void sigusr2_handler(int signum __attribute__((unused))) +static void signal_handler(int signum) +{ + caught_signal = signum; +} + +static void do_restart(void) { setenv(REEXEC_FLAG, "1", 1); fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: re-executing\n"); @@ -111,7 +134,7 @@ execv(wrapper_argv[0], wrapper_argv); } -static void sigint_handler(int signum __attribute__((unused))) +static void do_shutdown(void) { int i, pid; char **pid_strv = NULL; @@ -147,25 +170,23 @@ --argc; ++argv; init(argc, argv); - signal(SIGINT, &sigint_handler); - signal(SIGUSR2, &sigusr2_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); if (getenv(REEXEC_FLAG) != NULL) { /* We are being re-executed: restart HAProxy gracefully */ int i; char **pid_strv = NULL; int nb_pid = read_pids(&pid_strv); - sigset_t sigs; unsetenv(REEXEC_FLAG); spawn_haproxy(pid_strv, nb_pid); - /* Unblock SIGUSR2 which was blocked by the signal handler - * before re-exec */ - sigprocmask(SIG_BLOCK, NULL, &sigs); - sigdelset(&sigs, SIGUSR2); - sigprocmask(SIG_SETMASK, &sigs, NULL); - for (i = 0; i < nb_pid; ++i) free(pid_strv[i]); free(pid_strv); @@ -176,8 +197,16 @@ } status = -1; - while (-1 != wait(&status) || errno == EINTR) - ; + while (-1 != wait(&status) || errno == EINTR) { + if (caught_signal == SIGUSR2 || caught_signal == SIGHUP) { + caught_signal = 0; + do_restart(); + } + else if (caught_signal == SIGINT || caught_signal == SIGTERM) { + caught_signal = 0; + do_shutdown(); + } + } fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: exit, haproxy RC=%d\n", status); diff -Nru haproxy-1.5.4/src/proto_http.c haproxy-1.5.5/src/proto_http.c --- haproxy-1.5.4/src/proto_http.c 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/src/proto_http.c 2014-10-08 08:07:23.000000000 +0000 @@ -2393,6 +2393,59 @@ return 0; } +void http_adjust_conn_mode(struct session *s, struct http_txn *txn, struct http_msg *msg) +{ + int tmp = TX_CON_WANT_KAL; + + if (!((s->fe->options2|s->be->options2) & PR_O2_FAKE_KA)) { + if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_TUN || + (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_TUN) + tmp = TX_CON_WANT_TUN; + + if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL || + (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL) + tmp = TX_CON_WANT_TUN; + } + + if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_SCL || + (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_SCL) { + /* option httpclose + server_close => forceclose */ + if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL || + (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL) + tmp = TX_CON_WANT_CLO; + else + tmp = TX_CON_WANT_SCL; + } + + if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_FCL || + (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_FCL) + tmp = TX_CON_WANT_CLO; + + if ((txn->flags & TX_CON_WANT_MSK) < tmp) + txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | tmp; + + if (!(txn->flags & TX_HDR_CONN_PRS) && + (txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) { + /* parse the Connection header and possibly clean it */ + int to_del = 0; + if ((msg->flags & HTTP_MSGF_VER_11) || + ((txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL && + !((s->fe->options2|s->be->options2) & PR_O2_FAKE_KA))) + to_del |= 2; /* remove "keep-alive" */ + if (!(msg->flags & HTTP_MSGF_VER_11)) + to_del |= 1; /* remove "close" */ + http_parse_connection_header(txn, msg, to_del); + } + + /* check if client or config asks for explicit close in KAL/SCL */ + if (((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL || + (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) && + ((txn->flags & TX_HDR_CONN_CLO) || /* "connection: close" */ + (!(msg->flags & HTTP_MSGF_VER_11) && !(txn->flags & TX_HDR_CONN_KAL)) || /* no "connection: k-a" in 1.0 */ + !(msg->flags & HTTP_MSGF_XFER_LEN) || /* no length known => close */ + s->fe->state == PR_STSTOPPED)) /* frontend is stopping */ + txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO; +} /* This stream analyser waits for a complete HTTP request. It returns 1 if the * processing can continue on next analysers, or zero if it either needs more @@ -2929,58 +2982,8 @@ * time. */ if (!(txn->flags & TX_HDR_CONN_PRS) || - ((s->fe->options & PR_O_HTTP_MODE) != (s->be->options & PR_O_HTTP_MODE))) { - int tmp = TX_CON_WANT_KAL; - - if (!((s->fe->options2|s->be->options2) & PR_O2_FAKE_KA)) { - if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_TUN || - (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_TUN) - tmp = TX_CON_WANT_TUN; - - if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL || - (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL) - tmp = TX_CON_WANT_TUN; - } - - if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_SCL || - (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_SCL) { - /* option httpclose + server_close => forceclose */ - if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL || - (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL) - tmp = TX_CON_WANT_CLO; - else - tmp = TX_CON_WANT_SCL; - } - - if ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_FCL || - (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_FCL) - tmp = TX_CON_WANT_CLO; - - if ((txn->flags & TX_CON_WANT_MSK) < tmp) - txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | tmp; - - if (!(txn->flags & TX_HDR_CONN_PRS) && - (txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) { - /* parse the Connection header and possibly clean it */ - int to_del = 0; - if ((msg->flags & HTTP_MSGF_VER_11) || - ((txn->flags & TX_CON_WANT_MSK) >= TX_CON_WANT_SCL && - !((s->fe->options2|s->be->options2) & PR_O2_FAKE_KA))) - to_del |= 2; /* remove "keep-alive" */ - if (!(msg->flags & HTTP_MSGF_VER_11)) - to_del |= 1; /* remove "close" */ - http_parse_connection_header(txn, msg, to_del); - } - - /* check if client or config asks for explicit close in KAL/SCL */ - if (((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL || - (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) && - ((txn->flags & TX_HDR_CONN_CLO) || /* "connection: close" */ - (!(msg->flags & HTTP_MSGF_VER_11) && !(txn->flags & TX_HDR_CONN_KAL)) || /* no "connection: k-a" in 1.0 */ - !(msg->flags & HTTP_MSGF_XFER_LEN) || /* no length known => close */ - s->fe->state == PR_STSTOPPED)) /* frontend is stopping */ - txn->flags = (txn->flags & ~TX_CON_WANT_MSK) | TX_CON_WANT_CLO; - } + ((s->fe->options & PR_O_HTTP_MODE) != (s->be->options & PR_O_HTTP_MODE))) + http_adjust_conn_mode(s, txn, msg); /* end of job, return OK */ req->analysers &= ~an_bit; @@ -6249,7 +6252,7 @@ /* add response headers from the rule sets in the same order */ list_for_each_entry(wl, &rule_set->rsp_add, list) { - if (txn->status < 200) + if (txn->status < 200 && txn->status != 101) break; if (wl->cond) { int ret = acl_exec_cond(wl->cond, px, s, txn, SMP_OPT_DIR_RES|SMP_OPT_FINAL); @@ -6270,7 +6273,7 @@ } /* OK that's all we can do for 1xx responses */ - if (unlikely(txn->status < 200)) + if (unlikely(txn->status < 200 && txn->status != 101)) goto skip_header_mangling; /* @@ -6283,7 +6286,7 @@ /* * Check for cache-control or pragma headers if required. */ - if ((s->be->options & PR_O_CHK_CACHE) || (s->be->ck_opts & PR_CK_NOC)) + if (((s->be->options & PR_O_CHK_CACHE) || (s->be->ck_opts & PR_CK_NOC)) && txn->status != 101) check_response_for_cacheability(s, rep); /* @@ -6399,9 +6402,11 @@ * Adjust "Connection: close" or "Connection: keep-alive" if needed. * If an "Upgrade" token is found, the header is left untouched in order * not to have to deal with some client bugs : some of them fail an upgrade - * if anything but "Upgrade" is present in the Connection header. + * if anything but "Upgrade" is present in the Connection header. We don't + * want to touch any 101 response either since it's switching to another + * protocol. */ - if (!(txn->flags & TX_HDR_CONN_UPG) && + if ((txn->status != 101) && !(txn->flags & TX_HDR_CONN_UPG) && (((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) || ((s->fe->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL || (s->be->options & PR_O_HTTP_MODE) == PR_O_HTTP_PCL))) { diff -Nru haproxy-1.5.4/src/proto_tcp.c haproxy-1.5.5/src/proto_tcp.c --- haproxy-1.5.4/src/proto_tcp.c 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/src/proto_tcp.c 2014-10-08 08:07:23.000000000 +0000 @@ -1711,6 +1711,8 @@ warn++; } + /* the following function directly emits the warning */ + warnif_misplaced_tcp_cont(curpx, file, line, args[0]); LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list); } else if (strcmp(args[1], "connection") == 0) { @@ -1754,6 +1756,8 @@ warn++; } + /* the following function directly emits the warning */ + warnif_misplaced_tcp_conn(curpx, file, line, args[0]); LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list); } else { diff -Nru haproxy-1.5.4/src/proxy.c haproxy-1.5.5/src/proxy.c --- haproxy-1.5.4/src/proxy.c 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/src/proxy.c 2014-10-08 08:07:23.000000000 +0000 @@ -955,6 +955,14 @@ http_init_txn(s); } + /* If we chain to an HTTP backend running a different HTTP mode, we + * have to re-adjust the desired keep-alive/close mode to accommodate + * both the frontend's and the backend's modes. + */ + if (s->fe->mode == PR_MODE_HTTP && be->mode == PR_MODE_HTTP && + ((s->fe->options & PR_O_HTTP_MODE) != (be->options & PR_O_HTTP_MODE))) + http_adjust_conn_mode(s, &s->txn, &s->txn.req); + /* If an LB algorithm needs to access some pre-parsed body contents, * we must not start to forward anything until the connection is * confirmed otherwise we'll lose the pointer to these data and diff -Nru haproxy-1.5.4/VERDATE haproxy-1.5.5/VERDATE --- haproxy-1.5.4/VERDATE 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/VERDATE 2014-10-08 08:07:23.000000000 +0000 @@ -1,2 +1,2 @@ $Format:%ci$ -2014/09/02 +2014/10/07 diff -Nru haproxy-1.5.4/VERSION haproxy-1.5.5/VERSION --- haproxy-1.5.4/VERSION 2014-09-02 11:54:16.000000000 +0000 +++ haproxy-1.5.5/VERSION 2014-10-08 08:07:23.000000000 +0000 @@ -1 +1 @@ -1.5.4 +1.5.5