mirror of
https://github.com/nginx/nginx.git
synced 2025-06-22 22:10:45 +08:00

Since 4611:2b6cb7528409 responses from the gzip static, flv, and mp4 modules can be used with subrequests, though empty files were not properly handled. Empty gzipped, flv, and mp4 files thus resulted in "zero size buf in output" alerts. While valid corresponding files are not expected to be empty, such files shouldn't result in alerts. Fix is to set b->sync on such empty subrequest responses, similarly to what ngx_http_send_special() does. Additionally, the static module, the ngx_http_send_response() function, and file cache are modified to do the same instead of not sending the response body at all in such cases, since not sending the response body at all is believed to be at least questionable, and might break various filters which do not expect such behaviour.
262 lines
6.3 KiB
C
262 lines
6.3 KiB
C
|
|
/*
|
|
* Copyright (C) Igor Sysoev
|
|
* Copyright (C) Nginx, Inc.
|
|
*/
|
|
|
|
#include <ngx_config.h>
|
|
#include <ngx_core.h>
|
|
#include <ngx_http.h>
|
|
|
|
|
|
static char *ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
|
|
|
static ngx_command_t ngx_http_flv_commands[] = {
|
|
|
|
{ ngx_string("flv"),
|
|
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
|
|
ngx_http_flv,
|
|
0,
|
|
0,
|
|
NULL },
|
|
|
|
ngx_null_command
|
|
};
|
|
|
|
|
|
static u_char ngx_flv_header[] = "FLV\x1\x5\0\0\0\x9\0\0\0\0";
|
|
|
|
|
|
static ngx_http_module_t ngx_http_flv_module_ctx = {
|
|
NULL, /* preconfiguration */
|
|
NULL, /* postconfiguration */
|
|
|
|
NULL, /* create main configuration */
|
|
NULL, /* init main configuration */
|
|
|
|
NULL, /* create server configuration */
|
|
NULL, /* merge server configuration */
|
|
|
|
NULL, /* create location configuration */
|
|
NULL /* merge location configuration */
|
|
};
|
|
|
|
|
|
ngx_module_t ngx_http_flv_module = {
|
|
NGX_MODULE_V1,
|
|
&ngx_http_flv_module_ctx, /* module context */
|
|
ngx_http_flv_commands, /* module directives */
|
|
NGX_HTTP_MODULE, /* module type */
|
|
NULL, /* init master */
|
|
NULL, /* init module */
|
|
NULL, /* init process */
|
|
NULL, /* init thread */
|
|
NULL, /* exit thread */
|
|
NULL, /* exit process */
|
|
NULL, /* exit master */
|
|
NGX_MODULE_V1_PADDING
|
|
};
|
|
|
|
|
|
static ngx_int_t
|
|
ngx_http_flv_handler(ngx_http_request_t *r)
|
|
{
|
|
u_char *last;
|
|
off_t start, len;
|
|
size_t root;
|
|
ngx_int_t rc;
|
|
ngx_uint_t level, i;
|
|
ngx_str_t path, value;
|
|
ngx_log_t *log;
|
|
ngx_buf_t *b;
|
|
ngx_chain_t out[2];
|
|
ngx_open_file_info_t of;
|
|
ngx_http_core_loc_conf_t *clcf;
|
|
|
|
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
|
|
return NGX_HTTP_NOT_ALLOWED;
|
|
}
|
|
|
|
if (r->uri.data[r->uri.len - 1] == '/') {
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
rc = ngx_http_discard_request_body(r);
|
|
|
|
if (rc != NGX_OK) {
|
|
return rc;
|
|
}
|
|
|
|
last = ngx_http_map_uri_to_path(r, &path, &root, 0);
|
|
if (last == NULL) {
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
log = r->connection->log;
|
|
|
|
path.len = last - path.data;
|
|
|
|
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
|
|
"http flv filename: \"%V\"", &path);
|
|
|
|
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
|
|
|
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
|
|
|
|
of.read_ahead = clcf->read_ahead;
|
|
of.directio = clcf->directio;
|
|
of.valid = clcf->open_file_cache_valid;
|
|
of.min_uses = clcf->open_file_cache_min_uses;
|
|
of.errors = clcf->open_file_cache_errors;
|
|
of.events = clcf->open_file_cache_events;
|
|
|
|
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
|
|
!= NGX_OK)
|
|
{
|
|
switch (of.err) {
|
|
|
|
case 0:
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
|
|
case NGX_ENOENT:
|
|
case NGX_ENOTDIR:
|
|
case NGX_ENAMETOOLONG:
|
|
|
|
level = NGX_LOG_ERR;
|
|
rc = NGX_HTTP_NOT_FOUND;
|
|
break;
|
|
|
|
case NGX_EACCES:
|
|
#if (NGX_HAVE_OPENAT)
|
|
case NGX_EMLINK:
|
|
case NGX_ELOOP:
|
|
#endif
|
|
|
|
level = NGX_LOG_ERR;
|
|
rc = NGX_HTTP_FORBIDDEN;
|
|
break;
|
|
|
|
default:
|
|
|
|
level = NGX_LOG_CRIT;
|
|
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
|
|
ngx_log_error(level, log, of.err,
|
|
"%s \"%s\" failed", of.failed, path.data);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
if (!of.is_file) {
|
|
return NGX_DECLINED;
|
|
}
|
|
|
|
r->root_tested = !r->error_page;
|
|
|
|
start = 0;
|
|
len = of.size;
|
|
i = 1;
|
|
|
|
if (r->args.len) {
|
|
|
|
if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
|
|
|
|
start = ngx_atoof(value.data, value.len);
|
|
|
|
if (start == NGX_ERROR || start >= len) {
|
|
start = 0;
|
|
}
|
|
|
|
if (start) {
|
|
len = sizeof(ngx_flv_header) - 1 + len - start;
|
|
i = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
log->action = "sending flv to client";
|
|
|
|
r->headers_out.status = NGX_HTTP_OK;
|
|
r->headers_out.content_length_n = len;
|
|
r->headers_out.last_modified_time = of.mtime;
|
|
|
|
if (ngx_http_set_etag(r) != NGX_OK) {
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
if (ngx_http_set_content_type(r) != NGX_OK) {
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
if (i == 0) {
|
|
b = ngx_calloc_buf(r->pool);
|
|
if (b == NULL) {
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
b->pos = ngx_flv_header;
|
|
b->last = ngx_flv_header + sizeof(ngx_flv_header) - 1;
|
|
b->memory = 1;
|
|
|
|
out[0].buf = b;
|
|
out[0].next = &out[1];
|
|
}
|
|
|
|
|
|
b = ngx_calloc_buf(r->pool);
|
|
if (b == NULL) {
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
|
|
if (b->file == NULL) {
|
|
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
}
|
|
|
|
r->allow_ranges = 1;
|
|
|
|
rc = ngx_http_send_header(r);
|
|
|
|
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
|
|
return rc;
|
|
}
|
|
|
|
b->file_pos = start;
|
|
b->file_last = of.size;
|
|
|
|
b->in_file = b->file_last ? 1 : 0;
|
|
b->last_buf = (r == r->main) ? 1 : 0;
|
|
b->last_in_chain = 1;
|
|
b->sync = (b->last_buf || b->in_file) ? 0 : 1;
|
|
|
|
b->file->fd = of.fd;
|
|
b->file->name = path;
|
|
b->file->log = log;
|
|
b->file->directio = of.is_directio;
|
|
|
|
out[1].buf = b;
|
|
out[1].next = NULL;
|
|
|
|
return ngx_http_output_filter(r, &out[i]);
|
|
}
|
|
|
|
|
|
static char *
|
|
ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
|
{
|
|
ngx_http_core_loc_conf_t *clcf;
|
|
|
|
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
|
|
clcf->handler = ngx_http_flv_handler;
|
|
|
|
return NGX_CONF_OK;
|
|
}
|