Commit 7ab410e6 authored by Nils Goroll's avatar Nils Goroll

Support both Varnish 3 and Varnish 4. Allocate memory from workspace instead of stack for Varnish 4

* Pull in varnish version autoconf logic from libvmod-querystring
  Thanks to Dridi Boukelmoune <dridi.boukelmoune@gmail.com>

* Move allocation of memory for dcs_match to caller

* generate dcs_match_mem.h defining DCS_MATCH_MEM_SZ which is the memory size
  required by dcs_match

* move gen* perl and c source to the gen directory to avoid issues with
  make check and recursive subdirs

* first changes to make splint happier
parent bc7bd4dc
......@@ -55,9 +55,13 @@ src/dcs
src/dcs_test
src/gen/dcs_classifier.c
src/gen/dcs_classifier.h
src/gen/dcs_match_mem.h
src/gen/dcs_type.c
src/gen/dcs.db*
src/gen/fixup.*
src/gen/gen_dcs_match_mem
src/vcc_if.c
src/vcc_if.h
src/gen/dcs_classifier.checksum
src/*.rst
test-driver
......@@ -8,8 +8,8 @@ Varnish Device Classification Service Module
:Manual section: 3
:Authors: Nils Goroll
:Date: 2014-08-14
:Version: 0.3
:Date: 2014-08-31
:Version: 0.4
SYNOPSIS
========
......@@ -21,8 +21,8 @@ Command line
<prefix>/bin/dcs
Varnish VMOD
------------
Varnish VMOD (Varnish 3 and Varnish 4)
--------------------------------------
VCL:
......@@ -134,6 +134,7 @@ INTERFACES
The following use cases are supported
* Use as a Varnish 4 module
* Use as a Varnish 3 module
* Use with Varnish 2 as inline-C
* Command line tools
......@@ -181,6 +182,7 @@ To build the varnish module (VMOD)
* the source tree of the Varnish version the VMOD is to be used
with. Varnish must have been configured and compiled in this source
tree.
* python2 or higher
BUILDING
......@@ -517,6 +519,9 @@ HISTORY
* Version 0.3: Class assignments can now be defined in `src/classes.conf`
* Version 0.4: Also support Varnish 4. Take memory from Varnish workspace
instead of stack for Varnish 4.
BUGS
====
......
AC_PREREQ(2.69)
AC_COPYRIGHT([Copyright 2014 UPLEX - Nils Goroll Systemoptimierung])
AC_INIT([dcs_classifier], [0.3])
AC_INIT([dcs_classifier], [0.4])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR(src/gen_dcs_classifier.pl)
AC_CONFIG_SRCDIR(src/gen/gen_dcs_classifier.pl)
AM_CONFIG_HEADER(dcs_config.h)
AC_CANONICAL_SYSTEM
......@@ -118,25 +118,52 @@ else
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.])])
VARNISHSRC=`cd $VARNISHSRC && pwd`
AC_CHECK_FILE([$VARNISHSRC/include/varnishapi.h],
[],
[AC_MSG_FAILURE(["$VARNISHSRC" is not a Varnish source directory])]
)
Varnish_Version_MAJOR=
# varnish3
AC_CHECK_FILE([$VARNISHSRC/lib/libvmod_std/vmod.py], [
AC_SUBST([VMODTOOL], ['$(VARNISHSRC)/lib/libvmod_std/vmod.py'])
AC_SUBST([VCCFILE], [vmod_dcs3.vcc])
AC_DEFINE([VARNISH_MAJOR], [3], [Define the Varnish major version we compile against])
AC_SUBST([TESTDIR], [tests])
])
# varnish4
AC_CHECK_FILE([$VARNISHSRC/lib/libvcc/vmodtool.py], [
AC_SUBST([VMODTOOL], ['$(VARNISHSRC)/lib/libvcc/vmodtool.py'])
AC_SUBST([VCCFILE], [vmod_dcs4.vcc])
AC_DEFINE([VARNISH_MAJOR], [4], [Define the Varnish major version we compile against])
AC_SUBST([TESTDIR], [tests4])
])
# Check that varnishtest is built in the varnish source directory
AC_CHECK_FILE([$VARNISHSRC/bin/varnishtest/varnishtest],
[],
[AC_MSG_FAILURE([Can't find "$VARNISHSRC/bin/varnishtest/varnishtest". Please build your varnish source directory])]
[],
[AC_MSG_FAILURE([Could not find "$VARNISHSRC/bin/varnishtest/varnishtest". Please build your varnish source directory])]
)
# Check vanish ABI version
AC_CHECK_FILE([$VARNISHSRC/include/vmod_abi.h],
[
AC_MSG_CHECKING([varnish ABI version])
[VMOD_ABI_Version=`cut -d ' ' -f 4 "$VARNISHSRC/include/vmod_abi.h"`]
AC_MSG_RESULT([$VMOD_ABI_Version])
],
[
[VMOD_ABI_Version=3.0.0]
AC_MSG_NOTICE([Could not find "$VARNISHSRC/include/vmod_abi.h". Assuming varnish 3.0.0 ABI version])
]
)
# vmod installation dir
AC_ARG_VAR([VMODDIR], [vmod installation directory @<:@LIBDIR/varnish/vmods@:>@])
if test "x$VMODDIR" = x; then
VMODDIR=`pkg-config --variable=vmoddir varnishapi`
if test "x$VMODDIR" = x; then
AC_MSG_FAILURE([Can't determine vmod installation directory])
fi
VMODDIR=`pkg-config --variable=vmoddir varnishapi`
if test "x$VMODDIR" = x; then
AC_MSG_FAILURE([Could not determine vmod installation directory])
fi
fi
fi
############################################################
......
......@@ -9,6 +9,7 @@ SUBDIRS = gen
include_HEADERS = \
dcs_match.h \
gen/dcs_match_mem.h \
dcs_type.h
CHECKSUM = `cat gen/dcs_classifier.checksum`
......@@ -42,7 +43,6 @@ dcs_test_SOURCES = \
# -
EXTRA_DIST = \
gen_dcs_classifier.pl \
classes.conf
# included c source
......@@ -57,6 +57,7 @@ dist_data_DATA = \
dcs_varnish2.c \
dcs_varnish.c \
dcs_match.h \
gen/dcs_match_mem.h \
dcs_match.c \
dcs_type.h
......@@ -77,7 +78,7 @@ if BUILD_VMOD
vmoddir = $(VMODDIR)
vmod_LTLIBRARIES = libvmod_dcs.la
libvmod_dcs_la_CPPFLAGS = -Igen -I$(VARNISHSRC)/include -I$(VARNISHSRC)
libvmod_dcs_la_CPPFLAGS = -Igen -I$(VARNISHSRC)/include -I$(VARNISHSRC)/bin/varnishd -I$(VARNISHSRC)
libvmod_dcs_la_LDFLAGS = -module -export-dynamic -avoid-version
......@@ -92,8 +93,8 @@ vcc_if_INCLUDES = -I$(VARNISHSRC)/include -I$(VARNISHSRC)
vcc_dcs_INCLUDES = -I$(VARNISHSRC)/include -I$(VARNISHSRC)
vcc_if.c vcc_if.h: $(VARNISHSRC)/lib/libvmod_std/vmod.py vmod_dcs.vcc
@PYTHON@ $(VARNISHSRC)/lib/libvmod_std/vmod.py vmod_dcs.vcc
vcc_if.c vcc_if.h: @VMODTOOL@ @VCCFILE@
@PYTHON@ @VMODTOOL@ @VCCFILE@
BUILT_SOURCES = \
vcc_if.c \
......@@ -103,11 +104,11 @@ CLEANFILES = \
vcc_if.c \
vcc_if.h
VMOD_TESTS = tests/*.vtc
VMOD_TESTS = @TESTDIR@/*.vtc
.PHONY: $(VMOD_TESTS)
tests/*.vtc: libvmod_dcs.la
@TESTDIR@/*.vtc: libvmod_dcs.la
$(VARNISHSRC)/bin/varnishtest/varnishtest -Dvarnishd=$(VARNISHSRC)/bin/varnishd/varnishd -Dvmod_topbuild=$(abs_top_builddir) $@
check: $(VMOD_TESTS)
......
......@@ -39,13 +39,14 @@ main (void) {
char *line = NULL;
int e, t;
size_t linesz;
char mem[DCS_MATCH_MEM_SZ];
dcs_match_init();
while (getline(&line, &linesz, stdin) != -1) {
char *p = line;
for ( ; *p; ++p) *p = tolower(*p);
e = dcs_match(line);
e = dcs_match(line, mem, DCS_MATCH_MEM_SZ);
t = dcs_match_type_id(e);
printf("--\n%sentry id %d type %d - %s - %s\n", line,
dcs_match_id(e), t, dcs_type_class(t), dcs_type_name(t));
......
......@@ -45,17 +45,17 @@
#ifdef DEBUG_K_MATCH
#define p_entry(state, fmt, name) \
do { \
if (state.name ## _match_entry) { \
if (state->name ## _match_entry) { \
printf(fmt, #name, \
state.name ## _match_entry, \
dcs_type[dcs_entry[state.name ## _match_entry].type], \
dcs_entry[state.name ## _match_entry].key); \
state->name ## _match_entry, \
dcs_type[dcs_entry[state->name ## _match_entry].type], \
dcs_entry[state->name ## _match_entry].key); \
} \
} while(0)
#define p_entry_if_changed(state, prev_match,fmt) \
do if (state.last_match_entry != prev_match) { \
do if (state->last_match_entry != prev_match) { \
p_entry(state, fmt, last); \
prev_match = state.last_match_entry; \
prev_match = state->last_match_entry; \
} while(0)
#else
#define p_entry(...) do { } while(0)
......@@ -79,32 +79,37 @@ dcs_match_init(void) {
/*
* we deliberately return int instead of dcs_entry_id_t here in order to not
* require dcs_classifier.h for callers
*
* mem needs to be an allocation of at least DCS_MATCH_MEM_SZ bytes size
* sz is the size of the mem allocation
*/
int /* dcs_entry_id_t */
dcs_match(const char *p) {
struct dcs_matchstate state;
dcs_match(const char *p, void *mem, size_t sz) {
struct dcs_matchstate *state = mem;
#ifdef DEBUG_K_MATCH
dcs_entry_id_t prev_match = 0;
printf("\n--\n%s",p);
#endif
assert(p);
assert(p != NULL);
assert(state != NULL);
assert(sz >= DCS_MATCH_MEM_SZ);
dcs_init_matchstate(&state);
dcs_init_matchstate(state);
while (*p) {
dcs_parse_subkey(&state, p);
while (*p != '\0') {
dcs_parse_subkey(state, p);
p_entry_if_changed(state, prev_match, " %s match %d: %s - %s\n");
p++;
}
dcs_state_eval_candidates(&state);
dcs_state_eval_candidates(state);
p_entry_if_changed(state, prev_match, " new %s from candidates %d: %s - %s\n");
p_entry(state, " %s match %d: %s - %s\n", min);
p_entry(state, " %s match %d: %s - %s\n", first);
p_entry(state, " %s match %d: %s - %s\n", last);
return state.min_match_entry;
return (int)state->min_match_entry;
}
#define check_entry_index(entry_index, ret) \
......@@ -118,7 +123,7 @@ dcs_match(const char *p) {
int /* enum dcs_type */
dcs_match_id(int entry_index /* dcs_entry_id_t */) {
check_entry_index(entry_index, -1);
return dcs_entry[entry_index].id;
return (int)dcs_entry[entry_index].id;
}
int /* enum dcs_type */
......
......@@ -28,10 +28,12 @@
*
*/
#include "dcs_match_mem.h"
#ifndef DCS_MATCH_H
#define DCS_MATCH_H 1
int dcs_match_init(void);
int dcs_match(const char *p);
int dcs_match(const char *p, void *mem, size_t sz);
int dcs_match_id(int entry_id /* dcs_entry_id_t */);
int dcs_match_type_id(int entry_id /* dcs_entry_id_t */);
const char const *dcs_match_key(int entry_id /* dcs_entry_id_t */);
......
......@@ -96,6 +96,7 @@ fixup_out (FILE *f_remove, FILE *f_reorder, const int i, const int r) {
int
test (const char *fixup_remove_name, const char *fixup_reorder_name) {
char mem[DCS_MATCH_MEM_SZ];
int i, r, errcount = 0;
FILE *f_remove = NULL, *f_reorder = NULL;
......@@ -118,7 +119,7 @@ test (const char *fixup_remove_name, const char *fixup_reorder_name) {
for (i = NB_E_SPECIAL_LIMIT; i < DCS_ENTRY_COUNT; i++) {
if (dcs_matchstate_init.matchmask[i] == 0) {
r = dcs_match(dcs_entry[i].key);
r = dcs_match(dcs_entry[i].key, mem, DCS_MATCH_MEM_SZ);
if (i != r) {
test_p_diff("miss\t", i, dcs_entry[i].key, r);
fixup_out(f_remove, f_reorder, i, r);
......@@ -173,14 +174,14 @@ test (const char *fixup_remove_name, const char *fixup_reorder_name) {
}
}
r = dcs_match(poskey);
r = dcs_match(poskey, mem, DCS_MATCH_MEM_SZ);
if (i != r) {
test_p_diff("miss n-p", i, poskey, r);
fixup_out(f_remove, f_reorder, i, r);
errcount++;
}
r = dcs_match(negkey);
r = dcs_match(negkey, mem, DCS_MATCH_MEM_SZ);
if (r == i) {
test_p_diff("miss n-n", i, negkey, r);
fixup_out(f_remove, f_reorder, i, r);
......
......@@ -27,7 +27,7 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* common code for varnish 2 (inline-C) and varnish 3 (vmod)
* common code for varnish 2 (inline-C) and varnish 3/4 (vmod)
*
* this gets _INCLUDED_
*/
......@@ -40,13 +40,57 @@
#include "dcs_type.c"
#define UA_LIMIT 1024
#define DCS_VARNISH2_NHDRS 4
const char * const hdrs[DCS_VARNISH2_NHDRS] = {
[0] = "\025X-OperaMini-Phone-UA:",
[1] = "\024X-Device-User-Agent:",
[2] = "\026X-Original-User-Agent:",
[3] = "\016X-Goog-Source:"
#if VARNISH_MAJOR == 4
typedef const struct vrt_ctx dcs_ctx;
static inline
const char *DCS_GetHdr(dcs_ctx *ctx, const struct gethdr_s *hdr) {
return VRT_GetHdr(ctx, hdr);
}
/*
* in varnish 4, the default worker thread stack size has been drastically
* reduced to 48k, so we take our allocation from the workspace
*/
#define DCS_USE_WS
#else /* VARNISH_MAJOR == 3 */
struct gethdr_s {
enum gethdr_e where;
const char *what;
};
typedef struct sess dcs_ctx;
typedef int VCL_INT;
typedef const char * VCL_STRING;
static inline
const char *DCS_GetHdr(dcs_ctx *ctx, const struct gethdr_s *hdr) {
return VRT_GetHdr(ctx, hdr->where, hdr->what);
}
#endif
#ifdef DCS_USE_WS
/* XXX better way? */
#undef assert
#include "cache/cache.h"
#define DCS_USE_WS_MINIMUM PRNDUP(64 * 1024 + DCS_MATCH_MEM_SZ + UA_LIMIT)
#endif
const struct gethdr_s hdr_ua = {HDR_REQ, "\013User-Agent:"};
const struct gethdr_s hdr_wap = {HDR_REQ, "\016x-wap-profile:"};
const struct gethdr_s hdrs[DCS_VARNISH2_NHDRS] = {
[0] = { HDR_REQ, "\025X-OperaMini-Phone-UA:" },
[1] = { HDR_REQ, "\024X-Device-User-Agent:" },
[2] = { HDR_REQ, "\026X-Original-User-Agent:" },
[3] = { HDR_REQ, "\016X-Goog-Source:" }
};
const char * const ua_prepend[DCS_VARNISH2_NHDRS] = {
......@@ -68,22 +112,42 @@ const char * const ua_prepend[DCS_VARNISH2_NHDRS] = {
w += l;
static int
dcs_varnish_classify(const struct sess *sp) {
const char *ua = VRT_GetHdr(sp, HDR_REQ, "\013User-Agent:");
dcs_varnish_classify(dcs_ctx *ctx) {
const char *ua = DCS_GetHdr(ctx, &hdr_ua);
const char *r;
char *w = NULL;
int i;
int i, ret;
size_t l;
#ifdef DCS_USE_WS
#define WS_SIZE (DCS_MATCH_MEM_SZ + UA_LIMIT)
unsigned space = WS_Reserve(ctx->ws, WS_SIZE);
void *mem = ctx->ws->f;
char *uabuf = ctx->ws->f + DCS_MATCH_MEM_SZ;
assert(space != 0);
assert(space >= WS_SIZE);
space -= DCS_MATCH_MEM_SZ;
if (space > UA_LIMIT)
space = UA_LIMIT;
#else
size_t space = UA_LIMIT;
char mem[DCS_MATCH_MEM_SZ];
char uabuf[space];
#endif
if ((! ua) || (! *ua))
return NB_E_SPECIAL_UNIDENTIFIED;
if ((! ua) || (! *ua)) {
ret = NB_E_SPECIAL_UNIDENTIFIED;
goto out;
}
if (VRT_GetHdr(sp, HDR_REQ, "\016x-wap-profile:"))
return NB_E_SPECIAL_GENERIC_WAP;
if (DCS_GetHdr(ctx, &hdr_wap)) {
ret = NB_E_SPECIAL_GENERIC_WAP;
goto out;
}
/* we need to copy to downcase the string for matching */
w = uabuf;
......@@ -93,7 +157,7 @@ dcs_varnish_classify(const struct sess *sp) {
ua = uabuf;
for (i = 0; i < DCS_VARNISH2_NHDRS; i++) {
r = VRT_GetHdr(sp, HDR_REQ, hdrs[i]);
r = DCS_GetHdr(ctx, &hdrs[i]);
if (r && *r) {
if (ua_prepend[i])
appnd(w, space, ua_prepend[i], l);
......@@ -101,13 +165,24 @@ dcs_varnish_classify(const struct sess *sp) {
}
}
uabuf[UA_LIMIT] = '\0';
/* ensure the string is always terminated */
if (w == (uabuf + UA_LIMIT))
uabuf[UA_LIMIT] = '\0';
else
w[-1] = '\0';
assert(ua == uabuf);
assert(w > uabuf);
assert(w <= (uabuf + UA_LIMIT));
for (w = uabuf; *w; ++w) *w = tolower(*w);
return dcs_match(ua);
ret = dcs_match(ua, mem, DCS_MATCH_MEM_SZ);
out:
#ifdef DCS_USE_WS
/* used workspace only as scratch - releasing all */
WS_Release(ctx->ws, 0);
#endif
return (ret);
}
......@@ -3,16 +3,34 @@
BUILT_SOURCES = \
dcs_classifier.c \
dcs_classifier.h \
dcs_type.c
dcs_type.c \
dcs_match_mem.h
EXTRA_DIST = \
gen_dcs_classifier.pl \
gen_dcs_match_mem.c
dcs_classifier.c dcs_classifier.h dcs_type.c: ../gen_dcs_classifier.pl $(DCS_DBFILE) ../classes.conf
# -- the classifier
dcs_classifier.c dcs_classifier.h dcs_type.c: gen_dcs_classifier.pl $(DCS_DBFILE) ../classes.conf
if [[ -f fixup.remove ]] && [[ -f fixup.reorder ]] ; then \
../gen_dcs_classifier.pl $(DCS_DBFILE) $(DCS_KEY) ../classes.conf fixup.remove fixup.reorder ; \
./gen_dcs_classifier.pl $(DCS_DBFILE) $(DCS_KEY) ../classes.conf fixup.remove fixup.reorder ; \
else \
../gen_dcs_classifier.pl $(DCS_DBFILE) $(DCS_KEY) ../classes.conf ; \
./gen_dcs_classifier.pl $(DCS_DBFILE) $(DCS_KEY) ../classes.conf ; \
fi
# fixup db
# -- gen_dcs_match_mem
noinst_PROGRAMS = gen_dcs_match_mem
gen_dcs_match_mem_SOURCES = \
gen_dcs_match_mem.c \
dcs_classifier.h
dcs_match_mem.h: gen_dcs_match_mem
./gen_dcs_match_mem >dcs_match_mem.h
# -- fixup db
fixup.remove fixup.reorder fixup.out:
- rm fixup.remove fixup.reorder fixup.out
- rm dcs_classifier.c dcs_classifier.h dcs_type.c
......@@ -26,6 +44,8 @@ fixup.remove fixup.reorder fixup.out:
fixup-local: fixup.remove fixup.reorder fixup.out
# -- load db
load-local: dcs.db.refresh dcs.db
dcs.db.tstamp:
......@@ -59,12 +79,13 @@ dcs.db: dcs.db.tstamp
mv -f dcs.db.tmp dcs.db ; \
fi
# --
CLEANFILES = \
dcs_classifier.c \
dcs_classifier.h \
dcs_type.c \
$(BUILT_SOURCES) \
fixup.remove \
fixup.reorder \
fixup.out \
dcs.db \
dcs.db.tstamp
......@@ -1021,6 +1021,8 @@ struct dcs_subkey {
struct dcs_matchstate {
#define DCS_MATCHSTATE_MAGIC 0x32bc524d
uint32_t magic;
uint32_t chk_entry_count;
uint32_t chk_subkey_count;
dcs_entry_id_t min_match_entry;
dcs_entry_id_t first_match_entry;
......@@ -1038,7 +1040,7 @@ const struct dcs_entry dcs_entry[DCS_ENTRY_COUNT];
const struct dcs_subkey dcs_subkey[DCS_SUBKEY_COUNT];
const struct dcs_matchstate dcs_matchstate_init;
void dcs_init_matchstate (struct dcs_matchstate *state);
void dcs_init_matchstate (/*\@out\@*/ struct dcs_matchstate *state);
void dcs_parse_subkey(struct dcs_matchstate *state, const char const *m);
void dcs_state_eval_candidates(struct dcs_matchstate *state);
......@@ -1119,6 +1121,9 @@ _VCL (F_CLASSIFIER_C, VCL_TOP, <<'EOF');
const struct dcs_matchstate dcs_matchstate_init = {
.magic = DCS_MATCHSTATE_MAGIC,
.chk_entry_count = DCS_ENTRY_COUNT,
.chk_subkey_count = DCS_SUBKEY_COUNT,
.min_match_entry = 0,
.first_match_entry = 0,
.last_match_entry = 0,
......@@ -1167,7 +1172,7 @@ _VCL(F_CLASSIFIER_C, undef, <<'EOF');
#undef PARSE_REGISTER
void
dcs_init_matchstate (struct dcs_matchstate *state) {
dcs_init_matchstate (/*\@out\@*/ struct dcs_matchstate *state) {
/* if this fails, fix the init code */
assert(NB_T_UNIDENTIFIED == 0);
......@@ -1175,7 +1180,16 @@ dcs_init_matchstate (struct dcs_matchstate *state) {
assert(DCS_ENTRY_COUNT <= DCS_ENTRY_INDEX_MAX);
assert(DCS_SUBKEY_COUNT <= DCS_SUBKEY_ID_MAX);
/*
* something must be terribly wrong if the const matchstate_init and
* this code don't agree on these counts
*/
assert(DCS_ENTRY_COUNT == dcs_matchstate_init.chk_entry_count);
assert(DCS_SUBKEY_COUNT == dcs_matchstate_init.chk_subkey_count);
assert(sizeof(*state) == sizeof(dcs_matchstate_init));
memcpy(state, &dcs_matchstate_init, sizeof(*state));
assert(state->magic == DCS_MATCHSTATE_MAGIC);
assert(state->min_match_entry == 0);
}
......
/*
* Copyright (c) 2014 UPLEX - Nils Goroll Systemoptimierung
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS _AS IS_ AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "dcs_config.h"
#include <stdio.h>
#include "dcs_classifier.h"
/*
* generate the dcs_match_mem.h file telling callers of dcs_match the size of
* the required private data structure (struct dcs_matchstate)
*/
static const char *pre =
"/*\n"
" * THIS FILE HAS BEEN AUTOMATICALLY GENERATED. DO NOT EDIT\n"
" */\n\n"
"#ifndef DCS_MATCH_MEM_H\n"
"#define DCS_MATCH_MEM_H 1\n\n";
static const char *post =
"\n"
"#endif\n";
int
main(void) {
printf(pre);
printf("#define DCS_MATCH_MEM_SZ %zu /* sizeof(struct dcs_matchstate) */\n",
sizeof(struct dcs_matchstate));
printf(post);
}
# $Id$
varnishtest "Test dcs vmod"
server s1 {
rxreq
txresp
} -start
varnish v1 -vcl+backend {
import dcs from "${vmod_topbuild}/src/.libs/libvmod_dcs.so";
sub vcl_recv {
# this code will classify
#
# for best performance in production, only call classify
# once or use inline-C to save the return value of
# classify (int)
# exotic use cases
set req.http.xx-entry-key = dcs.entry_key(dcs.classify());
set req.http.xx-type-id = dcs.type_id(dcs.classify());
# common use cases
set req.http.x-nb-classified = dcs.type_name(dcs.classify());
set req.http.X-DeviceClass = dcs.type_class(dcs.classify());
return (synth(200));
}
sub vcl_synth {
set resp.http.xx-entry-key = req.http.xx-entry-key;
set resp.http.xx-type-id = req.http.xx-type-id;
set resp.http.x-nb-classified = req.http.x-nb-classified;
set resp.http.X-DeviceClass = req.http.X-DeviceClass;
synthetic ({"classified ad here
"});
return (deliver);
}
} -start
client c1 {
# no User-Agent
txreq -url "/"
rxresp
expect resp.status == 200
expect resp.http.xx-entry-key == "unidentified"
expect resp.http.xx-type-id == "0"
expect resp.http.x-nb-classified == "unidentified"
expect resp.http.X-DeviceClass == "desktop"
} -run
client c1 {
txreq -hdr "user-agent: willgetignored" -hdr "x-wap-profile: anything"
rxresp
expect resp.status == 200
expect resp.http.xx-entry-key == "generic wap"
# cant test - can change easily
# expect resp.http.xx-type-id == "11"
expect resp.http.x-nb-classified == "Mobile Phone"
expect resp.http.X-DeviceClass == "smartphone"
} -run
client c1 {
txreq -hdr "User-Agent: Opera/9.80 (Android; Opera Mini/19.0.1340/34.1309; U; de) Presto/2.8.119 Version/11.10" \
-hdr "X-OperaMini-Phone-UA: Mozilla/5.0 (Linux; U; Android 4.3; de-de; ME302C Build/JSS15Q) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30"
rxresp
expect resp.status == 200
expect resp.http.xx-entry-key == "android*me302c"
# cant test - can change easily
# expect resp.http.xx-type-id == "11"
expect resp.http.x-nb-classified == "Tablet"
expect resp.http.X-DeviceClass == "tablet"
} -run
# $Id$
varnishtest "Test dcs vmod excessively long ua"
server s1 {
rxreq
txresp
} -start
varnish v1 -vcl+backend {
import dcs from "${vmod_topbuild}/src/.libs/libvmod_dcs.so";
sub vcl_recv {
# this code will classify
#
# for best performance in production, only call classify
# once or use inline-C to save the return value of
# classify (int)
# exotic use cases
set req.http.xx-entry-key = dcs.entry_key(dcs.classify());
set req.http.xx-type-id = dcs.type_id(dcs.classify());
# common use cases
set req.http.x-nb-classified = dcs.type_name(dcs.classify());
set req.http.X-DeviceClass = dcs.type_class(dcs.classify());
return (synth(200));
}
sub vcl_synth {
set resp.http.xx-entry-key = req.http.xx-entry-key;
set resp.http.xx-type-id = req.http.xx-type-id;
set resp.http.x-nb-classified = req.http.x-nb-classified;
set resp.http.X-DeviceClass = req.http.X-DeviceClass;
synthetic ({"classified ad here
"});
return (deliver);
}
} -start
client c1 {
txreq -hdr "User-Agent: motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits" \
-hdr "X-Goog-Source: even longer"
rxresp
expect resp.status == 200
expect resp.http.xx-entry-key == "motorokr z6/r60_g_80.xx.yyi mozilla/4.0 (compatible; msie 6.0 linux; motorokr z6;nnn) profile/midp-2.0 configuration/cldc-1.1 opera 8.50[yy] up.link/6.3.0.0.0 netbiscuits"
# cant test
# expect resp.http.xx-type-id == "11"
expect resp.http.x-nb-classified == "Mobile Phone"
expect resp.http.X-DeviceClass == "smartphone"
} -run
......@@ -40,38 +40,48 @@ vmod_init(struct vmod_priv *priv, const struct VCL_conf *cfg)
{
(void)priv;
(void)cfg;
#ifdef DCS_USE_WS
if (cache_param->workspace_client < DCS_USE_WS_MINIMUM) {
/* XXX better function for emiting this warning */
fprintf(stderr,
"notice: workspace_client is set too low for vmod_dcs, adjusting from %u to %lu bytes\n",
cache_param->workspace_client, DCS_USE_WS_MINIMUM);
cache_param->workspace_client = DCS_USE_WS_MINIMUM;
}
#endif
return dcs_match_init();
}
int
vmod_classify(struct sess *sp) {
return dcs_varnish_classify(sp);
VCL_INT
vmod_classify(dcs_ctx *ctx) {
return dcs_varnish_classify(ctx);
}
const char *
vmod_entry_key(struct sess *sp, int e) {
(void) sp;
VCL_STRING
vmod_entry_key(dcs_ctx *ctx, VCL_INT e) {
(void) ctx;
return dcs_match_key(e);
}
int
vmod_type_id(struct sess *sp, int e) {
(void) sp;
VCL_INT
vmod_type_id(dcs_ctx *ctx, VCL_INT e) {
(void) ctx;
return dcs_match_type_id(e);
}
const char *
vmod_type_name(struct sess *sp, int e) {
const int t = dcs_match_type_id(e);
VCL_STRING
vmod_type_name(dcs_ctx *ctx, VCL_INT e) {
const VCL_INT t = dcs_match_type_id(e);
(void) sp;
(void) ctx;
return dcs_type_name(t > 0 ? t : 0);
}
const char *
vmod_type_class(struct sess *sp, int e) {
const int t = dcs_match_type_id(e);
VCL_STRING
vmod_type_class(dcs_ctx *ctx, VCL_INT e) {
const VCL_INT t = dcs_match_type_id(e);
(void) sp;
(void) ctx;
return dcs_type_class(t > 0 ? t : 0);
}
$Module dcs 3 Varnish Device Classification Service Module
$Init vmod_init
$Function INT classify()
$Function STRING entry_key(INT)
$Function INT type_id(INT)
$Function STRING type_name(INT)
$Function STRING type_class(INT)
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