binary_streaming.1.5.0-svn-r2146.mod_flv_streaming.c

snailfly, 2008-05-04 09:33

Download (8.5 kB)

 
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
#include "response.h"
9
#include "stat_cache.h"
10

    
11
#include "plugin.h"
12

    
13
#ifdef HAVE_CONFIG_H
14
#include "config.h"
15
#endif
16

    
17
/* plugin config for all request/connections */
18

    
19
typedef struct {
20
        array *extensions;
21
        unsigned short binary_streaming;
22
} plugin_config;
23

    
24
typedef struct {
25
        PLUGIN_DATA;
26

    
27
        buffer *query_str;
28
        array *get_params;
29

    
30
        plugin_config **config_storage;
31

    
32
        plugin_config conf;
33
} plugin_data;
34

    
35
/* init the plugin data */
36
INIT_FUNC(mod_flv_streaming_init) {
37
        plugin_data *p;
38

    
39
        UNUSED(srv);
40

    
41
        p = calloc(1, sizeof(*p));
42

    
43
        p->query_str = buffer_init();
44
        p->get_params = array_init();
45

    
46
        return p;
47
}
48

    
49
/* detroy the plugin data */
50
FREE_FUNC(mod_flv_streaming_free) {
51
        plugin_data *p = p_d;
52

    
53
        UNUSED(srv);
54

    
55
        if (!p) return HANDLER_GO_ON;
56

    
57
        if (p->config_storage) {
58
                size_t i;
59

    
60
                for (i = 0; i < srv->config_context->used; i++) {
61
                        plugin_config *s = p->config_storage[i];
62

    
63
                        if (!s) continue;
64

    
65
                        array_free(s->extensions);
66

    
67
                        free(s);
68
                }
69
                free(p->config_storage);
70
        }
71

    
72
        buffer_free(p->query_str);
73
        array_free(p->get_params);
74

    
75
        free(p);
76

    
77
        return HANDLER_GO_ON;
78
}
79

    
80
/* handle plugin config and check values */
81

    
82
SETDEFAULTS_FUNC(mod_flv_streaming_set_defaults) {
83
        plugin_data *p = p_d;
84
        size_t i = 0;
85

    
86
        config_values_t cv[] = {
87
                { "flv-streaming.extensions",   NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
88
                { "flv-streaming.binary-streaming", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
89
                { NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
90
        };
91

    
92
        if (!p) return HANDLER_ERROR;
93

    
94
        p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
95

    
96
        for (i = 0; i < srv->config_context->used; i++) {
97
                plugin_config *s;
98

    
99
                s = calloc(1, sizeof(plugin_config));
100
                s->extensions     = array_init();
101
                s->binary_streaming     = 0;
102

    
103
                cv[0].destination = s->extensions;
104
                cv[1].destination = &(s->binary_streaming);
105

    
106
                p->config_storage[i] = s;
107

    
108
                if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
109
                        return HANDLER_ERROR;
110
                }
111
        }
112

    
113
        return HANDLER_GO_ON;
114
}
115

    
116
static int mod_flv_streaming_patch_connection(server *srv, connection *con, plugin_data *p) {
117
        size_t i, j;
118
        plugin_config *s = p->config_storage[0];
119

    
120
        PATCH_OPTION(extensions);
121
        PATCH_OPTION(binary_streaming);
122

    
123
        /* skip the first, the global context */
124
        for (i = 1; i < srv->config_context->used; i++) {
125
                data_config *dc = (data_config *)srv->config_context->data[i];
126
                s = p->config_storage[i];
127

    
128
                /* condition didn't match */
129
                if (!config_check_cond(srv, con, dc)) continue;
130

    
131
                /* merge config */
132
                for (j = 0; j < dc->value->used; j++) {
133
                        data_unset *du = dc->value->data[j];
134

    
135
                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("flv-streaming.extensions"))) {
136
                                PATCH_OPTION(extensions);
137
                        }else if (buffer_is_equal_string(du->key, CONST_STR_LEN("flv-streaming.binary-streaming"))) {
138
                                PATCH_OPTION(binary_streaming);
139
                        }
140
                }
141
        }
142

    
143
        return 0;
144
}
145

    
146
static int split_get_params(array *get_params, buffer *qrystr) {
147
        size_t is_key = 1;
148
        size_t i;
149
        char *key = NULL, *val = NULL;
150

    
151
        key = qrystr->ptr;
152

    
153
        /* we need the \0 */
154
        for (i = 0; i < qrystr->used; i++) {
155
                switch(qrystr->ptr[i]) {
156
                case '=':
157
                        if (is_key) {
158
                                val = qrystr->ptr + i + 1;
159

    
160
                                qrystr->ptr[i] = '\0';
161

    
162
                                is_key = 0;
163
                        }
164

    
165
                        break;
166
                case '&':
167
                case '\0': /* fin symbol */
168
                        if (!is_key) {
169
                                data_string *ds;
170
                                /* we need at least a = since the last & */
171

    
172
                                /* terminate the value */
173
                                qrystr->ptr[i] = '\0';
174

    
175
                                if (NULL == (ds = (data_string *)array_get_unused_element(get_params, TYPE_STRING))) {
176
                                        ds = data_string_init();
177
                                }
178
                                buffer_copy_string_len(ds->key, key, strlen(key));
179
                                buffer_copy_string_len(ds->value, val, strlen(val));
180

    
181
                                array_insert_unique(get_params, (data_unset *)ds);
182
                        }
183

    
184
                        key = qrystr->ptr + i + 1;
185
                        val = NULL;
186
                        is_key = 1;
187
                        break;
188
                }
189
        }
190

    
191
        return 0;
192
}
193

    
194
URIHANDLER_FUNC(mod_flv_streaming_path_handler) {
195
        plugin_data *p = p_d;
196
        int s_len;
197
        size_t k;
198

    
199
        UNUSED(srv);
200

    
201
        if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
202

    
203
        if (con->conf.log_request_handling) {
204
                TRACE("-- handling %s in mod_flv_streaming", BUF_STR(con->physical.path));
205
        }
206

    
207
        mod_flv_streaming_patch_connection(srv, con, p);
208

    
209
        s_len = con->physical.path->used - 1;
210

    
211
        for (k = 0; k < p->conf.extensions->used; k++) {
212
                data_string *ds = (data_string *)p->conf.extensions->data[k];
213
                int ct_len = ds->value->used - 1;
214

    
215
                if (ct_len > s_len) continue;
216
                if (ds->value->used == 0) continue;
217

    
218
                if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
219
                        data_string *get_param;
220
                        stat_cache_entry *sce = NULL;
221
                        buffer *b;
222
                        long start;
223
                        char *err = NULL;
224
                        /* if there is a start=[0-9]+ in the header use it as start,
225
                         * otherwise send the full file */
226

    
227
                        array_reset(p->get_params);
228
                        buffer_copy_string_buffer(p->query_str, con->uri.query);
229
                        split_get_params(p->get_params, p->query_str);
230

    
231
                        if (NULL == (get_param = (data_string *)array_get_element(p->get_params, CONST_STR_LEN("start")))) {
232
                                if (con->conf.log_request_handling) {
233
                                        TRACE("start=... not found, skipping %s", BUF_STR(con->physical.path));
234
                                }
235

    
236
                                return HANDLER_GO_ON;
237
                        }
238

    
239
                        /* too short */
240
                        if (get_param->value->used < 2) {
241
                                if (con->conf.log_request_handling) {
242
                                        TRACE("start=... found, but empty, skipping %s", BUF_STR(con->physical.path));
243
                                }
244

    
245
                                return HANDLER_GO_ON;
246
                        }
247

    
248
                        /* check if it is a number */
249
                        start = strtol(get_param->value->ptr, &err, 10);
250
                        if (*err != '\0') {
251
                                if (con->conf.log_request_handling) {
252
                                        TRACE("parsing start '%s' as number failed, skipping %s", 
253
                                                        BUF_STR(get_param->value), BUF_STR(con->physical.path));
254
                                }
255

    
256
                                return HANDLER_GO_ON;
257
                        }
258

    
259
                        if (start <= 0) {
260
                                if (con->conf.log_request_handling) {
261
                                        TRACE("start is <= 0, skipping %s", BUF_STR(con->physical.path));
262
                                }
263

    
264
                                return HANDLER_GO_ON;
265
                        }
266

    
267
                        /* check if start is > filesize */
268
                        if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
269
                                if (con->conf.log_request_handling) {
270
                                        TRACE("stat() for %s failed", BUF_STR(con->physical.path));
271
                                }
272

    
273
                                return HANDLER_GO_ON;
274
                        }
275

    
276
                        if (start > sce->st.st_size) {
277
                                if (con->conf.log_request_handling) {
278
                                        TRACE("start > file-size, skipping %s", BUF_STR(con->physical.path));
279
                                }
280

    
281
                                return HANDLER_GO_ON;
282
                        }
283

    
284
                        if(p->conf.binary_streaming){
285
                                
286
                                /* we are safe now, let's build a Range request header */
287
                                
288
                                //request: Range: bytes=217404-
289
                                //response: Content-Range: bytes 217404-3043664/3043665
290
                                
291
                                data_string *hdr;
292
                                
293
                                if (NULL == (hdr = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
294
                                        hdr = data_string_init();
295
                                }
296
                        
297
                                buffer_copy_string(hdr->key, "Range");
298
                                buffer_copy_string(hdr->value, "bytes=");
299
                                buffer_append_long(hdr->value, start);
300
                                buffer_append_string(hdr->value, "-");
301
                        
302
                                array_insert_unique(con->request.headers, (data_unset *)hdr);
303
                        
304
                                sendlen = sce->st.st_size - start;
305
                                if (con->conf.log_request_handling) {
306
                                        TRACE("sending %s from position %d, size %d ( %d to %d ).", con->physical.path->ptr, start, sendlen, start, sce->st.st_size);
307
                                }
308
                        
309
                                return HANDLER_GO_ON;
310
                        }else{
311
                                /* we are safe now, let's build a flv header */
312
                                b = chunkqueue_get_append_buffer(con->send);
313
                                BUFFER_COPY_STRING_CONST(b, "FLV\x1\x1\0\0\0\x9\0\0\0\x9");
314
                                
315
                                chunkqueue_append_file(con->send, con->physical.path, start, sce->st.st_size - start);
316
                                
317
                                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv"));
318
                                
319
                                con->send->is_closed = 1;
320
                                
321
                                if (con->conf.log_request_handling) {
322
                                        TRACE("sending %s from position %ld", BUF_STR(con->physical.path), start);
323
                                }
324
                                
325
                                return HANDLER_FINISHED;
326
                        }
327
                }
328
        }
329

    
330
        if (con->conf.log_request_handling) {
331
                TRACE("none of the extensions matched %s, leaving", BUF_STR(con->physical.path));
332
        }
333

    
334
        /* not found */
335
        return HANDLER_GO_ON;
336
}
337

    
338
/* this function is called at dlopen() time and inits the callbacks */
339

    
340
LI_EXPORT int mod_flv_streaming_plugin_init(plugin *p) {
341
        p->version     = LIGHTTPD_VERSION_ID;
342
        p->name        = buffer_init_string("flv_streaming");
343

    
344
        p->init        = mod_flv_streaming_init;
345
        p->handle_physical = mod_flv_streaming_path_handler;
346
        p->set_defaults  = mod_flv_streaming_set_defaults;
347
        p->cleanup     = mod_flv_streaming_free;
348

    
349
        p->data        = NULL;
350

    
351
        return 0;
352
}