Skip to content

Commit 29efc88

Browse files
committed
basic implementation ordering the parameters
1 parent ec4ad9f commit 29efc88

File tree

4 files changed

+341
-0
lines changed

4 files changed

+341
-0
lines changed

README.md

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
Nginx Sorted Querystring Module
2+
===============================
3+
4+
A module to order the querystring parameters in a variable to be used as cache key.
5+
6+
Requests like /index.html?b=2&a=1&c=3, /index.html?b=2&c=3&a=1, /index.html?c=3&a=1&b=2, /index.html?c=3&b=2&a=1 will produce the same value for `$sorted_querystring_args` _'a=1&b=2&c=3'_.
7+
8+
_This module is not distributed with the Nginx source. See [the installation instructions](#installation)._
9+
10+
11+
Configuration
12+
-------------
13+
14+
An example:
15+
16+
pid logs/nginx.pid;
17+
error_log logs/nginx-main_error.log debug;
18+
19+
worker_processes 2;
20+
21+
events {
22+
worker_connections 1024;
23+
#use kqueue; # MacOS
24+
use epoll; # Linux
25+
}
26+
27+
http {
28+
default_type text/plain;
29+
30+
types {
31+
text/html html;
32+
}
33+
34+
log_format main '[$time_local] $host "$request" $request_time s '
35+
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
36+
'cache_status: "$upstream_cache_status" args: "$args '
37+
'sorted_args: "$sorted_querystring_args" ';
38+
39+
access_log logs/nginx-http_access.log;
40+
41+
proxy_cache_path /tmp/cache levels=1:2 keys_zone=zone:10m inactive=10d max_size=100m;
42+
43+
server {
44+
listen 8080;
45+
server_name localhost;
46+
47+
access_log logs/nginx-http_access.log main;
48+
49+
location / {
50+
proxy_pass http://localhost:8081;
51+
52+
proxy_cache zone;
53+
proxy_cache_key "$sorted_querystring_args";
54+
proxy_cache_valid 200 10m;
55+
}
56+
}
57+
58+
server {
59+
listen 8081;
60+
61+
location / {
62+
return 200 "$args\n";
63+
}
64+
}
65+
}
66+
67+
68+
Variables
69+
---------
70+
71+
* **$sorted_querystring_args** - just list the IP considered as remote IP on the connection
72+
73+
74+
<a id="installation"></a>Installation instructions
75+
--------------------------------------------------
76+
77+
[Download Nginx Stable](http://nginx.org/en/download.html) source and uncompress it (ex.: to ../nginx). You must then run ./configure with --add-module pointing to this project as usual. Something in the lines of:
78+
79+
$ ./configure \
80+
--add-module=../nginx-sorted-querystring-module \
81+
--prefix=/home/user/dev-workspace/nginx
82+
$ make
83+
$ make install
84+
85+
86+
Running tests
87+
-------------
88+
89+
This project uses [nginx_test_helper](https://github.com/wandenberg/nginx_test_helper) on the test suite. So, after you've installed the module, you can just download the necessary gems:
90+
91+
$ cd test
92+
$ bundle install
93+
94+
And run rspec pointing to where your Nginx binary is (default: /usr/local/nginx/sbin/nginx):
95+
96+
$ NGINX_EXEC=../path/to/my/nginx rspec .
97+
98+
99+
Changelog
100+
---------
101+
102+
This is still a work in progress. Be the change. And take a look on the Changelog file.

config

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ngx_addon_name=ngx_http_sorted_querystring_module
2+
HTTP_MODULES="$HTTP_MODULES $ngx_addon_name"
3+
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/$ngx_addon_name.c"

nginx.conf

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
pid logs/nginx.pid;
2+
error_log logs/nginx-main_error.log debug;
3+
4+
# Development Mode
5+
master_process off;
6+
daemon off;
7+
worker_processes 2;
8+
9+
events {
10+
worker_connections 1024;
11+
#use kqueue; # MacOS
12+
use epoll; # Linux
13+
}
14+
15+
http {
16+
default_type text/plain;
17+
18+
types {
19+
text/html html;
20+
}
21+
22+
log_format main '[$time_local] $host "$request" $request_time s '
23+
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
24+
'cache_status: "$upstream_cache_status" args: "$args '
25+
'sorted_args: "$sorted_querystring_args" ';
26+
27+
access_log logs/nginx-http_access.log;
28+
29+
proxy_cache_path /tmp/cache levels=1:2 keys_zone=zone:10m inactive=10d max_size=100m;
30+
31+
server {
32+
listen 8080;
33+
server_name localhost;
34+
35+
access_log logs/nginx-http_access.log main;
36+
37+
location / {
38+
proxy_pass http://localhost:8081;
39+
40+
proxy_cache zone;
41+
proxy_cache_key "$sorted_querystring_args";
42+
proxy_cache_valid 200 10m;
43+
}
44+
}
45+
46+
server {
47+
listen 8081;
48+
49+
location / {
50+
return 200 "$args\n";
51+
}
52+
}
53+
}

ngx_http_sorted_querystring_module.c

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#include <nginx.h>
2+
#include <ngx_config.h>
3+
#include <ngx_core.h>
4+
#include <ngx_http.h>
5+
6+
7+
ngx_int_t ngx_http_sorted_querystring_pre_config(ngx_conf_t *cf);
8+
ngx_int_t ngx_http_sorted_querystring_args_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
9+
ngx_int_t ngx_http_sorted_querystring_cmp_parameters(const ngx_queue_t *one, const ngx_queue_t *two);
10+
11+
typedef struct {
12+
ngx_queue_t args_queue;
13+
} ngx_http_sorted_querystring_ctx_t;
14+
15+
16+
typedef struct {
17+
ngx_queue_t queue;
18+
ngx_str_t key;
19+
ngx_str_t complete;
20+
} ngx_http_sorted_querystring_parameter_t;
21+
22+
23+
static ngx_http_variable_t ngx_http_sorted_querystring_vars[] = {
24+
{ ngx_string("sorted_querystring_args"),
25+
NULL,
26+
ngx_http_sorted_querystring_args_variable,
27+
0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
28+
29+
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
30+
};
31+
32+
33+
static ngx_command_t ngx_http_sorted_querystring_commands[] = {
34+
ngx_null_command
35+
};
36+
37+
38+
static ngx_http_module_t ngx_http_sorted_querystring_module_ctx = {
39+
ngx_http_sorted_querystring_pre_config, /* preconfiguration */
40+
NULL, /* postconfiguration */
41+
42+
NULL, /* create main configuration */
43+
NULL, /* init main configuration */
44+
45+
NULL, /* create server configuration */
46+
NULL, /* merge server configuration */
47+
48+
NULL, /* create location configuration */
49+
NULL /* merge location configuration */
50+
};
51+
52+
53+
ngx_module_t ngx_http_sorted_querystring_module = {
54+
NGX_MODULE_V1,
55+
&ngx_http_sorted_querystring_module_ctx, /* module context */
56+
ngx_http_sorted_querystring_commands, /* module directives */
57+
NGX_HTTP_MODULE, /* module type */
58+
NULL, /* init master */
59+
NULL, /* init module */
60+
NULL, /* init process */
61+
NULL, /* init thread */
62+
NULL, /* exit thread */
63+
NULL, /* exit process */
64+
NULL, /* exit master */
65+
NGX_MODULE_V1_PADDING
66+
};
67+
68+
69+
ngx_int_t
70+
ngx_http_sorted_querystring_pre_config(ngx_conf_t *cf)
71+
{
72+
ngx_http_variable_t *var, *v;
73+
74+
for (v = ngx_http_sorted_querystring_vars; v->name.len; v++) {
75+
var = ngx_http_add_variable(cf, &v->name, v->flags);
76+
if (var == NULL) {
77+
return NGX_ERROR;
78+
}
79+
80+
var->get_handler = v->get_handler;
81+
var->data = v->data;
82+
}
83+
84+
return NGX_OK;
85+
}
86+
87+
88+
ngx_int_t
89+
ngx_http_sorted_querystring_args_variable(ngx_http_request_t *r, ngx_http_variable_value_t *var, uintptr_t data)
90+
{
91+
ngx_http_sorted_querystring_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_sorted_querystring_module);
92+
ngx_http_sorted_querystring_parameter_t *param;
93+
u_char *p, *ampersand, *equal, *last;
94+
ngx_queue_t *q;
95+
96+
if (r->args.len == 0) {
97+
var->len = 0;
98+
var->data = (u_char*) "";
99+
return NGX_OK;
100+
}
101+
102+
if (var->len > 0) {
103+
return NGX_OK;
104+
}
105+
106+
if (ctx == NULL) {
107+
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sorted_querystring_ctx_t));
108+
if (ctx == NULL) {
109+
return NGX_ERROR;
110+
}
111+
ngx_http_set_ctx(r, ctx, ngx_http_sorted_querystring_module);
112+
113+
ngx_queue_init(&ctx->args_queue);
114+
115+
p = r->args.data;
116+
last = p + r->args.len;
117+
118+
for ( /* void */ ; p < last; p++) {
119+
param = ngx_pcalloc(r->pool, sizeof(ngx_http_sorted_querystring_parameter_t));
120+
if (param == NULL) {
121+
return NGX_ERROR;
122+
}
123+
124+
ampersand = ngx_strlchr(p, last, '&');
125+
if (ampersand == NULL) {
126+
ampersand = last;
127+
}
128+
129+
equal = ngx_strlchr(p, last, '=');
130+
if ((equal == NULL) || (equal > ampersand)) {
131+
equal = ampersand;
132+
}
133+
134+
param->key.data = p;
135+
param->key.len = equal - p;
136+
137+
param->complete.data = p;
138+
param->complete.len = ampersand - p;
139+
140+
ngx_queue_insert_tail(&ctx->args_queue, &param->queue);
141+
142+
p = ampersand;
143+
}
144+
145+
ngx_queue_sort(&ctx->args_queue, ngx_http_sorted_querystring_cmp_parameters);
146+
}
147+
148+
var->data = ngx_pcalloc(r->pool, r->args.len + 2); // 1 char for extra ampersand and 1 for the \0
149+
if (var->data == NULL) {
150+
return NGX_ERROR;
151+
}
152+
153+
p = var->data;
154+
for (q = ngx_queue_head(&ctx->args_queue); q != ngx_queue_sentinel(&ctx->args_queue); q = ngx_queue_next(q)) {
155+
param = ngx_queue_data(q, ngx_http_sorted_querystring_parameter_t, queue);
156+
p = ngx_sprintf(p, "%V&", &param->complete);
157+
}
158+
159+
var->len = p - var->data - 1;
160+
161+
return NGX_OK;
162+
}
163+
164+
165+
ngx_int_t
166+
ngx_http_sorted_querystring_cmp_parameters(const ngx_queue_t *one, const ngx_queue_t *two)
167+
{
168+
ngx_http_sorted_querystring_parameter_t *first, *second;
169+
ngx_int_t rc;
170+
171+
first = ngx_queue_data(one, ngx_http_sorted_querystring_parameter_t, queue);
172+
second = ngx_queue_data(two, ngx_http_sorted_querystring_parameter_t, queue);
173+
174+
rc = ngx_strncasecmp(first->key.data, second->key.data, ngx_min(first->key.len, second->key.len));
175+
if (rc == 0) {
176+
rc = ngx_strncasecmp(first->complete.data, second->complete.data, ngx_min(first->complete.len, second->complete.len));
177+
if (rc == 0) {
178+
rc = -1;
179+
}
180+
}
181+
182+
return rc;
183+
}

0 commit comments

Comments
 (0)