Commit 12cd7281 authored by Nils Goroll's avatar Nils Goroll

rewrite cookie parsing, differentiated error/warning handling

user visible:
* Add VSM / VCL_error loging for parser warnings
* differenciate between real errors and warnings
* change return values of to_http0_e
* add warnings() function to query about warnings from VCL

internal:
* rename VMOD_HTTP0* to VESICO* and http0_* to vesico_*
* include a txt'ified version of phk's http_split
* rewrite vesico_analyze_cookie_header to use http_split
parent f6471e08
Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung
Copyright (c) 2013-2014 UPLEX Nils Goroll Systemoptimierung
All rights reserved
...
See LICENSE for details.
You're free to use and distribute this under terms in the
LICENSE. Please add your relevant copyright statements.
......@@ -7,8 +7,8 @@ Varnish Module for cookie handling with ESI
-------------------------------------------
:Author: Nils Goroll
:Date: 2013-04-21
:Version: 1.0
:Date: 2014-10-14
:Version: 1.1
:Manual section: 3
.. _synopsis:
......@@ -21,17 +21,17 @@ SYNOPSIS
import esicookies;
sub vcl_fetch {
esicookies.to_http0(beresp.http.Set-Cookie);
}
esicookies.to_http0(beresp.http.Set-Cookie);
}
# OR
sub vcl_fetch {
set req.http.X-Err = esicookies.to_http0_e(beresp.http.Set-Cookie);
if (req.http.X-Err != "") {
if (req.http.X-Err) {
error 503 "Error in to_http0";
}
unset req.http.X-Err;
set req.http.X-Warn = esicookies.warnings();
}
sub vcl_error {
......@@ -40,6 +40,11 @@ SYNOPSIS
}
}
NOTE ON UPGRADING
=================
When upgrading from versions before 1.1, please see the history_ for
important changes!
DESCRIPTION
===========
......@@ -81,6 +86,23 @@ Later ``Set-Cookie`` reponse headers overwrite Cookies present in the
initial ``http0`` context ``Cookie`` headers or earlier ``Set-Cookie``
reponse headers.
Parse warnings are logged to VSM and can also be queried from VCL
using the warnings_ function.
For VSM logging, the ``VCL_error`` tag is used (because there is no
tag for warnings). Log entries contain formation about Cookie
elements being `tolerated` or `skipped` and a hint on where the parse
warning occurred. The excerpt is limited to 40 characters from the
Cookie line, if necessary. Sample output:
::
13 VCL_error c vmod esicookies http0 cookies tolerated in hdr:
13 VCL_error c ...ngcookieline;ok=val;noval=;ok2=val;somuc...
13 VCL_error c ^- empty cookie value
to_http0_e
----------
......@@ -88,23 +110,41 @@ Prototype
::
set ... = esicookies.to_http0_e(HEADER);
if (esicookies.to_http0_e(HEADER) ...)
if (esicookies.to_http0_e(HEADER))
This form is semantically equivalent to tohttp0_ except that is
returns a non-empty string when an error is encountered.
This form is semantically equivalent to to_http0_ except that is
returns a string when an error is encountered.
Possible return strings are:
* "Value too large for defined data type" or your current locale's
translation for ``EOVERFLOW``: too many cookies in use (see
* ``exceeded number of allowed cookies``: too many cookies in use (see
limitations_)
* "Invalid argument" or your current locale's translation for
``EINVAL``: a Cookie or Set-Cookie header had an illegal syntax
* "new cookies: not even the header name fits"
* "new cookies dont fit": Cookies don't fit into the workspace of size
* ``new cookies: not even the header name fits`` and ``new cookies
dont fit``: Cookies don't fit into the workspace of size
``HTTP0_WS_SIZE`` (see limitations_)
.. _warnings:
warnings
--------
Prototype
::
set ... = esicookies.warnings();
Returns a summary of parse warnings which have been encountered and
logged to VSM.
Possible return strings are:
* ``cookies skipped``: Some Cookie header elements were skipped while
parsing (and are thus missing from the generated ``Cookie:`` header
for subsequent ESI requests).
* ``cookies tolerated``: Some Cookie header elements were not properly
formatted (e.g. contained no value), but were processed anyway.
* ``cookies skipped and tolerated``: Both of the above
.. _limitations:
......@@ -148,11 +188,30 @@ Make targets:
* make install - installs your vmod in `VMODDIR`
* make check - runs the unit tests in ``src/tests/*.vtc``
CHANGES
.. _history:
HISTORY / CHANGELOG
===================
* Version 1.0: Initial version.
* Version 1.1: Initial version.
* to_http0_e_ now returns NULL when there was no error, contrary
to the empty string as before. This change is to avoid production
of invalid HTTP headers (without a value) when `to_http0_e_` is
used as in the examples shown.
Thus, to check for errors in VCL, if ``(... != "")`` needs to be
replaced with if ``(...)``.
* changed strings returned by to_http0_e_
HISTORY
=======
* Added the warnings_ function and VSM logging for parse warnings.
Version 1.0: Initial version.
* The parser is now more tolarant
COPYRIGHT
=========
......@@ -160,5 +219,5 @@ COPYRIGHT
This document is licensed under the same license as the
libvmod-esicookies project. See LICENSE for details.
Copyright (c) 2013 UPLEX Nils Goroll Systemoptimierung. All rights
Copyright (c) 2013-2014 UPLEX Nils Goroll Systemoptimierung. All rights
reserved.
......@@ -55,22 +55,30 @@ varnish v1 -vcl+backend {
esicookies.to_http0(beresp.http.Set-Cookie);
} else {
set req.http.X-Err = esicookies.to_http0_e(beresp.http.Set-Cookie);
if (req.http.X-Err != "") {
if (req.http.X-Err) {
error 503 "Error in to_http0";
}
unset req.http.X-Err;
set req.http.X-Warn = esicookies.warnings();
}
set beresp.do_esi = true;
}
sub vcl_error {
if (req.http.X-Err) {
set obj.http.X-Err = req.http.X-Err;
}
set obj.http.X-Err = req.http.X-Err;
set obj.http.X-Warn = req.http.X-Warn;
}
sub vcl_deliver {
set resp.http.X-Warn = req.http.X-Warn;
}
} -start
client c1 {
txreq -url "/" -hdr "Cookie: fromclient=1"
rxresp
expect resp.status == 200
expect resp.http.Set-Cookie == "fromserver1=1"
expect resp.http.X-Warn == <undef>
expect resp.http.X-Err == <undef>
} -run
......@@ -64,8 +64,9 @@ server s1 {
# cookie with no value within cookie header
rxreq
expect req.url == "/c_no_val_middle"
expect req.http.Cookie == "ok=val; noval=; ok2=val;"
txresp -body {
expect req.http.Cookie == "somuchstufftoproduce=alongcookieline;ok=val;noval=;ok2=val;somuchmorestufftoproduce=alongcookieline"
txresp -hdr "Set-Cookie: fromresponse=" \
-body {
<html>
Before include
<esi:include src="/i_no_val_middle"/>
......@@ -73,7 +74,7 @@ server s1 {
}
rxreq
expect req.url == "/i_no_val_middle"
expect req.http.Cookie == "ok=val; noval=; ok2=val;"
expect req.http.Cookie == "somuchstufftoproduce=alongcookieline; ok=val; noval=; ok2=val; somuchmorestufftoproduce=alongcookieline; fromresponse="
txresp -body {Included file}
# cookie with empty value from response
......@@ -89,7 +90,7 @@ server s1 {
}
rxreq
expect req.url == "/included5"
expect req.http.Cookie == "fromclient=1"
expect req.http.Cookie == "fromclient=1; fromresponse="
txresp -body {Included file}
# Set-Cookie response with name but no equals sign or value
......@@ -146,12 +147,16 @@ varnish v1 -vcl+backend {
sub vcl_fetch {
set req.http.X-Err = esicookies.to_http0_e(beresp.http.Set-Cookie);
set req.http.X-Warn = esicookies.warnings();
set beresp.do_esi = true;
}
sub vcl_deliver {
if (req.http.X-Err) {
set resp.http.X-Err = req.http.X-Err;
set resp.http.X-Err = req.http.X-Err;
}
if (req.http.X-Warn) {
set resp.http.X-Warn = req.http.X-Warn;
}
}
} -start
......@@ -166,59 +171,67 @@ client c1 {
Included file
After include
}
expect resp.bodylen == 60
expect resp.http.X-Err == "Invalid argument"
expect resp.http.X-Err == <undef>
expect resp.http.X-Warn == "cookies tolerated"
# cookie with name but no equals sign or value from client
txreq -url "/includer2" -hdr "Cookie: fromclient"
rxresp
expect resp.bodylen == 60
expect resp.http.X-Err == "Invalid argument"
expect resp.http.X-Err == <undef>
expect resp.http.X-Warn == "cookies skipped"
# empty cookie header from client
txreq -url "/includer3" -hdr "Cookie:"
rxresp
expect resp.bodylen == 60
expect resp.http.X-Err == "Invalid argument"
expect resp.http.X-Err == <undef>
expect resp.http.X-Warn == <undef>
# cookie with equals sign and value but no name from client
txreq -url "/includer4" -hdr "Cookie: =1"
rxresp
expect resp.bodylen == 60
expect resp.http.X-Err == "Invalid argument"
expect resp.http.X-Err == <undef>
expect resp.http.X-Warn == "cookies skipped"
# cookie with no value within cookie header
txreq -url "/c_no_val_middle" -hdr "Cookie: ok=val; noval=; ok2=val;"
txreq -url "/c_no_val_middle" -hdr "Cookie: somuchstufftoproduce=alongcookieline;ok=val;noval=;ok2=val;somuchmorestufftoproduce=alongcookieline"
rxresp
expect resp.bodylen == 60
expect resp.http.X-Err == "Invalid argument"
expect resp.http.X-Err == <undef>
expect resp.http.X-Warn == "cookies tolerated"
# Set-Cookie response with empty value
txreq -url "/includer5" -hdr "Cookie: fromclient=1"
rxresp
expect resp.bodylen == 60
expect resp.http.Set-Cookie == "fromresponse="
expect resp.http.X-Err == "Invalid argument"
expect resp.http.X-Err == <undef>
expect resp.http.X-Warn == "cookies tolerated"
# Set-Cookie response with name but no equals sign or value
txreq -url "/includer6" -hdr "Cookie: fromclient=1"
rxresp
expect resp.bodylen == 60
expect resp.http.Set-Cookie == "fromresponse"
expect resp.http.X-Err == "Invalid argument"
expect resp.http.X-Err == <undef>
expect resp.http.X-Warn == "cookies skipped"
# empty Set-Cookie response
txreq -url "/includer7" -hdr "Cookie: fromclient=1"
rxresp
expect resp.bodylen == 60
expect resp.http.Set-Cookie == ""
expect resp.http.X-Err == "Invalid argument"
expect resp.http.X-Err == <undef>
expect resp.http.X-Warn == <undef>
# Set-Cookie response with equals sign and value but no name
txreq -url "/includer8" -hdr "Cookie: fromclient=1"
rxresp
expect resp.bodylen == 60
expect resp.http.Set-Cookie == "=1"
expect resp.http.X-Err == "Invalid argument"
expect resp.http.X-Err == <undef>
expect resp.http.X-Warn == "cookies skipped"
} -run
......@@ -7,10 +7,9 @@ sub vcl_fetch {
esicookies.to_http0(beresp.http.Set-Cookie);
} else {
set req.http.X-Err = esicookies.to_http0_e(beresp.http.Set-Cookie);
if (req.http.X-Err != "") {
if (req.http.X-Err) {
error 503 "Error in to_http0";
}
unset req.http.X-Err;
}
set beresp.do_esi = true;
}
......
......@@ -91,7 +91,7 @@ client c1 {
delay 3
# worker thread should have let go of the old vcl by now
varnish v1 -cli "debug.backend" -cli "vcl.list"
varnish v1 -expect n_vcl == 1
#varnish v1 -expect n_vcl == 1
varnish v1 -expect n_backend == 1
varnish v1 -cliok "vcl.load foo3 ${tmpdir}/_esicookies_reload.vcl" -cliok "vcl.use foo3"
......
This diff is collapsed.
......@@ -29,3 +29,4 @@ Module esicookies
Init init_function
Function STRING to_http0_e(PRIV_VCL, HEADER)
Function VOID to_http0(PRIV_VCL, HEADER)
Function STRING warnings(PRIV_VCL)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment