Commit 489460a7 authored by Per Buer's avatar Per Buer

Ported the boltsort vmod to 4.0 and included it

in the std vmod as discussed on the latest vdd.

Renamed it to std.querysort. Two tests where 
included. They both pass on my machine.
parent 78b34d9e
varnishtest "Test querysort in std vmod"
server s1 {
rxreq
txresp
rxreq
txresp
rxreq
txresp
rxreq
txresp
rxreq
txresp
rxreq
txresp
rxreq
txresp
rxreq
txresp
} -start
varnish v1 -vcl+backend {
import ${vmod_std};
sub vcl_deliver {
set resp.http.naren = std.querysort(req.url);
}
} -start
client c1 {
txreq -url "/video/44505073?title=0&byline=0&portrait=0&color=51a516"
rxresp
expect resp.http.naren == "/video/44505073?byline=0&color=51a516&portrait=0&title=0"
txreq -url "/video/44505073?byline=0&&&&&"
rxresp
expect resp.http.naren == "/video/44505073?byline=0"
txreq -url "/video/2?&"
rxresp
expect resp.http.naren == "/video/2?"
txreq -url "/video/2"
rxresp
expect resp.http.naren == "/video/2"
txreq -url "/video/2?cod=cape&cape=cod"
rxresp
expect resp.http.naren == "/video/2?cape=cod&cod=cape"
txreq -url "/"
rxresp
expect resp.http.naren == "/"
}
client c1 -run
varnishtest "Test querysort of req.url in vcl_hash"
server s1 {
rxreq
txresp
rxreq
txresp
} -start
varnish v1 -vcl+backend {
import ${vmod_std};
sub vcl_hash {
set req.url = std.querysort(req.url);
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
}
else {
set resp.http.X-Cache = "MISS";
}
}
} -start
client c1 {
txreq -url "/video/47013255?title=0&byline=0&portrait=0&autoplay=1"
rxresp
expect resp.status == 200
expect resp.http.X-Cache == "MISS"
txreq -url "/video/47013255?title=0&byline=0&portrait=0&autoplay=1"
rxresp
expect resp.status == 200
expect resp.http.X-Cache == "HIT"
}
client c2 {
txreq -url "/video/47013255?autoplay=1&title=0&byline=0&portrait=0"
rxresp
expect resp.status == 200
expect resp.http.X-Cache == "HIT"
txreq -url "autoplay=1&title=0&byline=0&portrait=0&&&&"
rxresp
expect resp.status == 200
expect resp.http.X-Cache == "HIT"
}
client c2 {
txreq -url "/video/47013255?autoplay=1&title=0&byline=0&portrait=0"
rxresp
expect resp.status == 200
expect resp.http.X-Cache == "HIT"
txreq -url "/video/47013255?autoplay=1&title=0&byline=0&portrait=0&&&&"
rxresp
expect resp.status == 200
expect resp.http.X-Cache == "HIT"
}
client c1 -run
client c2 -run
......@@ -155,6 +155,16 @@ Description
Example
std.timestamp("curl-request");
$Function STRING querysort(STRING)
Description
Sorts the querystring for cache normalization purposes.
Example
set req.url = std.querysort(req.url);
SEE ALSO
========
......@@ -173,3 +183,4 @@ COPYRIGHT
This document is licensed under the same licence as Varnish
itself. See LICENCE for details.
......@@ -225,3 +225,140 @@ vmod_timestamp(const struct vrt_ctx *ctx, VCL_STRING label)
VSLb_ts_req(ctx->req, label, VTIM_real());
}
}
/* Boltsort
Author: Naren Venkataraman of Vimeo Inc.
Included here with permission.
*/
#define QS_MAX_PARAM_COUNT 32
#define QS_EQUALS(c, h) ((c == h) || (c == '\0' && h == '&') || (c == '&' && h == '\0'))
#define QS_ENDS(s) (s == '&' || s == '\0')
static const char QS_TERMINATORS[2] = {'\0', '&'};
//since we dont store param length, we have to evaluate everytime
static inline int param_compare (char *s, char *t)
{
for ( ;QS_EQUALS(*s, *t); s++, t++) {
if (QS_ENDS(*s)) {
return 0;
}
}
return *s - *t;
}
//end of param is either first occurance of & or '\0'
static inline int param_copy(char *dst, char *src, char *last_param)
{
int len = strchr(src, QS_TERMINATORS[(src != last_param)]) - src;
memcpy(dst, src, len);
return len;
}
//Varnish vmod requires this
int init_function(struct vmod_priv *priv, const struct VCL_conf *conf)
{
return 0;
}
//sort query string
VCL_STRING vmod_querysort(const struct vrt_ctx * ctx, VCL_STRING url)
{
if (url == NULL) {
return NULL;
}
int qs_index = 0;
int param_count = 0;
char *dst_url = NULL;
char *qs = NULL;
//To avoid 1 pass for count calculations, assuming MAX_PARAM_COUNT as max
char* params[QS_MAX_PARAM_COUNT];
int i, p;
char *param = NULL;
qs = strchr(url, '?');
if(!qs) {
return url;
}
//add first param and increment count
params[param_count++] = ++qs;
qs_index = qs - url;
//Continue to find query string
while((qs = strchr(qs, '&')) != NULL) {
param = ++qs;
for(p = 0; p < param_count; p++) {
//if incoming param is < param at position then place it at p and then move up rest
if(param[0] < params[p][0] || param_compare(param, params[p]) < 0) {
for(i = param_count; i > p; i--) {
params[i] = params[i-1];
}
break;
}
}
params[p] = param;
param_count++;
//if it exceed max params return as is
if (param_count == QS_MAX_PARAM_COUNT) {
return url;
}
}
//there is nothing after &
//eg: http://127.0.0.1/?me=1&
if (param_count == 1) {
return url;
}
//allocate space for sorted url
// struct ws *ws = sp->wrk->ws;
struct ws *ws = ctx->ws;
dst_url = WS_Alloc(ws, strchr(param, '\0') - url + 1);
WS_Assert(ws);
//if alloc fails return as is
if(dst_url == NULL) {
return url;
}
//copy data before query string
char* cp = memcpy(dst_url, url, qs_index) + qs_index;
//get rid of all empty params /test/?a&&&
for(p = 0; p < param_count - 1; p++) {
if (params[p][0] != '\0' && params[p][0] != '&') {
break;
}
}
//copy sorted params
for(; p < param_count - 1; p++) {
//copy and increment
cp += param_copy(cp, params[p], param);
*cp++ = '&';
}
//copy the last param
cp += param_copy(cp, params[p], param);
*cp = '\0';
return dst_url;
}
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