static ngx_int_t ngx_http_mp4frag_handler(ngx_http_request_t *r)
{
    ngx_int_t    rc;
    ngx_chain_t  out;
    u_char *resp_body;
    ngx_buf_t   *resp;
    char *lastslash;
    char *mediafilename;
    u_char *last;
    size_t root;
    ngx_str_t path;
    ngx_str_t index_map = ngx_null_string;
    const u_char *indexptr;
    ngx_str_t mediafile_map = ngx_null_string;
    unsigned medianum, fragnum;
    uint16_t nmedia;
    ngx_str_t videocodecdata, audiocodecdata;
    uint32_t mediaoffset, fragments_offset;
    uint16_t mediafilenamelen;
    uint16_t nsamples, nfragments;
    uint32_t totalsize;
    unsigned int iii;
    ngx_table_elt_t *self;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
        return NGX_HTTP_NOT_ALLOWED;
    }
 
    if ( (rc = ngx_http_discard_request_body(r)) != NGX_OK ) {
        return rc;
    }

    if ( (self = ngx_list_push(&r->headers_out.headers)) == NULL ) {
        ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "Insufficient memory for ngx_list_push");
        return NGX_ERROR;
    }
    self->hash = 1;
    ngx_str_set(&self->key, "X-Mp4frag-Version");
    ngx_str_set(&self->value, VERSION);
 
    last = ngx_http_map_uri_to_path(r, &path, &root, 0);

    ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "path=%s", path.data);

    lastslash = strrchr((char*)path.data, '/');
    if ( lastslash ) {
        path.len = lastslash - (char*)path.data;
    }

    /* определить путь к файлу индекса и номер фрагмента: */
    *lastslash++ = 0;
    // uri - dirname, lastslash - basename
    if ( memcmp(lastslash, "Seg1-Frag", 9) != 0 ) {
        return NGX_HTTP_NOT_FOUND;
    }
    fragnum = atoi(lastslash + 9);

    lastslash = strrchr((char*)path.data, '/');
    if ( !lastslash ) {
        return NGX_HTTP_NOT_FOUND;
    }
    medianum = atoi(lastslash + 1);

    memcpy(lastslash + 1, "index", 6);
    ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "index_path=%s", path.data);
    
    if ( (rc = make_mapping((const char *)path.data, &index_map, r)) != NGX_OK ) {
        return rc;
    }
    indexptr = index_map.data;

#define CHECKMAP(nbytes) do { if (indexptr + nbytes >= index_map.data + index_map.len) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "index underrun, offset 0x%0x", nbytes); goto GENERAL_ERROR;}  } while(0)

    CHECKMAP(10); /* 8 for signature and 2 for media count */
    if ( memcmp(index_map.data, "mp4frag", 7) != 0 || index_map.data[7] /* version */ > 2 ) {
        free_mapping(&index_map);
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Bad index format in %s", path.data);
        goto GENERAL_ERROR;
    }
    indexptr += 8;

    nmedia = get16(indexptr);
    if ( medianum >= nmedia ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "No media #%d in %s, total %d media", medianum, path.data, nmedia);
        goto GENERAL_ERROR;
    }
    indexptr += 2;
    CHECKMAP(nmedia * 4);

    mediaoffset = get32(indexptr + medianum * 4);
    if ( mediaoffset >= index_map.len ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "short index in %s", path.data);
        goto GENERAL_ERROR;
    }
    indexptr = index_map.data;
    CHECKMAP(mediaoffset);
    indexptr = index_map.data + mediaoffset;

    CHECKMAP(2);
    mediafilenamelen = get16(indexptr);
    indexptr += 2;
    CHECKMAP(mediafilenamelen);
    if ( index_map.data[7] == 1 ) {
        if ( (mediafilename = ngx_pcalloc(r->pool, mediafilenamelen + 1)) == NULL ) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Insufficient memory");
            goto GENERAL_ERROR;
        }
        memcpy(mediafilename, (const char *)indexptr, mediafilenamelen);
        mediafilename[mediafilenamelen] = 0;
    }
    else /* index_map.data[7] == 2 */ {
        mediafilename = (char *)indexptr;
    }

    indexptr += mediafilenamelen;
 
    CHECKMAP(2);
    videocodecdata.len = get16(indexptr);
    indexptr += 2;
    CHECKMAP(videocodecdata.len);
    videocodecdata.data = (u_char*)indexptr;
    indexptr += videocodecdata.len;
    CHECKMAP(2);
    audiocodecdata.len = get16(indexptr);
    indexptr += 2;
    CHECKMAP(audiocodecdata.len);
    audiocodecdata.data = (u_char*)indexptr;
    indexptr += audiocodecdata.len;

    /* number of fragments in the media */
    CHECKMAP(2);
    nfragments = get16(indexptr);
    if ( fragnum > nfragments || fragnum < 1 ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "No fragment #%d in media #%d in file %s", fragnum, medianum, path.data);
        goto NOT_FOUND;
    }
    indexptr += 2;

    CHECKMAP(nfragments * 4);

    fragments_offset = get32(indexptr + (fragnum - 1) * 4);
    indexptr = index_map.data;
    CHECKMAP(fragments_offset);
    indexptr += fragments_offset;

    CHECKMAP(6);  /* first entry should present anyway */
    nsamples = get16(indexptr);
    if ( nsamples < 1 ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Number of samples is 0 for media #%d, fragment #%d, index %s",
                      medianum, fragnum, path.data);
        goto GENERAL_ERROR;
    }

    /* total fragment size in bytes: */
    totalsize = get32(indexptr + 2);

    ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "nsamples=%d, totalsize=%d", nsamples, totalsize);

    indexptr += 6;

    /* allocate memory for response */
    if ( (resp_body = ngx_pcalloc(r->pool, totalsize)) == NULL ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Insufficient memory");
        goto GENERAL_ERROR;
    }

    if ( (resp = ngx_pcalloc(r->pool, sizeof(ngx_buf_t))) == NULL ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Insufficient memory");
        goto GENERAL_ERROR;
    }
 
    out.buf = resp;
    out.next = NULL;

    resp->pos = resp_body;
    resp->last = resp_body + totalsize;
    resp->memory = 1; 
    resp->last_buf = 1;
 
    if ( make_mapping(mediafilename, &mediafile_map, r) != NGX_OK ) goto GENERAL_ERROR;
 
    /* generate the fragment */
    write32(resp_body, totalsize);
    memcpy(resp_body + 4, "mdat", 4);

    CHECKMAP(16 * nsamples - 1); /* check if the last byte still inside the index */

    /* fragment timestamp is equal to first sample timestamp */
    resp_body = write_fragment_prefix(resp_body + 8, &videocodecdata, &audiocodecdata, get32(indexptr + 8));

    for ( iii = 0; iii < nsamples; ++iii ) {
        uint32_t offset = get32(indexptr);
        uint32_t size = get32(indexptr + 4);
        uint32_t timestamp = get32(indexptr + 8);
        uint32_t composition_offset = get24(indexptr + 12);
        uint8_t flags = indexptr[15];
        indexptr += 16;
        if ( flags ) {
            resp_body = write_video_packet(resp_body, flags & 2, 0, composition_offset, timestamp,
                                     mediafile_map.data + offset, size);
        }
        else {
            resp_body = write_audio_packet(resp_body, 0, timestamp, mediafile_map.data + offset, size);
        }
    }

    free_mapping(&index_map);
    free_mapping(&mediafile_map);

    if ( resp_body != resp->last ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "response buffer overrun");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = totalsize;

    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only || r->method == NGX_HTTP_HEAD) {
        return rc;
    }

    /* send the buffer chain of your response */
    return ngx_http_output_filter(r, &out);

GENERAL_ERROR:
    free_mapping(&index_map);
    free_mapping(&mediafile_map);
    return NGX_HTTP_INTERNAL_SERVER_ERROR;

NOT_FOUND:
    free_mapping(&index_map);
    free_mapping(&mediafile_map);
    return NGX_HTTP_NOT_FOUND;

}
/****************************************************************** 
		index指令的理解,当访问某个目录时未指定文件,如果此location
		设置了index指令,将返回index指令设置的页面
 ******************************************************************/
static ngx_int_t
ngx_http_index_handler(ngx_http_request_t *r)
{
    u_char                       *p, *name;
    size_t                        len, root, reserve, allocated;
    ngx_int_t                     rc;
    ngx_str_t                     path, uri;
    ngx_uint_t                    i, dir_tested;
    ngx_http_index_t             *index;
    ngx_open_file_info_t          of;
    ngx_http_script_code_pt       code;
    ngx_http_script_engine_t      e;
    ngx_http_core_loc_conf_t     *clcf;
    ngx_http_index_loc_conf_t    *ilcf;
    ngx_http_script_len_code_pt   lcode;

    if (r->uri.data[r->uri.len - 1] != '/') {		//	uri不是“/”结尾时,说明uri中最后指定了文件,index和autoindex模块将不处理,交由static模块处理
        return NGX_DECLINED;
    }

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
        return NGX_DECLINED;
    }

	//	这里获取的r->loc_conf可能是server层的也有可能是location层的,需要检查是否有匹配到配置文件中的Location
    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);			
    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    allocated = 0;
    root = 0;
    dir_tested = 0;
    name = NULL;
    /* suppress MSVC warning */
    path.data = NULL;

    index = ilcf->indices->elts;
    for (i = 0; i < ilcf->indices->nelts; i++) {	//	遍历index指令指定的每个参数

        if (index[i].lengths == NULL) {				//	index指令参数中没有使用变量时

            if (index[i].name.data[0] == '/') {		//	index指定的参数以"/"开始的, 将产生内部重定向。
                return ngx_http_internal_redirect(r, &index[i].name, &r->args);
            }

            reserve = ilcf->max_index_len;
            len = index[i].name.len;

        } else {									//	index指令参数使用了变量

			//	计算index指令参数指定的变量+常量字符串的长度
            ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

            e.ip = index[i].lengths->elts;
            e.request = r;
            e.flushed = 1;

            /* 1 is for terminating '\0' as in static names */
            len = 1;

            while (*(uintptr_t *) e.ip) {
                lcode = *(ngx_http_script_len_code_pt *) e.ip;
                len += lcode(&e);
            }

            /* 16 bytes are preallocation */

            reserve = len + 16;
        }

        if (reserve > allocated) {

            name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
            if (name == NULL) {
                return NGX_ERROR;
            }

            allocated = path.data + path.len - name;			//	???
        }

        if (index[i].values == NULL) {

            /* index[i].name.len includes the terminating '\0' */

            ngx_memcpy(name, index[i].name.data, index[i].name.len);			//	拷贝index指令指定的参数(e.g. "/usr/local/nginx/html/index1/index.html")

            path.len = (name + index[i].name.len - 1) - path.data;

        } else {
            e.ip = index[i].values->elts;
            e.pos = name;

            while (*(uintptr_t *) e.ip) {
                code = *(ngx_http_script_code_pt *) e.ip;
                code((ngx_http_script_engine_t *) &e);
            }

            if (*name == '/') {
                uri.len = len - 1;
                uri.data = name;
                return ngx_http_internal_redirect(r, &uri, &r->args);
            }

            path.len = e.pos - path.data;

            *e.pos = '\0';
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "open index \"%V\"", &path);

        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.test_only = 1;
        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)
        {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
                           "%s \"%s\" failed", of.failed, path.data);

            if (of.err == 0) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

#if (NGX_HAVE_OPENAT)
            if (of.err == NGX_EMLINK
                || of.err == NGX_ELOOP)
            {
                return NGX_HTTP_FORBIDDEN;
            }
#endif

            if (of.err == NGX_ENOTDIR
                || of.err == NGX_ENAMETOOLONG
                || of.err == NGX_EACCES)
            {
                return ngx_http_index_error(r, clcf, path.data, of.err);
            }

            if (!dir_tested) {
                rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);

                if (rc != NGX_OK) {
                    return rc;
                }

                dir_tested = 1;
            }

            if (of.err == NGX_ENOENT) {
                continue;
            }

            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
                          "%s \"%s\" failed", of.failed, path.data);

            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        uri.len = r->uri.len + len - 1;

        if (!clcf->alias) {
            uri.data = path.data + root;

        } else {
            uri.data = ngx_pnalloc(r->pool, uri.len);
            if (uri.data == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            p = ngx_copy(uri.data, r->uri.data, r->uri.len);
            ngx_memcpy(p, name, len - 1);
        }

        return ngx_http_internal_redirect(r, &uri, &r->args);
    }//	End for

    return NGX_DECLINED;
}
static ngx_int_t
ngx_http_index_handler(ngx_http_request_t *r)
{
    u_char                       *p, *name;
    size_t                        len, root, reserve, allocated;
    ngx_int_t                     rc;
    ngx_str_t                     path, uri;
    ngx_uint_t                    i, dir_tested;
    ngx_http_index_t             *index;
    ngx_open_file_info_t          of;
    ngx_http_script_code_pt       code;
    ngx_http_script_engine_t      e;
    ngx_http_core_loc_conf_t     *clcf;
    ngx_http_index_loc_conf_t    *ilcf;
    ngx_http_script_len_code_pt   lcode;

    if (r->uri.data[r->uri.len - 1] != '/') {
        return NGX_DECLINED;
    }

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
        return NGX_DECLINED;
    }

    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    allocated = 0;
    root = 0;
    dir_tested = 0;
    name = NULL;
    /* suppress MSVC warning */
    path.data = NULL;

    index = ilcf->indices->elts;
    for (i = 0; i < ilcf->indices->nelts; i++) {

        if (index[i].lengths == NULL) {

            if (index[i].name.data[0] == '/') {
                return ngx_http_internal_redirect(r, &index[i].name, &r->args);
            }

            reserve = ilcf->max_index_len;
            len = index[i].name.len;

        } else {
            ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

            e.ip = index[i].lengths->elts;
            e.request = r;
            e.flushed = 1;

            /* 1 is for terminating '\0' as in static names */
            len = 1;

            while (*(uintptr_t *) e.ip) {
                lcode = *(ngx_http_script_len_code_pt *) e.ip;
                len += lcode(&e);
            }

            /* 16 bytes are preallocation */

            reserve = len + 16;
        }

        if (reserve > allocated) {

            name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
            if (name == NULL) {
                return NGX_ERROR;
            }

            allocated = path.data + path.len - name;
        }

        if (index[i].values == NULL) {

            /* index[i].name.len includes the terminating '\0' */

            ngx_memcpy(name, index[i].name.data, index[i].name.len);

            path.len = (name + index[i].name.len - 1) - path.data;

        } else {
            e.ip = index[i].values->elts;
            e.pos = name;

            while (*(uintptr_t *) e.ip) {
                code = *(ngx_http_script_code_pt *) e.ip;
                code((ngx_http_script_engine_t *) &e);
            }

            if (*name == '/') {
                uri.len = len - 1;
                uri.data = name;
                return ngx_http_internal_redirect(r, &uri, &r->args);
            }

            path.len = e.pos - path.data;

            *e.pos = '\0';
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "open index \"%V\"", &path);

        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.test_only = 1;
        of.errors = clcf->open_file_cache_errors;
        of.events = clcf->open_file_cache_events;

        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
            != NGX_OK)
        {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
                           "%s \"%s\" failed", of.failed, path.data);

            if (of.err == 0) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            if (of.err == NGX_ENOTDIR
                || of.err == NGX_ENAMETOOLONG
                || of.err == NGX_EACCES)
            {
                return ngx_http_index_error(r, clcf, path.data, of.err);
            }

            if (!dir_tested) {
                rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);

                if (rc != NGX_OK) {
                    return rc;
                }

                dir_tested = 1;
            }

            if (of.err == NGX_ENOENT) {
                continue;
            }

            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
                          "%s \"%s\" failed", of.failed, path.data);

            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        uri.len = r->uri.len + len - 1;

        if (!clcf->alias) {
            uri.data = path.data + root;

        } else {
            uri.data = ngx_pnalloc(r->pool, uri.len);
            if (uri.data == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            p = ngx_copy(uri.data, r->uri.data, r->uri.len);
            ngx_memcpy(p, name, len - 1);
        }

        return ngx_http_internal_redirect(r, &uri, &r->args);
    }

    return NGX_DECLINED;
}
static ngx_int_t
ngx_http_autoindex_handler(ngx_http_request_t *r)
{
    u_char                         *last, *filename, scale;
    off_t                           length;
    size_t                          len, utf_len, allocated, root;
    ngx_tm_t                        tm;
    ngx_err_t                       err;
    ngx_buf_t                      *b;
    ngx_int_t                       rc, size;
    ngx_str_t                       path;
    ngx_dir_t                       dir;
    ngx_uint_t                      i, level, utf8;
    ngx_pool_t                     *pool;
    ngx_time_t                     *tp;
    ngx_chain_t                     out;
    ngx_array_t                     entries;
    ngx_http_autoindex_entry_t     *entry;
    ngx_http_autoindex_loc_conf_t  *alcf;

    static char  *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

    if (r->uri.data[r->uri.len - 1] != '/') {
        return NGX_DECLINED;
    }

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
        return NGX_DECLINED;
    }

    alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);

    if (!alcf->enable) {
        return NGX_DECLINED;
    }

    /* NGX_DIR_MASK_LEN is lesser than NGX_HTTP_AUTOINDEX_PREALLOCATE */

    last = ngx_http_map_uri_to_path(r, &path, &root,
                                    NGX_HTTP_AUTOINDEX_PREALLOCATE);
    if (last == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    allocated = path.len;
    path.len = last - path.data;
    if (path.len > 1) {
        path.len--;
    }
    path.data[path.len] = '\0';

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http autoindex: \"%s\"", path.data);

    if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
        err = ngx_errno;

        if (err == NGX_ENOENT
            || err == NGX_ENOTDIR
            || err == NGX_ENAMETOOLONG)
        {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_NOT_FOUND;

        } else if (err == NGX_EACCES) {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_FORBIDDEN;

        } else {
            level = NGX_LOG_CRIT;
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        ngx_log_error(level, r->connection->log, err,
                      ngx_open_dir_n " \"%s\" failed", path.data);

        return rc;
    }

#if (NGX_SUPPRESS_WARN)

    /* MSVC thinks 'entries' may be used without having been initialized */
    ngx_memzero(&entries, sizeof(ngx_array_t));

#endif

    /* TODO: pool should be temporary pool */
    pool = r->pool;

    if (ngx_array_init(&entries, pool, 40, sizeof(ngx_http_autoindex_entry_t))
        != NGX_OK)
    {
        return ngx_http_autoindex_error(r, &dir, &path);
    }

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_type_len = sizeof("text/html") - 1;
    ngx_str_set(&r->headers_out.content_type, "text/html");

    rc = ngx_http_send_header(r);

    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        if (ngx_close_dir(&dir) == NGX_ERROR) {
            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                          ngx_close_dir_n " \"%V\" failed", &path);
        }

        return rc;
    }

    filename = path.data;
    filename[path.len] = '/';

    if (r->headers_out.charset.len == 5
        && ngx_strncasecmp(r->headers_out.charset.data, (u_char *) "utf-8", 5)
           == 0)
    {
        utf8 = 1;

    } else {
        utf8 = 0;
    }

    for ( ;; ) {
        ngx_set_errno(0);

        if (ngx_read_dir(&dir) == NGX_ERROR) {
            err = ngx_errno;

            if (err != NGX_ENOMOREFILES) {
                ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                              ngx_read_dir_n " \"%V\" failed", &path);
                return ngx_http_autoindex_error(r, &dir, &path);
            }

            break;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http autoindex file: \"%s\"", ngx_de_name(&dir));

        len = ngx_de_namelen(&dir);

        if (ngx_de_name(&dir)[0] == '.') {
            continue;
        }

        if (!dir.valid_info) {

            /* 1 byte for '/' and 1 byte for terminating '\0' */

            if (path.len + 1 + len + 1 > allocated) {
                allocated = path.len + 1 + len + 1
                                     + NGX_HTTP_AUTOINDEX_PREALLOCATE;

                filename = ngx_pnalloc(pool, allocated);
                if (filename == NULL) {
                    return ngx_http_autoindex_error(r, &dir, &path);
                }

                last = ngx_cpystrn(filename, path.data, path.len + 1);
                *last++ = '/';
            }

            ngx_cpystrn(last, ngx_de_name(&dir), len + 1);

            if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
                err = ngx_errno;

                if (err != NGX_ENOENT) {
                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                                  ngx_de_info_n " \"%s\" failed", filename);

                    if (err == NGX_EACCES) {
                        continue;
                    }

                    return ngx_http_autoindex_error(r, &dir, &path);
                }

                if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                                  ngx_de_link_info_n " \"%s\" failed",
                                  filename);
                    return ngx_http_autoindex_error(r, &dir, &path);
                }
            }
        }

        entry = ngx_array_push(&entries);
        if (entry == NULL) {
            return ngx_http_autoindex_error(r, &dir, &path);
        }

        entry->name.len = len;

        entry->name.data = ngx_pnalloc(pool, len + 1);
        if (entry->name.data == NULL) {
            return ngx_http_autoindex_error(r, &dir, &path);
        }

        ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);

        entry->escape = 2 * ngx_escape_uri(NULL, ngx_de_name(&dir), len,
                                           NGX_ESCAPE_HTML);

        if (utf8) {
            entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len);
        } else {
            entry->utf_len = len;
        }

        entry->colon = (ngx_strchr(entry->name.data, ':') != NULL);

        entry->dir = ngx_de_is_dir(&dir);
        entry->mtime = ngx_de_mtime(&dir);
        entry->size = ngx_de_size(&dir);
    }

    if (ngx_close_dir(&dir) == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                      ngx_close_dir_n " \"%s\" failed", &path);
    }

    len = sizeof(title) - 1
          + r->uri.len
          + sizeof(header) - 1
          + r->uri.len
          + sizeof("</h1>") - 1
          + sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1
          + sizeof("</pre><hr>") - 1
          + sizeof(tail) - 1;

    entry = entries.elts;
    for (i = 0; i < entries.nelts; i++) {
        len += sizeof("<a href=\"") - 1
            + entry[i].name.len + entry[i].escape
            + 1                                          /* 1 is for "/" */
            + sizeof("\">") - 1
            + entry[i].name.len - entry[i].utf_len + entry[i].colon * 2
            + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof("&gt;") - 2
            + sizeof("</a>") - 1
            + sizeof(" 28-Sep-1970 12:00 ") - 1
            + 20                                         /* the file size */
            + 2;
    }

    b = ngx_create_temp_buf(r->pool, len);
    if (b == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if (entries.nelts > 1) {
        ngx_qsort(entry, (size_t) entries.nelts,
                  sizeof(ngx_http_autoindex_entry_t),
                  ngx_http_autoindex_cmp_entries);
    }

    b->last = ngx_cpymem(b->last, title, sizeof(title) - 1);
    b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
    b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);
    b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
    b->last = ngx_cpymem(b->last, "</h1>", sizeof("</h1>") - 1);

    b->last = ngx_cpymem(b->last, "<hr><pre><a href=\"../\">../</a>" CRLF,
                         sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1);

    tp = ngx_timeofday();

    for (i = 0; i < entries.nelts; i++) {
        b->last = ngx_cpymem(b->last, "<a href=\"", sizeof("<a href=\"") - 1);

        if (entry[i].colon) {
            *b->last++ = '.';
            *b->last++ = '/';
        }

        if (entry[i].escape) {
            ngx_escape_uri(b->last, entry[i].name.data, entry[i].name.len,
                           NGX_ESCAPE_HTML);

            b->last += entry[i].name.len + entry[i].escape;

        } else {
            b->last = ngx_cpymem(b->last, entry[i].name.data,
                                 entry[i].name.len);
        }

        if (entry[i].dir) {
            *b->last++ = '/';
        }

        *b->last++ = '"';
        *b->last++ = '>';

        len = entry[i].utf_len;

        if (entry[i].name.len != len) {
            if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
                utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;

            } else {
                utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
            }

            b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,
                                       utf_len, entry[i].name.len + 1);
            last = b->last;

        } else {
            b->last = ngx_cpystrn(b->last, entry[i].name.data,
                                  NGX_HTTP_AUTOINDEX_NAME_LEN + 1);
            last = b->last - 3;
        }

        if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
            b->last = ngx_cpymem(last, "..&gt;</a>", sizeof("..&gt;</a>") - 1);

        } else {
            if (entry[i].dir && NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {
                *b->last++ = '/';
                len++;
            }

            b->last = ngx_cpymem(b->last, "</a>", sizeof("</a>") - 1);
            ngx_memset(b->last, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN - len);
            b->last += NGX_HTTP_AUTOINDEX_NAME_LEN - len;
        }

        *b->last++ = ' ';

        ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm);

        b->last = ngx_sprintf(b->last, "%02d-%s-%d %02d:%02d ",
                              tm.ngx_tm_mday,
                              months[tm.ngx_tm_mon - 1],
                              tm.ngx_tm_year,
                              tm.ngx_tm_hour,
                              tm.ngx_tm_min);

        if (alcf->exact_size) {
            if (entry[i].dir) {
                b->last = ngx_cpymem(b->last,  "                  -",
                                     sizeof("                  -") - 1);
            } else {
                b->last = ngx_sprintf(b->last, "%19O", entry[i].size);
            }

        } else {
            if (entry[i].dir) {
                b->last = ngx_cpymem(b->last,  "      -",
                                     sizeof("      -") - 1);

            } else {
                length = entry[i].size;

                if (length > 1024 * 1024 * 1024 - 1) {
                    size = (ngx_int_t) (length / (1024 * 1024 * 1024));
                    if ((length % (1024 * 1024 * 1024))
                                                > (1024 * 1024 * 1024 / 2 - 1))
                    {
                        size++;
                    }
                    scale = 'G';

                } else if (length > 1024 * 1024 - 1) {
                    size = (ngx_int_t) (length / (1024 * 1024));
                    if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) {
                        size++;
                    }
                    scale = 'M';

                } else if (length > 9999) {
                    size = (ngx_int_t) (length / 1024);
                    if (length % 1024 > 511) {
                        size++;
                    }
                    scale = 'K';

                } else {
                    size = (ngx_int_t) length;
                    scale = '\0';
                }

                if (scale) {
                    b->last = ngx_sprintf(b->last, "%6i%c", size, scale);

                } else {
                    b->last = ngx_sprintf(b->last, " %6i", size);
                }
            }
        }

        *b->last++ = CR;
        *b->last++ = LF;
    }

    /* TODO: free temporary pool */

    b->last = ngx_cpymem(b->last, "</pre><hr>", sizeof("</pre><hr>") - 1);

    b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);

    if (r == r->main) {
        b->last_buf = 1;
    }

    b->last_in_chain = 1;

    out.buf = b;
    out.next = NULL;

    return ngx_http_output_filter(r, &out);
}
static ngx_int_t
ngx_http_static_handler(ngx_http_request_t *r)
{
    u_char                    *last, *location;
    size_t                     root, len;
    ngx_str_t                  path;
    ngx_int_t                  rc;
    ngx_uint_t                 level;
    ngx_log_t                 *log;
    ngx_buf_t                 *b;
    ngx_chain_t                out;
    ngx_open_file_info_t       of;
    ngx_http_core_loc_conf_t  *clcf;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
        return NGX_HTTP_NOT_ALLOWED;
    }

    if (r->uri.data[r->uri.len - 1] == '/') {
        return NGX_DECLINED;
    }

    log = r->connection->log;

    /*
     * ngx_http_map_uri_to_path() allocates memory for terminating '\0'
     * so we do not need to reserve memory for '/' for possible redirect
     */

    last = ngx_http_map_uri_to_path(r, &path, &root, 0);
    if (last == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    path.len = last - path.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                   "http filename: \"%s\"", path.data);

    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_HAVE_OPENAT)
    of.disable_symlinks = clcf->disable_symlinks;
#endif

    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;
    }

    r->root_tested = !r->error_page;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);

    if (of.is_dir) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");

        ngx_http_clear_location(r);

        r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
        if (r->headers_out.location == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        len = r->uri.len + 1;

        if (!clcf->alias && clcf->root_lengths == NULL && r->args.len == 0) {
            location = path.data + clcf->root.len;

            *last = '/';

        } else {
            if (r->args.len) {
                len += r->args.len + 1;
            }

            location = ngx_pnalloc(r->pool, len);
            if (location == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            last = ngx_copy(location, r->uri.data, r->uri.len);

            *last = '/';

            if (r->args.len) {
                *++last = '?';
                ngx_memcpy(++last, r->args.data, r->args.len);
            }
        }

        /*
         * we do not need to set the r->headers_out.location->hash and
         * r->headers_out.location->key fields
         */

        r->headers_out.location->value.len = len;
        r->headers_out.location->value.data = location;

        return NGX_HTTP_MOVED_PERMANENTLY;
    }

#if !(NGX_WIN32) /* the not regular files are probably Unix specific */

    if (!of.is_file) {
        ngx_log_error(NGX_LOG_CRIT, log, 0,
                      "\"%s\" is not a regular file", path.data);

        return NGX_HTTP_NOT_FOUND;
    }

#endif

    if (r->method & NGX_HTTP_POST) {
        return NGX_HTTP_NOT_ALLOWED;
    }

    rc = ngx_http_discard_request_body(r);

    if (rc != NGX_OK) {
        return rc;
    }

    log->action = "sending response to client";

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = of.size;
    r->headers_out.last_modified_time = of.mtime;

    if (ngx_http_set_content_type(r) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if (r != r->main && of.size == 0) {
        return ngx_http_send_header(r);
    }

    r->allow_ranges = 1;

    /* we need to allocate all before the header would be sent */

    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    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;
    }

    rc = ngx_http_send_header(r);

    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    b->file_pos = 0;
    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->file->fd = of.fd;
    b->file->name = path;
    b->file->log = log;
    b->file->directio = of.is_directio;

    out.buf = b;
    out.next = NULL;

    return ngx_http_output_filter(r, &out);
}
static ngx_inline ngx_int_t
make_content_buf(
        ngx_http_request_t *r, ngx_buf_t **pb,
        ngx_http_fancyindex_loc_conf_t *alcf)
{
    ngx_http_fancyindex_entry_t *entry;

    off_t        length;
    size_t       len, root, copy, allocated;
    u_char      *filename, *last, scale;
    ngx_tm_t     tm;
    ngx_array_t  entries;
    ngx_time_t  *tp;
    ngx_uint_t   i;
    ngx_int_t    size;
    ngx_str_t    path;
    ngx_dir_t    dir;
    ngx_buf_t   *b;

    static char *months[] = {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
    };

    /*
     * NGX_DIR_MASK_LEN is lesser than NGX_HTTP_FANCYINDEX_PREALLOCATE
     */
    if ((last = ngx_http_map_uri_to_path(r, &path, &root,
                    NGX_HTTP_FANCYINDEX_PREALLOCATE)) == NULL)
        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    allocated = path.len;
    path.len  = last - path.data - 1;
    path.data[path.len] = '\0';

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http fancyindex: \"%s\"", path.data);

    if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
        ngx_int_t rc, err = ngx_errno;
        ngx_uint_t level;

        if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_NOT_FOUND;
        } else if (err == NGX_EACCES) {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_FORBIDDEN;
        } else {
            level = NGX_LOG_CRIT;
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        ngx_log_error(level, r->connection->log, err,
                ngx_open_dir_n " \"%s\" failed", path.data);

        return rc;
    }

#if (NGX_SUPPRESS_WARN)
    /* MSVC thinks 'entries' may be used without having been initialized */
    ngx_memzero(&entries, sizeof(ngx_array_t));
#endif /* NGX_SUPPRESS_WARN */


    if (ngx_array_init(&entries, r->pool, 40,
                sizeof(ngx_http_fancyindex_entry_t)) != NGX_OK)
        return ngx_http_fancyindex_error(r, &dir, &path);

    filename = path.data;
    filename[path.len] = '/';

    /* Read directory entries and their associated information. */
    for (;;) {
        ngx_set_errno(0);

        if (ngx_read_dir(&dir) == NGX_ERROR) {
            ngx_int_t err = ngx_errno;

            if (err != NGX_ENOMOREFILES) {
                ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                        ngx_read_dir_n " \"%V\" failed", &path);
                return ngx_http_fancyindex_error(r, &dir, &path);
            }
            break;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http fancyindex file: \"%s\"", ngx_de_name(&dir));

        len = ngx_de_namelen(&dir);

        if (ngx_de_name(&dir)[0] == '.')
            continue;

#if NGX_PCRE
        {
            ngx_str_t str = { len, ngx_de_name(&dir) };

            if (alcf->ignore && ngx_regex_exec_array(alcf->ignore, &str,
                                                     r->connection->log)
                != NGX_DECLINED)
            {
                continue;
            }
        }
#else /* !NGX_PCRE */
        if (alcf->ignore) {
            u_int match_found = 0;
            ngx_str_t *s = alcf->ignore->elts;

            for (i = 0; i < alcf->ignore->nelts; i++, s++) {
                if (ngx_strcmp(ngx_de_name(&dir), s->data) == 0) {
                    match_found = 1;
                    break;
                }
            }

            if (match_found) {
                continue;
            }
        }
#endif /* NGX_PCRE */

        if (!dir.valid_info) {
            /* 1 byte for '/' and 1 byte for terminating '\0' */
            if (path.len + 1 + len + 1 > allocated) {
                allocated = path.len + 1 + len + 1
                          + NGX_HTTP_FANCYINDEX_PREALLOCATE;

                if ((filename = ngx_palloc(r->pool, allocated)) == NULL)
                    return ngx_http_fancyindex_error(r, &dir, &path);

                last = ngx_cpystrn(filename, path.data, path.len + 1);
                *last++ = '/';
            }

            ngx_cpystrn(last, ngx_de_name(&dir), len + 1);

            if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
                ngx_int_t err = ngx_errno;

                if (err != NGX_ENOENT) {
                    ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
                            ngx_de_info_n " \"%s\" failed", filename);
                    continue;
                }

                if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                            ngx_de_link_info_n " \"%s\" failed", filename);
                    return ngx_http_fancyindex_error(r, &dir, &path);
                }
            }
        }

        if ((entry = ngx_array_push(&entries)) == NULL)
            return ngx_http_fancyindex_error(r, &dir, &path);

        entry->name.len  = len;
        entry->name.data = ngx_palloc(r->pool, len + 1);
        if (entry->name.data == NULL)
            return ngx_http_fancyindex_error(r, &dir, &path);

        ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);
        entry->escape = 2 * ngx_fancyindex_escape_uri(NULL,
                                                      ngx_de_name(&dir),
                                                      len);

        entry->dir     = ngx_de_is_dir(&dir);
        entry->mtime   = ngx_de_mtime(&dir);
        entry->size    = ngx_de_size(&dir);
        entry->utf_len = (r->headers_out.charset.len == 5 &&
                ngx_strncasecmp(r->headers_out.charset.data, (u_char*) "utf-8", 5) == 0)
            ?  ngx_utf8_length(entry->name.data, entry->name.len)
            : len;
    }

    if (ngx_close_dir(&dir) == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                ngx_close_dir_n " \"%s\" failed", &path);
    }

    /*
     * Calculate needed buffer length.
     */
    len = r->uri.len
        + ngx_sizeof_ssz(t05_body2)
        + ngx_sizeof_ssz(t06_list1)
        + ngx_sizeof_ssz(t07_list2)
        ;

    entry = entries.elts;
    for (i = 0; i < entries.nelts; i++) {
        /*
         * Genearated table rows are as follows, unneeded whitespace
         * is stripped out:
         *
         *   <tr class="X">
         *     <td><a href="U">fname</a></td>
         *     <td>size</td><td>date</td>
         *   </tr>
         */
        len += ngx_sizeof_ssz("<tr class=\"X\"><td><a href=\"")
            + entry[i].name.len + entry[i].escape /* Escaped URL */
            + ngx_sizeof_ssz("\">")
            + entry[i].name.len + entry[i].utf_len
            + NGX_HTTP_FANCYINDEX_NAME_LEN + ngx_sizeof_ssz("&gt;")
            + ngx_sizeof_ssz("</a></td><td>")
            + 20 /* File size */
            + ngx_sizeof_ssz("</td><td>")
            + ngx_sizeof_ssz(" 28-Sep-1970 12:00 ")
            + ngx_sizeof_ssz("</td></tr>\n")
            + 2 /* CR LF */
            ;
    }

    if ((b = ngx_create_temp_buf(r->pool, len)) == NULL)
        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    /* Sort entries, if needed */
    if (entries.nelts > 1) {
        ngx_qsort(entry, (size_t) entries.nelts,
                sizeof(ngx_http_fancyindex_entry_t),
                ngx_http_fancyindex_cmp_entries);
    }

    b->last = ngx_cpymem_str(b->last, r->uri);
    b->last = ngx_cpymem_ssz(b->last, t05_body2);
    b->last = ngx_cpymem_ssz(b->last, t06_list1);

    tp = ngx_timeofday();

    for (i = 0; i < entries.nelts; i++) {
        static const char _evenodd[] = { 'e', 'o' };
        b->last = ngx_cpymem_ssz(b->last, "<tr class=\"");
        *b->last++ = _evenodd[i & 0x01];
        /*
         * Alternative implementation:
         *   *b->last++ = (i & 0x01) ? 'e' : 'o';
         */
        b->last = ngx_cpymem_ssz(b->last, "\"><td><a href=\"");

        if (entry[i].escape) {
            ngx_fancyindex_escape_uri(b->last,
                                      entry[i].name.data,
                                      entry[i].name.len);

            b->last += entry[i].name.len + entry[i].escape;

        } else {
            b->last = ngx_cpymem_str(b->last, entry[i].name);
        }

        if (entry[i].dir) {
            *b->last++ = '/';
        }

        *b->last++ = '"';
        *b->last++ = '>';

        len = entry[i].utf_len;

        if (entry[i].name.len - len) {
            if (len > NGX_HTTP_FANCYINDEX_NAME_LEN) {
                copy = NGX_HTTP_FANCYINDEX_NAME_LEN - 3 + 1;
            } else {
                copy = NGX_HTTP_FANCYINDEX_NAME_LEN + 1;
            }

            b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,
                                          copy, entry[i].name.len);
            last = b->last;

        } else {
            b->last = ngx_cpystrn(b->last, entry[i].name.data,
                                  NGX_HTTP_FANCYINDEX_NAME_LEN + 1);
            last = b->last - 3;
        }

        if (len > NGX_HTTP_FANCYINDEX_NAME_LEN) {
            b->last = ngx_cpymem_ssz(last, "..&gt;</a></td><td>");

        } else {
            if (entry[i].dir && NGX_HTTP_FANCYINDEX_NAME_LEN - len > 0) {
                *b->last++ = '/';
                len++;
            }

            b->last = ngx_cpymem_ssz(b->last, "</a></td><td>");
        }

        if (alcf->exact_size) {
            if (entry[i].dir) {
                *b->last++ = '-';
            } else {
                b->last = ngx_sprintf(b->last, "%19O", entry[i].size);
            }

        } else {
            if (entry[i].dir) {
                *b->last++ = '-';
            } else {
                length = entry[i].size;

                if (length > 1024 * 1024 * 1024 - 1) {
                    size = (ngx_int_t) (length / (1024 * 1024 * 1024));
                    if ((length % (1024 * 1024 * 1024))
                                                > (1024 * 1024 * 1024 / 2 - 1))
                    {
                        size++;
                    }
                    scale = 'G';

                } else if (length > 1024 * 1024 - 1) {
                    size = (ngx_int_t) (length / (1024 * 1024));
                    if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) {
                        size++;
                    }
                    scale = 'M';

                } else if (length > 9999) {
                    size = (ngx_int_t) (length / 1024);
                    if (length % 1024 > 511) {
                        size++;
                    }
                    scale = 'K';

                } else {
                    size = (ngx_int_t) length;
                    scale = '\0';
                }

                if (scale) {
                    b->last = ngx_sprintf(b->last, "%6i%c", size, scale);

                } else {
                    b->last = ngx_sprintf(b->last, " %6i", size);
                }
            }
        }

        ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm);

        b->last = ngx_sprintf(b->last, "</td><td>%02d-%s-%d %02d:%02d</td></tr>",
                              tm.ngx_tm_mday,
                              months[tm.ngx_tm_mon - 1],
                              tm.ngx_tm_year,
                              tm.ngx_tm_hour,
                              tm.ngx_tm_min);


        *b->last++ = CR;
        *b->last++ = LF;
    }

    /* Output table bottom */
    b->last = ngx_cpymem_ssz(b->last, t07_list2);

    *pb = b;
    return NGX_OK;
}
static ngx_int_t ngx_streaming_handler(ngx_http_request_t *r) {
  size_t                      root;
  ngx_int_t                   rc;
  ngx_uint_t                  level;
  ngx_str_t                   path;
  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;

  mp4_split_options_t *options = mp4_split_options_init(r);

  if(r->args.len && !mp4_split_options_set(r, options, (const char *)r->args.data, r->args.len)) {
    mp4_split_options_exit(r, options);
    return NGX_DECLINED;
  }

  if(!options) return NGX_DECLINED;

  if(!ngx_http_map_uri_to_path(r, &path, &root, 1)) {
    mp4_split_options_exit(r, options);
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
  }

  u_int m3u8 = 0;

  struct bucket_t *bucket = bucket_init(r);
  int result = 0;
  {
    if(ngx_strstr(path.data, "m3u8")) m3u8 = 1;
    char *ext = strrchr((const char *)path.data, '.');
    strcpy(ext, ".mp4");
    path.len = ((u_char *)ext - path.data) + 4;
    // ngx_open_and_stat_file in ngx_open_cached_file expects the name to be zero-terminated.
    path.data[path.len] = '\0';
  }

  ngx_log_t *nlog = r->connection->log;
  ngx_log_debug1(NGX_LOG_DEBUG_HTTP, nlog, 0, "http mp4 filename: \"%s\"", path.data);

  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 = NGX_MAX_OFF_T_VALUE;
  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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) {
    mp4_split_options_exit(r, options);
    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:
      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, nlog, of.err,
                    ngx_open_file_n " \"%s\" failed", path.data);
    }

    return rc;
  }

  if(!of.is_file) {
    mp4_split_options_exit(r, options);
    if(ngx_close_file(of.fd) == NGX_FILE_ERROR) {
      ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno,
                    ngx_close_file_n " \"%s\" failed", path.data);
    }
    return NGX_DECLINED;
  }

  ngx_file_t *file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
  if(file == NULL) {
    mp4_split_options_exit(r, options);
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
  }
  file->fd = of.fd;
  file->name = path;
  file->log = nlog;

  mp4_context_t *mp4_context = mp4_open(r, file, of.size, MP4_OPEN_MOOV);
  if(!mp4_context) {
    mp4_split_options_exit(r, options);
    ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, "mp4_open failed");
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
  }

  mp4_context->root = root;
  if(m3u8) {
    if((result = mp4_create_m3u8(mp4_context, bucket))) {
      char action[50];
      sprintf(action, "ios_playlist&segments=%d", result);
      view_count(mp4_context, (char *)path.data, options ? options->hash : NULL, action);
    }
    r->allow_ranges = 0;
  } else {
    result = output_ts(mp4_context, bucket, options);
    if(!options || !result) {
      mp4_close(mp4_context);
      ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, "output_ts failed");
      return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    char action[50] = "ios_view";
    view_count(mp4_context, (char *)path.data, options->hash, action);
    r->allow_ranges = 1;
  }

  mp4_close(mp4_context);
  mp4_split_options_exit(r, options);

  result = result == 0 ? 415 : 200;

  r->root_tested = !r->error_page;

  if(result && bucket) {
    nlog->action = "sending mp4 to client";

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, nlog, 0, "content_length: %d", bucket->content_length);
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = bucket->content_length;
    r->headers_out.last_modified_time = of.mtime;

    if (ngx_http_set_content_type(r) != NGX_OK) {
      return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    //ngx_table_elt_t *h = ngx_list_push(&r->headers_out.headers);
    //if(h == NULL) return NGX_HTTP_INTERNAL_SERVER_ERROR;

    //h->hash = 1;

    //h->key.len = sizeof(X_MOD_HLS_KEY) - 1;
    //h->key.data = (u_char *)X_MOD_HLS_KEY;
    //h->value.len = sizeof(X_MOD_HLS_VERSION) - 1;
    //h->value.data = (u_char *)X_MOD_HLS_VERSION;

    rc = ngx_http_send_header(r);

    if(rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
      ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, ngx_close_file_n "ngx_http_send_header failed");
      return rc;
    }

    return ngx_http_output_filter(r, bucket->first);
  } else return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
}
Exemple #8
0
static ssize_t
ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script,
    u_char **name, u_char *buf, size_t len)
{
		syslog(LOG_INFO, "[%s:%s:%d]", __FILE__, __func__, __LINE__);
    size_t                     root;
    ssize_t                    n;
    ngx_str_t                  log, path;
    ngx_open_file_info_t       of;
    ngx_http_log_loc_conf_t   *llcf;
    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!r->root_tested) {

        /* test root directory existence */

        if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
            /* simulate successful logging */
            return len;
        }

        path.data[root] = '\0';

        ngx_memzero(&of, sizeof(ngx_open_file_info_t));

        of.valid = clcf->open_file_cache_valid;
        of.min_uses = clcf->open_file_cache_min_uses;
        of.test_dir = 1;
        of.test_only = 1;
        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) {
            /* simulate successful logging */
            return len;
        }

        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
            != NGX_OK)
        {
            if (of.err == 0) {
                /* simulate successful logging */
                return len;
            }

            ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err,
                          "testing \"%s\" existence failed", path.data);

            /* simulate successful logging */
            return len;
        }

        if (!of.is_dir) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ENOTDIR,
                          "testing \"%s\" existence failed", path.data);

            /* simulate successful logging */
            return len;
        }
    }

    if (ngx_http_script_run(r, &log, script->lengths->elts, 1,
                            script->values->elts)
        == NULL)
    {
        /* simulate successful logging */
        return len;
    }

    log.data[log.len - 1] = '\0';
    *name = log.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http log \"%s\"", log.data);

    llcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);

    ngx_memzero(&of, sizeof(ngx_open_file_info_t));

    of.log = 1;
    of.valid = llcf->open_file_cache_valid;
    of.min_uses = llcf->open_file_cache_min_uses;
    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;

    if (ngx_http_set_disable_symlinks(r, clcf, &log, &of) != NGX_OK) {
        /* simulate successful logging */
        return len;
    }

    if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool)
        != NGX_OK)
    {
        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                      "%s \"%s\" failed", of.failed, log.data);
        /* simulate successful logging */
        return len;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http log #%d", of.fd);

    n = ngx_write_fd(of.fd, buf, len);

    return n;
}
static ngx_inline ngx_int_t
ngx_http_modsecurity_load_request(ngx_http_request_t *r)
{
    ngx_http_modsecurity_ctx_t  *ctx;
    request_rec                 *req;
    ngx_str_t                    str;
    size_t                       root;
    ngx_str_t                    path;

    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
    req = ctx->req;

    /* request line */
    req->method = (char *)ngx_pstrdup0(r->pool, &r->method_name);

    /* TODO: how to use ap_method_number_of ?
     * req->method_number = ap_method_number_of(req->method);
     */

    req->method_number = ngx_http_modsecurity_method_number(r->method);

    /* ngx_http_map_uri_to_path() allocates memory for terminating '\0' */
    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
        return NGX_ERROR;
    }

    req->filename = (char *) path.data;
    req->path_info = req->filename;

    req->args = (char *)ngx_pstrdup0(r->pool, &r->args);

    req->proto_num = r->http_major *1000 + r->http_minor;
    req->protocol = (char *)ngx_pstrdup0(r->pool, &r->http_protocol);
    req->request_time = apr_time_make(r->start_sec, r->start_msec);
    req->the_request = (char *)ngx_pstrdup0(r->pool, &r->request_line);

    req->unparsed_uri = (char *)ngx_pstrdup0(r->pool, &r->unparsed_uri);
    req->uri = (char *)ngx_pstrdup0(r->pool, &r->uri);

    req->parsed_uri.scheme = "http";

#if (NGX_HTTP_SSL)
    if (r->connection->ssl) {
        req->parsed_uri.scheme = "https";
    }
#endif

    req->parsed_uri.path = (char *)ngx_pstrdup0(r->pool, &r->uri);
    req->parsed_uri.is_initialized = 1;

    str.data = r->port_start;
    str.len = r->port_end - r->port_start;
    req->parsed_uri.port = ngx_atoi(str.data, str.len);
    req->parsed_uri.port_str = (char *)ngx_pstrdup0(r->pool, &str);

    req->parsed_uri.query = r->args.len ? req->args : NULL;
    req->parsed_uri.dns_looked_up = 0;
    req->parsed_uri.dns_resolved = 0;

    // req->parsed_uri.password = (char *)ngx_pstrdup0(r->pool, &r->headers_in.passwd);
    // req->parsed_uri.user = (char *)ngx_pstrdup0(r->pool, &r->headers_in.user);
    req->parsed_uri.fragment = (char *)ngx_pstrdup0(r->pool, &r->exten);

    req->hostname = (char *)ngx_pstrdup0(r->pool, (ngx_str_t *)&ngx_cycle->hostname);

    req->header_only = r->header_only ? r->header_only : (r->method == NGX_HTTP_HEAD);

    return NGX_OK;
}
static ngx_int_t
ngx_http_dav_ext_send_propfind(ngx_http_request_t *r)
{
	size_t                    root;
	ngx_str_t                 path, spath, suri;
	ngx_chain_t               *l = NULL, **ll = &l;
	DIR                       *dir;
	int                       depth;
	struct dirent             *de;
	size_t                    len;
	ngx_http_variable_value_t vv;
	ngx_str_t                 depth_name = ngx_string("depth");
	u_char                    *p;

	if (ngx_http_variable_unknown_header(&vv, &depth_name, 
					&r->headers_in.headers.part, 0) == NGX_OK
		&& vv.valid)
	{
		if (vv.len == sizeof("infinity") -1 
			&& !ngx_strncasecmp(vv.data, (u_char*)"infinity", vv.len))
		{
			depth = DAV_EXT_INFINITY; 
		} else {
			depth = ngx_atoi(vv.data, vv.len);
		}

	} else {
		depth = DAV_EXT_INFINITY;
	}

	p = ngx_http_map_uri_to_path(r, &path, &root, 0);

	if (p == NULL || !path.len) {

		ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
				"dav_ext error mapping uri to path");

		return NGX_ERROR;
	}

	path.len = p - path.data;
	*p = 0;

	ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
			"http propfind path: \"%V\"", &path);

	NGX_HTTP_DAV_EXT_OUTL(
		"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
		"<D:multistatus xmlns:D=\"DAV:\">\n"
		);

	ngx_http_dav_ext_flush(r, ll);

	ngx_http_dav_ext_send_propfind_item(r, (char*)path.data, &r->uri);

	if (depth) {

		/* treat infinite depth as 1 for performance reasons */

		if ((dir = opendir((char*)path.data))) {

			while((de = readdir(dir))) {

				if (!strcmp(de->d_name, ".")
					|| !strcmp(de->d_name, ".."))
				{
					continue;
				}

				len = strlen(de->d_name);

				ngx_http_dav_ext_make_child(r->pool, &path, 
					(u_char*)de->d_name, len, &spath);

				ngx_http_dav_ext_make_child(r->pool, &r->uri, 
					(u_char*)de->d_name, len, &suri);

				ngx_http_dav_ext_send_propfind_item(r, (char*)spath.data, &suri);

			}

			closedir(dir);
		}

	}

	NGX_HTTP_DAV_EXT_OUTL(
		"</D:multistatus>\n"
		);

	if (*ll && (*ll)->buf) {
		(*ll)->buf->last_buf = 1;
	}

	ngx_http_dav_ext_flush(r, ll);

	return NGX_OK;
}
static ngx_int_t ngx_http_f4fhds_handler(ngx_http_request_t *r)
{
    ngx_int_t    rc;
    ngx_chain_t  out;
    u_char *resp_body;
    ngx_buf_t   *resp;
    char *lastslash;
    char *segsuffix;
    size_t root;
    ngx_str_t path;
    ngx_str_t index_map = ngx_null_string;
    ngx_str_t mediafile_map = ngx_null_string;
    unsigned fragnum;
    uint32_t totalsize;
    ngx_table_elt_t *self;

    unsigned afraentries;
    unsigned g_afraentries, g_entry_size = 0;
    unsigned globaltable_offset;
    unsigned long long afraoffset = 0, offsetfromafra = 0;
    unsigned entryindex = 0;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
        return NGX_HTTP_NOT_ALLOWED;
    }
 
    if ( (rc = ngx_http_discard_request_body(r)) != NGX_OK ) {
        return rc;
    }

    if ( (self = ngx_list_push(&r->headers_out.headers)) == NULL ) {
        ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "Insufficient memory for ngx_list_push");
        return NGX_ERROR;
    }
    self->hash = 1;
    ngx_str_set(&self->key, "X-Inventos-F4FHDS-Version");
    ngx_str_set(&self->value, VERSION);
 
    ngx_http_map_uri_to_path(r, &path, &root, 0);

    ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "path=%s", path.data);

    lastslash = strrchr((char*)path.data, '/');
    ++lastslash;
    segsuffix = (char *)memmem(lastslash, path.len - (lastslash - (char *)path.data), "Seg1-Frag", 9);
    if ( segsuffix == NULL ) {
        return NGX_HTTP_NOT_FOUND;
    }
    segsuffix += 4; /* "Seg1" */

    fragnum = atoi(segsuffix + /* "-Frag" */ 5);
    if ( fragnum < 1 ) {
        return NGX_HTTP_NOT_FOUND;
    }

    memcpy(segsuffix, ".f4x", 4);
    path.len = segsuffix - (char*)path.data + 4;
    segsuffix[4] = 0;

    ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "f4x_path=%s", path.data);
    if ( (rc = make_mapping((const char *)path.data, &index_map, r)) != NGX_OK ) {
        return rc;
    }

    if ( index_map.len < 8 ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Wrong f4x afra size: %d in %s", index_map.len, path.data);
        goto GENERAL_ERROR;
    }
    if ( memcmp(index_map.data + 4, "afra", 4) != 0 ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Wrong f4x format: %s", path.data);
        goto GENERAL_ERROR;
    }
    if ( getboxlen(index_map.data) != index_map.len ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Wrong f4x prefix size: %d in %s", get32(index_map.data), path.data);
        goto GENERAL_ERROR;
    }

    if ( (index_map.data[12] & 0x20) == 0 ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Global entries are not present in %s", path.data);
        goto GENERAL_ERROR;
    }

    afraentries = get32(index_map.data + 17);
    globaltable_offset = 17 + 4 + afraentries * (8 /* time */ + ((index_map.data[12] & 0x40) ? 8 : 4)) + 4;
    if ( index_map.len < globaltable_offset ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Index file too short: %s", path.data);
        goto GENERAL_ERROR;
    }
    g_afraentries = get32(index_map.data + globaltable_offset - 4);

    switch ( index_map.data[12] & 0xc0 ) {
    case 0xc0: 
        g_entry_size = 32; 
        break;
    case 0x80: 
        g_entry_size = 24; 
        break;
    case 0x40: 
        g_entry_size = 28; 
        break;
    case 0:    
        g_entry_size = 20; 
        break;
    }

    ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "globaltable_offset=%d, afraentries=%d, g_afraentries=%d, entrysize=%d", 
                      globaltable_offset, afraentries, g_afraentries, g_entry_size
                     );

    if ( index_map.len < globaltable_offset + g_afraentries * g_entry_size ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Index file too short: %s, len=%d, need=%d, globaltable_offset=%d, afraentries=%d, g_afraentries=%d, entrysize=%d", 
                      path.data, index_map.len, globaltable_offset + g_afraentries * g_entry_size,
                      globaltable_offset, afraentries, g_afraentries, g_entry_size
                     );
        goto GENERAL_ERROR;
    }

#define FIND_SEGMENT(SEGSIZE, FRAGSIZE, AOSIZE, OFASIZE) \
        for ( entryindex = 0; entryindex < g_afraentries; ++entryindex ) { \
            u_char *pentry = index_map.data + globaltable_offset + entryindex * g_entry_size; \
            unsigned segment = get##SEGSIZE (pentry + 8); \
            unsigned fragment = get##FRAGSIZE (pentry + 8 + SEGSIZE/8); \
            if ( segment == 1 && fragment == fragnum ) { \
                afraoffset = get##AOSIZE (pentry + 8 + SEGSIZE/8 + FRAGSIZE/8); \
                offsetfromafra = get##OFASIZE (pentry + 8 + SEGSIZE/8 + FRAGSIZE/8 + AOSIZE/8); \
                break; \
            } \
        }


    switch ( index_map.data[12] & 0xc0 ) {
    case 0xc0: 
        FIND_SEGMENT(32, 32, 64, 64);
        break;
    case 0x80: 
        FIND_SEGMENT(32, 32, 32, 32);
        break;
    case 0x40: 
        FIND_SEGMENT(16, 16, 64, 64);
        break;
    case 0:    
        FIND_SEGMENT(16, 16, 32, 32);
        break;
    }
    if ( entryindex == g_afraentries ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Fragment #%d not found in %s, g_afraentries=%d", fragnum, path.data, g_afraentries);
        goto NOT_FOUND;
    }
    if ( offsetfromafra != 0 ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "OffsetFromAfra=%llu not supported, %s", offsetfromafra, path.data);
        goto GENERAL_ERROR;
    }


    memcpy(segsuffix, ".f4f", 4);
    if ( (rc = make_mapping((const char *)path.data, &mediafile_map, r)) != NGX_OK ) {
        return rc;
    }

    if ( mediafile_map.len < afraoffset + 8 ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Mediafile is too short: %s", path.data);
        goto GENERAL_ERROR;
    }

    {
    const u_char *afra = mediafile_map.data + afraoffset;
    unsigned long long afralen = getboxlen(afra);
    const u_char *abst = afra + afralen;
    unsigned long long abstlen = getboxlen(abst);
    const u_char *moof = abst + abstlen;
    unsigned long long mooflen = getboxlen(moof);
    const u_char *mdat = moof + mooflen;
    unsigned long long mdatlen = getboxlen(mdat);
    totalsize = afralen + abstlen + mooflen + mdatlen;
    if ( (resp_body = ngx_pcalloc(r->pool, totalsize)) == NULL ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Insufficient memory");
        goto GENERAL_ERROR;
    }
    memcpy(resp_body, afra, totalsize);
    }

    if ( (resp = ngx_pcalloc(r->pool, sizeof(ngx_buf_t))) == NULL ) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Insufficient memory");
        goto GENERAL_ERROR;
    }
 
    out.buf = resp;
    out.next = NULL;

    resp->pos = resp_body;
    resp->last = resp_body + totalsize;
    resp->memory = 1; 
    resp->last_buf = 1;
 
    free_mapping(&index_map);
    free_mapping(&mediafile_map);

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = totalsize;

    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only || r->method == NGX_HTTP_HEAD) {
        return rc;
    }

    return ngx_http_output_filter(r, &out);

GENERAL_ERROR:
    free_mapping(&index_map);
    free_mapping(&mediafile_map);
    return NGX_HTTP_INTERNAL_SERVER_ERROR;

NOT_FOUND:
    free_mapping(&index_map);
    free_mapping(&mediafile_map);
    return NGX_HTTP_NOT_FOUND;

}
static ngx_int_t ngx_estreaming_handler(ngx_http_request_t * r) {
    size_t root;
    ngx_int_t rc;
    ngx_uint_t level;
    ngx_str_t path;
    ngx_open_file_info_t of;
    ngx_http_core_loc_conf_t *clcf;
    video_buffer *destination;

    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;

    mp4_split_options_t * options = mp4_split_options_init(r);
    
    if (!ngx_http_map_uri_to_path(r, &path, &root, 1)) {
        mp4_split_options_exit(r, options);
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    
    if (r->args.len && !mp4_split_options_set(r, options, (const char *) r->args.data, r->args.len)) {
        mp4_split_options_exit(r, options);
        return NGX_DECLINED;
    }


    if (!options) return NGX_DECLINED;

    ngx_log_t * nlog = r->connection->log;

    struct bucket_t * bucket = bucket_init(r);
    int result = 0;

    u_int m3u8 = 0, len_ = 0;
    int64_t duration = 0;

    if (ngx_memcmp(r->exten.data, "mp4", r->exten.len) == 0) {
        return ngx_http_mp4_handler(r);
    } else if (ngx_memcmp(r->exten.data, "m3u8", r->exten.len) == 0) {
        m3u8 = 1;
    } else if (ngx_memcmp(r->exten.data, "len", r->exten.len) == 0) {// this is for length request
        len_ = 1;
    } else if (ngx_memcmp(r->exten.data, "ts", r->exten.len) == 0) {
        // don't do anything 
    } else {
        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
    }
    // change file name to mp4
    // in order to lookup file in filesystem
    char *ext = strrchr((const char *) path.data, '.');
    strcpy(ext, ".mp4");
    path.len = ((u_char *) ext - path.data) + 4;
    path.data[path.len] = '\0';

    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 = NGX_MAX_OFF_T_VALUE;
    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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) {
        mp4_split_options_exit(r, options);
        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:
                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, nlog, of.err,
                    ngx_open_file_n " \"%s\" failed", path.data);
        }

        return rc;
    }


    if (!of.is_file) {
        mp4_split_options_exit(r, options);
        if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
            ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno,
                    ngx_close_file_n " \"%s\" failed", path.data);
        }
        return NGX_DECLINED;
    }

    /* move atom to beginning of file if it's in the last*/
    hls_conf_t *mlcf;
    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_estreaming_module);
    if (mlcf->mp4_enhance == 1) {
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                "examine mp4 filename: \"%V\"", &path);
        if (ngx_http_enable_fast_start(&path, of.fd, r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

    }

    ngx_file_t *file = ngx_pcalloc(r->pool, sizeof (ngx_file_t));

    if (file == NULL) {
        mp4_split_options_exit(r, options);
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    file->fd = of.fd;
    file->name = path;
    file->log = nlog;
    mp4_context_t *mp4_context = mp4_open(r, file, of.size, MP4_OPEN_MOOV);

    if (!mp4_context) {
        mp4_split_options_exit(r, options);
        ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, "mp4_open failed");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    mp4_context->root = root;

    if (m3u8 || len_) {
        int ret, video_width = 0;
        // only request for master playlist need to use ffmpeg api to open video
        // this takes time and cpu, we should avoid it as much as possible 
        if ((m3u8) && (options->adbr || options->org)) goto no_ffmpeg;
        AVFormatContext *fmt_ctx = NULL;
        unsigned int i;
        av_register_all();
        if ((ret = avformat_open_input(&fmt_ctx, (const char*) path.data, NULL, NULL)) < 0) {
            mp4_split_options_exit(r, options);
            mp4_close(mp4_context);
            if (fmt_ctx) avformat_close_input(&fmt_ctx);
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
        if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
            if (fmt_ctx) avformat_close_input(&fmt_ctx);
            mp4_close(mp4_context);
            mp4_split_options_exit(r, options);
            av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
        if (len_) {
            duration = fmt_ctx->duration;
            u_char *buffer = (u_char *) ngx_palloc(mp4_context->r->pool, 10 * sizeof (char));
            u_char * p = buffer;
            if (buffer == NULL) {
                mp4_split_options_exit(r, options);
                mp4_close(mp4_context);
                if (fmt_ctx) avformat_close_input(&fmt_ctx);
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
            // this is stolen from ffmpeg source code
            if (duration != AV_NOPTS_VALUE) {
                duration = duration + 5000;
                int secs;
                secs = duration / AV_TIME_BASE;
                p = ngx_sprintf(p, "%02d\n", secs);
            } else {
                p = ngx_sprintf(p, "N/A\n");
            }
            bucket_insert(bucket, buffer, p - buffer);
            ngx_pfree(mp4_context->r->pool, buffer);
            if (fmt_ctx) avformat_close_input(&fmt_ctx);
            r->allow_ranges = 0;
            result = 1;
            goto response;
        } else {
            for (i = 0; i < fmt_ctx->nb_streams; i++) {
                AVStream *stream;
                AVCodecContext *codec_ctx;
                stream = fmt_ctx->streams[i];
                codec_ctx = stream->codec;
                if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
                    av_log(NULL, AV_LOG_ERROR, "source video w:%d", codec_ctx->width);
                    if (video_width == 0) {
                        video_width = codec_ctx->width;
                    } else if ((video_width != 0) && (video_width < codec_ctx->width)) {
                        // has 2 video streams
                        video_width = codec_ctx->width;
                    } else
                        break;
                }
            }
            avformat_close_input(&fmt_ctx);
        }
        // finish getting video width
no_ffmpeg:
        if ((result = mp4_create_m3u8(mp4_context, bucket, options, video_width))) {
            char action[50];
            sprintf(action, "ios_playlist&segments=%d", result);
            view_count(mp4_context, (char *) path.data, options ? options->hash : NULL, action);
        }
        r->allow_ranges = 0;
    } else {
        result = output_ts(mp4_context, bucket, options);
        if (!options || !result) {
            mp4_close(mp4_context);
            ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, "output_ts failed");
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
        if (options->adbr) {
            destination = ngx_pcalloc(r->pool, sizeof (video_buffer));
            destination->data = NULL;
            destination->len = 0;
            destination->pool = r->pool;

            if (ngx_estreaming_adaptive_bitrate(r, bucket->first,
                    destination, options) == NGX_OK) {
                ngx_buf_t *b = ngx_pcalloc(r->pool, sizeof (ngx_buf_t));
                if (b == NULL) {
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }
                bucket->first->buf = b;
                bucket->first->next = NULL;
                b->pos = destination->data;
                b->last = destination->data + (destination->len * sizeof (unsigned char));
                b->memory = 1;
                b->last_buf = 1;
                bucket->content_length = destination->len;
            }
            ngx_pfree(r->pool, destination);
        }
        char action[50] = "ios_view";
        view_count(mp4_context, (char *) path.data, options->hash, action);
        r->allow_ranges = 1;
    }
response:
    mp4_close(mp4_context);
    mp4_split_options_exit(r, options);
    result = result == 0 ? 415 : 200;
    r->root_tested = !r->error_page;
    if (result && bucket) {
        nlog->action = "sending mp4 to client";
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, nlog, 0, "content_length: %d", bucket->content_length);
        if (bucket->content_length == 0) return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
        r->headers_out.status = NGX_HTTP_OK;
        r->headers_out.content_length_n = bucket->content_length;
        r->headers_out.last_modified_time = of.mtime;
        if (m3u8) {
            r->headers_out.content_type.len = sizeof ("application/vnd.apple.mpegurl") - 1;
            r->headers_out.content_type.data = (u_char *) "application/vnd.apple.mpegurl";
        } else if (len_) {
            r->headers_out.content_type.len = sizeof ("text/html") - 1;
            r->headers_out.content_type.data = (u_char *) "text/html";
        } else {
            r->headers_out.content_type.len = sizeof ("video/MP2T") - 1;
            r->headers_out.content_type.data = (u_char *) "video/MP2T";
        }
        rc = ngx_http_send_header(r);
        if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
            ngx_log_error(NGX_LOG_ALERT, nlog, ngx_errno, ngx_close_file_n "ngx_http_send_header failed");
            return rc;
        }
        return ngx_http_output_filter(r, bucket->first);
    } else return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
}
static ngx_inline ngx_int_t
ngx_http_modsecurity_load_request(ngx_http_request_t *r)
{
    ngx_http_modsecurity_ctx_t  *ctx;
    request_rec                 *req;
    size_t                       root;
    ngx_str_t                    path;
    ngx_uint_t                   port;
    struct sockaddr_in          *sin;
#if (NGX_HAVE_INET6)
    struct sockaddr_in6         *sin6;
#endif

    ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
    req = ctx->req;

    /* request line */
    req->method = (char *)ngx_pstrdup0(r->pool, &r->method_name);

    /* TODO: how to use ap_method_number_of ?
     * req->method_number = ap_method_number_of(req->method);
     */

    req->method_number = ngx_http_modsecurity_method_number(r->method);

    /* ngx_http_map_uri_to_path() allocates memory for terminating '\0' */
    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
        return NGX_ERROR;
    }

    req->filename = (char *) path.data;
    req->path_info = req->filename;

    req->args = (char *)ngx_pstrdup0(r->pool, &r->args);

    req->proto_num = r->http_major *1000 + r->http_minor;
    req->protocol = (char *)ngx_pstrdup0(r->pool, &r->http_protocol);
    req->request_time = apr_time_make(r->start_sec, r->start_msec);
    req->the_request = (char *)ngx_pstrdup0(r->pool, &r->request_line);

    req->unparsed_uri = (char *)ngx_pstrdup0(r->pool, &r->unparsed_uri);
    req->uri = (char *)ngx_pstrdup0(r->pool, &r->uri);

    req->parsed_uri.scheme = "http";

#if (NGX_HTTP_SSL)
    if (r->connection->ssl) {
        req->parsed_uri.scheme = "https";
    }
#endif

    req->parsed_uri.path = (char *)ngx_pstrdup0(r->pool, &r->uri);
    req->parsed_uri.is_initialized = 1;

    switch (r->connection->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
    case AF_INET6:
        sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr;
        port = ntohs(sin6->sin6_port);
        break;
#endif

#if (NGX_HAVE_UNIX_DOMAIN)
    case AF_UNIX:
        port = 0;
        break;
#endif

    default: /* AF_INET */
        sin = (struct sockaddr_in *) r->connection->local_sockaddr;
        port = ntohs(sin->sin_port);
        break;
    }

    req->parsed_uri.port = port;
    req->parsed_uri.port_str = ngx_pnalloc(r->pool, sizeof("65535"));
    (void) ngx_sprintf((u_char *)req->parsed_uri.port_str, "%ui%c", port, '\0');

    req->parsed_uri.query = r->args.len ? req->args : NULL;
    req->parsed_uri.dns_looked_up = 0;
    req->parsed_uri.dns_resolved = 0;

    // req->parsed_uri.password = (char *)ngx_pstrdup0(r->pool, &r->headers_in.passwd);
    // req->parsed_uri.user = (char *)ngx_pstrdup0(r->pool, &r->headers_in.user);
    req->parsed_uri.fragment = (char *)ngx_pstrdup0(r->pool, &r->exten);

    req->hostname = (char *)ngx_pstrdup0(r->pool, (ngx_str_t *)&ngx_cycle->hostname);

    req->header_only = r->header_only ? r->header_only : (r->method == NGX_HTTP_HEAD);

    return NGX_OK;
}
//ngx_http_static_module模块主要是在nginx系统中查找uri指定文件是否存在,存在则直接返回给客户端
static ngx_int_t
ngx_http_static_handler(ngx_http_request_t *r)
{//注意:ngx_http_static_handler如果uri不是以/结尾返回,ngx_http_index_handler不以/结尾返回
//ngx_http_static_handler ngx_http_index_handler每次都要获取缓存信息stat信息,因此每次获取很可能是上一次stat执行的时候获取的信息,除非缓存过期
    u_char                    *last, *location;
    size_t                     root, len;
    ngx_str_t                  path;
    ngx_int_t                  rc;
    ngx_uint_t                 level;
    ngx_log_t                 *log;
    ngx_buf_t                 *b;
    ngx_chain_t                out;
    ngx_open_file_info_t       of;
    ngx_http_core_loc_conf_t  *clcf;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
        return NGX_HTTP_NOT_ALLOWED;
    }

    if (r->uri.data[r->uri.len - 1] == '/') { 
    //注意:ngx_http_static_handler如果uri不是以/结尾返回,ngx_http_index_handler不以/结尾返回
        return NGX_DECLINED;
    }

    log = r->connection->log;

    /*
     * ngx_http_map_uri_to_path() allocates memory for terminating '\0'
     * so we do not need to reserve memory for '/' for possible redirect
     */

    last = ngx_http_map_uri_to_path(r, &path, &root, 0); //通过r->uri获取整个路径或者文件绝对路径
    if (last == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    path.len = last - path.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                   "http filename: \"%s\"", path.data);

    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;
        }

        /* 
           如果文件不存在,则返回出去后会结束请求
           2016/02/16 10:27:36[            ngx_http_static_handler,   139]  [error] 19131#19131: *1 open() "/var/yyz/www/ttt/xx.html" failed (2: No such file or directory), client: 10.2.13.167, server: localhost, request: "GET / HTTP/1.1", host: "10.2.13.167"
           2016/02/16 10:27:36[          ngx_http_finalize_request,  2598]  [debug] 19131#19131: *1 http finalize request rc: 404, "/ttt/xx.html?" a:1, c:2
          */
        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;
    }

    r->root_tested = !r->error_page;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);

    if (of.is_dir) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");

        ngx_http_clear_location(r);

        r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
        if (r->headers_out.location == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        len = r->uri.len + 1;

        if (!clcf->alias && clcf->root_lengths == NULL && r->args.len == 0) {
            location = path.data + clcf->root.len;

            *last = '/';

        } else {
            if (r->args.len) {
                len += r->args.len + 1;
            }

            location = ngx_pnalloc(r->pool, len);
            if (location == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            last = ngx_copy(location, r->uri.data, r->uri.len);

            *last = '/';

            if (r->args.len) {
                *++last = '?';
                ngx_memcpy(++last, r->args.data, r->args.len);
            }
        }

        /*
         * we do not need to set the r->headers_out.location->hash and
         * r->headers_out.location->key fields
         */

        r->headers_out.location->value.len = len;
        r->headers_out.location->value.data = location;

        return NGX_HTTP_MOVED_PERMANENTLY;
    }

#if !(NGX_WIN32) /* the not regular files are probably Unix specific */

    if (!of.is_file) {
        ngx_log_error(NGX_LOG_CRIT, log, 0,
                      "\"%s\" is not a regular file", path.data);

        return NGX_HTTP_NOT_FOUND;
    }

#endif

    if (r->method & NGX_HTTP_POST) {
        return NGX_HTTP_NOT_ALLOWED;
    }

    rc = ngx_http_discard_request_body(r);

    if (rc != NGX_OK) {
        return rc;
    }

    log->action = "sending response to client";

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = of.size;
    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 (r != r->main && of.size == 0) {
        return ngx_http_send_header(r);
    }

    r->allow_ranges = 1;

    /* we need to allocate all before the header would be sent */

    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    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;
    }

    rc = ngx_http_send_header(r);

    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    b->file_pos = 0;
    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->file->fd = of.fd;
    b->file->name = path;
    b->file->log = log;
    b->file->directio = of.is_directio; //注意这里如果文件大小大于direction设置,则置1,后面会使能direct I/O方式,见ngx_directio_on

    out.buf = b;
    out.next = NULL;

    return ngx_http_output_filter(r, &out);
}
static ngx_int_t
ngx_http_ts_split_handler(ngx_http_request_t *r)
{
    u_char                    *last;
    size_t                     root;
    ngx_str_t                  path, index_path, media_path;
    ngx_str_t                  first, second, media_name;
    ngx_int_t                  rc, media_seq;
    ngx_uint_t                 media_begin, media_len;
    ngx_uint_t                 level;
    ngx_buf_t                 *b;
    ngx_chain_t                out;
    ngx_open_file_info_t       of;
    ngx_log_t                 *log;
    ngx_http_core_loc_conf_t  *clcf;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
          return NGX_HTTP_NOT_ALLOWED;
      }

    if (r->uri.data[r->uri.len - 1] == '/') {
      return NGX_DECLINED;
    }

    ngx_str_null(&media_name);
    ngx_str_null(&second);
    log = r->connection->log;

    /*
     * ngx_http_map_uri_to_path() allocates memory for terminating '\0'
     * so we do not need to reserve memory for '/' for possible redirect
     */

    last = ngx_http_map_uri_to_path(r, &path, &root, 0);
    if (last == NULL) {
      return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    path.len = last - path.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                 "http filename: \"%s\"", path.data);



    ngx_http_ts_split_rpartition(&path, &first, &second, '/');

    index_path.len = first.len + sizeof("index.m3u8") + 1;
    index_path.data = ngx_palloc(r->pool, index_path.len);

    ngx_snprintf(index_path.data, index_path.len, "%V/index.m3u8", &first);


    index_path.data[index_path.len-1] = '\0';

    media_name = ngx_http_ts_split_get_media_name(&second);

    media_path.len = first.len + 5 + media_name.len;
    media_path.data = ngx_palloc(r->pool, media_path.len);

    ngx_snprintf(media_path.data, media_path.len, "%V/%V.ts", &first,
                &media_name);


    media_path.data[media_path.len-1] = '\0';


    media_seq = ngx_http_ts_split_get_media_seq(&second);
    media_begin = 0;
    media_len = 0;
    if (ngx_http_ts_split_index_info(r, &index_path, media_seq, &media_begin,
            &media_len) != NGX_OK) {
        return NGX_ERROR;
    }
    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, &media_path, &of) != NGX_OK) {
      return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if (ngx_open_cached_file(clcf->open_file_cache, &media_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, media_path.data);
        }

        return rc;
    }

    r->root_tested = !r->error_page;




#if !(NGX_WIN32) /* the not regular files are probably Unix specific */

    if (!of.is_file) {
        ngx_log_error(NGX_LOG_CRIT, log, 0,
                      "\"%s\" is not a regular file", path.data);

        return NGX_HTTP_NOT_FOUND;
    }

#endif

    if (r->method & NGX_HTTP_POST) {
        return NGX_HTTP_NOT_ALLOWED;
    }

    rc = ngx_http_discard_request_body(r);

    if (rc != NGX_OK) {
        return rc;
    }

    log->action = "sending response to client";

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = media_len;

    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 (r != r->main && of.size == 0) {
        return ngx_http_send_header(r);
    }

    /* we need to allocate all before the header would be sent */

    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    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;
    }

    rc = ngx_http_send_header(r);

    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    b->file_pos = media_begin;
    b->file_last = media_begin + media_len;

    b->in_file = b->file_last ? 1: 0;
    b->last_buf = (r == r->main) ? 1: 0;
    b->last_in_chain = 1;

    b->file->fd = of.fd;
    b->file->name = media_path;
    b->file->log = log;
    b->file->directio = of.is_directio;

    out.buf = b;
    out.next = NULL;

    return ngx_http_output_filter(r, &out);
}
static ngx_int_t
ngx_http_slice_handler(ngx_http_request_t *r)
{
    u_char                    *last;
    off_t                      begin, end, 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[3];
    ngx_open_file_info_t       of;
    ngx_http_core_loc_conf_t  *clcf;
    ngx_http_slice_loc_conf_t *slcf;

    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;
    }

    slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_module);

    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 slice 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_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:

            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) {

        if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                          ngx_close_file_n " \"%s\" failed", path.data);
        }

        return NGX_DECLINED;
    }

    r->root_tested = !r->error_page;

    begin = 0;
    end = of.size;

    if (r->args.len) {

        if (ngx_http_arg(r, slcf->begin.data, slcf->begin.len, &value)
            == NGX_OK)
        {
            begin = ngx_atoof(value.data, value.len);

            if (begin == NGX_ERROR || begin >= of.size) {
                begin = 0;
            }
        }

        if (ngx_http_arg(r, slcf->end.data, slcf->end.len, &value) == NGX_OK) {

            end = ngx_atoof(value.data, value.len);

            if (end == NGX_ERROR || end >= of.size) {
                end = of.size;
            }
        }
    }

    end = end < begin ? of.size : end;

    len = (end == begin) ? 0 : ((end - begin)
            + ((begin == 0 && slcf->header_first) ? slcf->header.len : 0)
            + ((end == of.size && slcf->footer_last) ? slcf->footer.len : 0));

    log->action = "sending slice 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_content_type(r) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if (len == 0) {
        r->header_only = 1;
        return ngx_http_send_header(r);
    }

    /*
     * add header when the first header is not denied
     */
    if (slcf->header.len
        && !(begin == 0 && !slcf->header_first))
    {
        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
        if (b == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        b->pos = slcf->header.data;
        b->last = slcf->header.data + slcf->header.len;
        b->memory = 1;

        out[0].buf = b;
        out[0].next = &out[1];

        i = 0;
    } else {
        i = 1;
    }

    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    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 = begin;
    b->file_last = end;

    b->in_file = b->file_last ? 1: 0;
    b->last_buf = 1;
    b->last_in_chain = 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;

    /*
     * add footer when the last footer is not denied
     */
    if (slcf->footer.len
        && !(end == of.size && !slcf->footer_last))
    {
        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
        if (b == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        b->pos = slcf->footer.data;
        b->last = slcf->footer.data + slcf->footer.len;
        b->memory = 1;
        b->last_buf = 1;
        b->last_in_chain = 1;

        out[2].buf = b;
        out[2].next = NULL;

        out[1].buf->last_buf = 0;
        out[1].buf->last_in_chain = 0;
        out[1].next = &out[2];
    }

    return ngx_http_output_filter(r, &out[i]);
}
Exemple #17
0
ngx_int_t
passenger_content_handler(ngx_http_request_t *r)
{
    ngx_int_t              rc;
    ngx_http_upstream_t   *u;
    passenger_loc_conf_t  *slcf;
    ngx_str_t              path, base_uri;
    u_char                *path_last, *end;
    u_char                 root_path_str[NGX_MAX_PATH + 1];
    ngx_str_t              root_path;
    size_t                 root_len, len;
    u_char                 page_cache_file_str[NGX_MAX_PATH + 1];
    ngx_str_t              page_cache_file;
    passenger_context_t   *context;
    PP_Error               error;

    if (passenger_main_conf.root_dir.len == 0) {
        return NGX_DECLINED;
    } else if (r->subrequest_in_memory) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                      "ngx_http_passenger_module does not support "
                      "subrequest in memory");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    slcf = ngx_http_get_module_loc_conf(r, ngx_http_passenger_module);

    /* Let the next content handler take care of this request if Phusion
     * Passenger is disabled for this URL.
     */
    if (!slcf->enabled) {
        return NGX_DECLINED;
    }

    /* Let the next content handler take care of this request if this URL
     * maps to an existing file.
     */
    path_last = ngx_http_map_uri_to_path(r, &path, &root_len, 0);
    if (path_last != NULL && file_exists(path.data, 0)) {
        return NGX_DECLINED;
    }

    /* Create a string containing the root path. This path already
     * contains a trailing slash.
     */
    end = ngx_copy(root_path_str, path.data, root_len);
    *end = '\0';
    root_path.data = root_path_str;
    root_path.len  = root_len;


    context = ngx_pcalloc(r->pool, sizeof(passenger_context_t));
    if (context == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    ngx_http_set_ctx(r, context, ngx_http_passenger_module);


    /* Find the base URI for this web application, if any. */
    if (find_base_uri(r, slcf, &base_uri)) {
        /* Store the found base URI into context->public_dir. We infer that
         * the 'public' directory of the web app equals document root + base URI.
         */
        if (slcf->document_root.data != NULL) {
            len = slcf->document_root.len + 1;
            context->public_dir.data = ngx_palloc(r->pool, sizeof(u_char) * len);
            end = ngx_copy(context->public_dir.data, slcf->document_root.data,
                           slcf->document_root.len);
        } else {
            len = root_path.len + base_uri.len + 1;
            context->public_dir.data = ngx_palloc(r->pool, sizeof(u_char) * len);
            end = ngx_copy(context->public_dir.data, root_path.data, root_path.len);
            end = ngx_copy(end, base_uri.data, base_uri.len);
        }
        *end = '\0';
        context->public_dir.len = len - 1;
        context->base_uri = base_uri;
    } else {
        /* No base URI directives are applicable for this request. So assume that
         * the web application's public directory is the document root.
         * context->base_uri is now a NULL string.
         */
        len = sizeof(u_char *) * (root_path.len + 1);
        context->public_dir.data = ngx_palloc(r->pool, len);
        end = ngx_copy(context->public_dir.data, root_path.data,
                       root_path.len);
        *end = '\0';
        context->public_dir.len  = root_path.len;
    }

    /* If there's a corresponding page cache file for this URL, then serve that
     * file instead.
     */
    page_cache_file.data = page_cache_file_str;
    page_cache_file.len  = sizeof(page_cache_file_str);
    if (map_uri_to_page_cache_file(r, &context->public_dir, path.data,
                                   path_last - path.data, &page_cache_file)) {
        return passenger_static_content_handler(r, &page_cache_file);
    }

    if (slcf->app_type.data == NULL) {
        pp_error_init(&error);
        if (slcf->app_root.data == NULL) {
            context->app_type = pp_app_type_detector_check_document_root(
                                    pp_app_type_detector,
                                    (const char *) context->public_dir.data, context->public_dir.len,
                                    context->base_uri.len != 0,
                                    &error);
        } else {
            context->app_type = pp_app_type_detector_check_app_root(
                                    pp_app_type_detector,
                                    (const char *) slcf->app_root.data, slcf->app_root.len,
                                    &error);
        }
        if (context->app_type == PAT_NONE) {
            return NGX_DECLINED;
        } else if (context->app_type == PAT_ERROR) {
            if (error.errnoCode == EACCES) {
                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                              "%s; This error means that the Nginx worker process (PID %d, "
                              "running as UID %d) does not have permission to access this file. "
                              "Please read the manual to learn how to fix this problem: "
                              "section 'Troubleshooting' -> 'Upon accessing the web app, Nginx "
                              "reports a \"Permission denied\" error'; Extra info",
                              error.message,
                              (int) getpid(),
                              (int) getuid());
            } else {
                ngx_log_error(NGX_LOG_ALERT, r->connection->log,
                              (error.errnoCode == PP_NO_ERRNO) ? 0 : error.errnoCode,
                              "%s",
                              error.message);
            }
            pp_error_destroy(&error);
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    } else {
        context->app_type = pp_get_app_type2((const char *) slcf->app_type.data,
                                             slcf->app_type.len);
        if (context->app_type == PAT_NONE) {
            return NGX_DECLINED;
        }
    }


    /* Setup upstream stuff and prepare sending the request to the HelperAgent. */

    if (ngx_http_upstream_create(r) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    u = r->upstream;

    u->schema = pp_schema_string;
    u->output.tag = (ngx_buf_tag_t) &ngx_http_passenger_module;
    set_upstream_server_address(u, &slcf->upstream_config);
    u->conf = &slcf->upstream_config;

#if (NGX_HTTP_CACHE)
    u->create_key       = create_key;
#endif
    u->create_request   = create_request;
    u->reinit_request   = reinit_request;
    u->process_header   = process_status_line;
    u->abort_request    = abort_request;
    u->finalize_request = finalize_request;
    r->state = 0;

    u->buffering = slcf->upstream_config.buffering;

    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
    if (u->pipe == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    u->pipe->input_filter = ngx_event_pipe_copy_input_filter;
    u->pipe->input_ctx = r;

    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);

    fix_peer_address(r);

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }

    return NGX_DONE;
}
Exemple #18
0
ngx_int_t
passenger_content_handler(ngx_http_request_t *r)
{
    ngx_int_t              rc;
    ngx_http_upstream_t   *u;
    passenger_loc_conf_t  *slcf;
    ngx_str_t              path, base_uri;
    u_char                *path_last, *end;
    u_char                 root_path_str[NGX_MAX_PATH + 1];
    ngx_str_t              root_path;
    size_t                 root, len;
    u_char                 page_cache_file_str[NGX_MAX_PATH + 1];
    ngx_str_t              page_cache_file;
    passenger_context_t   *context;

    if (r->subrequest_in_memory) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                      "ngx_http_passenger_module does not support "
                      "subrequest in memory");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    slcf = ngx_http_get_module_loc_conf(r, ngx_http_passenger_module);

    /* Let the next content handler take care of this request if Phusion
     * Passenger is disabled for this URL.
     */
    if (!slcf->enabled) {
        return NGX_DECLINED;
    }
    
    /* Let the next content handler take care of this request if this URL
     * maps to an existing file.
     */
    path_last = ngx_http_map_uri_to_path(r, &path, &root, 0);
    if (path_last != NULL && file_exists(path.data, 0)) {
        return NGX_DECLINED;
    }
    
    /* Create a string containing the root path. This path already
     * contains a trailing slash.
     */
    end = ngx_copy(root_path_str, path.data, root);
    *end = '\0';
    root_path.data = root_path_str;
    root_path.len  = root;
    
    
    context = ngx_pcalloc(r->pool, sizeof(passenger_context_t));
    if (context == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    ngx_http_set_ctx(r, context, ngx_http_passenger_module);
    
    
    /* Find the base URI for this web application, if any. */
    if (find_base_uri(r, slcf, &base_uri)) {
        /* Store the found base URI in context->public_dir. We infer that the 'public'
         * directory of the web application is document root + base URI.
         */
        len = root_path.len + base_uri.len + 1;
        context->public_dir.data = ngx_palloc(r->pool, sizeof(u_char) * len);
        end = ngx_copy(context->public_dir.data, root_path.data, root_path.len);
        end = ngx_copy(end, base_uri.data, base_uri.len);
        *end = '\0';
        context->public_dir.len = len - 1;
        context->base_uri = base_uri;
    } else {
        /* No base URI directives are applicable for this request. So assume that
         * the web application's public directory is the document root.
         * context->base_uri is now a NULL string.
         */
        len = sizeof(u_char *) * (root_path.len + 1);
        context->public_dir.data = ngx_palloc(r->pool, len);
        end = ngx_copy(context->public_dir.data, root_path.data,
                       root_path.len);
        *end = '\0';
        context->public_dir.len  = root_path.len;
    }
    
    /* If there's a corresponding page cache file for this URL, then serve that
     * file instead.
     */
    page_cache_file.data = page_cache_file_str;
    page_cache_file.len  = sizeof(page_cache_file_str);
    if (map_uri_to_page_cache_file(r, &context->public_dir, path.data,
                                   path_last - path.data, &page_cache_file)) {
        return passenger_static_content_handler(r, &page_cache_file);
    }
    
    context->app_type = detect_application_type(&context->public_dir);
    if (context->app_type == AP_NONE) {
        return NGX_DECLINED;
    }
    
    
    /* Setup upstream stuff and prepare sending the request to the backend. */
    
    u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
    if (u == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    
#if NGINX_VERSION_NUM >= 7000
    u->schema = passenger_schema_string;
#endif

    u->peer.log = r->connection->log;
    u->peer.log_error = NGX_ERROR_ERR;
#if (NGX_THREADS)
    u->peer.lock = &r->connection->lock;
#endif

    u->output.tag = (ngx_buf_tag_t) &ngx_http_passenger_module;

    u->conf = &slcf->upstream;

    u->create_request   = create_request;
    u->reinit_request   = reinit_request;
    u->process_header   = process_status_line;
    u->abort_request    = abort_request;
    u->finalize_request = finalize_request;

    u->buffering = 1;

    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
    if (u->pipe == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    u->pipe->input_filter = ngx_event_pipe_copy_input_filter;

    r->upstream = u;

    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }

    return NGX_DONE;
}
static ngx_int_t ngx_http_image_handler(ngx_http_request_t *r)
{
	u_char                    *last;
	size_t                     root;
	ngx_int_t                  rc;
	ngx_str_t                  path;
	char                       request_uri[255];
	int                        request_uri_len;
	ngx_image_conf_t  *conf;
	conf = ngx_http_get_module_loc_conf(r, ngx_http_image_module);
	if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD)))
	{
		return NGX_HTTP_NOT_ALLOWED;
	}
	if (r->headers_in.if_modified_since)
	{
		return NGX_HTTP_NOT_MODIFIED;
	}
	
	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;
	}
	if(file_exists((char*) path.data) == -1)
	{
		request_uri_len = strlen((char *)r->uri_start) - strlen((char *)r->uri_end);
		strncpy(request_uri, (char *)r->uri_start, request_uri_len);
		request_uri[request_uri_len] = '\0';
		dirname(request_uri,conf->request_dir);
		conf->url = request_uri;//请求的URL地址
		conf->dest_file = (char *)path.data;
		check_image_type(conf);//检查图片类型(根据后缀进行简单判断)
		if( conf->dest_type > 0 )
		{

			if (parse_image_info(conf) == 0)//解析并处理请求的图片URL
			{

				make_thumb(conf);//生成图片缩略图
				water_mark(conf);//图片打上水印
				thumb_to_string(conf);//GD对象转换成二进制字符串
				if(conf->image_output == 0)
				{
					write_img(conf);//保存图片缩略图到文件
				}
				if(conf->image_output == 1)
				{
					return output(r,conf,ngx_http_image_types[conf->dest_type]);
				}
			}
		}
	}
	return NGX_DECLINED;
}
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_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:

            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) {

        if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                          ngx_close_file_n " \"%s\" failed", path.data);
        }

        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_content_type(r) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if (i == 0) {
        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
        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_pcalloc(r->pool, sizeof(ngx_buf_t));
    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 = 1;
    b->last_in_chain = 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 ngx_int_t
ngx_http_gzip_static_handler(ngx_http_request_t *r)
{
    u_char                       *p;
    size_t                        root;
    ngx_str_t                     path;
    ngx_int_t                     rc;
    ngx_uint_t                    level;
    ngx_log_t                    *log;
    ngx_buf_t                    *b;
    ngx_chain_t                   out;
    ngx_table_elt_t              *h;
    ngx_open_file_info_t          of;
    ngx_http_core_loc_conf_t     *clcf;
    ngx_http_gzip_static_conf_t  *gzcf;

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
        return NGX_DECLINED;
    }

    if (r->uri.data[r->uri.len - 1] == '/') {
        return NGX_DECLINED;
    }

    gzcf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_static_module);

    if (!gzcf->enable) {
        return NGX_DECLINED;
    }

    rc = ngx_http_gzip_ok(r);

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    if (!clcf->gzip_vary && rc != NGX_OK) {
        return NGX_DECLINED;
    }

    log = r->connection->log;

    p = ngx_http_map_uri_to_path(r, &path, &root, sizeof(".gz") - 1);
    if (p == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    *p++ = '.';
    *p++ = 'g';
    *p++ = 'z';
    *p = '\0';

    path.len = p - path.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                   "http filename: \"%s\"", path.data);

    ngx_memzero(&of, sizeof(ngx_open_file_info_t));

#if (NGX_WIN32 && NGX_HAVE_FILE_AIO)
    of.aio = clcf->aio;
#endif
    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_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:

            return NGX_DECLINED;

        case NGX_EACCES:

            level = NGX_LOG_ERR;
            break;

        default:

            level = NGX_LOG_CRIT;
            break;
        }

        ngx_log_error(level, log, of.err,
                      "%s \"%s\" failed", of.failed, path.data);

        return NGX_DECLINED;
    }

    r->gzip_vary = 1;

    if (rc != NGX_OK) {
        return NGX_DECLINED;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);

    if (of.is_dir) {
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
        return NGX_DECLINED;
    }

#if !(NGX_WIN32) /* the not regular files are probably Unix specific */

    if (!of.is_file) {
        ngx_log_error(NGX_LOG_CRIT, log, 0,
                      "\"%s\" is not a regular file", path.data);

        return NGX_HTTP_NOT_FOUND;
    }

#endif

    r->root_tested = !r->error_page;

    rc = ngx_http_discard_request_body(r);

    if (rc != NGX_OK) {
        return rc;
    }

    log->action = "sending response to client";

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = of.size;
    r->headers_out.last_modified_time = of.mtime;

    if (ngx_http_set_content_type(r) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    h = ngx_list_push(&r->headers_out.headers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    h->hash = 1;
    ngx_str_set(&h->key, "Content-Encoding");
    ngx_str_set(&h->value, "gzip");
    r->headers_out.content_encoding = h;

    r->ignore_content_encoding = 1;

    /* we need to allocate all before the header would be sent */

    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    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;
    }

    rc = ngx_http_send_header(r);

    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    b->file_pos = 0;
    b->file_last = of.size;

    b->in_file = b->file_last ? 1 : 0;
    b->last_buf = 1;
    b->last_in_chain = 1;

    b->file->fd = of.fd;
    b->file->name = path;
    b->file->log = log;
    b->file->directio = of.is_directio;

    out.buf = b;
    out.next = NULL;

    return ngx_http_output_filter(r, &out);
}
Exemple #22
0
static void
ngx_http_dav_put_handler(ngx_http_request_t *r)
{
    size_t                    root;
    time_t                    date;
    ngx_str_t                *temp, path;
    ngx_uint_t                status;
    ngx_file_info_t           fi;
    ngx_ext_rename_file_t     ext;
    ngx_http_dav_loc_conf_t  *dlcf;

    if (r->request_body == NULL) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "PUT request body is unavailable");
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }

    if (r->request_body->temp_file == NULL) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "PUT request body must be in a file");
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }

    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }

    path.len--;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http put filename: \"%s\"", path.data);

    temp = &r->request_body->temp_file->file.name;

    if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
        status = NGX_HTTP_CREATED;

    } else {
        status = NGX_HTTP_NO_CONTENT;

        if (ngx_is_dir(&fi)) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
                          "\"%s\" could not be created", path.data);

            if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {
                ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                              ngx_delete_file_n " \"%s\" failed",
                              temp->data);
            }

            ngx_http_finalize_request(r, NGX_HTTP_CONFLICT);
            return;
        }
    }

    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

    ext.access = dlcf->access;
    ext.path_access = dlcf->access;
    ext.time = -1;
    ext.create_path = dlcf->create_full_put_path;
    ext.delete_file = 1;
    ext.log = r->connection->log;

    if (r->headers_in.date) {
        date = ngx_parse_http_time(r->headers_in.date->value.data,
                                   r->headers_in.date->value.len);

        if (date != NGX_ERROR) {
            ext.time = date;
            ext.fd = r->request_body->temp_file->file.fd;
        }
    }

    if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) {
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }

    if (status == NGX_HTTP_CREATED) {
        if (ngx_http_dav_location(r, path.data) != NGX_OK) {
            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return;
        }

        r->headers_out.content_length_n = 0;
    }

    r->headers_out.status = status;
    r->header_only = 1;

    ngx_http_finalize_request(r, ngx_http_send_header(r));
    return;
}
static ngx_int_t
ngx_http_autoindex_handler(ngx_http_request_t *r)
{
    u_char                         *last, *filename;
    size_t                          len, allocated, root;
    ngx_err_t                       err;
    ngx_buf_t                      *b;
    ngx_int_t                       rc;
    ngx_str_t                       path, callback;
    ngx_dir_t                       dir;
    ngx_uint_t                      level, format;
    ngx_pool_t                     *pool;
    ngx_chain_t                     out;
    ngx_array_t                     entries;
    ngx_http_autoindex_entry_t     *entry;
    ngx_http_autoindex_loc_conf_t  *alcf;

    if (r->uri.data[r->uri.len - 1] != '/') {
        return NGX_DECLINED;
    }

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
        return NGX_DECLINED;
    }

    alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);

    if (!alcf->enable) {
        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,
                                    NGX_HTTP_AUTOINDEX_PREALLOCATE);
    if (last == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    allocated = path.len;
    path.len = last - path.data;
    if (path.len > 1) {
        path.len--;
    }
    path.data[path.len] = '\0';

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http autoindex: \"%s\"", path.data);

    format = alcf->format;

    if (format == NGX_HTTP_AUTOINDEX_JSONP) {
        if (ngx_http_autoindex_jsonp_callback(r, &callback) != NGX_OK) {
            return NGX_HTTP_BAD_REQUEST;
        }

        if (callback.len == 0) {
            format = NGX_HTTP_AUTOINDEX_JSON;
        }
    }

    if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
        err = ngx_errno;

        if (err == NGX_ENOENT
            || err == NGX_ENOTDIR
            || err == NGX_ENAMETOOLONG)
        {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_NOT_FOUND;

        } else if (err == NGX_EACCES) {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_FORBIDDEN;

        } else {
            level = NGX_LOG_CRIT;
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        ngx_log_error(level, r->connection->log, err,
                      ngx_open_dir_n " \"%s\" failed", path.data);

        return rc;
    }

#if (NGX_SUPPRESS_WARN)

    /* MSVC thinks 'entries' may be used without having been initialized */
    ngx_memzero(&entries, sizeof(ngx_array_t));

#endif

    /* TODO: pool should be temporary pool */
    pool = r->pool;

    if (ngx_array_init(&entries, pool, 40, sizeof(ngx_http_autoindex_entry_t))
        != NGX_OK)
    {
        return ngx_http_autoindex_error(r, &dir, &path);
    }

    r->headers_out.status = NGX_HTTP_OK;

    switch (format) {

    case NGX_HTTP_AUTOINDEX_JSON:
        ngx_str_set(&r->headers_out.content_type, "application/json");
        break;

    case NGX_HTTP_AUTOINDEX_JSONP:
        ngx_str_set(&r->headers_out.content_type, "application/javascript");
        break;

    case NGX_HTTP_AUTOINDEX_XML:
        ngx_str_set(&r->headers_out.content_type, "text/xml");
        ngx_str_set(&r->headers_out.charset, "utf-8");
        break;

    default: /* NGX_HTTP_AUTOINDEX_HTML */
        ngx_str_set(&r->headers_out.content_type, "text/html");
        break;
    }

    r->headers_out.content_type_len = r->headers_out.content_type.len;
    r->headers_out.content_type_lowcase = NULL;

    rc = ngx_http_send_header(r);

    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        if (ngx_close_dir(&dir) == NGX_ERROR) {
            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                          ngx_close_dir_n " \"%V\" failed", &path);
        }

        return rc;
    }

    filename = path.data;
    filename[path.len] = '/';

    for ( ;; ) {
        ngx_set_errno(0);

        if (ngx_read_dir(&dir) == NGX_ERROR) {
            err = ngx_errno;

            if (err != NGX_ENOMOREFILES) {
                ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                              ngx_read_dir_n " \"%V\" failed", &path);
                return ngx_http_autoindex_error(r, &dir, &path);
            }

            break;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http autoindex file: \"%s\"", ngx_de_name(&dir));

        len = ngx_de_namelen(&dir);

        if (ngx_de_name(&dir)[0] == '.') {
            continue;
        }

        if (!dir.valid_info) {

            /* 1 byte for '/' and 1 byte for terminating '\0' */

            if (path.len + 1 + len + 1 > allocated) {
                allocated = path.len + 1 + len + 1
                                     + NGX_HTTP_AUTOINDEX_PREALLOCATE;

                filename = ngx_pnalloc(pool, allocated);
                if (filename == NULL) {
                    return ngx_http_autoindex_error(r, &dir, &path);
                }

                last = ngx_cpystrn(filename, path.data, path.len + 1);
                *last++ = '/';
            }

            ngx_cpystrn(last, ngx_de_name(&dir), len + 1);

            if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
                err = ngx_errno;

                if (err != NGX_ENOENT && err != NGX_ELOOP) {
                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                                  ngx_de_info_n " \"%s\" failed", filename);

                    if (err == NGX_EACCES) {
                        continue;
                    }

                    return ngx_http_autoindex_error(r, &dir, &path);
                }

                if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                                  ngx_de_link_info_n " \"%s\" failed",
                                  filename);
                    return ngx_http_autoindex_error(r, &dir, &path);
                }
            }
        }

        entry = ngx_array_push(&entries);
        if (entry == NULL) {
            return ngx_http_autoindex_error(r, &dir, &path);
        }

        entry->name.len = len;

        entry->name.data = ngx_pnalloc(pool, len + 1);
        if (entry->name.data == NULL) {
            return ngx_http_autoindex_error(r, &dir, &path);
        }

        ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);

        entry->dir = ngx_de_is_dir(&dir);
        entry->file = ngx_de_is_file(&dir);
        entry->mtime = ngx_de_mtime(&dir);
        entry->size = ngx_de_size(&dir);
    }

    if (ngx_close_dir(&dir) == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                      ngx_close_dir_n " \"%V\" failed", &path);
    }

    if (entries.nelts > 1) {
        ngx_qsort(entries.elts, (size_t) entries.nelts,
                  sizeof(ngx_http_autoindex_entry_t),
                  ngx_http_autoindex_cmp_entries);
    }

    switch (format) {

    case NGX_HTTP_AUTOINDEX_JSON:
        b = ngx_http_autoindex_json(r, &entries, NULL);
        break;

    case NGX_HTTP_AUTOINDEX_JSONP:
        b = ngx_http_autoindex_json(r, &entries, &callback);
        break;

    case NGX_HTTP_AUTOINDEX_XML:
        b = ngx_http_autoindex_xml(r, &entries);
        break;

    default: /* NGX_HTTP_AUTOINDEX_HTML */
        b = ngx_http_autoindex_html(r, &entries);
        break;
    }

    if (b == NULL) {
        return NGX_ERROR;
    }

    /* TODO: free temporary pool */

    if (r == r->main) {
        b->last_buf = 1;
    }

    b->last_in_chain = 1;

    out.buf = b;
    out.next = NULL;

    return ngx_http_output_filter(r, &out);
}
Exemple #24
0
static ngx_int_t
ngx_http_dav_delete_handler(ngx_http_request_t *r)
{
    size_t                    root;
    ngx_err_t                 err;
    ngx_int_t                 rc, depth;
    ngx_uint_t                i, d, dir;
    ngx_str_t                 path;
    ngx_file_info_t           fi;
    ngx_http_dav_loc_conf_t  *dlcf;

    if (r->headers_in.content_length_n > 0) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "DELETE with body is unsupported");
        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
    }

    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

    if (dlcf->min_delete_depth) {
        d = 0;

        for (i = 0; i < r->uri.len; /* void */) {
            if (r->uri.data[i++] == '/') {
                if (++d >= dlcf->min_delete_depth && i < r->uri.len) {
                    goto ok;
                }
            }
        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "insufficient URI depth:%i to DELETE", d);
        return NGX_HTTP_CONFLICT;
    }

ok:

    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http delete filename: \"%s\"", path.data);

    if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
        err = ngx_errno;

        rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;

        return ngx_http_dav_error(r->connection->log, err,
                                  rc, ngx_link_info_n, path.data);
    }

    if (ngx_is_dir(&fi)) {

        if (r->uri.data[r->uri.len - 1] != '/') {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
                          "DELETE \"%s\" failed", path.data);
            return NGX_HTTP_CONFLICT;
        }

        depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);

        if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "\"Depth\" header must be infinity");
            return NGX_HTTP_BAD_REQUEST;
        }

        path.len -= 2;  /* omit "/\0" */

        dir = 1;

    } else {

        /*
         * we do not need to test (r->uri.data[r->uri.len - 1] == '/')
         * because ngx_link_info("/file/") returned NGX_ENOTDIR above
         */

        depth = ngx_http_dav_depth(r, 0);

        if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "\"Depth\" header must be 0 or infinity");
            return NGX_HTTP_BAD_REQUEST;
        }

        dir = 0;
    }

    rc = ngx_http_dav_delete_path(r, &path, dir);

    if (rc == NGX_OK) {
        return NGX_HTTP_NO_CONTENT;
    }

    return rc;
}
/*  配置index index.html index_large.html  gmime-gmime-cipher-context.html;
2025/02/14 08:24:04[   ngx_http_process_request_headers,  1412]  [debug] 2955#2955: *2 http header done
2025/02/14 08:24:04[                ngx_event_del_timer,    39]  [debug] 2955#2955: *2 < ngx_http_process_request,  2013>  event timer del: 3: 30909486
2025/02/14 08:24:04[        ngx_http_core_rewrite_phase,  1810]  [debug] 2955#2955: *2 rewrite phase: 0 (NGX_HTTP_SERVER_REWRITE_PHASE)
2025/02/14 08:24:04[    ngx_http_core_find_config_phase,  1868]  [debug] 2955#2955: *2 find config phase: 1 (NGX_HTTP_FIND_CONFIG_PHASE), uri:/
2025/02/14 08:24:04[ ngx_http_core_find_static_location,  2753]  [debug] 2955#2955: *2 static_locations test location: "/", client uri:/
2025/02/14 08:24:04[        ngx_http_core_find_location,  2693]  [debug] 2955#2955: *2 ngx pcre test location: ~ "/1mytest"
2025/02/14 08:24:04[    ngx_http_core_find_config_phase,  1888]  [debug] 2955#2955: *2 using configuration "/"
2025/02/14 08:24:04[    ngx_http_core_find_config_phase,  1895]  [debug] 2955#2955: *2 http cl:-1 max:1048576
2025/02/14 08:24:04[        ngx_http_core_rewrite_phase,  1810]  [debug] 2955#2955: *2 rewrite phase: 2 (NGX_HTTP_REWRITE_PHASE)
2025/02/14 08:24:04[   ngx_http_core_post_rewrite_phase,  1963]  [debug] 2955#2955: *2 post rewrite phase: 3 (NGX_HTTP_POST_REWRITE_PHASE)
2025/02/14 08:24:04[        ngx_http_core_generic_phase,  1746]  [debug] 2955#2955: *2 generic phase: 4 (NGX_HTTP_PREACCESS_PHASE)
2025/02/14 08:24:04[        ngx_http_core_generic_phase,  1746]  [debug] 2955#2955: *2 generic phase: 5 (NGX_HTTP_PREACCESS_PHASE)
2025/02/14 08:24:04[         ngx_http_core_access_phase,  2061]  [debug] 2955#2955: *2 access phase: 6 (NGX_HTTP_ACCESS_PHASE)
2025/02/14 08:24:04[         ngx_http_core_access_phase,  2061]  [debug] 2955#2955: *2 access phase: 7 (NGX_HTTP_ACCESS_PHASE)
2025/02/14 08:24:04[    ngx_http_core_post_access_phase,  2163]  [debug] 2955#2955: *2 post access phase: 8 (NGX_HTTP_POST_ACCESS_PHASE)
2025/02/14 08:24:04[        ngx_http_core_content_phase,  2491]  [debug] 2955#2955: *2 content phase: 9 (NGX_HTTP_CONTENT_PHASE)
2025/02/14 08:24:04[             ngx_http_index_handler,   144]  [debug] 2955#2955: *2 yang test ... index-count:3
2025/02/14 08:24:04[             ngx_http_index_handler,   216]  [debug] 2955#2955: *2 open index "/usr/local/nginx/html/index.html"
2025/02/14 08:24:04[         ngx_http_internal_redirect,  3853]  [debug] 2955#2955: *2 internal redirect: "/index.html?"
2025/02/14 08:24:04[        ngx_http_core_rewrite_phase,  1810]  [debug] 2955#2955: *2 rewrite phase: 0 (NGX_HTTP_SERVER_REWRITE_PHASE)
2025/02/14 08:24:04[    ngx_http_core_find_config_phase,  1868]  [debug] 2955#2955: *2 find config phase: 1 (NGX_HTTP_FIND_CONFIG_PHASE), uri:/index.html
2025/02/14 08:24:04[ ngx_http_core_find_static_location,  2753]  [debug] 2955#2955: *2 static_locations test location: "/", client uri:/index.html
2025/02/14 08:24:04[ ngx_http_core_find_static_location,  2753]  [debug] 2955#2955: *2 static_locations test location: "proxy1", client uri:/index.html
2025/02/14 08:24:04[ ngx_http_core_find_static_location,  2753]  [debug] 2955#2955: *2 static_locations test location: "mytest", client uri:/index.html
2025/02/14 08:24:04[        ngx_http_core_find_location,  2693]  [debug] 2955#2955: *2 ngx pcre test location: ~ "/1mytest"
2025/02/14 08:24:04[    ngx_http_core_find_config_phase,  1888]  [debug] 2955#2955: *2 using configuration "/"
2025/02/14 08:24:04[    ngx_http_core_find_config_phase,  1895]  [debug] 2955#2955: *2 http cl:-1 max:1048576
2025/02/14 08:24:04[        ngx_http_core_rewrite_phase,  1810]  [debug] 2955#2955: *2 rewrite phase: 2 (NGX_HTTP_REWRITE_PHASE)
2025/02/14 08:24:04[   ngx_http_core_post_rewrite_phase,  1963]  [debug] 2955#2955: *2 post rewrite phase: 3 (NGX_HTTP_POST_REWRITE_PHASE)
2025/02/14 08:24:04[        ngx_http_core_generic_phase,  1746]  [debug] 2955#2955: *2 generic phase: 4 (NGX_HTTP_PREACCESS_PHASE)
2025/02/14 08:24:04[        ngx_http_core_generic_phase,  1746]  [debug] 2955#2955: *2 generic phase: 5 (NGX_HTTP_PREACCESS_PHASE)
2025/02/14 08:24:04[         ngx_http_core_access_phase,  2061]  [debug] 2955#2955: *2 access phase: 6 (NGX_HTTP_ACCESS_PHASE)
2025/02/14 08:24:04[         ngx_http_core_access_phase,  2061]  [debug] 2955#2955: *2 access phase: 7 (NGX_HTTP_ACCESS_PHASE)
2025/02/14 08:24:04[    ngx_http_core_post_access_phase,  2163]  [debug] 2955#2955: *2 post access phase: 8 (NGX_HTTP_POST_ACCESS_PHASE)
2025/02/14 08:24:04[        ngx_http_core_content_phase,  2491]  [debug] 2955#2955: *2 content phase: 9 (NGX_HTTP_CONTENT_PHASE)
2025/02/14 08:24:04[        ngx_http_core_content_phase,  2491]  [debug] 2955#2955: *2 content phase: 10 (NGX_HTTP_CONTENT_PHASE)
2025/02/14 08:24:04[        ngx_http_core_content_phase,  2491]  [debug] 2955#2955: *2 content phase: 11 (NGX_HTTP_CONTENT_PHASE)
2025/02/14 08:24:04[            ngx_http_static_handler,    85]  [debug] 2955#2955: *2 http filename: "/usr/local/nginx/html/index.html"
2025/02/14 08:24:04[            ngx_http_static_handler,   145]  [debug] 2955#2955: *2 http static fd: 11
2025/02/14 08:24:04[      ngx_http_discard_request_body,   734]  [debug] 2955#2955: *2 http set discard body
*/
static ngx_int_t //主要功能是检查uri中的文件是否存在,不存在直接关闭连接,存在则做内部重定向,重定向后由于是文件路径,因此末尾没有/,走到该函数直接退出,然后在static-module中获取文件内容
ngx_http_index_handler(ngx_http_request_t *r)
{//注意:ngx_http_static_handler如果uri不是以/结尾返回,ngx_http_index_handler不以/结尾返回
//循环遍历index index.html index_large.html  gmime-gmime-cipher-context.html;配置的文件,存在则返回,找到一个不在遍历后面的文件
//ngx_http_static_handler ngx_http_index_handler每次都要获取缓存信息stat信息,因此每次获取很可能是上一次stat执行的时候获取的信息,除非缓存过期

    u_char                       *p, *name;
    size_t                        len, root, reserve, allocated;
    ngx_int_t                     rc;
    ngx_str_t                     path, uri;
    ngx_uint_t                    i, dir_tested;
    ngx_http_index_t             *index;
    ngx_open_file_info_t          of;
    ngx_http_script_code_pt       code;
    ngx_http_script_engine_t      e;
    ngx_http_core_loc_conf_t     *clcf;
    ngx_http_index_loc_conf_t    *ilcf;
    ngx_http_script_len_code_pt   lcode;

    /*
      一般匹配到location / {
    
      }的时候,才会执行下面的index,然后进行内部跳转
     */

    /*
    如果浏览器输入:http://10.135.10.167/ABC/,则也会满足要求,uri会变为/ABC/index.html,打印如下
     2015/10/16 12:08:03[                ngx_event_del_timer,    39]  [debug] 12610#12610: *2 < ngx_http_process_request,  2013>  event timer del: 3: 1859492499
     2015/10/16 12:08:03[        ngx_http_core_rewrite_phase,  1810]  [debug] 12610#12610: *2 rewrite phase: 0 (NGX_HTTP_SERVER_REWRITE_PHASE)
     2015/10/16 12:08:03[    ngx_http_core_find_config_phase,  1868]  [debug] 12610#12610: *2 find config phase: 1 (NGX_HTTP_FIND_CONFIG_PHASE), uri:/ABC/
     2015/10/16 12:08:03[ ngx_http_core_find_static_location,  2753]  [debug] 12610#12610: *2 static_locations test location: "/", client uri:/ABC/
     2015/10/16 12:08:03[ ngx_http_core_find_static_location,  2753]  [debug] 12610#12610: *2 static_locations test location: "proxy1", client uri:/ABC/
     2015/10/16 12:08:03[ ngx_http_core_find_static_location,  2753]  [debug] 12610#12610: *2 static_locations test location: "mytest", client uri:/ABC/
     2015/10/16 12:08:03[        ngx_http_core_find_location,  2693]  [debug] 12610#12610: *2 ngx pcre test location: ~ "\.php$"
     2015/10/16 12:08:03[        ngx_http_core_find_location,  2693]  [debug] 12610#12610: *2 ngx pcre test location: ~ "/1mytest"
     2015/10/16 12:08:03[    ngx_http_core_find_config_phase,  1888]  [debug] 12610#12610: *2 using configuration "/"
     2015/10/16 12:08:03[    ngx_http_core_find_config_phase,  1895]  [debug] 12610#12610: *2 http cl:-1 max:1048576
     2015/10/16 12:08:03[        ngx_http_core_rewrite_phase,  1810]  [debug] 12610#12610: *2 rewrite phase: 2 (NGX_HTTP_REWRITE_PHASE)
     2015/10/16 12:08:03[   ngx_http_core_post_rewrite_phase,  1963]  [debug] 12610#12610: *2 post rewrite phase: 3 (NGX_HTTP_POST_REWRITE_PHASE)
     2015/10/16 12:08:03[        ngx_http_core_generic_phase,  1746]  [debug] 12610#12610: *2 generic phase: 4 (NGX_HTTP_PREACCESS_PHASE)
     2015/10/16 12:08:03[        ngx_http_core_generic_phase,  1746]  [debug] 12610#12610: *2 generic phase: 5 (NGX_HTTP_PREACCESS_PHASE)
     2015/10/16 12:08:03[         ngx_http_core_access_phase,  2061]  [debug] 12610#12610: *2 access phase: 6 (NGX_HTTP_ACCESS_PHASE)
     2015/10/16 12:08:03[         ngx_http_core_access_phase,  2061]  [debug] 12610#12610: *2 access phase: 7 (NGX_HTTP_ACCESS_PHASE)
     2015/10/16 12:08:03[    ngx_http_core_post_access_phase,  2163]  [debug] 12610#12610: *2 post access phase: 8 (NGX_HTTP_POST_ACCESS_PHASE)
     2015/10/16 12:08:03[        ngx_http_core_content_phase,  2491]  [debug] 12610#12610: *2 content phase: 9 (NGX_HTTP_CONTENT_PHASE)
     2015/10/16 12:08:03[             ngx_http_index_handler,   191]  [debug] 12610#12610: *2 yang test ... index-count:3
     2015/10/16 12:08:03[             ngx_http_index_handler,   263]  [debug] 12610#12610: *2 open index "/var/yyz/www/ABC/index.html"
     2015/10/16 12:08:03[             ngx_http_index_handler,   283]  [debug] 12610#12610: *2 stat() "/var/yyz/www/ABC/index.html" failed (2: No such file or directory)
     2015/10/16 12:08:03[            ngx_http_index_test_dir,   364]  [debug] 12610#12610: *2 http index check dir: "/var/yyz/www/ABC"

     */ //默认http://10.2.13.167的时候,浏览器都会转换为http://10.2.13.167/发送到nginx服务器
    if (r->uri.data[r->uri.len - 1] != '/') { //末尾不是/,直接跳转到下一阶段
        return NGX_DECLINED;
    }

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
        return NGX_DECLINED;
    }

    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

    allocated = 0;
    root = 0;
    dir_tested = 0;
    name = NULL;
    /* suppress MSVC warning */
    path.data = NULL;

    index = ilcf->indices->elts;
    //indices上默认有一个NGX_HTTP_DEFAULT_INDEX
    for (i = 0; i < ilcf->indices->nelts; i++) {//循环遍历index配置的文件,如果有该文件,则进行内部重定向,从新走NGX_HTTP_SERVER_REWRITE_PHASE

        if (index[i].lengths == NULL) {

            if (index[i].name.data[0] == '/') {
                return ngx_http_internal_redirect(r, &index[i].name, &r->args);
            }

            reserve = ilcf->max_index_len;
            len = index[i].name.len;

        } else {
            ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

            e.ip = index[i].lengths->elts;
            e.request = r;
            e.flushed = 1;

            /* 1 is for terminating '\0' as in static names */
            len = 1;

            while (*(uintptr_t *) e.ip) {
                lcode = *(ngx_http_script_len_code_pt *) e.ip;
                len += lcode(&e);
            }

            /* 16 bytes are preallocation */

            reserve = len + 16;
        }

        if (reserve > allocated) {

            name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
            if (name == NULL) {
                return NGX_ERROR;
            }

            allocated = path.data + path.len - name;
        }

        if (index[i].values == NULL) {

            /* index[i].name.len includes the terminating '\0' */

            ngx_memcpy(name, index[i].name.data, index[i].name.len);

            path.len = (name + index[i].name.len - 1) - path.data;

        } else {
            e.ip = index[i].values->elts;
            e.pos = name;

            while (*(uintptr_t *) e.ip) {
                code = *(ngx_http_script_code_pt *) e.ip;
                code((ngx_http_script_engine_t *) &e);
            }

            if (*name == '/') {
                uri.len = len - 1;
                uri.data = name;
                return ngx_http_internal_redirect(r, &uri, &r->args);
            }

            path.len = e.pos - path.data;

            *e.pos = '\0';
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "open index \"%V\"", &path);

        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.test_only = 1;
        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)
        {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
                           "%s \"%s\" failed", of.failed, path.data);

            if (of.err == 0) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

#if (NGX_HAVE_OPENAT)
            if (of.err == NGX_EMLINK
                || of.err == NGX_ELOOP)
            {
                return NGX_HTTP_FORBIDDEN;
            }
#endif

            if (of.err == NGX_ENOTDIR
                || of.err == NGX_ENAMETOOLONG
                || of.err == NGX_EACCES)
            {
                return ngx_http_index_error(r, clcf, path.data, of.err);
            }

            if (!dir_tested) {
                rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);

                if (rc != NGX_OK) {
                    return rc;
                }

                dir_tested = 1;
            }

            if (of.err == NGX_ENOENT) {
                continue; //stat获取的参数file_name指定的文件不存在
            }

            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
                          "%s \"%s\" failed", of.failed, path.data);

            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        uri.len = r->uri.len + len - 1;

        if (!clcf->alias) {
            uri.data = path.data + root;

        } else {
            uri.data = ngx_pnalloc(r->pool, uri.len);
            if (uri.data == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            p = ngx_copy(uri.data, r->uri.data, r->uri.len);
            ngx_memcpy(p, name, len - 1);
        }

        return ngx_http_internal_redirect(r, &uri, &r->args); //内部重定向
    }

    return NGX_DECLINED;
}
Exemple #26
0
static ngx_int_t
ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
{
    u_char                   *p, *host, *last, ch;
    size_t                    len, root;
    ngx_err_t                 err;
    ngx_int_t                 rc, depth;
    ngx_uint_t                overwrite, slash, dir, flags;
    ngx_str_t                 path, uri, duri, args;
    ngx_tree_ctx_t            tree;
    ngx_copy_file_t           cf;
    ngx_file_info_t           fi;
    ngx_table_elt_t          *dest, *over;
    ngx_ext_rename_file_t     ext;
    ngx_http_dav_copy_ctx_t   copy;
    ngx_http_dav_loc_conf_t  *dlcf;

    if (r->headers_in.content_length_n > 0) {
        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
    }

    dest = r->headers_in.destination;

    if (dest == NULL) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "client sent no \"Destination\" header");
        return NGX_HTTP_BAD_REQUEST;
    }

    p = dest->value.data;
    /* there is always '\0' even after empty header value */
    if (p[0] == '/') {
        last = p + dest->value.len;
        goto destination_done;
    }

    len = r->headers_in.server.len;

    if (len == 0) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "client sent no \"Host\" header");
        return NGX_HTTP_BAD_REQUEST;
    }

#if (NGX_HTTP_SSL)

    if (r->connection->ssl) {
        if (ngx_strncmp(dest->value.data, "https://", sizeof("https://") - 1)
            != 0)
        {
            goto invalid_destination;
        }

        host = dest->value.data + sizeof("https://") - 1;

    } else
#endif
    {
        if (ngx_strncmp(dest->value.data, "http://", sizeof("http://") - 1)
            != 0)
        {
            goto invalid_destination;
        }

        host = dest->value.data + sizeof("http://") - 1;
    }

    if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "\"Destination\" URI \"%V\" is handled by "
                      "different repository than the source URI",
                      &dest->value);
        return NGX_HTTP_BAD_REQUEST;
    }

    last = dest->value.data + dest->value.len;

    for (p = host + len; p < last; p++) {
        if (*p == '/') {
            goto destination_done;
        }
    }

invalid_destination:

    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                  "client sent invalid \"Destination\" header: \"%V\"",
                  &dest->value);
    return NGX_HTTP_BAD_REQUEST;

destination_done:

    duri.len = last - p;
    duri.data = p;
    flags = NGX_HTTP_LOG_UNSAFE;

    if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {
        goto invalid_destination;
    }

    if ((r->uri.data[r->uri.len - 1] == '/' && *(last - 1) != '/')
        || (r->uri.data[r->uri.len - 1] != '/' && *(last - 1) == '/'))
    {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "both URI \"%V\" and \"Destination\" URI \"%V\" "
                      "should be either collections or non-collections",
                      &r->uri, &dest->value);
        return NGX_HTTP_CONFLICT;
    }

    depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);

    if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {

        if (r->method == NGX_HTTP_COPY) {
            if (depth != 0) {
                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                              "\"Depth\" header must be 0 or infinity");
                return NGX_HTTP_BAD_REQUEST;
            }

        } else {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "\"Depth\" header must be infinity");
            return NGX_HTTP_BAD_REQUEST;
        }
    }

    over = r->headers_in.overwrite;

    if (over) {
        if (over->value.len == 1) {
            ch = over->value.data[0];

            if (ch == 'T' || ch == 't') {
                overwrite = 1;
                goto overwrite_done;
            }

            if (ch == 'F' || ch == 'f') {
                overwrite = 0;
                goto overwrite_done;
            }

        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "client sent invalid \"Overwrite\" header: \"%V\"",
                      &over->value);
        return NGX_HTTP_BAD_REQUEST;
    }

    overwrite = 1;

overwrite_done:

    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http copy from: \"%s\"", path.data);

    uri = r->uri;
    r->uri = duri;

    if (ngx_http_map_uri_to_path(r, &copy.path, &root, 0) == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    r->uri = uri;

    copy.path.len--;  /* omit "\0" */

    if (copy.path.data[copy.path.len - 1] == '/') {
        slash = 1;
        copy.path.len--;
        copy.path.data[copy.path.len] = '\0';

    } else {
        slash = 0;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http copy to: \"%s\"", copy.path.data);

    if (ngx_link_info(copy.path.data, &fi) == NGX_FILE_ERROR) {
        err = ngx_errno;

        if (err != NGX_ENOENT) {
            return ngx_http_dav_error(r->connection->log, err,
                                      NGX_HTTP_NOT_FOUND, ngx_link_info_n,
                                      copy.path.data);
        }

        /* destination does not exist */

        overwrite = 0;
        dir = 0;

    } else {

        /* destination exists */

        if (ngx_is_dir(&fi) && !slash) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "\"%V\" could not be %Ved to collection \"%V\"",
                          &r->uri, &r->method_name, &dest->value);
            return NGX_HTTP_CONFLICT;
        }

        if (!overwrite) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EEXIST,
                          "\"%s\" could not be created", copy.path.data);
            return NGX_HTTP_PRECONDITION_FAILED;
        }

        dir = ngx_is_dir(&fi);
    }

    if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
        return ngx_http_dav_error(r->connection->log, ngx_errno,
                                  NGX_HTTP_NOT_FOUND, ngx_link_info_n,
                                  path.data);
    }

    if (ngx_is_dir(&fi)) {

        if (r->uri.data[r->uri.len - 1] != '/') {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "\"%V\" is collection", &r->uri);
            return NGX_HTTP_BAD_REQUEST;
        }

        if (overwrite) {
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http delete: \"%s\"", copy.path.data);

            rc = ngx_http_dav_delete_path(r, &copy.path, dir);

            if (rc != NGX_OK) {
                return rc;
            }
        }
    }

    if (ngx_is_dir(&fi)) {

        path.len -= 2;  /* omit "/\0" */

        if (r->method == NGX_HTTP_MOVE) {
            if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {
                return NGX_HTTP_CREATED;
            }
        }

        if (ngx_create_dir(copy.path.data, ngx_file_access(&fi))
            == NGX_FILE_ERROR)
        {
            return ngx_http_dav_error(r->connection->log, ngx_errno,
                                      NGX_HTTP_NOT_FOUND,
                                      ngx_create_dir_n, copy.path.data);
        }

        copy.len = path.len;

        tree.init_handler = NULL;
        tree.file_handler = ngx_http_dav_copy_tree_file;
        tree.pre_tree_handler = ngx_http_dav_copy_dir;
        tree.post_tree_handler = ngx_http_dav_copy_dir_time;
        tree.spec_handler = ngx_http_dav_noop;
        tree.data = &copy;
        tree.alloc = 0;
        tree.log = r->connection->log;

        if (ngx_walk_tree(&tree, &path) == NGX_OK) {

            if (r->method == NGX_HTTP_MOVE) {
                rc = ngx_http_dav_delete_path(r, &path, 1);

                if (rc != NGX_OK) {
                    return rc;
                }
            }

            return NGX_HTTP_CREATED;
        }

    } else {

        if (r->method == NGX_HTTP_MOVE) {

            dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

            ext.access = 0;
            ext.path_access = dlcf->access;
            ext.time = -1;
            ext.create_path = 1;
            ext.delete_file = 0;
            ext.log = r->connection->log;

            if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {
                return NGX_HTTP_NO_CONTENT;
            }

            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        cf.size = ngx_file_size(&fi);
        cf.buf_size = 0;
        cf.access = ngx_file_access(&fi);
        cf.time = ngx_file_mtime(&fi);
        cf.log = r->connection->log;

        if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {
            return NGX_HTTP_NO_CONTENT;
        }
    }

    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
static ngx_int_t
ngx_http_random_index_handler(ngx_http_request_t *r)
{
    u_char                            *last, *filename;
    size_t                             len, allocated, root;
    ngx_err_t                          err;
    ngx_int_t                          rc;
    ngx_str_t                          path, uri, *name;
    ngx_dir_t                          dir;
    ngx_uint_t                         n, level;
    ngx_array_t                        names;
    ngx_http_random_index_loc_conf_t  *rlcf;

    if (r->uri.data[r->uri.len - 1] != '/') {
        return NGX_DECLINED;
    }

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
        return NGX_DECLINED;
    }

    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_random_index_module);

    if (!rlcf->enable) {
        return NGX_DECLINED;
    }

#if (NGX_HAVE_D_TYPE)
    len = NGX_DIR_MASK_LEN;
#else
    len = NGX_HTTP_RANDOM_INDEX_PREALLOCATE;
#endif

    last = ngx_http_map_uri_to_path(r, &path, &root, len);
    if (last == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    allocated = path.len;

    path.len = last - path.data - 1;
    path.data[path.len] = '\0';

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http random index: \"%s\"", path.data);

    if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
        err = ngx_errno;

        if (err == NGX_ENOENT
            || err == NGX_ENOTDIR
            || err == NGX_ENAMETOOLONG)
        {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_NOT_FOUND;

        } else if (err == NGX_EACCES) {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_FORBIDDEN;

        } else {
            level = NGX_LOG_CRIT;
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        ngx_log_error(level, r->connection->log, err,
                      ngx_open_dir_n " \"%s\" failed", path.data);

        return rc;
    }

    if (ngx_array_init(&names, r->pool, 32, sizeof(ngx_str_t)) != NGX_OK) {
        return ngx_http_random_index_error(r, &dir, &path);
    }

    filename = path.data;
    filename[path.len] = '/';

    for ( ;; ) {
        ngx_set_errno(0);

        if (ngx_read_dir(&dir) == NGX_ERROR) {
            err = ngx_errno;

            if (err != NGX_ENOMOREFILES) {
                ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                              ngx_read_dir_n " \"%V\" failed", &path);
                return ngx_http_random_index_error(r, &dir, &path);
            }

            break;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http random index file: \"%s\"", ngx_de_name(&dir));

        if (ngx_de_name(&dir)[0] == '.') {
            continue;
        }

        len = ngx_de_namelen(&dir);

        if (dir.type == 0 || ngx_de_is_link(&dir)) {

            /* 1 byte for '/' and 1 byte for terminating '\0' */

            if (path.len + 1 + len + 1 > allocated) {
                allocated = path.len + 1 + len + 1
                                     + NGX_HTTP_RANDOM_INDEX_PREALLOCATE;

                filename = ngx_pnalloc(r->pool, allocated);
                if (filename == NULL) {
                    return ngx_http_random_index_error(r, &dir, &path);
                }

                last = ngx_cpystrn(filename, path.data, path.len + 1);
                *last++ = '/';
            }

            ngx_cpystrn(last, ngx_de_name(&dir), len + 1);

            if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
                err = ngx_errno;

                if (err != NGX_ENOENT) {
                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                                  ngx_de_info_n " \"%s\" failed", filename);
                    return ngx_http_random_index_error(r, &dir, &path);
                }

                if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                                  ngx_de_link_info_n " \"%s\" failed",
                                  filename);
                    return ngx_http_random_index_error(r, &dir, &path);
                }
            }
        }

        if (!ngx_de_is_file(&dir)) {
            continue;
        }

        name = ngx_array_push(&names);
        if (name == NULL) {
            return ngx_http_random_index_error(r, &dir, &path);
        }

        name->len = len;

        name->data = ngx_pnalloc(r->pool, len);
        if (name->data == NULL) {
            return ngx_http_random_index_error(r, &dir, &path);
        }

        ngx_memcpy(name->data, ngx_de_name(&dir), len);
    }

    if (ngx_close_dir(&dir) == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                      ngx_close_dir_n " \"%s\" failed", &path);
    }

    n = names.nelts;

    if (n == 0) {
        return NGX_DECLINED;
    }

    name = names.elts;

    n = (ngx_uint_t) (((uint64_t) ngx_random() * n) / 0x80000000);

    uri.len = r->uri.len + name[n].len;

    uri.data = ngx_pnalloc(r->pool, uri.len);
    if (uri.data == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    last = ngx_copy(uri.data, r->uri.data, r->uri.len);
    ngx_memcpy(last, name[n].data, name[n].len);

    return ngx_http_internal_redirect(r, &uri, &r->args);
}
static ngx_int_t
ngx_http_concat_handler(ngx_http_request_t *r)
{
    off_t                       length;
    size_t                      root, last_len;
    time_t                      last_modified;
    u_char                     *p, *v, *e, *last, *last_type;
    ngx_int_t                   rc;
    ngx_str_t                  *uri, *filename, path;
    ngx_buf_t                  *b;
    ngx_uint_t                  i, j, level;
    ngx_flag_t                  timestamp;
    ngx_array_t                 uris;
    ngx_chain_t                 out, **last_out, *cl;
    ngx_open_file_info_t        of;
    ngx_http_core_loc_conf_t   *ccf;
    ngx_http_concat_loc_conf_t *clcf;

    if (r->uri.data[r->uri.len - 1] != '/') {
        return NGX_DECLINED;
    }

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
        return NGX_DECLINED;
    }

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_concat_module);

    if (!clcf->enable) {
        return NGX_DECLINED;
    }

    /* the length of args must be greater than or equal to 2 */
    if (r->args.len < 2 || r->args.data[0] != '?') {
        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;
    }

    path.len = last - path.data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http concat root: \"%V\"", &path);

    ccf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

#if (NGX_SUPPRESS_WARN)
    ngx_memzero(&uris, sizeof(ngx_array_t));
#endif

    if (ngx_array_init(&uris, r->pool, 8, sizeof(ngx_str_t)) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    e = r->args.data + r->args.len;
    for (p = r->args.data + 1, v = p, timestamp = 0; p != e; p++) {

        if (*p == ',') {
            if (p == v || timestamp == 1) {
                v = p + 1;
                timestamp = 0;
                continue;
            }

            rc = ngx_http_concat_add_path(r, &uris, clcf->max_files, &path,
                                          p, v);
            if (rc != NGX_OK) {
                return rc;
            }

            v = p + 1;

        } else if (*p == '?') {
            if (timestamp == 1) {
                v = p;
                continue;
            }

            rc = ngx_http_concat_add_path(r, &uris, clcf->max_files, &path,
                                          p, v);
            if (rc != NGX_OK) {
                return rc;
            }

            v = p;
            timestamp = 1;
        }
    }

    if (p - v > 0 && timestamp == 0) {
        rc = ngx_http_concat_add_path(r, &uris, clcf->max_files, &path, p, v);
        if (rc != NGX_OK) {
            return rc;
        }
    }

    last_modified = 0;
    last_len = 0;
    last_out = NULL;
    b = NULL;
    last_type = NULL;
    length = 0;
    uri = uris.elts;
    for (i = 0; i < uris.nelts; i++) {
        filename = uri + i;

        for (j = filename->len - 1; j > 1; j--) {
            if (filename->data[j] == '.' && filename->data[j - 1] != '/') {

                r->exten.len = filename->len - j - 1;
                r->exten.data = &filename->data[j + 1];
                break;

            } else if (filename->data[j] == '/') {
                break;
            }
        }

        r->headers_out.content_type.len = 0;
        if (ngx_http_set_content_type(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        r->headers_out.content_type_lowcase = NULL;
        if (ngx_http_test_content_type(r, &clcf->types) == NULL) {
            return NGX_HTTP_BAD_REQUEST;
        }

        if (clcf->unique) { /* test if all the content types are the same */
            if ((i > 0)
                && (last_len != r->headers_out.content_type_len
                    || (last_type != NULL
                        && r->headers_out.content_type_lowcase != NULL
                        && ngx_memcmp(last_type,
                                      r->headers_out.content_type_lowcase,
                                      last_len) != 0)))
            {
                return NGX_HTTP_BAD_REQUEST;
            }

            last_len = r->headers_out.content_type_len;
            last_type = r->headers_out.content_type_lowcase;
        }

        ngx_memzero(&of, sizeof(ngx_open_file_info_t));

        of.read_ahead = ccf->read_ahead;
        of.directio = ccf->directio;
        of.valid = ccf->open_file_cache_valid;
        of.min_uses = ccf->open_file_cache_min_uses;
        of.errors = ccf->open_file_cache_errors;
        of.events = ccf->open_file_cache_events;

        if (ngx_open_cached_file(ccf->open_file_cache, filename, &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:

                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 || ccf->log_not_found) {
                ngx_log_error(level, r->connection->log, of.err,
                              "%s \"%V\" failed", of.failed, filename);
            }

            if (clcf->ignore_file_error
                && (rc == NGX_HTTP_NOT_FOUND || rc == NGX_HTTP_FORBIDDEN))
            {
                continue;
            }

            return rc;
        }

        if (!of.is_file) {
            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                          "\"%V\" is not a regular file", filename);
            if (clcf->ignore_file_error) {
                continue;
            }

            return NGX_HTTP_NOT_FOUND;
        }

        if (of.size == 0) {
            continue;
        }

        length += of.size;
        if (last_out == NULL) {
            last_modified = of.mtime;

        } else {
            if (of.mtime > last_modified) {
                last_modified = of.mtime;
            }
        }

        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
        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;
        }

        b->file_pos = 0;
        b->file_last = of.size;

        b->in_file = b->file_last ? 1 : 0;

        b->file->fd = of.fd;
        b->file->name = *filename;
        b->file->log = r->connection->log;

        b->file->directio = of.is_directio;

        if (last_out == NULL) {
            out.buf = b;
            last_out = &out.next;
            out.next = NULL;

        } else {
            cl = ngx_alloc_chain_link(r->pool);
            if (cl == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            cl->buf = b;

            *last_out = cl;
            last_out = &cl->next;
            cl->next = NULL;
        }

        if (i + 1 == uris.nelts || clcf->delimiter.len == 0) {
            continue;
        }

        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
        if (b == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        b->pos = clcf->delimiter.data;
        b->last = b->pos + clcf->delimiter.len;
        b->memory = 1;
        length += clcf->delimiter.len;

        cl = ngx_alloc_chain_link(r->pool);
        if (cl == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        cl->buf = b;
        *last_out = cl;
        last_out = &cl->next;
        cl->next = NULL;
    }

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = length;
    r->headers_out.last_modified_time = last_modified;

    if (b == NULL) {
        r->header_only = 1;
    }

    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    if (b != NULL) {
        b->last_in_chain = 1;
        b->last_buf = 1;
    }

    return ngx_http_output_filter(r, &out);
}