diff -Nru haproxy-1.7.1/CHANGELOG haproxy-1.7.2/CHANGELOG --- haproxy-1.7.1/CHANGELOG 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/CHANGELOG 2017-01-13 09:03:00.000000000 +0000 @@ -1,6 +1,48 @@ ChangeLog : =========== +2017/01/13 : 1.7.2 + - BUG/MEDIUM: lua: In some case, the return of sample-fetches is ignored (2) + - SCRIPTS: git-show-backports: fix a harmless typo + - SCRIPTS: git-show-backports: add -H to use the hash of the commit message + - BUG/MINOR: stream-int: automatically release SI_FL_WAIT_DATA on SHUTW_NOW + - DOC: lua: documentation about time parser functions + - DOC: lua: section declared twice + - BUG/MINOR: lua/cli: bad error message + - DOC: fix small typo in fe_id (backend instead of frontend) + - BUG/MINOR: Fix the sending function in Lua's cosocket + - BUG/MINOR: lua: memory leak executing tasks + - BUG/MINOR: lua: bad return code + - BUG/MEDIUM: ssl: properly reset the reused_sess during a forced handshake + - BUG/MEDIUM: ssl: avoid double free when releasing bind_confs + - BUG/MINOR: stats: fix be/sessions/current out in typed stats + - BUG/MINOR: backend: nbsrv() should return 0 if backend is disabled + - BUG/MEDIUM: ssl: for a handshake when server-side SNI changes + - BUG/MINOR: systemd: potential zombie processes + - DOC: Add timings events schemas + - BUILD: lua: build failed on FreeBSD. + - BUG/MINOR: option prefer-last-server must be ignored in some case + - MINOR: stats: Support "select all" for backend actions + - BUG/MINOR: sample-fetches/stick-tables: bad type for the sample fetches sc*_get_gpt0 + - BUG/MAJOR: channel: Fix the definition order of channel analyzers + - BUG/MINOR: http: report real parser state in error captures + - BUILD: scripts: automatically update the branch in version.h when releasing + - BUG/MAJOR: http: fix risk of getting invalid reports of bad requests + - MINOR: http: custom status reason. + - MINOR: connection: add sample fetch "fc_rcvd_proxy" + - BUG/MINOR: config: emit a warning if http-reuse is enabled with incompatible options + - BUG/MINOR: tools: fix off-by-one in port size check + - BUG/MEDIUM: server: consider AF_UNSPEC as a valid address family + - MEDIUM: server: split the address and the port into two different fields + - MINOR: tools: make str2sa_range() return the port in a separate argument + - MINOR: server: take the destination port from the port field, not the addr + - MEDIUM: server: disable protocol validations when the server doesn't resolve + - BUG/MEDIUM: tools: do not force an unresolved address to AF_INET:0.0.0.0 + - BUG/MINOR: ssl: EVP_PKEY must be freed after X509_get_pubkey usage + - MINOR: proto_http.c 502 error txt typo. + - DOC: add deprecation notice to "block" + - BUG/MINOR: Reset errno variable before calling strtol(3) + 2016/12/13 : 1.7.1 - BUG/MEDIUM: proxy: return "none" and "unknown" for unknown LB algos - BUG/MINOR: stats: make field_str() return an empty string on NULL diff -Nru haproxy-1.7.1/debian/changelog haproxy-1.7.2/debian/changelog --- haproxy-1.7.1/debian/changelog 2016-12-13 10:32:32.000000000 +0000 +++ haproxy-1.7.2/debian/changelog 2017-01-13 12:49:05.000000000 +0000 @@ -1,3 +1,22 @@ +haproxy (1.7.2-1) unstable; urgency=medium + + * New upstream release (see CHANGELOG): + + Fix a regression whereby fragmented requests were randomly flagged as + bad requests depending on previous buffer contents; this was noticable + under low load with authenticated requests. + + Fix dynamic address resolution for IPv6-only hosts. + + Make sure SSL sessions are not reused when the SNI changes. This makes + SNI and SSL health checks play nice together. + + Minor improvements: + - Add the ability to perform actions on multiple servers via the stats + page. + - Add the ability to specify a custom HTTP reason field in generated + responses. + - New sample fetch function, `fc_rcvd_proxy', indicating wheter the + PROXY protocol was used on the frontend for a connection or not. + + -- Apollon Oikonomopoulos Fri, 13 Jan 2017 14:49:05 +0200 + haproxy (1.7.1-1) unstable; urgency=medium * New upstream stable release. diff -Nru haproxy-1.7.1/debian/files haproxy-1.7.2/debian/files --- haproxy-1.7.1/debian/files 2016-12-13 10:32:32.000000000 +0000 +++ haproxy-1.7.2/debian/files 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -haproxy_1.7.1-1_source.buildinfo net optional diff -Nru haproxy-1.7.1/doc/configuration.txt haproxy-1.7.2/doc/configuration.txt --- haproxy-1.7.1/doc/configuration.txt 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/doc/configuration.txt 2017-01-13 09:03:00.000000000 +0000 @@ -4,7 +4,7 @@ ---------------------- version 1.7 willy tarreau - 2016/12/13 + 2017/01/13 This document covers the configuration language as implemented in the version @@ -1805,7 +1805,7 @@ balance X - X X bind - X X - bind-process X X X X -block - X X X +block (deprecated) - X X X capture cookie - X X - capture request header - X X - capture response header - X X - @@ -2468,7 +2468,7 @@ See also : "nbproc" in global section, and "process" in section 5.1. -block { if | unless } +block { if | unless } (deprecated) Block a layer 7 request if/unless a condition is matched May be used in sections : defaults | frontend | listen | backend no | yes | yes | yes @@ -2480,6 +2480,9 @@ conditions are met or not met. There is no fixed limit to the number of "block" statements per instance. + This form is deprecated, do not use it in any new configuration, use the new + "http-request deny" instead. + Example: acl invalid_src src 0.0.0.0/7 224.0.0.0/3 acl invalid_src src_port 0:1023 @@ -4122,7 +4125,7 @@ set-header | del-header | replace-header | replace-value | - set-status | + set-status [reason ] | set-log-level | set-mark | set-tos | add-acl() | del-acl() | @@ -4215,13 +4218,16 @@ Cache-Control: max-age=3600, private - "set-status" replaces the response status code with which must - be an integer between 100 and 999. Note that the reason is automatically - adapted to the new code. + be an integer between 100 and 999. Optionally, a custom reason text can be + provided defined by , or the default reason for the specified code + will be used as a fallback. Example: # return "431 Request Header Fields Too Large" http-response set-status 431 + # return "503 Slow Down", custom reason + http-response set-status 503 reason "Slow Down". - "set-nice" sets the "nice" factor of the current request being processed. It only has effect against the other requests being processed at the same @@ -13128,6 +13134,10 @@ a same server, or to pass the destination port information to a server using an HTTP header. +fc_rcvd_proxy : boolean + Returns true if the client initiated the connection with a PROXY protocol + header. + fc_rtt() : integer Returns the Round Trip Time (RTT) measured by the kernel for the client connection. is facultative, by default the unit is milliseconds. @@ -13182,7 +13192,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. fe_name : string @@ -15491,6 +15501,26 @@ mode, 5 control points are reported under the form "TR/Tw/Tc/Tr/Ta". In addition, three other measures are provided, "Th", "Ti", and "Tq". +Timings events in HTTP mode: + + first request 2nd request + |<-------------------------------->|<-------------- ... + t tr t tr ... + ---|----|----|----|----|----|----|----|----|-- + : Th Ti TR Tw Tc Tr Td : Ti ... + :<---- Tq ---->: : + :<-------------- Tt -------------->: + :<--------- Ta --------->: + +Timings events in TCP mode: + + TCP session + |<----------------->| + t t + ---|----|----|----|----|--- + | Th Tw Tc Td | + |<------ Tt ------->| + - Th: total time to accept tcp connection and execute handshakes for low level protocols. Currently, these protocoles are proxy-protocol and SSL. This may only happen once during the whole connection's lifetime. A large time here diff -Nru haproxy-1.7.1/doc/internals/filters.txt haproxy-1.7.2/doc/internals/filters.txt --- haproxy-1.7.1/doc/internals/filters.txt 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/doc/internals/filters.txt 2017-01-13 09:03:00.000000000 +0000 @@ -760,13 +760,13 @@ To surround activity of a filter during the channel analyzing, two new analyzers has been added: - * 'flt_start_analyze' (AN_FLT_START_FE/AN_FLT_START_BE): For a specific - filter, this analyzer is called before any call to the 'channel_analyze' - callback. From the filter point of view, it calls the + * 'flt_start_analyze' (AN_REQ/RES_FLT_START_FE/AN_REQ_RES_FLT_START_BE): For + a specific filter, this analyzer is called before any call to the + 'channel_analyze' callback. From the filter point of view, it calls the 'flt_ops.channel_start_analyze' callback. - * 'flt_end_analyze' (AN_FLT_END): For a specific filter, this analyzer is - called when all other analyzers have finished their processing. From the + * 'flt_end_analyze' (AN_REQ/RES_FLT_END): For a specific filter, this analyzer + is called when all other analyzers have finished their processing. From the filter point of view, it calls the 'flt_ops.channel_end_analyze' callback. For TCP streams, these analyzers are called only once. For HTTP streams, if the diff -Nru haproxy-1.7.1/doc/lua-api/index.rst haproxy-1.7.2/doc/lua-api/index.rst --- haproxy-1.7.1/doc/lua-api/index.rst 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/doc/lua-api/index.rst 2017-01-13 09:03:00.000000000 +0000 @@ -297,6 +297,93 @@ contains the current at the epoch format, and "usec" contains the current microseconds. +.. js:function:: core.http_date(date) + + **context**: body, init, task, action + + This function take a string repsenting http date, and returns an integer + containing the corresponding date with a epoch format. A valid http date + me respect the format IMF, RFC850 or ASCTIME. + + :param string date: a date http-date formatted + :returns: integer containing epoch date + :see: :js:func:`core.imf_date`. + :see: :js:func:`core.rfc850_date`. + :see: :js:func:`core.asctime_date`. + :see: https://tools.ietf.org/html/rfc7231#section-7.1.1.1 + +.. js:function:: core.imf_date(date) + + **context**: body, init, task, action + + This function take a string repsenting IMF date, and returns an integer + containing the corresponding date with a epoch format. + + :param string date: a date IMF formatted + :returns: integer containing epoch date + :see: https://tools.ietf.org/html/rfc7231#section-7.1.1.1 + + The IMF format is like this: + +.. code-block:: text + + Sun, 06 Nov 1994 08:49:37 GMT +.. + +.. js:function:: core.rfc850_date(date) + + **context**: body, init, task, action + + This function take a string repsenting RFC850 date, and returns an integer + containing the corresponding date with a epoch format. + + :param string date: a date RFC859 formatted + :returns: integer containing epoch date + :see: https://tools.ietf.org/html/rfc7231#section-7.1.1.1 + + The RFC850 format is like this: + +.. code-block:: text + + Sunday, 06-Nov-94 08:49:37 GMT +.. + +.. js:function:: core.asctime_date(date) + + **context**: body, init, task, action + + This function take a string repsenting ASCTIME date, and returns an integer + containing the corresponding date with a epoch format. + + :param string date: a date ASCTIME formatted + :returns: integer containing epoch date + :see: https://tools.ietf.org/html/rfc7231#section-7.1.1.1 + + The ASCTIME format is like this: + +.. code-block:: text + + Sun Nov 6 08:49:37 1994 +.. + +.. js:function:: core.rfc850_date(date) + + **context**: body, init, task, action + + This function take a string repsenting http date, and returns an integer + containing the corresponding date with a epoch format. + + :param string date: a date http-date formatted + +.. js:function:: core.asctime_date(date) + + **context**: body, init, task, action + + This function take a string repsenting http date, and returns an integer + containing the corresponding date with a epoch format. + + :param string date: a date http-date formatted + .. js:function:: core.msleep(milliseconds) **context**: body, init, task, action @@ -1347,13 +1434,15 @@ :param class_http http: The related http object. :param string uri: The new uri. -.. js:function:: HTTP.res_set_status(http, status) +.. js:function:: HTTP.res_set_status(http, status [, reason]) + + Rewrites the response status code with the parameter "code". - Rewrites the response status code with the parameter "code". Note that the - reason is automatically adapted to the new code. + If no custom reason is provided, it will be generated from the status. :param class_http http: The related http object. :param integer status: The new response status code. + :param string reason: The new response reason (optional). .. _txn_class: @@ -1978,17 +2067,14 @@ AppletHTTP.headers["accept"][2] = "*/*, q=0.1" .. -.. js:attribute:: AppletHTTP.headers - - Contains an array containing all the request headers. - -.. js:function:: AppletHTTP.set_status(applet, code) +.. js:function:: AppletHTTP.set_status(applet, code [, reason]) This function sets the HTTP status code for the response. The allowed code are from 100 to 599. :param class_AppletHTTP applet: An :ref:`applethttp_class` :param integer code: the status code returned to the client. + :param string reason: the status reason returned to the client (optional). .. js:function:: AppletHTTP.add_header(applet, name, value) diff -Nru haproxy-1.7.1/examples/haproxy.spec haproxy-1.7.2/examples/haproxy.spec --- haproxy-1.7.1/examples/haproxy.spec 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/examples/haproxy.spec 2017-01-13 09:03:00.000000000 +0000 @@ -1,6 +1,6 @@ Summary: HA-Proxy is a TCP/HTTP reverse proxy for high availability environments Name: haproxy -Version: 1.7.1 +Version: 1.7.2 Release: 1 License: GPL Group: System Environment/Daemons @@ -74,6 +74,9 @@ %attr(0755,root,root) %config %{_sysconfdir}/rc.d/init.d/%{name} %changelog +* Fri Jan 13 2017 Willy Tarreau +- updated to 1.7.2 + * Tue Dec 13 2016 Willy Tarreau - updated to 1.7.1 diff -Nru haproxy-1.7.1/include/common/standard.h haproxy-1.7.2/include/common/standard.h --- haproxy-1.7.1/include/common/standard.h 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/include/common/standard.h 2017-01-13 09:03:00.000000000 +0000 @@ -279,7 +279,10 @@ * address (typically the path to a unix socket). If use_dns is not true, * the funtion cannot accept the DNS resolution. */ -struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high, char **err, const char *pfx, char **fqdn, int use_dns); +struct sockaddr_storage *str2sa_range(const char *str, + int *port, int *low, int *high, + char **err, const char *pfx, + char **fqdn, int resolve); /* converts to a struct in_addr containing a network mask. It can be * passed in dotted form (255.255.255.0) or in CIDR form (24). It returns 1 diff -Nru haproxy-1.7.1/include/common/version.h haproxy-1.7.2/include/common/version.h --- haproxy-1.7.1/include/common/version.h 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/include/common/version.h 2017-01-13 09:03:00.000000000 +0000 @@ -33,7 +33,7 @@ #ifdef CONFIG_PRODUCT_BRANCH #define PRODUCT_BRANCH CONFIG_PRODUCT_BRANCH #else -#define PRODUCT_BRANCH "1.5" +#define PRODUCT_BRANCH "1.7" #endif #ifdef CONFIG_PRODUCT_URL diff -Nru haproxy-1.7.1/include/proto/proto_http.h haproxy-1.7.2/include/proto/proto_http.h --- haproxy-1.7.1/include/proto/proto_http.h 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/include/proto/proto_http.h 2017-01-13 09:03:00.000000000 +0000 @@ -108,7 +108,7 @@ int http_remove_header2(struct http_msg *msg, struct hdr_idx *idx, struct hdr_ctx *ctx); int http_header_add_tail2(struct http_msg *msg, struct hdr_idx *hdr_idx, const char *text, int len); int http_replace_req_line(int action, const char *replace, int len, struct proxy *px, struct stream *s); -void http_set_status(unsigned int status, struct stream *s); +void http_set_status(unsigned int status, const char *reason, struct stream *s); int http_transform_header_str(struct stream* s, struct http_msg *msg, const char* name, unsigned int name_len, const char *str, struct my_regex *re, int action); diff -Nru haproxy-1.7.1/include/types/action.h haproxy-1.7.2/include/types/action.h --- haproxy-1.7.1/include/types/action.h 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/include/types/action.h 2017-01-13 09:03:00.000000000 +0000 @@ -135,6 +135,7 @@ } cap; struct { unsigned int code; /* HTTP status code */ + const char *reason; /* HTTP status reason */ } status; struct { struct sample_expr *expr; diff -Nru haproxy-1.7.1/include/types/applet.h haproxy-1.7.2/include/types/applet.h --- haproxy-1.7.1/include/types/applet.h 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/include/types/applet.h 2017-01-13 09:03:00.000000000 +0000 @@ -137,6 +137,7 @@ int left_bytes; /* The max amount of bytes that we can read. */ int flags; int status; + const char *reason; struct task *task; } hlua_apphttp; struct { diff -Nru haproxy-1.7.1/include/types/channel.h haproxy-1.7.2/include/types/channel.h --- haproxy-1.7.1/include/types/channel.h 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/include/types/channel.h 2017-01-13 09:03:00.000000000 +0000 @@ -133,39 +133,51 @@ * The field is blanked by channel_init() and only by analysers themselves * afterwards. */ -/* unused: 0x00000001 */ +/* AN_REQ_FLT_START_FE: 0x00000001 */ #define AN_REQ_INSPECT_FE 0x00000002 /* inspect request contents in the frontend */ #define AN_REQ_WAIT_HTTP 0x00000004 /* wait for an HTTP request */ #define AN_REQ_HTTP_BODY 0x00000008 /* wait for HTTP request body */ #define AN_REQ_HTTP_PROCESS_FE 0x00000010 /* process the frontend's HTTP part */ #define AN_REQ_SWITCHING_RULES 0x00000020 /* apply the switching rules */ -#define AN_REQ_INSPECT_BE 0x00000040 /* inspect request contents in the backend */ -#define AN_REQ_HTTP_PROCESS_BE 0x00000080 /* process the backend's HTTP part */ -#define AN_REQ_SRV_RULES 0x00000100 /* use-server rules */ -#define AN_REQ_HTTP_INNER 0x00000200 /* inner processing of HTTP request */ -#define AN_REQ_HTTP_TARPIT 0x00000400 /* wait for end of HTTP tarpit */ -#define AN_REQ_STICKING_RULES 0x00000800 /* table persistence matching */ +/* AN_REQ_FLT_START_BE: 0x00000040 */ +#define AN_REQ_INSPECT_BE 0x00000080 /* inspect request contents in the backend */ +#define AN_REQ_HTTP_PROCESS_BE 0x00000100 /* process the backend's HTTP part */ +#define AN_REQ_HTTP_TARPIT 0x00000200 /* wait for end of HTTP tarpit */ +#define AN_REQ_SRV_RULES 0x00000400 /* use-server rules */ +#define AN_REQ_HTTP_INNER 0x00000800 /* inner processing of HTTP request */ #define AN_REQ_PRST_RDP_COOKIE 0x00001000 /* persistence on rdp cookie */ -#define AN_REQ_HTTP_XFER_BODY 0x00002000 /* forward request body */ -#define AN_REQ_ALL 0x00003ffe /* all of the request analysers */ +#define AN_REQ_STICKING_RULES 0x00002000 /* table persistence matching */ +/* AN_REQ_FLT_HTTP_HDRS: 0x00004000 */ +#define AN_REQ_HTTP_XFER_BODY 0x00008000 /* forward request body */ +/* AN_REQ_FLT_XFER_DATA: 0x00010000 */ +/* AN_REQ_FLT_END: 0x00020000 */ +#define AN_REQ_ALL 0x0000bfbe /* all of the request analysers */ /* response analysers */ -#define AN_RES_INSPECT 0x00010000 /* content inspection */ -#define AN_RES_WAIT_HTTP 0x00020000 /* wait for HTTP response */ -#define AN_RES_HTTP_PROCESS_BE 0x00040000 /* process backend's HTTP part */ -#define AN_RES_HTTP_PROCESS_FE 0x00040000 /* process frontend's HTTP part (same for now) */ -#define AN_RES_STORE_RULES 0x00080000 /* table persistence matching */ -#define AN_RES_HTTP_XFER_BODY 0x00100000 /* forward response body */ -#define AN_RES_ALL 0x001f0000 /* all of the response analysers */ +/* AN_RES_FLT_START_FE: 0x00040000 */ +/* AN_RES_FLT_START_BE: 0x00080000 */ +#define AN_RES_INSPECT 0x00100000 /* content inspection */ +#define AN_RES_WAIT_HTTP 0x00200000 /* wait for HTTP response */ +#define AN_RES_STORE_RULES 0x00400000 /* table persistence matching */ +#define AN_RES_HTTP_PROCESS_BE 0x00800000 /* process backend's HTTP part */ +#define AN_RES_HTTP_PROCESS_FE 0x00800000 /* process frontend's HTTP part (same for now) */ +/* AN_RES_FLT_HTTP_HDRS: 0x01000000 */ +#define AN_RES_HTTP_XFER_BODY 0x02000000 /* forward response body */ +/* AN_RES_FLT_XFER_DATA: 0x04000000 */ +/* AN_RES_FLT_END: 0x08000000 */ +#define AN_RES_ALL 0x02f00000 /* all of the response analysers */ -#define AN_FLT_START_FE 0x01000000 -#define AN_FLT_START_BE 0x02000000 -#define AN_FLT_END 0x04000000 -#define AN_FLT_XFER_DATA 0x08000000 -#define AN_FLT_HTTP_HDRS 0x10000000 +#define AN_REQ_FLT_START_FE 0x00000001 +#define AN_REQ_FLT_START_BE 0x00000040 +#define AN_REQ_FLT_HTTP_HDRS 0x00004000 +#define AN_REQ_FLT_XFER_DATA 0x00010000 +#define AN_REQ_FLT_END 0x00020000 -#define AN_FLT_ALL_FE 0x0d000000 -#define AN_FLT_ALL_BE 0x0e000000 +#define AN_RES_FLT_START_FE 0x00040000 +#define AN_RES_FLT_START_BE 0x00080000 +#define AN_RES_FLT_HTTP_HDRS 0x01000000 +#define AN_RES_FLT_XFER_DATA 0x04000000 +#define AN_RES_FLT_END 0x08000000 /* Magic value to forward infinite size (TCP, ...), used with ->to_forward */ #define CHN_INFINITE_FORWARD MAX_RANGE(unsigned int) diff -Nru haproxy-1.7.1/include/types/connection.h haproxy-1.7.2/include/types/connection.h --- haproxy-1.7.1/include/types/connection.h 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/include/types/connection.h 2017-01-13 09:03:00.000000000 +0000 @@ -126,7 +126,10 @@ /* This connection may not be shared between clients */ CO_FL_PRIVATE = 0x10000000, - /* unused : 0x20000000, 0x40000000 */ + /* This flag is used to know that a PROXY protocol header was sent by the client */ + CO_FL_RCVD_PROXY = 0x20000000, + + /* unused : 0x40000000 */ /* This last flag indicates that the transport layer is used (for instance * by logs) and must not be cleared yet. The last call to conn_xprt_close() diff -Nru haproxy-1.7.1/include/types/hlua.h haproxy-1.7.2/include/types/hlua.h --- haproxy-1.7.1/include/types/hlua.h 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/include/types/hlua.h 2017-01-13 09:03:00.000000000 +0000 @@ -49,6 +49,22 @@ HLUA_E_ERR, /* LUA stack execution failed without error message. */ }; +/* This struct is use for storing HAProxy parsers state + * before executing some Lua code. The goal is we can + * check and compare the parser state a the end of Lua + * execution. If the state is changed by Lua towards + * an unexpected state, we can abort the transaction. + */ +struct hlua_consistency { + enum pr_mode mode; + union { + struct { + int dir; + enum ht_state state; + } http; + } data; +}; + struct hlua { lua_State *T; /* The LUA stack. */ int Tref; /* The reference of the stack in coroutine case. @@ -65,6 +81,7 @@ We must wake this task to continue the task execution */ struct list com; /* The list head of the signals attached to this task. */ struct ebpt_node node; + struct hlua_consistency cons; /* Store data consistency check. */ }; struct hlua_com { diff -Nru haproxy-1.7.1/include/types/proto_http.h haproxy-1.7.2/include/types/proto_http.h --- haproxy-1.7.1/include/types/proto_http.h 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/include/types/proto_http.h 2017-01-13 09:03:00.000000000 +0000 @@ -319,8 +319,9 @@ */ struct http_msg { enum ht_state msg_state; /* where we are in the current message parsing */ + enum ht_state err_state; /* the state where the parsing error was detected, only is MSG_ERROR */ unsigned char flags; /* flags describing the message (HTTP version, ...) */ - /* 6 bytes unused here */ + /* 5 bytes unused here */ struct channel *chn; /* pointer to the channel transporting the message */ unsigned int next; /* pointer to next byte to parse, relative to buf->p */ int sov; /* current header: start of value ; data: start of body */ diff -Nru haproxy-1.7.1/include/types/server.h haproxy-1.7.2/include/types/server.h --- haproxy-1.7.1/include/types/server.h 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/include/types/server.h 2017-01-13 09:03:00.000000000 +0000 @@ -230,7 +230,8 @@ const struct netns_entry *netns; /* contains network namespace name or NULL. Network namespace comes from configuration */ /* warning, these structs are huge, keep them at the bottom */ - struct sockaddr_storage addr; /* the address to connect to */ + struct sockaddr_storage addr; /* the address to connect to, doesn't include the port */ + unsigned int svc_port; /* the port to connect to (for relevant families) */ struct xprt_ops *xprt; /* transport-layer operations */ unsigned down_time; /* total time the server was down */ time_t last_change; /* last time, when the state was changed */ diff -Nru haproxy-1.7.1/README haproxy-1.7.2/README --- haproxy-1.7.1/README 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/README 2017-01-13 09:03:00.000000000 +0000 @@ -3,7 +3,7 @@ ---------------------- version 1.7 willy tarreau - 2016/12/13 + 2017/01/13 1) How to build it diff -Nru haproxy-1.7.1/scripts/create-release haproxy-1.7.2/scripts/create-release --- haproxy-1.7.1/scripts/create-release 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/scripts/create-release 2017-01-13 09:03:00.000000000 +0000 @@ -192,6 +192,10 @@ sed -e "s:Copyright 2000-[0-9]*\s*Willy Tarreau.*>:Copyright 2000-$YEAR Willy Tarreau :" \ -i src/haproxy.c +echo "Updating version.h ..." +sed -e "s:^\(#define\s*PRODUCT_BRANCH\s*\)\"[^\"]*\":\1\"$BRANCH\":" \ + -i include/common/version.h + if [ -n "$INTERACTIVE" ]; then vi CHANGELOG VERSION VERDATE examples/haproxy*.spec \ src/haproxy.c README doc/configuration.txt \ diff -Nru haproxy-1.7.1/scripts/git-show-backports haproxy-1.7.2/scripts/git-show-backports --- haproxy-1.7.1/scripts/git-show-backports 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/scripts/git-show-backports 2017-01-13 09:03:00.000000000 +0000 @@ -28,7 +28,7 @@ # show-backports -q -m -r hapee-r2 hapee-r1 -USAGE="Usage: ${0##*/} [-q] [-m] [-u] [-r reference] [-l logexpr] [-s subject] [-b base] {branch|range} [...]" +USAGE="Usage: ${0##*/} [-q] [-H] [-m] [-u] [-r reference] [-l logexpr] [-s subject] [-b base] {branch|range} [...]" BASES=( ) BRANCHES=( ) REF=master @@ -38,6 +38,7 @@ SUBJECT= MISSING= UPSTREAM= +BODYHASH= die() { [ "$#" -eq 0 ] || echo "$*" >&2 @@ -75,10 +76,12 @@ upstream="none" missing=0 + refbhash="" line="" for branch in "${BRANCHES[@]}"; do set -- $(grep -m 1 $ref "$WORK/${branch//\//_}") newhash=$1 ; shift + bhash="" # count the number of cherry-picks after this one. Since we shift, # the result is in "$#" while [ -n "$1" -a "$1" != "$ref" ]; do @@ -108,6 +111,19 @@ break fi done + if [ -z "$newhash" -a -n "$BODYHASH" ]; then + if [ -z "$refbhash" ]; then + refbhash=$(git log -1 --pretty="%an|%ae|%at|%B" "$ref" | sed -n '/^\(Signed-off-by\|(cherry picked\)/q;p' | md5sum) + fi + + + set -- $(grep -m 1 "H$refbhash\$" "$WORK/${branch//\//_}") + newhash=$1 ; shift + if [ -n "$newhash" ]; then + line="${line} $(short $newhash)+?" + break + fi + fi if [ -z "$newhash" ]; then line="${line} -" missing=1 @@ -136,6 +152,7 @@ -q) QUIET=1 ; shift ;; -m) MISSING=1 ; shift ;; -u) UPSTREAM=1 ; shift ;; + -H) BODYHASH=1 ; shift ;; -h|--help) quit "$USAGE" ;; *) die "$USAGE" ;; esac @@ -183,7 +200,7 @@ WORK=.git/.show-backports rm -f "$WORK/${REF//\//_}" -git log --reverse ${LOGEXPR:+--grep $LOGEXPR} --pretty="%H %s" "$BASE".."$REF" | grep "${SUBJECT}" > "$WORK/${branch//\//_}" > "$WORK/${REF//\//_}" +git log --reverse ${LOGEXPR:+--grep $LOGEXPR} --pretty="%H %s" "$BASE".."$REF" | grep "${SUBJECT}" > "$WORK/${REF//\//_}" # for each branch, enumerate all commits and their ancestry @@ -194,8 +211,13 @@ base="${base:-$BASE}" rm -f "$WORK/${branch//\//_}" git log --reverse --pretty="%H %s" "$base".."$branch" | grep "${SUBJECT}" | while read h subject; do - echo "$h" $(git log -1 --pretty --format=%B "$h" | \ + echo -n "$h" $(git log -1 --pretty --format=%B "$h" | \ sed -n 's/^commit \([^)]*\) upstream\.$/\1/p;s/^(cherry picked from commit \([^)]*\))/\1/p') + if [ -n "$BODYHASH" ]; then + echo " H$(git log -1 --pretty="%an|%ae|%at|%B" "$h" | sed -n '/^\(Signed-off-by\|(cherry picked\)/q;p' | md5sum)" + else + echo + fi done > "$WORK/${branch//\//_}" (( branch_num++ )) done diff -Nru haproxy-1.7.1/src/backend.c haproxy-1.7.2/src/backend.c --- haproxy-1.7.1/src/backend.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/backend.c 2017-01-13 09:03:00.000000000 +0000 @@ -564,12 +564,14 @@ objt_server(conn->target) && __objt_server(conn->target)->proxy == s->be && ((s->txn && s->txn->flags & TX_PREFER_LAST) || ((s->be->options & PR_O_PREF_LAST) && + (s->be->lbprm.algo & BE_LB_KIND) != BE_LB_KIND_HI && (!s->be->max_ka_queue || server_has_room(__objt_server(conn->target)) || (__objt_server(conn->target)->nbpend + 1) < s->be->max_ka_queue))) && srv_is_usable(__objt_server(conn->target))) { /* This stream was relying on a server in a previous request - * and the proxy has "option prefer-last-server" set, so + * and the proxy has "option prefer-last-server" set + * and balance algorithm dont tell us to do otherwise, so * let's try to reuse the same server. */ srv = __objt_server(conn->target); @@ -768,6 +770,7 @@ return SRV_STATUS_INTERNAL; srv_conn->addr.to = objt_server(s->target)->addr; + set_host_port(&srv_conn->addr.to, objt_server(s->target)->svc_port); if (!is_addr(&srv_conn->addr.to) && cli_conn) { /* if the server has no address, we use the same address @@ -1344,7 +1347,8 @@ int ret; struct sample smp; struct server *srv = px->srv; - struct sockaddr_in addr; + uint16_t port; + uint32_t addr; char *p; DPRINTF(stderr,"[%u] %s: stream=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%d analysers=%02x\n", @@ -1365,22 +1369,25 @@ if (ret == 0 || (smp.flags & SMP_F_MAY_CHANGE) || smp.data.u.str.len == 0) goto no_cookie; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - - /* Considering an rdp cookie detected using acl, str ended with and should return */ - addr.sin_addr.s_addr = strtoul(smp.data.u.str.str, &p, 10); + /* Considering an rdp cookie detected using acl, str ended with and should return. + * The cookie format is "." where "ip" is the integer corresponding to the + * server's IP address in network order, and "port" is the integer corresponding to the + * server's port in network order. Comments please Emeric. + */ + addr = strtoul(smp.data.u.str.str, &p, 10); if (*p != '.') goto no_cookie; p++; - addr.sin_port = (unsigned short)strtoul(p, &p, 10); + + port = ntohs(strtoul(p, &p, 10)); if (*p != '.') goto no_cookie; s->target = NULL; while (srv) { if (srv->addr.ss_family == AF_INET && - memcmp(&addr, &(srv->addr), sizeof(addr)) == 0) { + port == srv->svc_port && + addr == ((struct sockaddr_in *)&srv->addr)->sin_addr.s_addr) { if ((srv->state != SRV_ST_STOPPED) || (px->options & PR_O_PERSIST)) { /* we found the server and it is usable */ s->flags |= SF_DIRECT | SF_ASSIGNED; @@ -1608,7 +1615,9 @@ smp->data.type = SMP_T_SINT; px = args->data.prx; - if (px->srv_act) + if (px->state == PR_STSTOPPED) + smp->data.u.sint = 0; + else if (px->srv_act) smp->data.u.sint = px->srv_act; else if (px->lbprm.fbck) smp->data.u.sint = 1; diff -Nru haproxy-1.7.1/src/cfgparse.c haproxy-1.7.2/src/cfgparse.c --- haproxy-1.7.1/src/cfgparse.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/cfgparse.c 2017-01-13 09:03:00.000000000 +0000 @@ -243,7 +243,7 @@ *next++ = 0; } - ss2 = str2sa_range(str, &port, &end, err, + ss2 = str2sa_range(str, NULL, &port, &end, err, curproxy == global.stats_fe ? NULL : global.unix_bind.prefix, NULL, 1); if (!ss2) @@ -1651,7 +1651,7 @@ } } - sk = str2sa_range(args[1], &port1, &port2, &errmsg, NULL, NULL, 1); + sk = str2sa_range(args[1], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s': %s\n", file, linenum, args[0], errmsg); err_code |= ERR_ALERT | ERR_FATAL; @@ -2180,7 +2180,7 @@ newpeer->last_change = now.tv_sec; newpeer->id = strdup(args[1]); - sk = str2sa_range(args[2], &port1, &port2, &errmsg, NULL, NULL, 1); + sk = str2sa_range(args[2], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); err_code |= ERR_ALERT | ERR_FATAL; @@ -2394,7 +2394,7 @@ newnameserver->conf.line = linenum; newnameserver->id = strdup(args[1]); - sk = str2sa_range(args[2], &port1, &port2, &errmsg, NULL, NULL, 1); + sk = str2sa_range(args[2], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); err_code |= ERR_ALERT | ERR_FATAL; @@ -2610,7 +2610,7 @@ newmailer->id = strdup(args[1]); - sk = str2sa_range(args[2], &port1, &port2, &errmsg, NULL, NULL, 1); + sk = str2sa_range(args[2], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); err_code |= ERR_ALERT | ERR_FATAL; @@ -5874,7 +5874,7 @@ else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) err_code |= ERR_WARN; - sk = str2sa_range(args[1], &port1, &port2, &errmsg, NULL, NULL, 1); + sk = str2sa_range(args[1], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s' : %s\n", file, linenum, args[0], errmsg); err_code |= ERR_ALERT | ERR_FATAL; @@ -6204,7 +6204,7 @@ } } - sk = str2sa_range(args[1], &port1, &port2, &errmsg, NULL, NULL, 1); + sk = str2sa_range(args[1], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s': %s\n", file, linenum, args[0], errmsg); err_code |= ERR_ALERT | ERR_FATAL; @@ -6256,7 +6256,7 @@ curproxy->conn_src.iface_name = NULL; curproxy->conn_src.iface_len = 0; - sk = str2sa_range(args[1], &port1, &port2, &errmsg, NULL, NULL, 1); + sk = str2sa_range(args[1], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); @@ -6341,7 +6341,7 @@ } else { struct sockaddr_storage *sk; - sk = str2sa_range(args[cur_arg + 1], &port1, &port2, &errmsg, NULL, NULL, 1); + sk = str2sa_range(args[cur_arg + 1], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[cur_arg], args[cur_arg+1], errmsg); @@ -8752,6 +8752,25 @@ err_code |= ERR_WARN; } #endif + + if ((curproxy->options & PR_O_REUSE_MASK) != PR_O_REUSE_NEVR) { + if ((curproxy->conn_src.opts & CO_SRC_TPROXY_MASK) == CO_SRC_TPROXY_CLI || + (curproxy->conn_src.opts & CO_SRC_TPROXY_MASK) == CO_SRC_TPROXY_CIP || + (newsrv->conn_src.opts & CO_SRC_TPROXY_MASK) == CO_SRC_TPROXY_CLI || + (newsrv->conn_src.opts & CO_SRC_TPROXY_MASK) == CO_SRC_TPROXY_CIP) { + Warning("config : %s '%s' : connections to server '%s' use the client's IP address as the source while http-reuse is enabled and allows the same connection to be shared between multiple clients. It is strongly advised to disable 'usesrc' and to use the 'forwardfor' option instead.\n", + proxy_type_str(curproxy), curproxy->id, newsrv->id); + err_code |= ERR_WARN; + } + + + if (newsrv->pp_opts & (SRV_PP_V1|SRV_PP_V2)) { + Warning("config : %s '%s' : connections to server '%s' will have a PROXY protocol header announcing the first client's IP address while http-reuse is enabled and allows the same connection to be shared between multiple clients. It is strongly advised to disable 'send-proxy' and to use the 'forwardfor' option instead.\n", + proxy_type_str(curproxy), curproxy->id, newsrv->id); + err_code |= ERR_WARN; + } + } + newsrv = newsrv->next; } @@ -8799,11 +8818,11 @@ /* Add filters analyzers if needed */ if (!LIST_ISEMPTY(&curproxy->filter_configs)) { - curproxy->fe_req_ana |= AN_FLT_ALL_FE; - curproxy->fe_rsp_ana |= AN_FLT_ALL_FE; + curproxy->fe_req_ana |= AN_REQ_FLT_START_FE | AN_REQ_FLT_XFER_DATA | AN_REQ_FLT_END; + curproxy->fe_rsp_ana |= AN_RES_FLT_START_FE | AN_RES_FLT_XFER_DATA | AN_RES_FLT_END; if (curproxy->mode == PR_MODE_HTTP) { - curproxy->fe_req_ana |= AN_FLT_HTTP_HDRS; - curproxy->fe_rsp_ana |= AN_FLT_HTTP_HDRS; + curproxy->fe_req_ana |= AN_REQ_FLT_HTTP_HDRS; + curproxy->fe_rsp_ana |= AN_RES_FLT_HTTP_HDRS; } } } @@ -8829,11 +8848,11 @@ /* Add filters analyzers if needed */ if (!LIST_ISEMPTY(&curproxy->filter_configs)) { - curproxy->be_req_ana |= AN_FLT_ALL_BE; - curproxy->be_rsp_ana |= AN_FLT_ALL_BE; + curproxy->be_req_ana |= AN_REQ_FLT_START_BE | AN_REQ_FLT_XFER_DATA | AN_REQ_FLT_END; + curproxy->be_rsp_ana |= AN_RES_FLT_START_BE | AN_RES_FLT_XFER_DATA | AN_RES_FLT_END; if (curproxy->mode == PR_MODE_HTTP) { - curproxy->be_req_ana |= AN_FLT_HTTP_HDRS; - curproxy->be_rsp_ana |= AN_FLT_HTTP_HDRS; + curproxy->be_req_ana |= AN_REQ_FLT_HTTP_HDRS; + curproxy->be_rsp_ana |= AN_RES_FLT_HTTP_HDRS; } } } @@ -9050,6 +9069,13 @@ LIST_DEL(&bind_conf->keys_ref->list); free(bind_conf->keys_ref); } + bind_conf->keys_ref = NULL; + bind_conf->crl_file = NULL; + bind_conf->ecdhe = NULL; + bind_conf->ciphers = NULL; + bind_conf->ca_sign_pass = NULL; + bind_conf->ca_sign_file = NULL; + bind_conf->ca_file = NULL; #endif /* USE_OPENSSL */ } diff -Nru haproxy-1.7.1/src/checks.c haproxy-1.7.2/src/checks.c --- haproxy-1.7.1/src/checks.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/checks.c 2017-01-13 09:03:00.000000000 +0000 @@ -533,7 +533,10 @@ (s->state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise)); addr_to_str(&s->addr, addr, sizeof(addr)); - port_to_str(&s->addr, port, sizeof(port)); + if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6) + snprintf(port, sizeof(port), "%u", s->svc_port); + else + *port = 0; hlen += snprintf(buffer + hlen, size - hlen, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d", addr, port, s->proxy->id, s->id, @@ -1784,7 +1787,11 @@ addr_to_str(&s->addr, buf, sizeof(buf)); check->argv[3] = strdup(buf); - port_to_str(&s->addr, buf, sizeof(buf)); + + if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6) + snprintf(buf, sizeof(buf), "%u", s->svc_port); + else + *buf = 0; check->argv[4] = strdup(buf); for (i = 0; i < 5; i++) { @@ -3448,7 +3455,8 @@ */ if (srv->flags & SRV_F_MAPPORTS) return 0; - i = get_host_port(&srv->addr); /* by default */ + + i = srv->svc_port; /* by default */ if (i > 0) return i; diff -Nru haproxy-1.7.1/src/connection.c haproxy-1.7.2/src/connection.c --- haproxy-1.7.1/src/connection.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/connection.c 2017-01-13 09:03:00.000000000 +0000 @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef USE_OPENSSL #include @@ -602,6 +603,7 @@ } while (0); conn->flags &= ~flag; + conn->flags |= CO_FL_RCVD_PROXY; return 1; missing: @@ -1039,3 +1041,41 @@ return ret; } + +/* fetch if the received connection used a PROXY protocol header */ +int smp_fetch_fc_rcvd_proxy(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + struct connection *conn; + + conn = objt_conn(smp->sess->origin); + if (!conn) + return 0; + + if (!(conn->flags & CO_FL_CONNECTED)) { + smp->flags |= SMP_F_MAY_CHANGE; + return 0; + } + + smp->flags = 0; + smp->data.type = SMP_T_BOOL; + smp->data.u.sint = (conn->flags & CO_FL_RCVD_PROXY) ? 1 : 0; + + return 1; +} + +/* Note: must not be declared as its list will be overwritten. + * Note: fetches that may return multiple types must be declared as the lowest + * common denominator, the type that can be casted into all other ones. For + * instance v4/v6 must be declared v4. + */ +static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { + { "fc_rcvd_proxy", smp_fetch_fc_rcvd_proxy, 0, NULL, SMP_T_BOOL, SMP_USE_L4CLI }, + { /* END */ }, +}}; + + +__attribute__((constructor)) +static void __connection_init(void) +{ + sample_register_fetches(&sample_fetch_keywords); +} diff -Nru haproxy-1.7.1/src/filters.c haproxy-1.7.2/src/filters.c --- haproxy-1.7.1/src/filters.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/filters.c 2017-01-13 09:03:00.000000000 +0000 @@ -676,8 +676,16 @@ * so we do not need to check the filter list's emptiness. */ RESUME_FILTER_LOOP(s, chn) { - if (an_bit == AN_FLT_START_BE && !(filter->flags & FLT_FL_IS_BACKEND_FILTER)) - continue; + if (!(chn->flags & CF_ISRESP)) { + if (an_bit == AN_REQ_FLT_START_BE && + !(filter->flags & FLT_FL_IS_BACKEND_FILTER)) + continue; + } + else { + if (an_bit == AN_RES_FLT_START_BE && + !(filter->flags & FLT_FL_IS_BACKEND_FILTER)) + continue; + } FLT_NXT(filter, chn) = 0; FLT_FWD(filter, chn) = 0; @@ -746,9 +754,9 @@ } /* - * This function is the AN_FLT_HTTP_HDRS analyzer, used to filter HTTP headers - * or a request or a response. Returns 0 if an error occurs or if it needs to - * wait, any other value otherwise. + * This function is the AN_REQ/RES_FLT_HTTP_HDRS analyzer, used to filter HTTP + * headers or a request or a response. Returns 0 if an error occurs or if it + * needs to wait, any other value otherwise. */ int flt_analyze_http_headers(struct stream *s, struct channel *chn, unsigned int an_bit) @@ -810,7 +818,7 @@ /* Check if 'channel_end_analyze' callback has been called for the * request and the response. */ - if (!(s->req.analysers & AN_FLT_END) && !(s->res.analysers & AN_FLT_END)) { + if (!(s->req.analysers & AN_REQ_FLT_END) && !(s->res.analysers & AN_RES_FLT_END)) { /* When we are waiting for a new request, so we must reset * stream analyzers. The input must not be closed the request * channel, else it is useless to wait. */ @@ -949,11 +957,11 @@ /* * Called when TCP data must be filtered on a channel. This function is the - * AN_FLT_XFER_DATA analyzer. When called, it is responsible to forward data - * when the proxy is not in http mode. Behind the scene, it calls consecutively - * 'tcp_data' and 'tcp_forward_data' callbacks for all "data" filters attached - * to a stream. Returns 0 if an error occurs or if it needs to wait, any other - * value otherwise. + * AN_REQ/RES_FLT_XFER_DATA analyzer. When called, it is responsible to forward + * data when the proxy is not in http mode. Behind the scene, it calls + * consecutively 'tcp_data' and 'tcp_forward_data' callbacks for all "data" + * filters attached to a stream. Returns 0 if an error occurs or if it needs to + * wait, any other value otherwise. */ int flt_xfer_data(struct stream *s, struct channel *chn, unsigned int an_bit) @@ -1027,12 +1035,12 @@ channel_abort(&s->res); if (!(chn->flags & CF_ISRESP)) { - s->req.analysers &= AN_FLT_END; + s->req.analysers &= AN_REQ_FLT_END; finst = SF_FINST_R; /* FIXME: incr counters */ } else { - s->res.analysers &= AN_FLT_END; + s->res.analysers &= AN_RES_FLT_END; finst = SF_FINST_H; /* FIXME: incr counters */ } diff -Nru haproxy-1.7.1/src/haproxy.c haproxy-1.7.2/src/haproxy.c --- haproxy-1.7.1/src/haproxy.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/haproxy.c 2017-01-13 09:03:00.000000000 +0000 @@ -1,6 +1,6 @@ /* * HA-Proxy : High Availability-enabled HTTP/TCP proxy - * Copyright 2000-2016 Willy Tarreau . + * Copyright 2000-2017 Willy Tarreau . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -274,7 +274,7 @@ void display_version() { printf("HA-Proxy version " HAPROXY_VERSION " " HAPROXY_DATE"\n"); - printf("Copyright 2000-2016 Willy Tarreau \n\n"); + printf("Copyright 2000-2017 Willy Tarreau \n\n"); } void display_build_opts() @@ -2038,7 +2038,7 @@ /* it's OK because "-Ds -f x" is the shortest form going here */ memcpy(argv[0] + strlen(argv[0]), "-master", 8); 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 -Nru haproxy-1.7.1/src/hlua.c haproxy-1.7.2/src/hlua.c --- haproxy-1.7.1/src/hlua.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/hlua.c 2017-01-13 09:03:00.000000000 +0000 @@ -1884,14 +1884,19 @@ * the request buffer if its not required. */ if (socket->s->req.buf->size == 0) { - si_applet_cant_put(&socket->s->si[0]); - goto hlua_socket_write_yield_return; + appctx = hlua->task->context; + if (!channel_alloc_buffer(&socket->s->req, &appctx->buffer_wait)) + goto hlua_socket_write_yield_return; } /* Check for avalaible space. */ len = buffer_total_space(socket->s->req.buf); - if (len <= 0) + if (len <= 0) { + appctx = objt_appctx(socket->s->si[0].end); + if (!hlua_com_new(hlua, &appctx->ctx.hlua.wake_on_write)) + WILL_LJMP(luaL_error(L, "out of memory")); goto hlua_socket_write_yield_return; + } /* send data */ if (len < send_len) @@ -1929,9 +1934,6 @@ return 1; hlua_socket_write_yield_return: - appctx = objt_appctx(socket->s->si[0].end); - if (!hlua_com_new(hlua, &appctx->ctx.hlua.wake_on_write)) - WILL_LJMP(luaL_error(L, "out of memory")); WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_socket_write_yield, TICK_ETERNITY, 0)); return 0; } @@ -2187,7 +2189,7 @@ conn->target = socket->s->target; /* Parse ip address. */ - addr = str2sa_range(ip, &low, &high, NULL, NULL, NULL, 0); + addr = str2sa_range(ip, NULL, &low, &high, NULL, NULL, NULL, 0); if (!addr) WILL_LJMP(luaL_error(L, "connect: cannot parse destination address '%s'", ip)); if (low != high) @@ -2397,33 +2399,49 @@ } } -/* Check the protocole integrity after the Lua manipulations. Close the stream - * and returns 0 if fails, otherwise returns 1. The direction is set using dir, - * which equals either SMP_OPT_DIR_REQ or SMP_OPT_DIR_RES. +/* This function is called before the Lua execution. It stores + * the differents parsers state before executing some Lua code. */ -static int hlua_check_proto(struct stream *stream, int dir) +static inline void consistency_set(struct stream *stream, int opt, struct hlua_consistency *c) { - const struct chunk msg = { .len = 0 }; + c->mode = stream->be->mode; + switch (c->mode) { + case PR_MODE_HTTP: + c->data.http.dir = opt & SMP_OPT_DIR; + if (c->data.http.dir == SMP_OPT_DIR_REQ) + c->data.http.state = stream->txn->req.msg_state; + else + c->data.http.state = stream->txn->rsp.msg_state; + break; + default: + break; + } +} - /* Protocol HTTP. The message parsing state must match the request or - * response state. The problem that may happen is that Lua modifies - * the request or response message *after* it was parsed, and corrupted - * it so that it could not be processed anymore. We just need to verify - * if the parser is still expected to run or not. - */ - if (stream->be->mode == PR_MODE_HTTP) { - if (dir == SMP_OPT_DIR_REQ && - !(stream->req.analysers & AN_REQ_WAIT_HTTP) && - stream->txn->req.msg_state < HTTP_MSG_ERROR) { - stream_int_retnclose(&stream->si[0], &msg); - return 0; - } - else if (dir == SMP_OPT_DIR_RES && - !(stream->res.analysers & AN_RES_WAIT_HTTP) && - stream->txn->rsp.msg_state < HTTP_MSG_ERROR) { - stream_int_retnclose(&stream->si[0], &msg); +/* This function is called after the Lua execution. it + * returns true if the parser state is consistent, otherwise, + * it return false. + * + * In HTTP mode, the parser state must be in the same state + * or greater when we exit the function. Even if we do a + * control yield. This prevent to break the HTTP message + * from the Lua code. + */ +static inline int consistency_check(struct stream *stream, int opt, struct hlua_consistency *c) +{ + if (c->mode != stream->be->mode) + return 0; + + switch (c->mode) { + case PR_MODE_HTTP: + if (c->data.http.dir != (opt & SMP_OPT_DIR)) return 0; - } + if (c->data.http.dir == SMP_OPT_DIR_REQ) + return stream->txn->req.msg_state >= c->data.http.state; + else + return stream->txn->rsp.msg_state >= c->data.http.state; + default: + return 1; } return 1; } @@ -3482,6 +3500,7 @@ lua_rawseti(L, -2, 0); appctx->appctx = ctx; appctx->appctx->ctx.hlua_apphttp.status = 200; /* Default status code returned. */ + appctx->appctx->ctx.hlua_apphttp.reason = NULL; /* Use default reason based on status */ appctx->htxn.s = s; appctx->htxn.p = px; @@ -3923,6 +3942,7 @@ { struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1)); int status = MAY_LJMP(luaL_checkinteger(L, 2)); + const char *reason = MAY_LJMP(luaL_optlstring(L, 3, NULL, NULL)); if (status < 100 || status > 599) { lua_pushboolean(L, 0); @@ -3930,6 +3950,7 @@ } appctx->appctx->ctx.hlua_apphttp.status = status; + appctx->appctx->ctx.hlua_apphttp.reason = reason; lua_pushboolean(L, 1); return 1; } @@ -3982,12 +4003,16 @@ int hdr_connection = 0; int hdr_contentlength = -1; int hdr_chunked = 0; + const char *reason = appctx->appctx->ctx.hlua_apphttp.reason; + + if (reason == NULL) + reason = get_reason(appctx->appctx->ctx.hlua_apphttp.status); /* Use the same http version than the request. */ chunk_appendf(tmp, "HTTP/1.%c %d %s\r\n", appctx->appctx->ctx.hlua_apphttp.flags & APPLET_HTTP11 ? '1' : '0', appctx->appctx->ctx.hlua_apphttp.status, - get_reason(appctx->appctx->ctx.hlua_apphttp.status)); + reason); /* Get the array associated to the field "response" in the object AppletHTTP. */ lua_pushvalue(L, 0); @@ -4541,17 +4566,18 @@ return 1; } -/* This function set the response code. */ +/* This function set the response code & optionally reason. */ static int hlua_http_res_set_status(lua_State *L) { struct hlua_txn *htxn = MAY_LJMP(hlua_checkhttp(L, 1)); unsigned int code = MAY_LJMP(luaL_checkinteger(L, 2)); + const char *reason = MAY_LJMP(luaL_optlstring(L, 3, NULL, NULL)); /* Check if a valid response is parsed */ if (unlikely(htxn->s->txn->rsp.msg_state < HTTP_MSG_BODY)) return 0; - http_set_status(code, htxn->s); + http_set_status(code, reason, htxn->s); return 0; } @@ -5115,6 +5141,7 @@ /* finished or yield */ case HLUA_E_OK: hlua_ctx_destroy(hlua); + free(hlua); task_delete(task); task_free(task); break; @@ -5128,6 +5155,7 @@ case HLUA_E_ERRMSG: SEND_ERR(NULL, "Lua task: %s.\n", lua_tostring(hlua->T, -1)); hlua_ctx_destroy(hlua); + free(hlua); task_delete(task); task_free(task); break; @@ -5136,6 +5164,7 @@ default: SEND_ERR(NULL, "Lua task: unknown error.\n"); hlua_ctx_destroy(hlua); + free(hlua); task_delete(task); task_free(task); break; @@ -5324,6 +5353,7 @@ struct hlua_function *fcn = private; struct stream *stream = smp->strm; const char *error; + const struct chunk msg = { .len = 0 }; if (!stream) return 0; @@ -5338,6 +5368,8 @@ return 0; } + consistency_set(stream, smp->opt, &stream->hlua.cons); + /* If it is the first run, initialize the data for the call. */ if (!HLUA_IS_RUNNING(&stream->hlua)) { @@ -5393,8 +5425,10 @@ switch (hlua_ctx_resume(&stream->hlua, 0)) { /* finished. */ case HLUA_E_OK: - if (!hlua_check_proto(stream, (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES)) + if (!consistency_check(stream, smp->opt, &stream->hlua.cons)) { + stream_int_retnclose(&stream->si[0], &msg); return 0; + } /* Convert the returned value in sample. */ hlua_lua2smp(stream->hlua.T, -1, smp); lua_pop(stream->hlua.T, 1); @@ -5405,13 +5439,15 @@ /* yield. */ case HLUA_E_AGAIN: - hlua_check_proto(stream, (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES); + if (!consistency_check(stream, smp->opt, &stream->hlua.cons)) + stream_int_retnclose(&stream->si[0], &msg); SEND_ERR(smp->px, "Lua sample-fetch '%s': cannot use yielded functions.\n", fcn->name); return 0; /* finished with error. */ case HLUA_E_ERRMSG: - hlua_check_proto(stream, (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES); + if (!consistency_check(stream, smp->opt, &stream->hlua.cons)) + stream_int_retnclose(&stream->si[0], &msg); /* Display log. */ SEND_ERR(smp->px, "Lua sample-fetch '%s': %s.\n", fcn->name, lua_tostring(stream->hlua.T, -1)); @@ -5419,7 +5455,8 @@ return 0; case HLUA_E_ERR: - hlua_check_proto(stream, (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES); + if (!consistency_check(stream, smp->opt, &stream->hlua.cons)) + stream_int_retnclose(&stream->si[0], &msg); /* Display log. */ SEND_ERR(smp->px, "Lua sample-fetch '%s' returns an unknown error.\n", fcn->name); @@ -5556,6 +5593,7 @@ unsigned int analyzer; int dir; const char *error; + const struct chunk msg = { .len = 0 }; switch (rule->from) { case ACT_F_TCP_REQ_CNT: analyzer = AN_REQ_INSPECT_FE ; dir = SMP_OPT_DIR_REQ; break; @@ -5567,6 +5605,8 @@ return ACT_RET_CONT; } + consistency_set(s, dir, &s->hlua.cons); + /* In the execution wrappers linked with a stream, the * Lua context can be not initialized. This behavior * permits to save performances because a systematic @@ -5635,8 +5675,10 @@ switch (hlua_ctx_resume(&s->hlua, !(flags & ACT_FLAG_FINAL))) { /* finished. */ case HLUA_E_OK: - if (!hlua_check_proto(s, dir)) + if (!consistency_check(s, dir, &s->hlua.cons)) { + stream_int_retnclose(&s->si[0], &msg); return ACT_RET_ERR; + } if (s->hlua.flags & HLUA_STOP) return ACT_RET_STOP; return ACT_RET_CONT; @@ -5661,12 +5703,17 @@ } if (HLUA_IS_WAKEREQWR(&s->hlua)) s->req.flags |= CF_WAKE_WRITE; + /* We can quit the fcuntion without consistency check + * because HAProxy is not able to manipulate data, it + * is only allowed to call me again. */ return ACT_RET_YIELD; /* finished with error. */ case HLUA_E_ERRMSG: - if (!hlua_check_proto(s, dir)) + if (!consistency_check(s, dir, &s->hlua.cons)) { + stream_int_retnclose(&s->si[0], &msg); return ACT_RET_ERR; + } /* Display log. */ SEND_ERR(px, "Lua function '%s': %s.\n", rule->arg.hlua_rule->fcn.name, lua_tostring(s->hlua.T, -1)); @@ -5674,8 +5721,10 @@ return ACT_RET_CONT; case HLUA_E_ERR: - if (!hlua_check_proto(s, dir)) + if (!consistency_check(s, dir, &s->hlua.cons)) { + stream_int_retnclose(&s->si[0], &msg); return ACT_RET_ERR; + } /* Display log. */ SEND_ERR(px, "Lua function '%s' return an unknown error.\n", rule->arg.hlua_rule->fcn.name); @@ -6411,8 +6460,8 @@ */ appctx->ctx.hlua_cli.task = task_new(); if (!appctx->ctx.hlua_cli.task) { - SEND_ERR(NULL, "Lua applet tcp '%s': out of memory.\n", fcn->name); - return 0; + SEND_ERR(NULL, "Lua cli '%s': out of memory.\n", fcn->name); + return 1; } appctx->ctx.hlua_cli.task->nice = 0; appctx->ctx.hlua_cli.task->context = appctx; diff -Nru haproxy-1.7.1/src/hlua_fcn.c haproxy-1.7.2/src/hlua_fcn.c --- haproxy-1.7.1/src/hlua_fcn.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/hlua_fcn.c 2017-01-13 09:03:00.000000000 +0000 @@ -1016,14 +1016,16 @@ return 1; } } else { - if (((addr1->addr.v6.ip.s6_addr32[0] & addr2->addr.v6.mask.s6_addr32[0]) == - (addr2->addr.v6.ip.s6_addr32[0] & addr1->addr.v6.mask.s6_addr32[0])) && - ((addr1->addr.v6.ip.s6_addr32[1] & addr2->addr.v6.mask.s6_addr32[1]) == - (addr2->addr.v6.ip.s6_addr32[1] & addr1->addr.v6.mask.s6_addr32[1])) && - ((addr1->addr.v6.ip.s6_addr32[2] & addr2->addr.v6.mask.s6_addr32[2]) == - (addr2->addr.v6.ip.s6_addr32[2] & addr1->addr.v6.mask.s6_addr32[2])) && - ((addr1->addr.v6.ip.s6_addr32[3] & addr2->addr.v6.mask.s6_addr32[3]) == - (addr2->addr.v6.ip.s6_addr32[3] & addr1->addr.v6.mask.s6_addr32[3]))) { + int i; + + for (i = 0; i < 16; i += 4) { + if ((*(uint32_t *)&addr1->addr.v6.ip.s6_addr[i] & + *(uint32_t *)&addr2->addr.v6.mask.s6_addr[i]) != + (*(uint32_t *)&addr2->addr.v6.ip.s6_addr[i] & + *(uint32_t *)&addr1->addr.v6.mask.s6_addr[i])) + break; + } + if (i == 16) { lua_pushboolean(L, 1); return 1; } diff -Nru haproxy-1.7.1/src/proto_http.c haproxy-1.7.2/src/proto_http.c --- haproxy-1.7.1/src/proto_http.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/proto_http.c 2017-01-13 09:03:00.000000000 +0000 @@ -343,7 +343,7 @@ case 499: return "client has closed connection"; case 500: return "Internal Server Error"; case 501: return "Not Implemented"; - case 502: return "Bad Gateway ou Proxy Error"; + case 502: return "Bad Gateway or Proxy Error"; case 503: return "Service Unavailable"; case 504: return "Gateway Time-out"; case 505: return "HTTP Version not supported"; @@ -1350,7 +1350,7 @@ /* either we find an LF at or we jump to . */ -#define EXPECT_LF_HERE(ptr, bad) do { if (unlikely(*(ptr) != '\n')) goto bad; } while (0) +#define EXPECT_LF_HERE(ptr, bad, st) do { if (unlikely(*(ptr) != '\n')) { state = st; goto bad;}; } while (0) /* plays with variables , and . Jumps to if OK, * otherwise to with set to . @@ -1401,6 +1401,7 @@ msg->sl.st.v_l = ptr - msg_start; EAT_AND_JUMP_OR_RETURN(http_msg_rpver_sp, HTTP_MSG_RPVER_SP); } + msg->err_state = HTTP_MSG_RPVER; state = HTTP_MSG_ERROR; break; @@ -1413,6 +1414,7 @@ if (likely(HTTP_IS_SPHT(*ptr))) EAT_AND_JUMP_OR_RETURN(http_msg_rpver_sp, HTTP_MSG_RPVER_SP); /* so it's a CR/LF, this is invalid */ + msg->err_state = HTTP_MSG_RPVER_SP; state = HTTP_MSG_ERROR; break; @@ -1525,6 +1527,7 @@ msg->sl.rq.v_l = 0; goto http_msg_rqline_eol; } + msg->err_state = HTTP_MSG_RQMETH; state = HTTP_MSG_ERROR; break; @@ -1557,6 +1560,10 @@ ptr += sizeof(int); } #endif + if (ptr >= end) { + state = HTTP_MSG_RQURI; + goto http_msg_ood; + } http_msg_rquri2: if (likely((unsigned char)(*ptr - 33) <= 93)) /* 33 to 126 included */ EAT_AND_JUMP_OR_RETURN(http_msg_rquri2, HTTP_MSG_RQURI); @@ -1586,6 +1593,7 @@ /* OK forbidden chars, 0..31 or 127 */ invalid_char: msg->err_pos = ptr - msg_start; + msg->err_state = HTTP_MSG_RQURI; state = HTTP_MSG_ERROR; break; @@ -1619,6 +1627,7 @@ } /* neither an HTTP_VER token nor a CRLF */ + msg->err_state = HTTP_MSG_RQVER; state = HTTP_MSG_ERROR; break; @@ -1777,8 +1786,10 @@ goto http_msg_rpver; } - if (unlikely(!HTTP_IS_CRLF(*ptr))) + if (unlikely(!HTTP_IS_CRLF(*ptr))) { + state = HTTP_MSG_RPBEFORE; goto http_msg_invalid; + } if (unlikely(*ptr == '\n')) EAT_AND_JUMP_OR_RETURN(http_msg_rpbefore, HTTP_MSG_RPBEFORE); @@ -1787,7 +1798,7 @@ case HTTP_MSG_RPBEFORE_CR: http_msg_rpbefore_cr: - EXPECT_LF_HERE(ptr, http_msg_invalid); + EXPECT_LF_HERE(ptr, http_msg_invalid, HTTP_MSG_RPBEFORE_CR); EAT_AND_JUMP_OR_RETURN(http_msg_rpbefore, HTTP_MSG_RPBEFORE); /* stop here */ @@ -1816,7 +1827,7 @@ case HTTP_MSG_RPLINE_END: http_msg_rpline_end: /* msg->sol must point to the first of CR or LF. */ - EXPECT_LF_HERE(ptr, http_msg_invalid); + EXPECT_LF_HERE(ptr, http_msg_invalid, HTTP_MSG_RPLINE_END); EAT_AND_JUMP_OR_RETURN(http_msg_hdr_first, HTTP_MSG_HDR_FIRST); /* stop here */ @@ -1842,8 +1853,10 @@ goto http_msg_rqmeth; } - if (unlikely(!HTTP_IS_CRLF(*ptr))) + if (unlikely(!HTTP_IS_CRLF(*ptr))) { + state = HTTP_MSG_RQBEFORE; goto http_msg_invalid; + } if (unlikely(*ptr == '\n')) EAT_AND_JUMP_OR_RETURN(http_msg_rqbefore, HTTP_MSG_RQBEFORE); @@ -1852,7 +1865,7 @@ case HTTP_MSG_RQBEFORE_CR: http_msg_rqbefore_cr: - EXPECT_LF_HERE(ptr, http_msg_invalid); + EXPECT_LF_HERE(ptr, http_msg_invalid, HTTP_MSG_RQBEFORE_CR); EAT_AND_JUMP_OR_RETURN(http_msg_rqbefore, HTTP_MSG_RQBEFORE); /* stop here */ @@ -1886,7 +1899,7 @@ if (unlikely(msg->sl.rq.v_l == 0)) goto http_msg_last_lf; - EXPECT_LF_HERE(ptr, http_msg_invalid); + EXPECT_LF_HERE(ptr, http_msg_invalid, HTTP_MSG_RQLINE_END); EAT_AND_JUMP_OR_RETURN(http_msg_hdr_first, HTTP_MSG_HDR_FIRST); /* stop here */ @@ -1913,8 +1926,10 @@ if (likely(*ptr == ':')) EAT_AND_JUMP_OR_RETURN(http_msg_hdr_l1_sp, HTTP_MSG_HDR_L1_SP); - if (likely(msg->err_pos < -1) || *ptr == '\n') + if (likely(msg->err_pos < -1) || *ptr == '\n') { + state = HTTP_MSG_HDR_NAME; goto http_msg_invalid; + } if (msg->err_pos == -1) /* capture error pointer */ msg->err_pos = ptr - buf->p; /* >= 0 now */ @@ -1941,7 +1956,7 @@ case HTTP_MSG_HDR_L1_LF: http_msg_hdr_l1_lf: - EXPECT_LF_HERE(ptr, http_msg_invalid); + EXPECT_LF_HERE(ptr, http_msg_invalid, HTTP_MSG_HDR_L1_LF); EAT_AND_JUMP_OR_RETURN(http_msg_hdr_l1_lws, HTTP_MSG_HDR_L1_LWS); case HTTP_MSG_HDR_L1_LWS: @@ -1983,6 +1998,10 @@ ptr += sizeof(int); } #endif + if (ptr >= end) { + state = HTTP_MSG_HDR_VAL; + goto http_msg_ood; + } http_msg_hdr_val2: if (likely(!HTTP_IS_CRLF(*ptr))) EAT_AND_JUMP_OR_RETURN(http_msg_hdr_val2, HTTP_MSG_HDR_VAL); @@ -1998,7 +2017,7 @@ case HTTP_MSG_HDR_L2_LF: http_msg_hdr_l2_lf: - EXPECT_LF_HERE(ptr, http_msg_invalid); + EXPECT_LF_HERE(ptr, http_msg_invalid, HTTP_MSG_HDR_L2_LF); EAT_AND_JUMP_OR_RETURN(http_msg_hdr_l2_lws, HTTP_MSG_HDR_L2_LWS); case HTTP_MSG_HDR_L2_LWS: @@ -2018,8 +2037,10 @@ * header into the index. */ if (unlikely(hdr_idx_add(msg->eol - msg->sol, buf->p[msg->eol] == '\r', - idx, idx->tail) < 0)) + idx, idx->tail) < 0)) { + state = HTTP_MSG_HDR_L2_LWS; goto http_msg_invalid; + } msg->sol = ptr - buf->p; if (likely(!HTTP_IS_CRLF(*ptr))) { @@ -2036,7 +2057,7 @@ * Sets ->sov and ->next to the total header length, ->eoh to * the last CRLF, and ->eol to the last CRLF length (1 or 2). */ - EXPECT_LF_HERE(ptr, http_msg_invalid); + EXPECT_LF_HERE(ptr, http_msg_invalid, HTTP_MSG_LAST_LF); ptr++; msg->sov = msg->next = ptr - buf->p; msg->eoh = msg->sol; @@ -2064,6 +2085,7 @@ http_msg_invalid: /* invalid message */ + msg->err_state = state; msg->msg_state = HTTP_MSG_ERROR; msg->next = ptr - buf->p; return; @@ -2711,14 +2733,15 @@ /* we cannot return any message on error */ if (msg->err_pos >= 0) { - http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->err_state, sess->fe); stream_inc_http_err_ctr(s); } txn->status = 400; + msg->err_state = msg->msg_state; msg->msg_state = HTTP_MSG_ERROR; http_reply_and_close(s, txn->status, NULL); - req->analysers &= AN_FLT_END; + req->analysers &= AN_REQ_FLT_END; stream_inc_http_req_ctr(s); proxy_inc_fe_req_ctr(sess->fe); sess->fe->fe_counters.failed_req++; @@ -2743,13 +2766,14 @@ /* read timeout : give up with an error message. */ if (msg->err_pos >= 0) { - http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->err_state, sess->fe); stream_inc_http_err_ctr(s); } txn->status = 408; + msg->err_state = msg->msg_state; msg->msg_state = HTTP_MSG_ERROR; http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_408)); - req->analysers &= AN_FLT_END; + req->analysers &= AN_REQ_FLT_END; stream_inc_http_req_ctr(s); proxy_inc_fe_req_ctr(sess->fe); @@ -2774,11 +2798,12 @@ goto failed_keep_alive; if (msg->err_pos >= 0) - http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->err_state, sess->fe); txn->status = 400; + msg->err_state = msg->msg_state; msg->msg_state = HTTP_MSG_ERROR; http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_400)); - req->analysers &= AN_FLT_END; + req->analysers &= AN_REQ_FLT_END; stream_inc_http_err_ctr(s); stream_inc_http_req_ctr(s); proxy_inc_fe_req_ctr(sess->fe); @@ -2834,7 +2859,7 @@ */ txn->status = 0; msg->msg_state = HTTP_MSG_RQBEFORE; - req->analysers &= AN_FLT_END; + req->analysers &= AN_REQ_FLT_END; s->logs.logwait = 0; s->logs.level = 0; s->res.flags &= ~CF_EXPECT_MORE; /* speed up sending a previous response */ @@ -2866,7 +2891,7 @@ * to block on that, so we have to capture it now. */ if (unlikely(msg->err_pos >= 0)) - http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->err_state, sess->fe); /* * 1: identify the method @@ -3144,9 +3169,10 @@ /* we detected a parsing error. We want to archive this request * in the dedicated proxy area for later troubleshooting. */ - http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->err_state, sess->fe); } + txn->req.err_state = txn->req.msg_state; txn->req.msg_state = HTTP_MSG_ERROR; txn->status = 400; http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_400)); @@ -3161,7 +3187,7 @@ if (!(s->flags & SF_FINST_MASK)) s->flags |= SF_FINST_R; - req->analysers &= AN_FLT_END; + req->analysers &= AN_REQ_FLT_END; req->analyse_exp = TICK_ETERNITY; return 0; } @@ -4219,8 +4245,8 @@ bi_fast_delete(req->chn->buf, req->sov); req->next -= req->sov; req->sov = 0; - s->req.analysers = AN_REQ_HTTP_XFER_BODY | (s->req.analysers & AN_FLT_END); - s->res.analysers = AN_RES_HTTP_XFER_BODY | (s->req.analysers & AN_FLT_END); + s->req.analysers = AN_REQ_HTTP_XFER_BODY | (s->req.analysers & AN_REQ_FLT_END); + s->res.analysers = AN_RES_HTTP_XFER_BODY | (s->req.analysers & AN_RES_FLT_END); req->msg_state = HTTP_MSG_CLOSED; res->msg_state = HTTP_MSG_DONE; /* Trim any possible response */ @@ -4236,7 +4262,7 @@ trash.len += 23; } http_reply_and_close(s, txn->status, &trash); - req->chn->analysers &= AN_FLT_END; + req->chn->analysers &= AN_REQ_FLT_END; } if (!(s->flags & SF_ERR_MASK)) @@ -4379,8 +4405,8 @@ s->flags |= SF_FINST_R; /* enable the minimally required analyzers to handle keep-alive and compression on the HTTP response */ - req->analysers &= (AN_REQ_HTTP_BODY | AN_FLT_HTTP_HDRS | AN_FLT_END); - req->analysers &= ~AN_FLT_XFER_DATA; + req->analysers &= (AN_REQ_HTTP_BODY | AN_REQ_FLT_HTTP_HDRS | AN_REQ_FLT_END); + req->analysers &= ~AN_REQ_FLT_XFER_DATA; req->analysers |= AN_REQ_HTTP_XFER_BODY; goto done; } @@ -4437,7 +4463,7 @@ if (s->be->cookie_name || sess->fe->capture_name) manage_client_side_cookies(s, req); - req->analysers &= AN_FLT_END; /* remove switching rules etc... */ + req->analysers &= AN_REQ_FLT_END; /* remove switching rules etc... */ req->analysers |= AN_REQ_HTTP_TARPIT; req->analyse_exp = tick_add_ifset(now_ms, s->be->timeout.tarpit); if (!req->analyse_exp) @@ -4475,9 +4501,10 @@ /* we detected a parsing error. We want to archive this request * in the dedicated proxy area for later troubleshooting. */ - http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->err_state, sess->fe); } + txn->req.err_state = txn->req.msg_state; txn->req.msg_state = HTTP_MSG_ERROR; txn->status = 400; http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_400)); @@ -4492,7 +4519,7 @@ if (!(s->flags & SF_FINST_MASK)) s->flags |= SF_FINST_R; - req->analysers &= AN_FLT_END; + req->analysers &= AN_REQ_FLT_END; req->analyse_exp = TICK_ETERNITY; return 0; @@ -4546,9 +4573,10 @@ /* Note that for now we don't reuse existing proxy connections */ if (unlikely((conn = si_alloc_conn(&s->si[1])) == NULL)) { + txn->req.err_state = txn->req.msg_state; txn->req.msg_state = HTTP_MSG_ERROR; txn->status = 500; - req->analysers &= AN_FLT_END; + req->analysers &= AN_REQ_FLT_END; http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_500)); if (!(s->flags & SF_ERR_MASK)) @@ -4780,7 +4808,7 @@ } if (msg->flags & HTTP_MSGF_XFER_LEN) { - req->analysers &= ~AN_FLT_XFER_DATA; + req->analysers &= ~AN_REQ_FLT_XFER_DATA; req->analysers |= AN_REQ_HTTP_XFER_BODY; #ifdef TCP_QUICKACK /* We expect some data from the client. Unless we know for sure @@ -4818,12 +4846,13 @@ /* we detected a parsing error. We want to archive this request * in the dedicated proxy area for later troubleshooting. */ - http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->err_state, sess->fe); } + txn->req.err_state = txn->req.msg_state; txn->req.msg_state = HTTP_MSG_ERROR; txn->status = 400; - req->analysers &= AN_FLT_END; + req->analysers &= AN_REQ_FLT_END; http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_400)); sess->fe->fe_counters.failed_req++; @@ -4867,7 +4896,7 @@ if (!(req->flags & CF_READ_ERROR)) http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_500)); - req->analysers &= AN_FLT_END; + req->analysers &= AN_REQ_FLT_END; req->analyse_exp = TICK_ETERNITY; if (!(s->flags & SF_ERR_MASK)) @@ -5012,6 +5041,7 @@ return 1; return_bad_req: /* let's centralize all bad requests */ + txn->req.err_state = txn->req.msg_state; txn->req.msg_state = HTTP_MSG_ERROR; txn->status = 400; http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_400)); @@ -5022,7 +5052,7 @@ s->flags |= SF_FINST_R; return_err_msg: - req->analysers &= AN_FLT_END; + req->analysers &= AN_REQ_FLT_END; sess->fe->fe_counters.failed_req++; if (sess->listener->counters) sess->listener->counters->failed_req++; @@ -5272,8 +5302,8 @@ } if (HAS_FILTERS(s)) { - s->req.analysers &= AN_FLT_END; - s->res.analysers &= AN_FLT_END; + s->req.analysers &= AN_REQ_FLT_END; + s->res.analysers &= AN_RES_FLT_END; } else { s->req.analysers = strm_li(s) ? strm_li(s)->analysers : 0; @@ -5405,6 +5435,7 @@ goto http_msg_closed; } else if (chn->flags & CF_SHUTW) { + txn->req.err_state = txn->req.msg_state; txn->req.msg_state = HTTP_MSG_ERROR; goto wait_other_side; } @@ -5531,6 +5562,7 @@ goto http_msg_closed; } else if (chn->flags & CF_SHUTW) { + txn->req.err_state = txn->req.msg_state; txn->rsp.msg_state = HTTP_MSG_ERROR; s->be->be_counters.cli_aborts++; if (objt_server(s->target)) @@ -5591,25 +5623,25 @@ txn->rsp.msg_state == HTTP_MSG_TUNNEL || (txn->req.msg_state == HTTP_MSG_CLOSED && txn->rsp.msg_state == HTTP_MSG_CLOSED)) { - s->req.analysers &= AN_FLT_END; + s->req.analysers &= AN_REQ_FLT_END; channel_auto_close(&s->req); channel_auto_read(&s->req); - s->res.analysers &= AN_FLT_END; + s->res.analysers &= AN_RES_FLT_END; channel_auto_close(&s->res); channel_auto_read(&s->res); if (txn->req.msg_state == HTTP_MSG_TUNNEL && HAS_REQ_DATA_FILTERS(s)) - s->req.analysers |= AN_FLT_XFER_DATA; + s->req.analysers |= AN_REQ_FLT_XFER_DATA; if (txn->rsp.msg_state == HTTP_MSG_TUNNEL && HAS_RSP_DATA_FILTERS(s)) - s->res.analysers |= AN_FLT_XFER_DATA; + s->res.analysers |= AN_RES_FLT_XFER_DATA; } else if ((txn->req.msg_state >= HTTP_MSG_DONE && (txn->rsp.msg_state == HTTP_MSG_CLOSED || (s->res.flags & CF_SHUTW))) || txn->rsp.msg_state == HTTP_MSG_ERROR || txn->req.msg_state == HTTP_MSG_ERROR) { - s->res.analysers &= AN_FLT_END; + s->res.analysers &= AN_RES_FLT_END; channel_auto_close(&s->res); channel_auto_read(&s->res); - s->req.analysers &= AN_FLT_END; + s->req.analysers &= AN_REQ_FLT_END; channel_abort(&s->req); channel_auto_close(&s->req); channel_auto_read(&s->req); @@ -5665,6 +5697,7 @@ /* Output closed while we were sending data. We must abort and * wake the other side up. */ + msg->err_state = msg->msg_state; msg->msg_state = HTTP_MSG_ERROR; http_resync_states(s); return 1; @@ -5724,7 +5757,6 @@ if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN) channel_dont_close(req); - ret = msg->msg_state; if (http_resync_states(s)) { /* some state changes occurred, maybe the analyser * was disabled too. */ @@ -5735,7 +5767,7 @@ goto aborted_xfer; } if (msg->err_pos >= 0) - http_capture_bad_message(&sess->fe->invalid_req, s, msg, ret, s->be); + http_capture_bad_message(&sess->fe->invalid_req, s, msg, msg->err_state, s->be); goto return_bad_req; } return 1; @@ -5810,6 +5842,7 @@ sess->listener->counters->failed_req++; return_bad_req_stats_ok: + txn->req.err_state = txn->req.msg_state; txn->req.msg_state = HTTP_MSG_ERROR; if (txn->status) { /* Note: we don't send any error if some data were already sent */ @@ -5818,8 +5851,8 @@ txn->status = 400; http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_400)); } - req->analysers &= AN_FLT_END; - s->res.analysers &= AN_FLT_END; /* we're in data phase, we want to abort both directions */ + req->analysers &= AN_REQ_FLT_END; + s->res.analysers &= AN_RES_FLT_END; /* we're in data phase, we want to abort both directions */ if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_PRXCOND; @@ -5832,6 +5865,7 @@ return 0; aborted_xfer: + txn->req.err_state = txn->req.msg_state; txn->req.msg_state = HTTP_MSG_ERROR; if (txn->status) { /* Note: we don't send any error if some data were already sent */ @@ -5840,8 +5874,8 @@ txn->status = 502; http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_502)); } - req->analysers &= AN_FLT_END; - s->res.analysers &= AN_FLT_END; /* we're in data phase, we want to abort both directions */ + req->analysers &= AN_REQ_FLT_END; + s->res.analysers &= AN_RES_FLT_END; /* we're in data phase, we want to abort both directions */ sess->fe->fe_counters.srv_aborts++; s->be->be_counters.srv_aborts++; @@ -5968,7 +6002,7 @@ */ hdr_response_bad: if (msg->msg_state == HTTP_MSG_ERROR || msg->err_pos >= 0) - http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->err_state, sess->fe); s->be->be_counters.failed_resp++; if (objt_server(s->target)) { @@ -5977,7 +6011,7 @@ } abort_response: channel_auto_close(rep); - rep->analysers &= AN_FLT_END; + rep->analysers &= AN_RES_FLT_END; txn->status = 502; s->si[1].flags |= SI_FL_NOLINGER; channel_truncate(rep); @@ -6001,7 +6035,7 @@ /* read error */ else if (rep->flags & CF_READ_ERROR) { if (msg->err_pos >= 0) - http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->err_state, sess->fe); else if (txn->flags & TX_NOT_FIRST) goto abort_keep_alive; @@ -6012,7 +6046,7 @@ } channel_auto_close(rep); - rep->analysers &= AN_FLT_END; + rep->analysers &= AN_RES_FLT_END; txn->status = 502; s->si[1].flags |= SI_FL_NOLINGER; channel_truncate(rep); @@ -6028,7 +6062,7 @@ /* read timeout : return a 504 to the client. */ 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, sess->fe); + http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->err_state, sess->fe); s->be->be_counters.failed_resp++; if (objt_server(s->target)) { @@ -6037,7 +6071,7 @@ } channel_auto_close(rep); - rep->analysers &= AN_FLT_END; + rep->analysers &= AN_RES_FLT_END; txn->status = 504; s->si[1].flags |= SI_FL_NOLINGER; channel_truncate(rep); @@ -6057,7 +6091,7 @@ if (objt_server(s->target)) objt_server(s->target)->counters.cli_aborts++; - rep->analysers &= AN_FLT_END; + rep->analysers &= AN_RES_FLT_END; channel_auto_close(rep); txn->status = 400; @@ -6076,7 +6110,7 @@ /* close from server, capture the response if the server has started to respond */ else if (rep->flags & CF_SHUTR) { if (msg->msg_state >= HTTP_MSG_RPVER || msg->err_pos >= 0) - http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->err_state, sess->fe); else if (txn->flags & TX_NOT_FIRST) goto abort_keep_alive; @@ -6087,7 +6121,7 @@ } channel_auto_close(rep); - rep->analysers &= AN_FLT_END; + rep->analysers &= AN_RES_FLT_END; txn->status = 502; s->si[1].flags |= SI_FL_NOLINGER; channel_truncate(rep); @@ -6103,12 +6137,12 @@ /* write error to client (we don't send any message then) */ else if (rep->flags & CF_WRITE_ERROR) { if (msg->err_pos >= 0) - http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->err_state, sess->fe); else if (txn->flags & TX_NOT_FIRST) goto abort_keep_alive; s->be->be_counters.failed_resp++; - rep->analysers &= AN_FLT_END; + rep->analysers &= AN_RES_FLT_END; channel_auto_close(rep); if (!(s->flags & SF_ERR_MASK)) @@ -6131,7 +6165,7 @@ */ if (unlikely(msg->err_pos >= 0)) - http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->msg_state, sess->fe); + http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->err_state, sess->fe); /* * 1: get the status code @@ -6467,8 +6501,8 @@ * any other information so that the client retries. */ txn->status = 0; - rep->analysers &= AN_FLT_END; - s->req.analysers &= AN_FLT_END; + rep->analysers &= AN_RES_FLT_END; + s->req.analysers &= AN_REQ_FLT_END; channel_auto_close(rep); s->logs.logwait = 0; s->logs.level = 0; @@ -6568,7 +6602,7 @@ } s->be->be_counters.failed_resp++; return_srv_prx_502: - rep->analysers &= AN_FLT_END; + rep->analysers &= AN_RES_FLT_END; txn->status = 502; s->logs.t_data = -1; /* was not a valid response */ s->si[1].flags |= SI_FL_NOLINGER; @@ -6788,7 +6822,7 @@ skip_header_mangling: if ((msg->flags & HTTP_MSGF_XFER_LEN) || HAS_FILTERS(s) || (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_TUN) { - rep->analysers &= ~AN_FLT_XFER_DATA; + rep->analysers &= ~AN_RES_FLT_XFER_DATA; rep->analysers |= AN_RES_HTTP_XFER_BODY; } @@ -6850,6 +6884,7 @@ /* Output closed while we were sending data. We must abort and * wake the other side up. */ + msg->err_state = msg->msg_state; msg->msg_state = HTTP_MSG_ERROR; http_resync_states(s); return 1; @@ -6886,7 +6921,6 @@ (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL) channel_dont_close(res); - ret = msg->msg_state; if (http_resync_states(s)) { /* some state changes occurred, maybe the analyser was disabled * too. */ @@ -6897,7 +6931,7 @@ goto aborted_xfer; } if (msg->err_pos >= 0) - http_capture_bad_message(&s->be->invalid_rep, s, msg, ret, strm_fe(s)); + http_capture_bad_message(&s->be->invalid_rep, s, msg, msg->err_state, strm_fe(s)); goto return_bad_res; } return 1; @@ -6963,11 +6997,12 @@ objt_server(s->target)->counters.failed_resp++; return_bad_res_stats_ok: + txn->rsp.err_state = txn->rsp.msg_state; txn->rsp.msg_state = HTTP_MSG_ERROR; /* don't send any error message as we're in the body */ http_reply_and_close(s, txn->status, NULL); - res->analysers &= AN_FLT_END; - s->req.analysers &= AN_FLT_END; /* we're in data phase, we want to abort both directions */ + res->analysers &= AN_RES_FLT_END; + s->req.analysers &= AN_REQ_FLT_END; /* we're in data phase, we want to abort both directions */ if (objt_server(s->target)) health_adjust(objt_server(s->target), HANA_STATUS_HTTP_HDRRSP); @@ -6978,11 +7013,12 @@ return 0; aborted_xfer: + txn->rsp.err_state = txn->rsp.msg_state; txn->rsp.msg_state = HTTP_MSG_ERROR; /* don't send any error message as we're in the body */ http_reply_and_close(s, txn->status, NULL); - res->analysers &= AN_FLT_END; - s->req.analysers &= AN_FLT_END; /* we're in data phase, we want to abort both directions */ + res->analysers &= AN_RES_FLT_END; + s->req.analysers &= AN_REQ_FLT_END; /* we're in data phase, we want to abort both directions */ sess->fe->fe_counters.cli_aborts++; s->be->be_counters.cli_aborts++; @@ -10158,6 +10194,7 @@ */ if (unlikely(s->req.buf->i + s->req.buf->p > s->req.buf->data + s->req.buf->size - global.tune.maxrewrite)) { + msg->err_state = msg->msg_state; msg->msg_state = HTTP_MSG_ERROR; smp->data.u.sint = 1; return 1; @@ -12417,14 +12454,14 @@ /* This function replace the HTTP status code and the associated message. The * variable contains the new status code. This function never fails. */ -void http_set_status(unsigned int status, struct stream *s) +void http_set_status(unsigned int status, const char *reason, struct stream *s) { struct http_txn *txn = s->txn; char *cur_ptr, *cur_end; int delta; char *res; int c_l; - const char *msg; + const char *msg = reason; int msg_len; chunk_reset(&trash); @@ -12435,9 +12472,10 @@ trash.str[c_l] = ' '; trash.len = c_l + 1; - msg = get_reason(status); + /* Do we have a custom reason format string? */ + if (msg == NULL) + msg = get_reason(status); msg_len = strlen(msg); - strncpy(&trash.str[trash.len], msg, trash.size - trash.len); trash.len += msg_len; @@ -12483,7 +12521,7 @@ enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px, struct session *sess, struct stream *s, int flags) { - http_set_status(rule->arg.status.code, s); + http_set_status(rule->arg.status.code, rule->arg.status.reason, s); return ACT_RET_CONT; } @@ -12559,7 +12597,7 @@ /* Check if an argument is available */ if (!*args[*orig_arg]) { - memprintf(err, "expects exactly 1 argument "); + memprintf(err, "expects 1 argument: ; or 3 arguments: reason "); return ACT_RET_PRS_ERR; } @@ -12571,6 +12609,16 @@ } (*orig_arg)++; + + /* scustom reason string */ + rule->arg.status.reason = NULL; // If null, we use the default reason for the status code. + if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 && + (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) { + (*orig_arg)++; + rule->arg.status.reason = strdup(args[*orig_arg]); + (*orig_arg)++; + } + return ACT_RET_PRS_OK; } @@ -13046,14 +13094,14 @@ chunk_appendf(&trash, ", server %s (#%d), event #%u\n" " src %s:%d, session #%d, session flags 0x%08x\n" - " HTTP msg state %d, msg flags 0x%08x, tx flags 0x%08x\n" + " HTTP msg state %s(%d), msg flags 0x%08x, tx flags 0x%08x\n" " HTTP chunk len %lld bytes, HTTP body len %lld bytes\n" " buffer flags 0x%08x, out %d bytes, total %lld bytes\n" " pending %d bytes, wrapping at %d, error at position %d:\n \n", es->srv ? es->srv->id : "", es->srv ? es->srv->puid : -1, es->ev_id, pn, port, es->sid, es->s_flags, - es->state, es->m_flags, es->t_flags, + http_msg_state_str(es->state), es->state, es->m_flags, es->t_flags, es->m_clen, es->m_blen, es->b_flags, es->b_out, es->b_tot, es->len, es->b_wrap, es->pos); diff -Nru haproxy-1.7.1/src/proxy.c haproxy-1.7.2/src/proxy.c --- haproxy-1.7.1/src/proxy.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/proxy.c 2017-01-13 09:03:00.000000000 +0000 @@ -1172,7 +1172,7 @@ /* Be sure to filter request headers if the backend is an HTTP proxy and * if there are filters attached to the stream. */ if (s->be->mode == PR_MODE_HTTP && HAS_FILTERS(s)) - s->req.analysers |= AN_FLT_HTTP_HDRS; + s->req.analysers |= AN_REQ_FLT_HTTP_HDRS; if (s->txn) { if (be->options2 & PR_O2_RSPBUG_OK) diff -Nru haproxy-1.7.1/src/server.c haproxy-1.7.2/src/server.c --- haproxy-1.7.1/src/server.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/server.c 2017-01-13 09:03:00.000000000 +0000 @@ -968,7 +968,7 @@ if (!defsrv) { struct sockaddr_storage *sk; - int port1, port2; + int port1, port2, port; struct protocol *proto; struct dns_resolution *curr_resolution; @@ -1006,7 +1006,7 @@ * - IP:+N => port=+N, relative * - IP:-N => port=-N, relative */ - sk = str2sa_range(args[2], &port1, &port2, &errmsg, NULL, &fqdn, 0); + sk = str2sa_range(args[2], &port, &port1, &port2, &errmsg, NULL, &fqdn, 0); if (!sk) { Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg); err_code |= ERR_ALERT | ERR_FATAL; @@ -1014,7 +1014,7 @@ } proto = protocol_by_family(sk->ss_family); - if (!proto || !proto->connect) { + if (!fqdn && (!proto || !proto->connect)) { Alert("parsing [%s:%d] : '%s %s' : connect() not supported for this address family.\n", file, linenum, args[0], args[1]); err_code |= ERR_ALERT | ERR_FATAL; @@ -1063,9 +1063,10 @@ skip_name_resolution: newsrv->addr = *sk; + newsrv->svc_port = port; newsrv->xprt = newsrv->check.xprt = newsrv->agent.xprt = &raw_sock; - if (!protocol_by_family(newsrv->addr.ss_family)) { + if (!newsrv->hostname && !protocol_by_family(newsrv->addr.ss_family)) { Alert("parsing [%s:%d] : Unknown protocol family %d '%s'\n", file, linenum, newsrv->addr.ss_family, args[2]); err_code |= ERR_ALERT | ERR_FATAL; @@ -1393,7 +1394,7 @@ int port1, port2; struct protocol *proto; - sk = str2sa_range(args[cur_arg + 1], &port1, &port2, &errmsg, NULL, NULL, 1); + sk = str2sa_range(args[cur_arg + 1], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s' : %s\n", file, linenum, args[cur_arg], errmsg); @@ -1605,7 +1606,7 @@ } newsrv->conn_src.opts |= CO_SRC_BIND; - sk = str2sa_range(args[cur_arg + 1], &port_low, &port_high, &errmsg, NULL, NULL, 1); + sk = str2sa_range(args[cur_arg + 1], NULL, &port_low, &port_high, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[cur_arg], args[cur_arg+1], errmsg); @@ -1705,7 +1706,7 @@ struct sockaddr_storage *sk; int port1, port2; - sk = str2sa_range(args[cur_arg + 1], &port1, &port2, &errmsg, NULL, NULL, 1); + sk = str2sa_range(args[cur_arg + 1], NULL, &port1, &port2, &errmsg, NULL, NULL, 1); if (!sk) { Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[cur_arg], args[cur_arg+1], errmsg); @@ -2810,12 +2811,13 @@ chunk_appendf(msg, ", "); /* collecting data currently setup */ - current_port = get_host_port(&s->addr); + current_port = s->svc_port; /* check if PORT change is required */ port_change_required = 0; sign = *port; + errno = 0; new_port = strtol(port, &endptr, 10); if ((errno != 0) || (port == endptr)) { chunk_appendf(msg, "problem converting port '%s' to an int", port); @@ -2851,7 +2853,7 @@ /* applying PORT changes if required and update response message */ if (port_change_required) { /* apply new port */ - set_host_port(&s->addr, new_port); + s->svc_port = new_port; /* prepare message */ chunk_appendf(msg, "port changed from '"); @@ -3012,6 +3014,9 @@ serverip = &((struct sockaddr_in6 *)&s->addr)->sin6_addr.s6_addr; break; + case AF_UNSPEC: + break; + default: goto invalid; } diff -Nru haproxy-1.7.1/src/ssl_sock.c haproxy-1.7.2/src/ssl_sock.c --- haproxy-1.7.1/src/ssl_sock.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/ssl_sock.c 2017-01-13 09:03:00.000000000 +0000 @@ -799,11 +799,14 @@ if (!callback) { struct ocsp_cbk_arg *cb_arg = calloc(1, sizeof(*cb_arg)); + EVP_PKEY *pkey; cb_arg->is_single = 1; cb_arg->s_ocsp = iocsp; - cb_arg->single_kt = EVP_PKEY_base_id(X509_get_pubkey(x)); + pkey = X509_get_pubkey(x); + cb_arg->single_kt = EVP_PKEY_base_id(pkey); + EVP_PKEY_free(pkey); SSL_CTX_set_tlsext_status_cb(ctx, ssl_sock_ocsp_stapling_cbk); SSL_CTX_set_tlsext_status_arg(ctx, cb_arg); @@ -816,6 +819,7 @@ struct certificate_ocsp *tmp_ocsp; int index; int key_type; + EVP_PKEY *pkey; #ifdef SSL_CTX_get_tlsext_status_arg SSL_CTX_ctrl(ctx, SSL_CTRL_GET_TLSEXT_STATUS_REQ_CB_ARG, 0, &cb_arg); @@ -834,7 +838,10 @@ cb_arg->is_single = 0; cb_arg->single_kt = 0; - key_type = EVP_PKEY_base_id(X509_get_pubkey(x)); + pkey = X509_get_pubkey(x); + key_type = EVP_PKEY_base_id(pkey); + EVP_PKEY_free(pkey); + index = ssl_sock_get_ocsp_arg_kt_index(key_type); if (index >= 0 && !cb_arg->m_ocsp[index]) cb_arg->m_ocsp[index] = iocsp; @@ -3334,6 +3341,8 @@ EVP_PKEY_free(bind_conf->ca_sign_pkey); if (bind_conf->ca_sign_cert) X509_free(bind_conf->ca_sign_cert); + bind_conf->ca_sign_pkey = NULL; + bind_conf->ca_sign_cert = NULL; } /* @@ -3654,8 +3663,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; + } if (!(objt_server(conn->target)->ssl_ctx.options & SRV_SSL_O_NO_REUSE)) objt_server(conn->target)->ssl_ctx.reused_sess = SSL_get1_session(conn->xprt_ctx); @@ -4139,12 +4150,27 @@ return (char *)SSL_get_version(conn->xprt_ctx); } +/* Sets advertised SNI for outgoing connections. Please set to NULL + * to disable SNI. + */ void ssl_sock_set_servername(struct connection *conn, const char *hostname) { #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + char *prev_name; + if (!ssl_sock_is_ssl(conn)) return; + /* if the SNI changes, we must destroy the reusable context so that a + * new connection will present a new SNI. As an optimization we could + * later imagine having a small cache of ssl_ctx to hold a few SNI per + * server. + */ + prev_name = (char *)SSL_get_servername(conn->xprt_ctx, TLSEXT_NAMETYPE_host_name); + if ((!prev_name && hostname) || + (prev_name && (!hostname || strcmp(hostname, prev_name) != 0))) + SSL_set_session(conn->xprt_ctx, NULL); + SSL_set_tlsext_host_name(conn->xprt_ctx, hostname); #endif } diff -Nru haproxy-1.7.1/src/standard.c haproxy-1.7.2/src/standard.c --- haproxy-1.7.1/src/standard.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/standard.c 2017-01-13 09:03:00.000000000 +0000 @@ -816,7 +816,7 @@ * When a file descriptor is passed, its value is put into the s_addr part of * the address when cast to sockaddr_in and the address family is AF_UNSPEC. */ -struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high, char **err, const char *pfx, char **fqdn, int resolve) +struct sockaddr_storage *str2sa_range(const char *str, int *port, int *low, int *high, char **err, const char *pfx, char **fqdn, int resolve) { static struct sockaddr_storage ss; struct sockaddr_storage *ret = NULL; @@ -958,14 +958,7 @@ * set or if resolve is set, otherwise it's an error. */ if (str2ip2(str2, &ss, 0) == NULL) { - if (!resolve && fqdn) { - /* we'll still want to store the port, so let's - * force it to IPv4 for now. - */ - memset(&ss, 0, sizeof(ss)); - ss.ss_family = AF_INET; - } - else if ((!resolve && !fqdn) || + if ((!resolve && !fqdn) || (resolve && str2ip2(str2, &ss, 1) == NULL)) { memprintf(err, "invalid address: '%s' in '%s'\n", str2, str); goto out; @@ -983,6 +976,8 @@ ret = &ss; out: + if (port) + *port = porta; if (low) *low = portl; if (high) @@ -1409,7 +1404,7 @@ uint16_t port; - if (size < 5) + if (size < 6) return 0; *str = '\0'; diff -Nru haproxy-1.7.1/src/stats.c haproxy-1.7.2/src/stats.c --- haproxy-1.7.1/src/stats.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/stats.c 2017-01-13 09:03:00.000000000 +0000 @@ -636,7 +636,8 @@ if (flags & ST_SHOWADMIN) chunk_appendf(out, - "", + "", + field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME)); chunk_appendf(out, @@ -1444,11 +1445,11 @@ switch (addr_to_str(&sv->addr, str, sizeof(str))) { case AF_INET: stats[ST_F_ADDR] = mkf_str(FO_CONFIG|FS_SERVICE, chunk_newstr(out)); - chunk_appendf(out, "%s:%d", str, get_host_port(&sv->addr)); + chunk_appendf(out, "%s:%d", str, sv->svc_port); break; case AF_INET6: stats[ST_F_ADDR] = mkf_str(FO_CONFIG|FS_SERVICE, chunk_newstr(out)); - chunk_appendf(out, "[%s]:%d", str, get_host_port(&sv->addr)); + chunk_appendf(out, "[%s]:%d", str, sv->svc_port); break; case AF_UNIX: stats[ST_F_ADDR] = mkf_str(FO_CONFIG|FS_SERVICE, "unix"); @@ -1501,7 +1502,7 @@ stats[ST_F_MODE] = mkf_str(FO_CONFIG|FS_SERVICE, proxy_mode_str(px->mode)); stats[ST_F_QCUR] = mkf_u32(0, px->nbpend); stats[ST_F_QMAX] = mkf_u32(FN_MAX, px->be_counters.nbpend_max); - stats[ST_F_SCUR] = mkf_u32(FO_CONFIG|FN_LIMIT, px->beconn); + stats[ST_F_SCUR] = mkf_u32(0, px->beconn); stats[ST_F_SMAX] = mkf_u32(FN_MAX, px->be_counters.conn_max); stats[ST_F_SLIM] = mkf_u32(FO_CONFIG|FN_LIMIT, px->fullconn); stats[ST_F_STOT] = mkf_u64(FN_COUNTER, px->be_counters.cum_conn); @@ -1642,7 +1643,12 @@ if ((px->cap & PR_CAP_BE) && px->srv && (appctx->ctx.stats.flags & STAT_ADMIN)) { /* Column heading for Enable or Disable server */ - chunk_appendf(&trash, ""); + chunk_appendf(&trash, + "", + px->id, + px->id); } chunk_appendf(&trash, diff -Nru haproxy-1.7.1/src/stick_table.c haproxy-1.7.2/src/stick_table.c --- haproxy-1.7.1/src/stick_table.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/stick_table.c 2017-01-13 09:03:00.000000000 +0000 @@ -2840,7 +2840,7 @@ { "sc_conn_cnt", smp_fetch_sc_conn_cnt, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_conn_cur", smp_fetch_sc_conn_cur, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_conn_rate", smp_fetch_sc_conn_rate, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, - { "sc_get_gpt0", smp_fetch_sc_get_gpt0, ARG2(1,SINT,TAB), NULL, SMP_T_BOOL, SMP_USE_INTRN, }, + { "sc_get_gpt0", smp_fetch_sc_get_gpt0, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_get_gpc0", smp_fetch_sc_get_gpc0, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_gpc0_rate", smp_fetch_sc_gpc0_rate, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc_http_err_cnt", smp_fetch_sc_http_err_cnt, ARG2(1,SINT,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, @@ -2860,7 +2860,7 @@ { "sc0_conn_cnt", smp_fetch_sc_conn_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_conn_cur", smp_fetch_sc_conn_cur, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_conn_rate", smp_fetch_sc_conn_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, - { "sc0_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(0,TAB), NULL, SMP_T_BOOL, SMP_USE_INTRN, }, + { "sc0_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_get_gpc0", smp_fetch_sc_get_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_gpc0_rate", smp_fetch_sc_gpc0_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc0_http_err_cnt", smp_fetch_sc_http_err_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, @@ -2880,7 +2880,7 @@ { "sc1_conn_cnt", smp_fetch_sc_conn_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_conn_cur", smp_fetch_sc_conn_cur, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_conn_rate", smp_fetch_sc_conn_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, - { "sc1_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(0,TAB), NULL, SMP_T_BOOL, SMP_USE_INTRN, }, + { "sc1_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_get_gpc0", smp_fetch_sc_get_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_gpc0_rate", smp_fetch_sc_gpc0_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc1_http_err_cnt", smp_fetch_sc_http_err_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, @@ -2900,7 +2900,7 @@ { "sc2_conn_cnt", smp_fetch_sc_conn_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_conn_cur", smp_fetch_sc_conn_cur, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_conn_rate", smp_fetch_sc_conn_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, - { "sc2_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(0,TAB), NULL, SMP_T_BOOL, SMP_USE_INTRN, }, + { "sc2_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_get_gpc0", smp_fetch_sc_get_gpc0, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_gpc0_rate", smp_fetch_sc_gpc0_rate, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, { "sc2_http_err_cnt", smp_fetch_sc_http_err_cnt, ARG1(0,TAB), NULL, SMP_T_SINT, SMP_USE_INTRN, }, @@ -2920,7 +2920,7 @@ { "src_conn_cnt", smp_fetch_sc_conn_cnt, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_conn_cur", smp_fetch_sc_conn_cur, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_conn_rate", smp_fetch_sc_conn_rate, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, - { "src_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(1,TAB), NULL, SMP_T_BOOL, SMP_USE_L4CLI, }, + { "src_get_gpt0", smp_fetch_sc_get_gpt0, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_get_gpc0", smp_fetch_sc_get_gpc0, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_gpc0_rate", smp_fetch_sc_gpc0_rate, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, { "src_http_err_cnt", smp_fetch_sc_http_err_cnt, ARG1(1,TAB), NULL, SMP_T_SINT, SMP_USE_L4CLI, }, diff -Nru haproxy-1.7.1/src/stream.c haproxy-1.7.2/src/stream.c --- haproxy-1.7.1/src/stream.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/stream.c 2017-01-13 09:03:00.000000000 +0000 @@ -725,7 +725,7 @@ /* Be sure to filter response headers if the backend is an HTTP proxy * and if there are filters attached to the stream. */ if (s->be->mode == PR_MODE_HTTP && HAS_FILTERS(s)) - rep->analysers |= AN_FLT_HTTP_HDRS; + rep->analysers |= AN_RES_FLT_HTTP_HDRS; rep->flags |= CF_READ_ATTACHED; /* producer is now attached */ if (req->flags & CF_WAKE_CONNECT) { @@ -1155,7 +1155,7 @@ if (fe == s->be) { s->req.analysers &= ~AN_REQ_INSPECT_BE; s->req.analysers &= ~AN_REQ_HTTP_PROCESS_BE; - s->req.analysers &= ~AN_FLT_START_BE; + s->req.analysers &= ~AN_REQ_FLT_START_BE; } /* as soon as we know the backend, we must check if we have a matching forced or ignored @@ -1196,7 +1196,7 @@ if (s->txn) s->txn->status = 500; - s->req.analysers &= AN_FLT_END; + s->req.analysers &= AN_REQ_FLT_END; s->req.analyse_exp = TICK_ETERNITY; return 0; } @@ -1465,7 +1465,7 @@ /* These 2 following macros call an analayzer for the specified channel if the * right flag is set. The first one is used for "filterable" analyzers. If a * stream has some registered filters, pre and post analyaze callbacks are - * called. The second are used for other analyzers (AN_FLT_* and + * called. The second are used for other analyzers (AN_REQ/RES_FLT_* and * AN_REQ/RES_HTTP_XFER_BODY) */ #define FLT_ANALYZE(strm, chn, fun, list, back, flag, ...) \ { \ @@ -1785,13 +1785,13 @@ ana_list = ana_back = req->analysers; while (ana_list && max_loops--) { /* Warning! ensure that analysers are always placed in ascending order! */ - ANALYZE (s, req, flt_start_analyze, ana_list, ana_back, AN_FLT_START_FE); + ANALYZE (s, req, flt_start_analyze, ana_list, ana_back, AN_REQ_FLT_START_FE); FLT_ANALYZE(s, req, tcp_inspect_request, ana_list, ana_back, AN_REQ_INSPECT_FE); FLT_ANALYZE(s, req, http_wait_for_request, ana_list, ana_back, AN_REQ_WAIT_HTTP); FLT_ANALYZE(s, req, http_wait_for_request_body, ana_list, ana_back, AN_REQ_HTTP_BODY); FLT_ANALYZE(s, req, http_process_req_common, ana_list, ana_back, AN_REQ_HTTP_PROCESS_FE, sess->fe); FLT_ANALYZE(s, req, process_switching_rules, ana_list, ana_back, AN_REQ_SWITCHING_RULES); - ANALYZE (s, req, flt_start_analyze, ana_list, ana_back, AN_FLT_START_BE); + ANALYZE (s, req, flt_start_analyze, ana_list, ana_back, AN_REQ_FLT_START_BE); FLT_ANALYZE(s, req, tcp_inspect_request, ana_list, ana_back, AN_REQ_INSPECT_BE); FLT_ANALYZE(s, req, http_process_req_common, ana_list, ana_back, AN_REQ_HTTP_PROCESS_BE, s->be); FLT_ANALYZE(s, req, http_process_tarpit, ana_list, ana_back, AN_REQ_HTTP_TARPIT); @@ -1799,10 +1799,10 @@ FLT_ANALYZE(s, req, http_process_request, ana_list, ana_back, AN_REQ_HTTP_INNER); FLT_ANALYZE(s, req, tcp_persist_rdp_cookie, ana_list, ana_back, AN_REQ_PRST_RDP_COOKIE); FLT_ANALYZE(s, req, process_sticking_rules, ana_list, ana_back, AN_REQ_STICKING_RULES); - ANALYZE (s, req, flt_analyze_http_headers, ana_list, ana_back, AN_FLT_HTTP_HDRS); + ANALYZE (s, req, flt_analyze_http_headers, ana_list, ana_back, AN_REQ_FLT_HTTP_HDRS); ANALYZE (s, req, http_request_forward_body, ana_list, ana_back, AN_REQ_HTTP_XFER_BODY); - ANALYZE (s, req, flt_xfer_data, ana_list, ana_back, AN_FLT_XFER_DATA); - ANALYZE (s, req, flt_end_analyze, ana_list, ana_back, AN_FLT_END); + ANALYZE (s, req, flt_xfer_data, ana_list, ana_back, AN_REQ_FLT_XFER_DATA); + ANALYZE (s, req, flt_end_analyze, ana_list, ana_back, AN_REQ_FLT_END); break; } } @@ -1872,16 +1872,16 @@ ana_list = ana_back = res->analysers; while (ana_list && max_loops--) { /* Warning! ensure that analysers are always placed in ascending order! */ - ANALYZE (s, res, flt_start_analyze, ana_list, ana_back, AN_FLT_START_FE); - ANALYZE (s, res, flt_start_analyze, ana_list, ana_back, AN_FLT_START_BE); + ANALYZE (s, res, flt_start_analyze, ana_list, ana_back, AN_RES_FLT_START_FE); + ANALYZE (s, res, flt_start_analyze, ana_list, ana_back, AN_RES_FLT_START_BE); FLT_ANALYZE(s, res, tcp_inspect_response, ana_list, ana_back, AN_RES_INSPECT); FLT_ANALYZE(s, res, http_wait_for_response, ana_list, ana_back, AN_RES_WAIT_HTTP); FLT_ANALYZE(s, res, process_store_rules, ana_list, ana_back, AN_RES_STORE_RULES); FLT_ANALYZE(s, res, http_process_res_common, ana_list, ana_back, AN_RES_HTTP_PROCESS_BE, s->be); - ANALYZE (s, res, flt_analyze_http_headers, ana_list, ana_back, AN_FLT_HTTP_HDRS); + ANALYZE (s, res, flt_analyze_http_headers, ana_list, ana_back, AN_RES_FLT_HTTP_HDRS); ANALYZE (s, res, http_response_forward_body, ana_list, ana_back, AN_RES_HTTP_XFER_BODY); - ANALYZE (s, res, flt_xfer_data, ana_list, ana_back, AN_FLT_XFER_DATA); - ANALYZE (s, res, flt_end_analyze, ana_list, ana_back, AN_FLT_END); + ANALYZE (s, res, flt_xfer_data, ana_list, ana_back, AN_RES_FLT_XFER_DATA); + ANALYZE (s, res, flt_end_analyze, ana_list, ana_back, AN_RES_FLT_END); break; } } diff -Nru haproxy-1.7.1/src/stream_interface.c haproxy-1.7.2/src/stream_interface.c --- haproxy-1.7.1/src/stream_interface.c 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/src/stream_interface.c 2017-01-13 09:03:00.000000000 +0000 @@ -455,9 +455,13 @@ oc->wex = TICK_ETERNITY; } - /* indicate that we may be waiting for data from the output channel */ + /* indicate that we may be waiting for data from the output channel or + * we're about to close and can't expect more data if SHUTW_NOW is there. + */ if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == 0 && channel_may_recv(oc)) si->flags |= SI_FL_WAIT_DATA; + else if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW) + si->flags &= ~SI_FL_WAIT_DATA; /* update OC timeouts and wake the other side up if it's waiting for room */ if (oc->flags & CF_WRITE_ACTIVITY) { diff -Nru haproxy-1.7.1/tests/setstatus.lua haproxy-1.7.2/tests/setstatus.lua --- haproxy-1.7.1/tests/setstatus.lua 1970-01-01 00:00:00.000000000 +0000 +++ haproxy-1.7.2/tests/setstatus.lua 2017-01-13 09:03:00.000000000 +0000 @@ -0,0 +1,26 @@ +-- http-response actions +core.register_action("set-status-418-defaultreason", {"http-res"}, function(txn) + txn.http:res_set_status(418) +end) +core.register_action("set-status-418-customreason", {"http-res"}, function(txn) + txn.http:res_set_status(418, "I'm a coffeepot") +end) + +-- http services +core.register_service("http418-default", "http", function(applet) + local response = "Hello World !" + applet:set_status(418) + applet:add_header("content-length", string.len(response)) + applet:add_header("content-type", "text/plain") + applet:start_response() + applet:send(response) +end) + +core.register_service("http418-coffeepot", "http", function(applet) + local response = "Hello World !" + applet:set_status(418, "I'm a coffeepot") + applet:add_header("content-length", string.len(response)) + applet:add_header("content-type", "text/plain") + applet:start_response() + applet:send(response) +end) diff -Nru haproxy-1.7.1/tests/test-http-set-status.cfg haproxy-1.7.2/tests/test-http-set-status.cfg --- haproxy-1.7.1/tests/test-http-set-status.cfg 1970-01-01 00:00:00.000000000 +0000 +++ haproxy-1.7.2/tests/test-http-set-status.cfg 2017-01-13 09:03:00.000000000 +0000 @@ -0,0 +1,32 @@ +global + maxconn 100 + +defaults + mode http + timeout client 10000 + timeout server 10000 + timeout connect 10000 + +# Expect HTTP/1.1 418 I'm a teapot +listen http-response-set-status-defaultreason + bind :8001 + server host 127.0.0.1:8080 + http-response set-status 418 + +# Expect HTTP/1.1 418 I'm a coffeepot +listen http-response-set-status-customreason + bind :8002 + server host 127.0.0.1:8080 + http-response set-status 418 reason "I'm a coffeepot" + +# Expect config parse fail +#listen parse-fail-string +# bind :8002 +# server host 127.0.0.1:8080 +# http-response set-status 418 reason + +# Expect config parse fail +#listen parse-fail-keyword +# bind :8002 +# server host 127.0.0.1:8080 +# http-response set-status 418 "Missing reason keyword" diff -Nru haproxy-1.7.1/tests/test-http-set-status-lua.cfg haproxy-1.7.2/tests/test-http-set-status-lua.cfg --- haproxy-1.7.1/tests/test-http-set-status-lua.cfg 1970-01-01 00:00:00.000000000 +0000 +++ haproxy-1.7.2/tests/test-http-set-status-lua.cfg 2017-01-13 09:03:00.000000000 +0000 @@ -0,0 +1,31 @@ +global + maxconn 100 + lua-load setstatus.lua + +defaults + mode http + timeout client 10000 + timeout server 10000 + timeout connect 10000 + +# Expect HTTP/1.1 418 I'm a teapot +listen lua-service-set-status-defaultreason + bind :8003 + http-request use-service lua.http418-default + +# Expect HTTP/1.1 418 I'm a coffeepot +listen lua-service-set-status-customreason + bind :8004 + http-request use-service lua.http418-coffeepot + +# Expect HTTP/1.1 418 I'm a teapot +listen lua-action-set-status-defaultreason + bind :8005 + http-response lua.set-status-418-defaultreason + server host 127.0.0.1:8080 + +# Expect HTTP/1.1 418 I'm a coffeepot +listen lua-action-set-status-customreason + bind :8006 + http-response lua.set-status-418-customreason + server host 127.0.0.1:8080 diff -Nru haproxy-1.7.1/VERDATE haproxy-1.7.2/VERDATE --- haproxy-1.7.1/VERDATE 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/VERDATE 2017-01-13 09:03:00.000000000 +0000 @@ -1,2 +1,2 @@ $Format:%ci$ -2016/12/13 +2017/01/13 diff -Nru haproxy-1.7.1/VERSION haproxy-1.7.2/VERSION --- haproxy-1.7.1/VERSION 2016-12-12 23:24:34.000000000 +0000 +++ haproxy-1.7.2/VERSION 2017-01-13 09:03:00.000000000 +0000 @@ -1 +1 @@ -1.7.1 +1.7.2