Project

General

Profile

mod_traffic.c

Terminar, 2006-12-30 22:08

 
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

    
25
typedef struct {
26
        array *match;
27
} plugin_config;
28

    
29
typedef struct {
30
        PLUGIN_DATA;
31

    
32
        buffer *match_buf;
33

    
34
        plugin_config **config_storage;
35

    
36
        plugin_config conf;
37
} plugin_data;
38

    
39
typedef struct {
40
        int proceeded;
41
        int handled;
42
} handler_ctx;
43

    
44
static 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

    
58
static 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 */
70
INIT_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 */
83
FREE_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

    
114
SETDEFAULTS_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

    
145
static 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

    
172
URIHANDLER_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

    
204
URIHANDLER_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

    
225
URIHANDLER_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

    
238
URIHANDLER_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

    
249
URIHANDLER_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

    
305
URIHANDLER_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 */
322
int 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
}