Ticket #954: mod_traffic.c

File mod_traffic.c, 7.1 kB (added by Terminar, 22 months ago)
Line 
1#include <ctype.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "base.h"
6#include "log.h"
7#include "buffer.h"
8
9#include "plugin.h"
10
11#ifdef HAVE_CONFIG_H
12#include "config.h"
13#endif
14
15
16/**
17 * this is a traffic control plugin derived from mod_skeleton.c for a lighttpd plugin
18 * do not use this in production environment! It's my first lighty plugin! :)
19 *
20 * Bjoern Kalkbrenner <terminar@cyberphoria.org>
21 */
22
23/* plugin config for all request/connections */
24
25typedef struct {
26        array *match;
27} plugin_config;
28
29typedef struct {
30        PLUGIN_DATA;
31
32        buffer *match_buf;
33
34        plugin_config **config_storage;
35
36        plugin_config conf;
37} plugin_data;
38
39typedef struct {
40        int proceeded;
41        int handled;
42} handler_ctx;
43
44static handler_ctx * handler_ctx_init() {
45        handler_ctx * hctx;
46
47
48        hctx = calloc(1, sizeof(*hctx));
49
50        TRACE("handler_ctx_init for %i",hctx);
51
52        hctx->handled = 0;
53        hctx->proceeded = 0;
54
55        return hctx;
56}
57
58static void handler_ctx_free(handler_ctx *hctx) 
59{
60    if (hctx)
61    {
62        TRACE("handler_ctx_free for %i",hctx);
63
64        free(hctx);
65    }
66   
67}
68
69/* init the plugin data */
70INIT_FUNC(mod_traffic_init) {
71        plugin_data *p;
72       
73        UNUSED(srv);
74
75        p = calloc(1, sizeof(*p));
76
77        p->match_buf = buffer_init();
78
79        return p;
80}
81
82/* detroy the plugin data */
83FREE_FUNC(mod_traffic_free) {
84        plugin_data *p = p_d;
85
86        UNUSED(srv);
87
88        if (!p) return HANDLER_GO_ON;
89
90        if (p->config_storage) {
91                size_t i;
92
93                for (i = 0; i < srv->config_context->used; i++) {
94                        plugin_config *s = p->config_storage[i];
95
96                        if (!s) continue;
97
98                        array_free(s->match);
99
100                        free(s);
101                }
102                free(p->config_storage);
103        }
104
105        buffer_free(p->match_buf);
106
107        free(p);
108
109        return HANDLER_GO_ON;
110}
111
112/* handle plugin config and check values */
113
114SETDEFAULTS_FUNC(mod_traffic_set_defaults) {
115        plugin_data *p = p_d;
116        size_t i = 0;
117
118        config_values_t cv[] = {
119                { "traffic.array",             NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
120                { NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
121        };
122
123        if (!p) return HANDLER_ERROR;
124
125        p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
126
127        for (i = 0; i < srv->config_context->used; i++) {
128                plugin_config *s;
129
130                s = calloc(1, sizeof(plugin_config));
131                s->match    = array_init();
132
133                cv[0].destination = s->match;
134
135                p->config_storage[i] = s;
136
137                if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
138                        return HANDLER_ERROR;
139                }
140        }
141
142        return HANDLER_GO_ON;
143}
144
145static int mod_traffic_patch_connection(server *srv, connection *con, plugin_data *p) {
146        size_t i, j;
147        plugin_config *s = p->config_storage[0];
148
149        PATCH_OPTION(match);
150
151        /* skip the first, the global context */
152        for (i = 1; i < srv->config_context->used; i++) {
153                data_config *dc = (data_config *)srv->config_context->data[i];
154                s = p->config_storage[i];
155
156                /* condition didn't match */
157                if (!config_check_cond(srv, con, dc)) continue;
158
159                /* merge config */
160                for (j = 0; j < dc->value->used; j++) {
161                        data_unset *du = dc->value->data[j];
162
163                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("traffic.array"))) {
164                                PATCH_OPTION(match);
165                        }
166                }
167        }
168
169        return 0;
170}
171
172URIHANDLER_FUNC(mod_traffic_uri_handler) {
173        plugin_data *p = p_d;
174        int s_len;
175        size_t k;
176
177        UNUSED(srv);
178
179        if (con->uri.path->used == 0) return HANDLER_GO_ON;
180
181        mod_traffic_patch_connection(srv, con, p);
182
183        s_len = con->uri.path->used - 1;
184
185        for (k = 0; k < p->conf.match->used; k++) {
186                data_string *ds = (data_string *)p->conf.match->data[k];
187                int ct_len = ds->value->used - 1;
188
189                if (ct_len > s_len) continue;
190                if (ds->value->used == 0) continue;
191
192                if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
193                        con->http_status = 403;
194
195                        return HANDLER_FINISHED;
196                }
197        }
198
199        /* not found */
200        return HANDLER_GO_ON;
201}
202
203
204URIHANDLER_FUNC(mod_traffic_start_backend)
205{
206    UNUSED(srv);
207    UNUSED(con);
208    UNUSED(p_d);
209
210    plugin_data *p = p_d;
211
212    handler_ctx *hctx = con->plugin_ctx[p->id];
213    if (hctx)
214    {
215        TRACE("%s","resetting handled, proceeded");
216        hctx->proceeded = 0;
217        hctx->handled = 0;
218    }
219   
220   
221    TRACE("%s","mod_traffic_start_backend");
222    return HANDLER_GO_ON;
223}
224
225URIHANDLER_FUNC(mod_traffic_send_request_content)
226{
227    UNUSED(srv);
228    UNUSED(con);
229    UNUSED(p_d);
230   
231    TRACE("%s","mod_traffic_send_request_content");
232   
233
234   
235    return HANDLER_GO_ON;
236}
237
238URIHANDLER_FUNC(mod_traffic_response_header)
239{
240    UNUSED(srv);
241    UNUSED(con);
242    UNUSED(p_d);
243   
244    TRACE("%s","mod_traffic_response_header");
245    return HANDLER_GO_ON;
246}
247
248
249URIHANDLER_FUNC(mod_traffic_read_response_content)
250{
251
252    TRACE("%s","mod_traffic_read_response_content");
253                               
254    /* not sure if this can go into mod_traffic_send_request_content
255     * we use it here to proceed headers which came from mod_proxy_core/fastcgi/php
256     * does this also work in mod_traffic_send_request_content even if connections.c doesn't set connection_state to previous state?
257     * if it doesn't, mod_traffic_send_request_content is called before the X-LIGHTTPD-KBytes-per-second is set
258     *
259     * can the plugin be detached from the current processing loop for specific connections if it is proceeded before?
260     */
261    plugin_data *p = p_d;
262    handler_ctx *hctx;
263   
264    if (con->plugin_ctx[p->id])
265    {
266        hctx = con->plugin_ctx[p->id];
267    } else
268    {
269        hctx = handler_ctx_init();
270        con->plugin_ctx[p->id] = hctx;
271    }
272
273    if (hctx && !hctx->proceeded || (hctx->proceeded && hctx->handled) )
274    {
275        if (!hctx->proceeded)
276        {
277            data_string *ds;
278       
279            if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "X-LIGHTTPD-KBytes-per-second"))) {
280                TRACE("Found my header, setting speed: %s",ds->value->ptr);
281                con->conf.kbytes_per_second = atol(ds->value->ptr);
282                hctx->handled = 1;         
283            }
284       
285            hctx->proceeded = 1;
286        }
287       
288        if (hctx->handled)
289        {
290            /* this is for processing updates later for other stuff, tracing only at the moment */
291            int t_diff;
292            /* we don't like div by zero */
293            if (0 == (t_diff = srv->cur_ts - con->connection_start)) t_diff = 1;
294            int speed = con->bytes_written / t_diff / 1024;
295
296            TRACE("currspeed: %i",speed);
297           
298        }
299
300    }
301       
302    return HANDLER_GO_ON;
303}
304
305URIHANDLER_FUNC(mod_traffic_connection_reset)
306{
307    TRACE("%s","traffic_connection_reset");
308   
309    plugin_data *p = p_d;
310    UNUSED(srv);
311   
312    if (con->plugin_ctx[p->id]) 
313    {
314        handler_ctx_free(con->plugin_ctx[p->id]);
315        con->plugin_ctx[p->id] = NULL;
316    }
317
318    return HANDLER_GO_ON;
319}
320
321/* this function is called at dlopen() time and inits the callbacks */
322int mod_traffic_plugin_init(plugin *p) {
323        p->version     = LIGHTTPD_VERSION_ID;
324        p->name        = buffer_init_string("traffic");
325
326        p->init        = mod_traffic_init;
327        p->handle_uri_clean  = mod_traffic_uri_handler;
328        p->set_defaults  = mod_traffic_set_defaults;
329        p->cleanup     = mod_traffic_free;
330
331        p->handle_start_backend = mod_traffic_start_backend;
332        p->handle_send_request_content = mod_traffic_send_request_content;
333        p->handle_response_header = mod_traffic_response_header;
334        p->handle_read_response_content = mod_traffic_read_response_content;
335       
336        p->connection_reset = mod_traffic_connection_reset;
337//      p->handle_connection_close = mod_traffic_connection_close;
338
339        p->data        = NULL;
340
341        return 0;
342}