upstream nginx-0.7.43
[nginx.git] / nginx / src / http / modules / ngx_http_flv_module.c
1
2 /*
3  * Copyright (C) Igor Sysoev
4  */
5
6 #include <ngx_config.h>
7 #include <ngx_core.h>
8 #include <ngx_http.h>
9
10
11 static char *ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
12
13 static ngx_command_t  ngx_http_flv_commands[] = {
14
15     { ngx_string("flv"),
16       NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
17       ngx_http_flv,
18       0,
19       0,
20       NULL },
21
22       ngx_null_command
23 };
24
25
26 static u_char  ngx_flv_header[] = "FLV\x1\x1\0\0\0\x9\0\0\0\x9";
27
28
29 static ngx_http_module_t  ngx_http_flv_module_ctx = {
30     NULL,                          /* preconfiguration */
31     NULL,                          /* postconfiguration */
32
33     NULL,                          /* create main configuration */
34     NULL,                          /* init main configuration */
35
36     NULL,                          /* create server configuration */
37     NULL,                          /* merge server configuration */
38
39     NULL,                          /* create location configuration */
40     NULL                           /* merge location configuration */
41 };
42
43
44 ngx_module_t  ngx_http_flv_module = {
45     NGX_MODULE_V1,
46     &ngx_http_flv_module_ctx,      /* module context */
47     ngx_http_flv_commands,         /* module directives */
48     NGX_HTTP_MODULE,               /* module type */
49     NULL,                          /* init master */
50     NULL,                          /* init module */
51     NULL,                          /* init process */
52     NULL,                          /* init thread */
53     NULL,                          /* exit thread */
54     NULL,                          /* exit process */
55     NULL,                          /* exit master */
56     NGX_MODULE_V1_PADDING
57 };
58
59
60 static ngx_int_t
61 ngx_http_flv_handler(ngx_http_request_t *r)
62 {
63     u_char                    *last;
64     off_t                      start, len;
65     size_t                     root;
66     ngx_int_t                  rc;
67     ngx_uint_t                 level, i;
68     ngx_str_t                  path, value;
69     ngx_log_t                 *log;
70     ngx_buf_t                 *b;
71     ngx_chain_t                out[2];
72     ngx_open_file_info_t       of;
73     ngx_http_core_loc_conf_t  *clcf;
74
75     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
76         return NGX_HTTP_NOT_ALLOWED;
77     }
78
79     if (r->uri.data[r->uri.len - 1] == '/') {
80         return NGX_DECLINED;
81     }
82
83     /* TODO: Win32 */
84     if (r->zero_in_uri) {
85         return NGX_DECLINED;
86     }
87
88     rc = ngx_http_discard_request_body(r);
89
90     if (rc != NGX_OK) {
91         return rc;
92     }
93
94     last = ngx_http_map_uri_to_path(r, &path, &root, 0);
95     if (last == NULL) {
96         return NGX_HTTP_INTERNAL_SERVER_ERROR;
97     }
98
99     log = r->connection->log;
100
101     path.len = last - path.data;
102
103     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
104                    "http flv filename: \"%V\"", &path);
105
106     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
107
108     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
109
110     of.directio = clcf->directio;
111     of.valid = clcf->open_file_cache_valid;
112     of.min_uses = clcf->open_file_cache_min_uses;
113     of.errors = clcf->open_file_cache_errors;
114     of.events = clcf->open_file_cache_events;
115
116     if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
117         != NGX_OK)
118     {
119         switch (of.err) {
120
121         case 0:
122             return NGX_HTTP_INTERNAL_SERVER_ERROR;
123
124         case NGX_ENOENT:
125         case NGX_ENOTDIR:
126         case NGX_ENAMETOOLONG:
127
128             level = NGX_LOG_ERR;
129             rc = NGX_HTTP_NOT_FOUND;
130             break;
131
132         case NGX_EACCES:
133
134             level = NGX_LOG_ERR;
135             rc = NGX_HTTP_FORBIDDEN;
136             break;
137
138         default:
139
140             level = NGX_LOG_CRIT;
141             rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
142             break;
143         }
144
145         if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
146             ngx_log_error(level, log, of.err,
147                           ngx_open_file_n " \"%s\" failed", path.data);
148         }
149
150         return rc;
151     }
152
153     if (!of.is_file) {
154
155         if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
156             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
157                           ngx_close_file_n " \"%s\" failed", path.data);
158         }
159
160         return NGX_DECLINED;
161     }
162
163     r->root_tested = !r->error_page;
164
165     start = 0;
166     len = of.size;
167     i = 1;
168
169     if (r->args.len) {
170
171         if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
172
173             start = ngx_atoof(value.data, value.len);
174
175             if (start == NGX_ERROR || start >= len) {
176                 start = 0;
177             }
178
179             if (start) {
180                 len = sizeof(ngx_flv_header) - 1 + len - start;
181                 i = 0;
182             }
183         }
184     }
185
186     log->action = "sending flv to client";
187
188     r->headers_out.status = NGX_HTTP_OK;
189     r->headers_out.content_length_n = len;
190     r->headers_out.last_modified_time = of.mtime;
191
192     if (ngx_http_set_content_type(r) != NGX_OK) {
193         return NGX_HTTP_INTERNAL_SERVER_ERROR;
194     }
195
196     if (i == 0) {
197         b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
198         if (b == NULL) {
199             return NGX_HTTP_INTERNAL_SERVER_ERROR;
200         }
201
202         b->pos = ngx_flv_header;
203         b->last = ngx_flv_header + sizeof(ngx_flv_header) - 1;
204         b->memory = 1;
205
206         out[0].buf = b;
207         out[0].next = &out[1];
208     }
209
210
211     b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
212     if (b == NULL) {
213         return NGX_HTTP_INTERNAL_SERVER_ERROR;
214     }
215
216     b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
217     if (b->file == NULL) {
218         return NGX_HTTP_INTERNAL_SERVER_ERROR;
219     }
220
221     r->allow_ranges = 1;
222
223     rc = ngx_http_send_header(r);
224
225     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
226         return rc;
227     }
228
229     b->file_pos = start;
230     b->file_last = of.size;
231
232     b->in_file = b->file_last ? 1: 0;
233     b->last_buf = 1;
234     b->last_in_chain = 1;
235
236     b->file->fd = of.fd;
237     b->file->name = path;
238     b->file->log = log;
239     b->file->directio = of.is_directio;
240
241     out[1].buf = b;
242     out[1].next = NULL;
243
244     return ngx_http_output_filter(r, &out[i]);
245 }
246
247
248 static char *
249 ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
250 {
251     ngx_http_core_loc_conf_t  *clcf;
252
253     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
254     clcf->handler = ngx_http_flv_handler;
255
256     return NGX_CONF_OK;
257 }