├── m4 └── PLACEHOLDER ├── src ├── vmod_header.vcc ├── tests │ ├── import.vtc │ ├── version.vtc │ ├── append.vtc │ ├── get.vtc │ ├── keep-others.vtc │ ├── copy.vtc │ ├── some-data.vtc │ └── remove.vtc ├── Makefile.am └── vmod_header.c ├── redhat ├── make-tarball └── vmod-header.spec ├── Makefile.am ├── .gitignore ├── autogen.sh ├── LICENSE ├── configure.ac └── README.rst /m4/PLACEHOLDER: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/vmod_header.vcc: -------------------------------------------------------------------------------- 1 | Module header 2 | Init init_function 3 | Function VOID append(HEADER,STRING_LIST) 4 | Function VOID remove(PRIV_CALL,HEADER,STRING) 5 | Function STRING get(PRIV_CALL,HEADER,STRING) 6 | Function VOID copy(HEADER,HEADER) 7 | Function STRING version() 8 | -------------------------------------------------------------------------------- /redhat/make-tarball: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -e 3 | BASEDIR=$(git rev-parse --show-toplevel) 4 | REL=$(basename $BASEDIR) 5 | 6 | #BRANCH=$(git branch | grep "*" | sed "s/* //") 7 | BRANCH=$(git rev-parse HEAD) 8 | 9 | RPMDIR=$(rpm --eval "%{_topdir}") 10 | TOFILE="$RPMDIR/SOURCES/${REL}.tar.gz" 11 | 12 | (cd $BASEDIR && git archive --prefix ${REL}/ --format tar ${BRANCH} | gzip > $TOFILE) 13 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | 3 | SUBDIRS = src 4 | 5 | dist_man_MANS = vmod_header.3 6 | MAINTAINERCLEANFILES = $(dist_man_MANS) 7 | EXTRA_DIST = README.rst 8 | 9 | vmod_header.3: README.rst 10 | if HAVE_RST2MAN 11 | ${RST2MAN} README.rst $@ 12 | else 13 | @echo "========================================" 14 | @echo "You need rst2man installed to make dist" 15 | @echo "========================================" 16 | @false 17 | endif 18 | -------------------------------------------------------------------------------- /src/tests/import.vtc: -------------------------------------------------------------------------------- 1 | varnishtest "Header-vmod: Test basic import" 2 | 3 | server s1 { 4 | timeout 10 5 | rxreq 6 | expect req.url == "/" 7 | txresp -status 200 -hdr "foo: 1" 8 | } -start 9 | 10 | varnish v1 -vcl+backend { 11 | import header from "${vmod_topbuild}/src/.libs/libvmod_header.so"; 12 | } -start 13 | 14 | client c1 { 15 | txreq -url "/" 16 | rxresp 17 | expect resp.status == 200 18 | expect resp.http.foo == 1 19 | } -run 20 | 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | aclocal.m4 4 | autom4te.cache/ 5 | config.guess 6 | config.h 7 | config.h.in 8 | config.log 9 | config.status 10 | config.sub 11 | configure 12 | depcomp 13 | install-sh 14 | libtool 15 | ltmain.sh 16 | m4/libtool.m4 17 | m4/ltoptions.m4 18 | m4/ltsugar.m4 19 | m4/ltversion.m4 20 | m4/lt~obsolete.m4 21 | missing 22 | src/.deps/ 23 | src/.libs/ 24 | src/Makefile 25 | src/Makefile.in 26 | src/libvmod_header.la 27 | src/vcc_if.c 28 | src/vcc_if.h 29 | src/vcc_if.lo 30 | src/vcc_if.o 31 | src/vmod_header.lo 32 | src/vmod_header.o 33 | stamp-h1 34 | vmod_header.3 35 | -------------------------------------------------------------------------------- /src/tests/version.vtc: -------------------------------------------------------------------------------- 1 | # Somewhat oversimplified, but at least it ensures that SOMETHING works 2 | # (The alternative would be to automakeify this test or manually update it 3 | # every time there's a new version, which defeats the purpose somewhat). 4 | 5 | varnishtest "Header-vmod: Test version string" 6 | 7 | server s1 { 8 | rxreq 9 | expect req.url == "/" 10 | txresp -status 200 11 | } -start 12 | 13 | varnish v1 -vcl+backend { 14 | import header from "${vmod_topbuild}/src/.libs/libvmod_header.so"; 15 | 16 | sub vcl_fetch { 17 | set beresp.http.x-version = header.version(); 18 | if (!beresp.http.x-version) { 19 | set beresp.status = 500; 20 | } 21 | return(deliver); 22 | } 23 | } -start 24 | 25 | client c1 { 26 | txreq -url "/" 27 | rxresp 28 | expect resp.status == 200 29 | } -run 30 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | INCLUDES = -I$(VARNISHSRC)/include -I$(VARNISHSRC) 2 | 3 | vmoddir = $(VMODDIR) 4 | vmod_LTLIBRARIES = libvmod_header.la 5 | 6 | libvmod_header_la_LDFLAGS = -module -export-dynamic -avoid-version 7 | 8 | libvmod_header_la_SOURCES = \ 9 | vcc_if.c \ 10 | vcc_if.h \ 11 | vmod_header.c 12 | 13 | vcc_if.c vcc_if.h: $(VARNISHSRC)/lib/libvmod_std/vmod.py $(top_srcdir)/src/vmod_header.vcc 14 | @PYTHON@ $(VARNISHSRC)/lib/libvmod_std/vmod.py $(top_srcdir)/src/vmod_header.vcc 15 | 16 | VMOD_TESTS = tests/*.vtc 17 | .PHONY: $(VMOD_TESTS) 18 | 19 | tests/*.vtc: 20 | $(VARNISHSRC)/bin/varnishtest/varnishtest -Dvarnishd=$(VARNISHSRC)/bin/varnishd/varnishd -Dvmod_topbuild=$(abs_top_builddir) $@ 21 | 22 | check: $(VMOD_TESTS) 23 | 24 | EXTRA_DIST = \ 25 | vmod_header.vcc \ 26 | $(VMOD_TESTS) 27 | 28 | CLEANFILES = $(builddir)/vcc_if.c $(builddir)/vcc_if.h 29 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | warn() { 4 | echo "WARNING: $@" 1>&2 5 | } 6 | 7 | case `uname -s` in 8 | Darwin) 9 | LIBTOOLIZE=glibtoolize 10 | ;; 11 | FreeBSD) 12 | LIBTOOLIZE=libtoolize 13 | ;; 14 | Linux) 15 | LIBTOOLIZE=libtoolize 16 | ;; 17 | SunOS) 18 | LIBTOOLIZE=libtoolize 19 | ;; 20 | *) 21 | warn "unrecognized platform:" `uname -s` 22 | LIBTOOLIZE=libtoolize 23 | esac 24 | 25 | automake_version=`automake --version | tr ' ' '\n' | egrep '^[0-9]\.[0-9a-z.-]+'` 26 | if [ -z "$automake_version" ] ; then 27 | warn "unable to determine automake version" 28 | else 29 | case $automake_version in 30 | 0.*|1.[0-8]|1.[0-8][.-]*) 31 | warn "automake ($automake_version) detected; 1.9 or newer recommended" 32 | ;; 33 | *) 34 | ;; 35 | esac 36 | fi 37 | 38 | set -ex 39 | 40 | $LIBTOOLIZE --copy --force 41 | aclocal -I m4 42 | autoheader 43 | automake --add-missing --copy --foreign 44 | autoconf 45 | -------------------------------------------------------------------------------- /src/tests/append.vtc: -------------------------------------------------------------------------------- 1 | varnishtest "Header-vmod: Test appending" 2 | 3 | server s1 { 4 | timeout 10 5 | 6 | rxreq 7 | expect req.url == "/foo" 8 | txresp -status 200 -hdr "foo: 1" -hdr "foo: 2" 9 | rxreq 10 | expect req.url == "/bar" 11 | txresp -status 200 -hdr "foo: 1" -hdr "foo: 2" 12 | } -start 13 | 14 | varnish v1 -vcl+backend { 15 | import header from "${vmod_topbuild}/src/.libs/libvmod_header.so"; 16 | 17 | sub vcl_fetch { 18 | if (req.url == "/foo") { 19 | set beresp.http.foo = "null"; 20 | } elsif ( req.url == "/bar") { 21 | header.append(beresp.http.foo, "blatti"); 22 | } 23 | return(deliver); 24 | } 25 | } -start 26 | 27 | client c1 { 28 | txreq -url "/foo" 29 | rxresp 30 | expect resp.status == 200 31 | expect resp.http.foo == "null" 32 | } -run 33 | 34 | client c2 { 35 | txreq -url "/bar" 36 | rxresp 37 | expect resp.status == 200 38 | expect resp.http.foo == 1 39 | } -run 40 | 41 | -------------------------------------------------------------------------------- /src/tests/get.vtc: -------------------------------------------------------------------------------- 1 | 2 | varnishtest "Header-vmod: Test fetching" 3 | 4 | server s1 { 5 | timeout 10 6 | 7 | rxreq 8 | expect req.url == "/" 9 | txresp -status 200 -hdr "foo: sillycookie=blah" -hdr "foo: realcookie=YAI" 10 | rxreq 11 | expect req.url == "/two" 12 | txresp -status 200 -hdr "foo: sillycookie=blah" -hdr "foo: realcookie=YAI" 13 | } -start 14 | 15 | varnish v1 -vcl+backend { 16 | import header from "${vmod_topbuild}/src/.libs/libvmod_header.so"; 17 | 18 | sub vcl_fetch { 19 | if (req.url == "/") { 20 | set beresp.http.xusr = header.get(beresp.http.foo,"realcookie="); 21 | } elsif (req.url == "/two") { 22 | set beresp.http.xusr = header.get(beresp.http.foo,"^realcookie="); 23 | } 24 | return(deliver); 25 | } 26 | } -start 27 | 28 | client c1 { 29 | txreq -url "/" 30 | rxresp 31 | expect resp.status == 200 32 | expect resp.http.xusr == "realcookie=YAI" 33 | 34 | txreq -url "/two" 35 | rxresp 36 | expect resp.status == 200 37 | expect resp.http.xusr == "realcookie=YAI" 38 | } -run 39 | 40 | 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Varnish Software AS 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /src/tests/keep-others.vtc: -------------------------------------------------------------------------------- 1 | # Bug #1 ! 2 | # The remove-function was removing somewhat vigorously. 3 | # This check ensures that other headers are kept intact. 4 | 5 | varnishtest "Header-vmod: Ensure other headers remain untouched" 6 | 7 | server s1 { 8 | timeout 10 9 | rxreq 10 | expect req.url == "/foo" 11 | txresp -status 200 -hdr "bar: xxx=y" -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" 12 | rxreq 13 | expect req.url == "/foo" 14 | txresp -status 200 -hdr "Content-Type: text/html" -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" 15 | rxreq 16 | expect req.url == "/foo" 17 | txresp -status 200 -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" -hdr "Content-Type: text/html" 18 | } -start 19 | 20 | varnish v1 -vcl+backend { 21 | import header from "${vmod_topbuild}/src/.libs/libvmod_header.so"; 22 | 23 | sub vcl_fetch { 24 | if (req.url == "/foo") { 25 | header.remove(beresp.http.foo,"one=1"); 26 | } 27 | return(hit_for_pass); 28 | } 29 | } -start 30 | 31 | client c1 { 32 | txreq -url "/foo" 33 | rxresp 34 | expect resp.status == 200 35 | expect resp.http.foo == "two=2" 36 | expect resp.http.bar == "xxx=y" 37 | txreq -url "/foo" 38 | rxresp 39 | expect resp.status == 200 40 | expect resp.http.foo == "two=2" 41 | expect resp.http.Content-Type == "text/html" 42 | txreq -url "/foo" 43 | rxresp 44 | expect resp.status == 200 45 | expect resp.http.foo == "two=2" 46 | expect resp.http.Content-Type == "text/html" 47 | } -run 48 | -------------------------------------------------------------------------------- /src/tests/copy.vtc: -------------------------------------------------------------------------------- 1 | 2 | # This assumes that: 3 | # 1. Append works 4 | # 2. The first header is the only tested in varnishtest when multiple 5 | # copies are present 6 | 7 | varnishtest "Header-vmod: Test copying" 8 | 9 | server s1 { 10 | timeout 10 11 | rxreq 12 | expect req.url == "/foo" 13 | txresp -status 200 -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" 14 | } -start 15 | 16 | varnish v1 -vcl+backend { 17 | import header from "${vmod_topbuild}/src/.libs/libvmod_header.so"; 18 | 19 | sub vcl_fetch { 20 | if (req.url == "/foo") { 21 | header.copy(beresp.http.foo,beresp.http.bar); 22 | set beresp.http.x-one = header.get(beresp.http.bar,"one"); 23 | set beresp.http.x-two = header.get(beresp.http.bar,"two"); 24 | set beresp.http.x-three = header.get(beresp.http.bar,"three"); 25 | set beresp.http.y-one = header.get(beresp.http.foo,"one"); 26 | set beresp.http.y-two = header.get(beresp.http.foo,"two"); 27 | set beresp.http.y-three = header.get(beresp.http.foo,"three"); 28 | } 29 | return(deliver); 30 | } 31 | } -start 32 | 33 | client c1 { 34 | txreq -url "/foo" 35 | rxresp 36 | expect resp.status == 200 37 | expect resp.http.bar == "one=1" 38 | expect resp.http.foo == "one=1" 39 | expect resp.http.x-one == "one=1" 40 | expect resp.http.x-two == "two=2" 41 | expect resp.http.x-three == "three=3" 42 | expect resp.http.y-one == "one=1" 43 | expect resp.http.y-two == "two=2" 44 | expect resp.http.y-three == "three=3" 45 | } -run 46 | -------------------------------------------------------------------------------- /redhat/vmod-header.spec: -------------------------------------------------------------------------------- 1 | # %define VARNISHVER 3.0.3 2 | Summary: Header VMOD for Varnish VCL 3 | Name: vmod-varnish-%{VARNISHVER}-header 4 | Version: 0.1 5 | Release: 1%{?dist} 6 | License: BSD 7 | Group: System Environment/Daemons 8 | Source0: ./libvmod-header.tar.gz 9 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 10 | Requires: varnish > 3.0 11 | BuildRequires: make, autoconf, automake, libtool, python-docutils 12 | 13 | %description 14 | libvmod-header 15 | 16 | %prep 17 | %setup -n libvmod-header 18 | 19 | %build 20 | ./autogen.sh 21 | # this assumes that VARNISHSRC is defined on the rpmbuild command line, like this: 22 | # rpmbuild -bb --define 'VARNISHSRC /home/user/rpmbuild/BUILD/varnish-3.0.3' redhat/*spec 23 | ./configure VARNISHSRC=%{VARNISHSRC} VMODDIR=/usr/lib64/varnish/vmods/ --prefix=/usr/ 24 | make 25 | 26 | %install 27 | make install DESTDIR=%{buildroot} 28 | mkdir -p %{buildroot}/usr/share/doc/%{name}/ 29 | cp README.rst %{buildroot}/usr/share/doc/%{name}/ 30 | cp LICENSE %{buildroot}/usr/share/doc/%{name}/ 31 | 32 | %clean 33 | rm -rf %{buildroot} 34 | 35 | %files 36 | %defattr(-,root,root,-) 37 | # /opt/varnish/lib/varnish/vmods/ 38 | /usr/lib64/varnish/vmods/ 39 | %doc /usr/share/doc/%{name}/* 40 | 41 | 42 | %if "%{RHVERSION}" == "EL5" 43 | /usr/man/man?/* 44 | %else 45 | /usr/share/man/man?/* 46 | %endif 47 | 48 | %changelog 49 | * Wed Oct 03 2012 Lasse Karstensen - 0.1-0.20120918 50 | - Initial version. 51 | -------------------------------------------------------------------------------- /src/tests/some-data.vtc: -------------------------------------------------------------------------------- 1 | # Got reports of no data being sent in return after 3.0.1, this test-case 2 | # tries to do some simple verification, though it fails to detect the 3 | # problem. 4 | 5 | 6 | varnishtest "Header-vmod: Send some data too" 7 | 8 | server s1 { 9 | timeout 10 10 | rxreq 11 | expect req.url == "/foo" 12 | txresp -status 200 -hdr "bar: xxx=y" -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" -body "Hello" 13 | rxreq 14 | expect req.url == "/foo" 15 | txresp -status 200 -hdr "Content-Type: text/html" -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" -body "Hello" 16 | rxreq 17 | expect req.url == "/foo" 18 | txresp -status 200 -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" -hdr "Content-Type: text/html" -body "Hello" 19 | } -start 20 | 21 | varnish v1 -vcl+backend { 22 | import header from "${vmod_topbuild}/src/.libs/libvmod_header.so"; 23 | 24 | sub vcl_fetch { 25 | if (req.url == "/foo") { 26 | header.remove(beresp.http.foo,"one=1"); 27 | } 28 | return(hit_for_pass); 29 | } 30 | } -start 31 | 32 | client c1 { 33 | txreq -url "/foo" 34 | rxresp 35 | expect resp.status == 200 36 | expect resp.http.foo == "two=2" 37 | expect resp.http.bar == "xxx=y" 38 | expect resp.bodylen == 5 39 | txreq -url "/foo" 40 | rxresp 41 | expect resp.status == 200 42 | expect resp.http.foo == "two=2" 43 | expect resp.http.Content-Type == "text/html" 44 | expect resp.bodylen == 5 45 | txreq -url "/foo" 46 | rxresp 47 | expect resp.status == 200 48 | expect resp.http.foo == "two=2" 49 | expect resp.http.Content-Type == "text/html" 50 | expect resp.bodylen == 5 51 | } -run 52 | -------------------------------------------------------------------------------- /src/tests/remove.vtc: -------------------------------------------------------------------------------- 1 | 2 | varnishtest "Header-vmod: Test removing" 3 | 4 | server s1 { 5 | timeout 10 6 | rxreq 7 | expect req.url == "/foo" 8 | txresp -status 200 -hdr "foo: notok=1" -hdr "foo: ok2k" -hdr "foo: notok=2" 9 | rxreq 10 | expect req.url == "/bar" 11 | txresp -status 200 -hdr "foo: notok=1" -hdr "foo: ok2k" -hdr "foo: notok=2" 12 | rxreq 13 | expect req.url == "/nothing" 14 | txresp -status 200 -hdr "foo: notok=1" -hdr "foo: ok2k" -hdr "foo: notok=2" 15 | rxreq 16 | expect req.url == "/blatti1" 17 | txresp -status 200 -hdr "foo: notok=1" -hdr "foo:notok=3" -hdr "foo: ok2k" -hdr "foo: notok=2" 18 | rxreq 19 | expect req.url == "/blatti2" 20 | txresp -status 200 -hdr "foo: notok=1" -hdr "foo: ok2k" -hdr "foo:notok=3" -hdr "foo: notok=2" 21 | rxreq 22 | expect req.url == "/blatti3" 23 | txresp -status 200 -hdr "set-cookie: analytics=1" -hdr "set-cookie: funcookie=ok2k" -hdr "set-cookie: uglycookie=3" -hdr "set-cookie: notok=2" 24 | 25 | } -start 26 | 27 | varnish v1 -vcl+backend { 28 | import header from "${vmod_topbuild}/src/.libs/libvmod_header.so"; 29 | 30 | sub vcl_fetch { 31 | if (req.url == "/foo") { 32 | header.remove(beresp.http.foo,"notok"); 33 | } 34 | if (req.url == "/nothing") { 35 | header.remove(beresp.http.foo,"."); 36 | } 37 | if (req.url == "/blatti1") { 38 | header.remove(beresp.http.foo,"^ no.ok=.$"); 39 | } 40 | if (req.url == "/blatti2") { 41 | header.remove(beresp.http.foo,"^no.ok=.$"); 42 | } 43 | if (req.url == "/blatti3") { 44 | header.remove(beresp.http.set-cookie,"^(?!(funcookie=))"); 45 | } 46 | if (beresp.http.foo) { 47 | set beresp.http.foo-exists = "yes"; 48 | } else { 49 | set beresp.http.foo-exists = "no"; 50 | } 51 | 52 | return(deliver); 53 | } 54 | } -start 55 | 56 | client c1 { 57 | # Remove one, "notok". Assumes ok2k (second in line) is tested next 58 | txreq -url "/foo" 59 | rxresp 60 | expect resp.status == 200 61 | expect resp.http.foo == "ok2k" 62 | 63 | # Remove nothing 64 | txreq -url "/bar" 65 | rxresp 66 | expect resp.status == 200 67 | expect resp.http.foo == "notok=1" 68 | 69 | # Remove everything (confusing names, huh?) 70 | txreq -url "/nothing" 71 | rxresp 72 | expect resp.status == 200 73 | expect resp.http.foo-exists == "no" 74 | 75 | # Remove with regex - fail due to whitespace in the regex 76 | txreq -url "/blatti1" 77 | rxresp 78 | expect resp.status == 200 79 | expect resp.http.foo-exists == "yes" 80 | expect resp.http.foo == "notok=1" 81 | 82 | # Remove with regex - work (remove leading whitespace) 83 | txreq -url "/blatti2" 84 | rxresp 85 | expect resp.status == 200 86 | expect resp.http.foo-exists == "yes" 87 | expect resp.http.foo == "ok2k" 88 | 89 | # Remove everything except fun-cookie 90 | txreq -url "/blatti3" 91 | rxresp 92 | expect resp.status == 200 93 | expect resp.http.foo-exists == "no" 94 | expect resp.http.set-cookie == "funcookie=ok2k" 95 | } -run 96 | 97 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ(2.59) 2 | AC_COPYRIGHT([Copyright (c) 2011 Varnish Software AS]) 3 | AC_INIT([libvmod-header], [0.3]) 4 | AC_CONFIG_MACRO_DIR([m4]) 5 | AC_CONFIG_SRCDIR(src/vmod_header.vcc) 6 | AM_CONFIG_HEADER(config.h) 7 | 8 | AC_CANONICAL_SYSTEM 9 | AC_LANG(C) 10 | 11 | AM_INIT_AUTOMAKE([foreign]) 12 | 13 | AC_GNU_SOURCE 14 | AC_PROG_CC 15 | AC_PROG_CC_STDC 16 | if test "x$ac_cv_prog_cc_c99" = xno; then 17 | AC_MSG_ERROR([Could not find a C99 compatible compiler]) 18 | fi 19 | AC_PROG_CPP 20 | 21 | AC_PROG_INSTALL 22 | AC_PROG_LIBTOOL 23 | AC_PROG_MAKE_SET 24 | 25 | # Check for rst utilities 26 | AC_CHECK_PROGS(RST2MAN, [rst2man rst2man.py], "no") 27 | if test "x$RST2MAN" = "xno"; then 28 | AC_MSG_WARN([rst2man not found - not building man pages]) 29 | fi 30 | AM_CONDITIONAL(HAVE_RST2MAN, [test "x$RST2MAN" != "xno"]) 31 | 32 | # Check for pkg-config 33 | PKG_PROG_PKG_CONFIG 34 | 35 | # Checks for header files. 36 | AC_HEADER_STDC 37 | AC_CHECK_HEADERS([sys/stdlib.h]) 38 | 39 | # Check for python 40 | AC_CHECK_PROGS(PYTHON, [python3 python3.1 python3.2 python2.7 python2.6 python2.5 python2 python], [AC_MSG_ERROR([Python is needed to build this vmod, please install python.])]) 41 | 42 | # Varnish source tree 43 | AC_ARG_VAR([VARNISHSRC], [path to Varnish source tree (mandatory)]) 44 | if test "x$VARNISHSRC" = x; then 45 | AC_MSG_ERROR([No Varnish source tree specified]) 46 | fi 47 | VARNISHSRC=`cd $VARNISHSRC && pwd` 48 | AC_CHECK_FILE([$VARNISHSRC/include/varnishapi.h], 49 | [], 50 | [AC_MSG_FAILURE(["$VARNISHSRC" is not a Varnish source directory])] 51 | ) 52 | 53 | # Check that varnishtest is built in the varnish source directory 54 | AC_CHECK_FILE([$VARNISHSRC/bin/varnishtest/varnishtest], 55 | [], 56 | [AC_MSG_FAILURE([Can't find "$VARNISHSRC/bin/varnishtest/varnishtest". Please build your varnish source directory])] 57 | ) 58 | 59 | # vmod installation dir 60 | AC_ARG_VAR([VMODDIR], [vmod installation directory @<:@LIBDIR/varnish/vmods@:>@]) 61 | if test "x$VMODDIR" = x; then 62 | VMODDIR=`pkg-config --variable=vmoddir varnishapi` 63 | if test "x$VMODDIR" = x; then 64 | AC_MSG_FAILURE([Can't determine vmod installation directory]) 65 | fi 66 | fi 67 | 68 | 69 | # This corresponds to FreeBSD's WARNS level 6 70 | DEVELOPER_CFLAGS="-Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wshadow -Wcast-align -Wchar-subscripts -Winline -Wnested-externs -Wredundant-decls -Wformat" 71 | 72 | # Additional flags for GCC 4 73 | EXTRA_DEVELOPER_CFLAGS="-Wextra -Wno-missing-field-initializers -Wno-sign-compare" 74 | 75 | # --enable-developer-warnings 76 | AC_ARG_ENABLE(developer-warnings, 77 | AS_HELP_STRING([--enable-developer-warnings],[enable strict warnings (default is NO)]), 78 | CFLAGS="${CFLAGS} ${DEVELOPER_CFLAGS}") 79 | 80 | # --enable-debugging-symbols 81 | AC_ARG_ENABLE(debugging-symbols, 82 | AS_HELP_STRING([--enable-debugging-symbols],[enable debugging symbols (default is NO)]), 83 | CFLAGS="${CFLAGS} -O0 -g -fno-inline") 84 | 85 | # --enable-diagnostics 86 | AC_ARG_ENABLE(diagnostics, 87 | AS_HELP_STRING([--enable-diagnostics],[enable run-time diagnostics (default is NO)]), 88 | CFLAGS="${CFLAGS} -DDIAGNOSTICS") 89 | 90 | # --enable-extra-developer-warnings 91 | AC_ARG_ENABLE(extra-developer-warnings, 92 | AS_HELP_STRING([--enable-extra-developer-warnings],[enable even stricter warnings (default is NO)]), 93 | [], 94 | [enable_extra_developer_warnings=no]) 95 | 96 | if test "x$enable_stack_protector" != "xno"; then 97 | save_CFLAGS="$CFLAGS" 98 | CFLAGS="${CFLAGS} ${EXTRA_DEVELOPER_CFLAGS}" 99 | AC_COMPILE_IFELSE( 100 | [AC_LANG_PROGRAM([],[],[])], 101 | [], 102 | [AC_MSG_WARN([All of ${EXTRA_DEVELOPER_CFLAGS} not supported, disabling]) 103 | CFLAGS="$save_CFLAGS"]) 104 | fi 105 | 106 | # --enable-stack-protector 107 | AC_ARG_ENABLE(stack-protector, 108 | AS_HELP_STRING([--enable-stack-protector],[enable stack protector (default is NO)]), 109 | [], 110 | [enable_stack_protector=no]) 111 | 112 | if test "x$enable_stack_protector" != "xno"; then 113 | save_CFLAGS="$CFLAGS" 114 | CFLAGS="${CFLAGS} -fstack-protector-all" 115 | AC_COMPILE_IFELSE( 116 | [AC_LANG_PROGRAM([],[],[])], 117 | [], 118 | [AC_MSG_WARN([-fstack-protector not supported, disabling]) 119 | CFLAGS="$save_CFLAGS"]) 120 | fi 121 | 122 | # --enable-tests 123 | AC_ARG_ENABLE(tests, 124 | AS_HELP_STRING([--enable-tests],[build test programs (default is NO)])) 125 | AM_CONDITIONAL([ENABLE_TESTS], [test x$enable_tests = xyes]) 126 | 127 | # --enable-werror 128 | AC_ARG_ENABLE(werror, 129 | AS_HELP_STRING([--enable-werror],[use -Werror (default is NO)]), 130 | CFLAGS="${CFLAGS} -Werror") 131 | 132 | 133 | AC_CONFIG_FILES([ 134 | Makefile 135 | src/Makefile 136 | ]) 137 | AC_OUTPUT 138 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | vmod_header 3 | =========== 4 | 5 | --------------------- 6 | Varnish Header Module 7 | --------------------- 8 | 9 | :Manual section: 3 10 | :Author: Kristian Lyngstøl 11 | :Date: 2011-08-12 12 | :Version: 0.3 13 | 14 | SYNOPSIS 15 | ======== 16 | 17 | :: 18 | 19 | import header; 20 | 21 | header.append(
, ) 22 | header.get(
, ) 23 | header.remove(
, ) 24 | header.copy(, ) 25 | header.version() 26 | 27 | DESCRIPTION 28 | =========== 29 | 30 | Varnish Module (vmod) for manipulation of duplicated headers (for instance 31 | multiple set-cookie headers). 32 | 33 | FUNCTIONS 34 | ========= 35 | 36 | Example VCL:: 37 | 38 | backend foo { ... }; 39 | 40 | import header; 41 | 42 | sub vcl_fetch { 43 | header.append(beresp.http.Set-Cookie,"foo=bar"); 44 | header.remove(beresp.http.Set-Cookie,"dontneedthiscookie"); 45 | } 46 | 47 | 48 | append 49 | ------ 50 | 51 | Prototype 52 | header.append(
, ) 53 | Returns 54 | void 55 | Description 56 | Append lets you add an extra occurrence of an existing header. 57 | Example 58 | ``header.append(beresp.http.Set-Cookie,"foo=bar")`` 59 | 60 | get 61 | --- 62 | 63 | Prototype 64 | header.get(
, ) 65 | Returns 66 | String 67 | Description 68 | Get fetches the value of the first `header` that matches the given 69 | regular expression. 70 | Example 71 | ``set beresp.http.xusr = header.get(beresp.http.set-cookie,"user=");`` 72 | 73 | remove 74 | ------ 75 | 76 | Prototype 77 | header.remove(
, ) 78 | Returns 79 | void 80 | Description 81 | remove() removes all occurences of `header` that matches the given 82 | regular expression. The example is a white-list of "funcookie=". 83 | Example 84 | ``header.remove(beresp.http.set-cookie,"^(?!(funcookie=))");`` 85 | 86 | copy 87 | ---- 88 | 89 | Prototype 90 | header.copy(, ) 91 | Returns 92 | void 93 | Description 94 | Copies all of the source headers to a new header. 95 | Example 96 | ``header.copy(beresp.http.set-cookie, beresp.http.x-old-cookie);`` 97 | 98 | version 99 | ------- 100 | 101 | Prototype 102 | header.version() 103 | Returns 104 | string 105 | Description 106 | Returns the string constant version-number of the header vmod. 107 | Example 108 | ``set resp.http.X-header-version = header.version();`` 109 | 110 | 111 | INSTALLATION 112 | ============ 113 | 114 | Installation requires the Varnish source tree (only the source matching the 115 | binary installation). 116 | 117 | 1. `./autogen.sh` (for git-installation) 118 | 2. `./configure VARNISHSRC=/path/to/your/varnish/source/varnish-cache` 119 | 3. `make` 120 | 4. `make install` (may require root: sudo make install) 121 | 5. `make check` (Optional for regression tests) 122 | 123 | VARNISHSRC is the directory of the Varnish source tree for which to 124 | compile your vmod. Both the VARNISHSRC and VARNISHSRC/include 125 | will be added to the include search paths for your module. 126 | 127 | Optionally you can also set the vmod install dir by adding VMODDIR=DIR 128 | (defaults to the pkg-config discovered directory from your Varnish 129 | installation). 130 | 131 | 132 | ACKNOWLEDGEMENTS 133 | ================ 134 | 135 | The development of this plugin was made possible by the sponsorship of 136 | Softonic, http://en.softonic.com/ . 137 | 138 | Author: Kristian Lyngstøl , Varnish Software AS 139 | Skeleton by Martin Blix Grydeland , vmods are 140 | part of Varnish Cache 3.0 and beyond. 141 | 142 | Also thanks to: Imo Klabun and Anders Nordby for bug reports. 143 | 144 | HISTORY 145 | ======= 146 | 147 | Version 0.1: Initial version, somewhat ambiguous where it starts and ends. 148 | 149 | Version 0.2: Bug fixes for string removal 150 | 151 | Version 0.3: Bug fixes for get/remove, add version() 152 | 153 | BUGS 154 | ==== 155 | 156 | You can't use dynamic regular expressions, which also holds true for normal 157 | regular expressions in regsub(), but VCL isn't able to warn you about this 158 | when it comes to vmods yet. 159 | 160 | Some overlap with varnishd exists, this will be mended as Varnish 3.0 161 | evolves. 162 | 163 | SEE ALSO 164 | ======== 165 | 166 | * varnishd(1) 167 | * vcl(7) 168 | * https://github.com/varnish/libvmod-header 169 | 170 | COPYRIGHT 171 | ========= 172 | 173 | This document is licensed under the same license as the 174 | libvmod-header project. See LICENSE for details. 175 | 176 | * Copyright (c) 2011-2013 Varnish Software 177 | -------------------------------------------------------------------------------- /src/vmod_header.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2011-2013 Varnish Software AS 3 | * All rights reserved. 4 | * 5 | * Author: Kristian Lyngstol 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | * SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | 32 | #include "vrt.h" 33 | #include "bin/varnishd/cache.h" 34 | #include "include/vct.h" 35 | 36 | #include "vcc_if.h" 37 | #include "config.h" 38 | 39 | /* 40 | * This mutex is used to avoid having two threads that initializes the same 41 | * regex at the same time. While it means that there's a single, global 42 | * lock for all libvmod-header actions dealing with regular expressions, 43 | * the contention only applies on the first request that calls that 44 | * specific function. 45 | */ 46 | pthread_mutex_t header_mutex; 47 | 48 | /* 49 | * Initialize the regex *s on priv, if it hasn't already been done. 50 | * XXX: We have to recheck the condition after grabbing the lock to avoid a 51 | * XXX: race condition. 52 | */ 53 | static void 54 | header_init_re(struct vmod_priv *priv, const char *s) 55 | { 56 | if (priv->priv == NULL) { 57 | assert(pthread_mutex_lock(&header_mutex) == 0); 58 | if (priv->priv == NULL) { 59 | VRT_re_init(&priv->priv, s); 60 | priv->free = VRT_re_fini; 61 | } 62 | pthread_mutex_unlock(&header_mutex); 63 | } 64 | } 65 | 66 | /* 67 | * Returns the right struct http * to use for a given type of header. 68 | * 69 | * FIXME: Stolen bluntly from cache_vrt.c 70 | */ 71 | static struct http * 72 | header_vrt_selecthttp(const struct sess *sp, enum gethdr_e where) 73 | { 74 | struct http *hp=NULL; 75 | 76 | CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); 77 | switch (where) { 78 | case HDR_REQ: 79 | hp = sp->http; 80 | break; 81 | case HDR_BEREQ: 82 | hp = sp->wrk->bereq; 83 | break; 84 | case HDR_BERESP: 85 | hp = sp->wrk->beresp; 86 | break; 87 | case HDR_RESP: 88 | hp = sp->wrk->resp; 89 | break; 90 | case HDR_OBJ: 91 | CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC); 92 | hp = sp->obj->http; 93 | break; 94 | default: 95 | assert("ops"); 96 | } 97 | CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); 98 | return (hp); 99 | } 100 | 101 | /* 102 | * Returns true if the *hdr header is the one pointed to by *hh. 103 | * 104 | * FIXME: duplication from varnishd. 105 | */ 106 | static int 107 | header_http_IsHdr(const txt *hh, const char *hdr) 108 | { 109 | unsigned l; 110 | 111 | Tcheck(*hh); 112 | AN(hdr); 113 | l = hdr[0]; 114 | assert(l == strlen(hdr + 1)); 115 | assert(hdr[l] == ':'); 116 | hdr++; 117 | return (!strncasecmp(hdr, hh->b, l)); 118 | } 119 | 120 | /* 121 | * Return true if the hp->hd[u] header matches *hdr and the regex *re 122 | * matches the content. 123 | * 124 | * If re is NULL, content is not tested and as long as it's the right 125 | * header, a match is returned. 126 | */ 127 | static int 128 | header_http_match(const struct sess *sp, const struct http *hp, unsigned u, void *re, const char *hdr) 129 | { 130 | char *start; 131 | unsigned l; 132 | 133 | assert(hdr); 134 | assert(hp); 135 | 136 | Tcheck(hp->hd[u]); 137 | if (hp->hd[u].b == NULL) 138 | return 0; 139 | 140 | l = hdr[0]; 141 | 142 | if (!header_http_IsHdr(&hp->hd[u], hdr)) 143 | return 0; 144 | 145 | if (re == NULL) 146 | return 1; 147 | 148 | start = hp->hd[u].b + l; 149 | while (*start != '\0' && *start == ' ') 150 | start++; 151 | 152 | if (!*start) 153 | return 0; 154 | 155 | if (VRT_re_match(sp, start, re)) 156 | return 1; 157 | 158 | return 0; 159 | } 160 | 161 | /* 162 | * Returns the (first) header named as *hdr that also matches the regular 163 | * expression *re. 164 | */ 165 | static unsigned 166 | header_http_findhdr(const struct sess *sp, const struct http *hp, const char *hdr, void *re) 167 | { 168 | unsigned u; 169 | 170 | for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) { 171 | if (header_http_match(sp, hp, u, re, hdr)) 172 | return (u); 173 | } 174 | return (0); 175 | } 176 | 177 | /* 178 | * Removes all copies of the header that matches *hdr with content that 179 | * matches *re. Same as http_Unset(), plus regex. 180 | */ 181 | static void 182 | header_http_Unset(struct sess *sp, struct http *hp, const char *hdr, void *re) 183 | { 184 | unsigned u, v; 185 | 186 | for (v = u = HTTP_HDR_FIRST; u < hp->nhd; u++) { 187 | if (header_http_match(sp, hp, u, re, hdr)) 188 | continue; 189 | if (v != u) { 190 | memcpy(&hp->hd[v], &hp->hd[u], sizeof *hp->hd); 191 | memcpy(&hp->hdf[v], &hp->hdf[u], sizeof *hp->hdf); 192 | } 193 | v++; 194 | } 195 | hp->nhd = v; 196 | } 197 | 198 | /* 199 | * Copies all occurrences of *hdr to a destination header *dst_h. Uses 200 | * vmod_header_append(), so all copies are kept intact. 201 | * 202 | * XXX: Not sure I like the idea of iterating a list of headers while 203 | * XXX: adding to it. It may be correct now, but perhaps not so much in 204 | * XXX: the future. 205 | */ 206 | static void 207 | header_http_cphdr(struct sess *sp, 208 | const struct http *hp, 209 | const char *hdr, 210 | enum gethdr_e dst_e, 211 | const char *dst_h) 212 | { 213 | unsigned u; 214 | char *p; 215 | 216 | for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) { 217 | if (!header_http_match(sp, hp, u, NULL, hdr)) 218 | continue; 219 | 220 | p = hp->hd[u].b + hdr[0]; 221 | while (vct_issp(*p)) 222 | p++; 223 | vmod_append(sp, dst_e, dst_h, p, vrt_magic_string_end); 224 | } 225 | } 226 | 227 | /* 228 | * vmod entrypoint. Sets up the header mutex. 229 | */ 230 | int 231 | init_function(struct vmod_priv *priv __attribute__((unused)), 232 | const struct VCL_conf *conf __attribute__((unused))) 233 | { 234 | assert(pthread_mutex_init(&header_mutex, NULL) == 0); 235 | return (0); 236 | } 237 | 238 | void __match_proto__() 239 | vmod_append(struct sess *sp, enum gethdr_e e, const char *h, const char *fmt, ...) 240 | { 241 | va_list ap; 242 | struct http *hp; 243 | char *b; 244 | CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); 245 | assert(fmt != NULL); 246 | 247 | hp = header_vrt_selecthttp(sp, e); 248 | va_start(ap, fmt); 249 | b = VRT_String(hp->ws, h + 1, fmt, ap); 250 | if (b == NULL) 251 | WSP(sp, SLT_LostHeader, "vmod_header: %s", h+1); 252 | else 253 | http_SetHeader(sp->wrk, sp->fd, hp, b); 254 | va_end(ap); 255 | } 256 | 257 | const char * __match_proto__() 258 | vmod_get(struct sess *sp, struct vmod_priv *priv, enum gethdr_e e, const char *h, const char *s) 259 | { 260 | struct http *hp; 261 | unsigned u; 262 | char *p; 263 | 264 | header_init_re(priv, s); 265 | 266 | hp = header_vrt_selecthttp(sp, e); 267 | u = header_http_findhdr(sp, hp, h, priv->priv); 268 | if (u == 0) { 269 | return NULL; 270 | } 271 | p = hp->hd[u].b + h[0]; 272 | while (vct_issp(*p)) 273 | p++; 274 | return p; 275 | } 276 | 277 | void __match_proto__() 278 | vmod_copy(struct sess *sp, enum gethdr_e src_e, const char *src_h, enum gethdr_e dst_e, const char *dst_h) 279 | { 280 | struct http *src_hp; 281 | 282 | src_hp = header_vrt_selecthttp(sp, src_e); 283 | header_http_cphdr(sp, src_hp, src_h, dst_e, dst_h); 284 | } 285 | 286 | void __match_proto__() 287 | vmod_remove(struct sess *sp, struct vmod_priv *priv, enum gethdr_e e, const char *h, const char *s) 288 | { 289 | struct http *hp; 290 | 291 | header_init_re(priv, s); 292 | hp = header_vrt_selecthttp(sp, e); 293 | header_http_Unset(sp, hp, h, priv->priv); 294 | } 295 | 296 | const char * __match_proto__() 297 | vmod_version(struct sess *sp __attribute__((unused))) 298 | { 299 | return VERSION; 300 | } 301 | --------------------------------------------------------------------------------