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