├── README.md
├── config
└── ngx_http_slice_module.c
/README.md:
--------------------------------------------------------------------------------
1 | # Nginx HTTP Slice module
2 |
3 | ## Introduction
4 |
5 | This is a module that is distributed with
6 | [tengine](http://tengine.taobao.org) which is a distribution of
7 | [Nginx](http://nginx.org) that is used by the e-commerce/auction site
8 | [Taobao.com](http://en.wikipedia.org/wiki/Taobao). This distribution
9 | contains some modules that are new on the Nginx scene. The
10 | `ngx_http_slice_module` module is one of them.
11 |
12 | This module can be thought out as a *reverse byte-range* request
13 | header. It's main utility is to allow Nginx to slice a big file in
14 | small pieces (byte-ranges) while permitting to use on-the-fly gzip
15 | compression.
16 |
17 | A typical example is for allowing someone to download a large video
18 | file while keeping the bandwith usage minimal. This might also be used
19 | as device for selling a video file by pieces where each link points to
20 | different zones of the file splitted by file ranges.
21 |
22 | Other use would be to use a generic CSS file and use only part of it
23 | for each section of a site. Granted that byte-range slicing isn't the
24 | most intuitive for such.
25 |
26 | Note also that using arguments is more **useful** than byte-ranges in
27 | the sense that they can be set in a normal link, while byte ranges
28 | require a special [HTTP header](https://en.wikipedia.org/wiki/Byte_serving).
29 |
30 | ## Configuration example
31 |
32 | location ^~ /video-dump/ {
33 | slice; # enable slicing
34 | slice_start_arg s;
35 | slice_end_arg e;
36 | }
37 |
38 | So we would request the first 1k of the file like this:
39 |
40 | http://example.com/video-dump/large_vid.mp4?s=0&e=1024
41 |
42 | Notice `s=0`, start at `0` and `e=1024`, stop at `1024` bytes (1k).
43 |
44 | ## Module directives
45 |
46 | **slice**
47 |
48 | **context:** `location`
49 |
50 | It enables the content slicing in a given location.
51 |
52 |
53 |
54 |
55 | **slice_arg_begin** `string`
56 |
57 | **default:** `begin`
58 |
59 | **context:** `http, server, location`
60 |
61 | Defines the argument that defines the request range of bytes **start**.
62 |
63 |
64 |
65 |
66 | **slice_arg_end** `string`
67 |
68 | **default:** `end`
69 |
70 | **context:** `http, server, location`
71 |
72 | Defines the argument that defines the request range of bytes **end**.
73 |
74 |
75 |
76 |
77 | **slice_header** `string`
78 |
79 | **context:** `http, server, location`
80 |
81 | Defines the string to be used as the **header** of each slice being
82 | served by Nginx.
83 |
84 |
85 |
86 |
87 | **slice_footer** `string`
88 |
89 | **context:** `http, server, location`
90 |
91 | Defines the string to be used as the **footer** of each slice being
92 | served by Nginx.
93 |
94 |
95 |
96 |
97 | **slice_header_first** `on` | `off`
98 |
99 | **default:** `on`
100 |
101 | **context:** `http, server, location`
102 |
103 | If set to `off` and when requesting the **first** byte of the file do **not
104 | serve** the header.
105 |
106 | This directive is particularly useful to differentiate the **first**
107 | slice from the remaining slices. The first slice is the one which has
108 | **no** header.
109 |
110 |
111 |
112 |
113 | **slice_footer_last** `on` | `off `
114 |
115 | **default:** `on`
116 |
117 | **context:** `http, server, location`
118 |
119 | If set to `off` and when requesting the **last** byte of the file do **not
120 | serve** the header.
121 |
122 | This directive is particularly useful to differentiate the **last**
123 | slice from the remaining slices. The last slice is the one which has
124 | **no** footer.
125 |
126 | ## Assorted examples
127 |
128 | Here's some examples that explore all the options.
129 |
130 | ### Serve a huge DB file while sending headers except on the first slice
131 |
132 | location ^~ /dbdumps/ {
133 | slice; # enable slicing
134 | slice_start_arg first;
135 | slice_end_arg last;
136 | slice_header '-- **db-slice-start**';
137 | slice_header_first off;
138 | }
139 |
140 | Then a request like this:
141 |
142 | http://example.com/dbdumps/somedb.sql?first=0&last=1048576
143 |
144 | Send the first 1M and skip the `-- **db-slice-start**` header.
145 |
146 |
147 | ### Serve a huge DB file while sending headers except on the first slice
148 |
149 | location ^~ /dbdumps/ {
150 | slice; # enable slicing
151 | slice_start_arg first;
152 | slice_end_arg last;
153 | slice_header '-- **db-slice-start**';
154 | slice_header_first off;
155 | slice_footer '-- **db-slice-end**';
156 | }
157 |
158 | This differs from the previous in the sense that it sends a footer.
159 |
160 | ### Serve a huge DB file while sending headers except on the first slice and send footer except on the last slice
161 |
162 | location ^~ /dbdumps/ {
163 | slice; # enable slicing
164 | slice_start_arg first;
165 | slice_end_arg last;
166 | slice_header '-- **db-slice-start**';
167 | slice_header_first off;
168 | slice_footer '-- **db-slice-end**';
169 | slice_footer_last off;
170 | }
171 |
172 | Then a request like this:
173 |
174 | http://example.com/dbdumps/somedb.sql?first=0&last=1048576
175 |
176 | Send the first 1M and skip the `-- **db-slice-start**` header.
177 |
178 | If the file is 200MB, we get the last slice with:
179 |
180 | http://example.com/dbdumps/somedb.sql?first=208666624&last=209715200
181 |
182 | this last slice has no footer.
183 |
184 | ## Installation
185 |
186 | 1. Clone the git repo.
187 |
188 | git clone git://github.com/alibaba/nginx-http-slice.git
189 |
190 | 2. Add the module to the build configuration by adding
191 | `--add-module=/path/to/nginx-http-slice`.
192 |
193 | 3. Build the nginx binary.
194 |
195 | 4. Install the nginx binary.
196 |
197 | 5. Configure contexts where concat is enabled.
198 |
199 | 6. Build your links such that the above format, i.e., all URIs that
200 | correspond to specific ranges. As example here's how to link to
201 | the first 4k of a file.
202 |
203 | db dump
204 |
205 | 7. Done.
206 |
207 | ## Tagging releases
208 |
209 | I'm tagging each release in synch with the
210 | [Tengine](http://tengine.taobao.org) releases.
211 |
212 | ## Other tengine modules on Github
213 |
214 | + [http concat](https://github.com/taobao/nginx-http-concat):
215 | allows to concatenate a given set of files and ship a single
216 | response from the server. It's particularly useful for **aggregating**
217 | CSS and Javascript files.
218 |
219 | + [footer filter](https://github.com/taobao/nginx-http-footer-filter):
220 | allows to add some extra data (markup or not) at the end of a
221 | request body. It's pratical for things like adding time stamps or
222 | other miscellaneous stuff without having to tweak your application.
223 |
224 | ## Other builds
225 |
226 | 1. As referred at the outset this module is part of the
227 | [`tengine`](http://tengine.taobao.org) Nginx distribution. So you
228 | might want to save yourself some work and just build it from
229 | scratch using `tengine` in lieu if the official Nginx source.
230 |
231 | 2. If you fancy a bleeding edge Nginx package (from the dev releases)
232 | for Debian made to measure then you might be interested in my
233 | [debian](http://debian.taobao.net/unstable) Nginx
234 | package. Instructions for using the repository and making the
235 | package live happily inside a stable distribution installation are
236 | [provided](http://debian.taobao.net).
237 |
238 | ## Acknowledgments
239 |
240 | Thanks to [Joshua Zhu](http://blog.zhuzhaoyuan.com) and the Taobao
241 | platform engineering team for releasing `tengine`. Also for being kind
242 | enough to clarify things regarding this module on the
243 | [Tengine mailing list](http://code.taobao.org/mailman/listinfo/tengine).
244 |
245 | ## License
246 |
247 | Copyright (C) 2010-2012 Alibaba Group Holding Limited
248 |
249 | Redistribution and use in source and binary forms, with or without
250 | modification, are permitted provided that the following conditions
251 | are met:
252 |
253 | 1. Redistributions of source code must retain the above copyright
254 | notice, this list of conditions and the following disclaimer.
255 |
256 | 2. Redistributions in binary form must reproduce the above copyright
257 | notice, this list of conditions and the following disclaimer in the
258 | documentation and/or other materials provided with the distribution.
259 |
260 | THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
261 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
262 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
263 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE
264 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
265 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
266 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
267 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
268 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
269 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
270 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
271 |
--------------------------------------------------------------------------------
/config:
--------------------------------------------------------------------------------
1 | ngx_addon_name=ngx_http_slice_module
2 | HTTP_MODULES="$HTTP_MODULES ngx_http_slice_module"
3 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_slice_module.c"
4 |
--------------------------------------------------------------------------------
/ngx_http_slice_module.c:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * Copyright (C) Igor Sysoev
4 | * Copyright (C) 2010-2012 Alibaba Group Holding Limited
5 | */
6 |
7 |
8 | #include
9 | #include
10 | #include
11 |
12 |
13 | typedef struct {
14 | ngx_str_t begin;
15 | ngx_str_t end;
16 |
17 | ngx_str_t header;
18 | ngx_flag_t header_first;
19 |
20 | ngx_str_t footer;
21 | ngx_flag_t footer_last;
22 | } ngx_http_slice_loc_conf_t;
23 |
24 |
25 | static void *ngx_http_slice_create_loc_conf(ngx_conf_t *cf);
26 | static char *ngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent,
27 | void *child);
28 | static char *ngx_http_slice(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
29 |
30 |
31 | static ngx_command_t ngx_http_slice_commands[] = {
32 |
33 | { ngx_string("slice"),
34 | NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
35 | ngx_http_slice,
36 | 0,
37 | 0,
38 | NULL },
39 |
40 | { ngx_string("slice_arg_begin"),
41 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
42 | ngx_conf_set_str_slot,
43 | NGX_HTTP_LOC_CONF_OFFSET,
44 | offsetof(ngx_http_slice_loc_conf_t, begin),
45 | NULL },
46 |
47 | { ngx_string("slice_arg_end"),
48 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
49 | ngx_conf_set_str_slot,
50 | NGX_HTTP_LOC_CONF_OFFSET,
51 | offsetof(ngx_http_slice_loc_conf_t, end),
52 | NULL },
53 |
54 | { ngx_string("slice_header"),
55 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
56 | ngx_conf_set_str_slot,
57 | NGX_HTTP_LOC_CONF_OFFSET,
58 | offsetof(ngx_http_slice_loc_conf_t, header),
59 | NULL },
60 |
61 | { ngx_string("slice_footer"),
62 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
63 | ngx_conf_set_str_slot,
64 | NGX_HTTP_LOC_CONF_OFFSET,
65 | offsetof(ngx_http_slice_loc_conf_t, footer),
66 | NULL },
67 |
68 | { ngx_string("slice_header_first"),
69 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
70 | ngx_conf_set_flag_slot,
71 | NGX_HTTP_LOC_CONF_OFFSET,
72 | offsetof(ngx_http_slice_loc_conf_t, header_first),
73 | NULL },
74 |
75 | { ngx_string("slice_footer_last"),
76 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
77 | ngx_conf_set_flag_slot,
78 | NGX_HTTP_LOC_CONF_OFFSET,
79 | offsetof(ngx_http_slice_loc_conf_t, footer_last),
80 | NULL },
81 |
82 | ngx_null_command
83 | };
84 |
85 |
86 | static ngx_http_module_t ngx_http_slice_module_ctx = {
87 | NULL, /* preconfiguration */
88 | NULL, /* postconfiguration */
89 |
90 | NULL, /* create main configuration */
91 | NULL, /* init main configuration */
92 |
93 | NULL, /* create server configuration */
94 | NULL, /* merge server configuration */
95 |
96 | ngx_http_slice_create_loc_conf,/* create location configuration */
97 | ngx_http_slice_merge_loc_conf /* merge location configuration */
98 | };
99 |
100 |
101 | ngx_module_t ngx_http_slice_module = {
102 | NGX_MODULE_V1,
103 | &ngx_http_slice_module_ctx, /* module context */
104 | ngx_http_slice_commands, /* module directives */
105 | NGX_HTTP_MODULE, /* module type */
106 | NULL, /* init master */
107 | NULL, /* init module */
108 | NULL, /* init process */
109 | NULL, /* init thread */
110 | NULL, /* exit thread */
111 | NULL, /* exit process */
112 | NULL, /* exit master */
113 | NGX_MODULE_V1_PADDING
114 | };
115 |
116 |
117 | static ngx_int_t
118 | ngx_http_slice_handler(ngx_http_request_t *r)
119 | {
120 | u_char *last;
121 | off_t begin, end, len;
122 | size_t root;
123 | ngx_int_t rc;
124 | ngx_uint_t level, i;
125 | ngx_str_t path, value;
126 | ngx_log_t *log;
127 | ngx_buf_t *b;
128 | ngx_chain_t out[3];
129 | ngx_open_file_info_t of;
130 | ngx_http_core_loc_conf_t *clcf;
131 | ngx_http_slice_loc_conf_t *slcf;
132 |
133 | if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
134 | return NGX_HTTP_NOT_ALLOWED;
135 | }
136 |
137 | if (r->uri.data[r->uri.len - 1] == '/') {
138 | return NGX_DECLINED;
139 | }
140 |
141 | slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_module);
142 |
143 | rc = ngx_http_discard_request_body(r);
144 |
145 | if (rc != NGX_OK) {
146 | return rc;
147 | }
148 |
149 | last = ngx_http_map_uri_to_path(r, &path, &root, 0);
150 | if (last == NULL) {
151 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
152 | }
153 |
154 | log = r->connection->log;
155 |
156 | path.len = last - path.data;
157 |
158 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
159 | "http slice filename: \"%V\"", &path);
160 |
161 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
162 |
163 | ngx_memzero(&of, sizeof(ngx_open_file_info_t));
164 |
165 | of.read_ahead = clcf->read_ahead;
166 | of.directio = clcf->directio;
167 | of.valid = clcf->open_file_cache_valid;
168 | of.min_uses = clcf->open_file_cache_min_uses;
169 | of.errors = clcf->open_file_cache_errors;
170 | of.events = clcf->open_file_cache_events;
171 |
172 | if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
173 | != NGX_OK)
174 | {
175 | switch (of.err) {
176 |
177 | case 0:
178 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
179 |
180 | case NGX_ENOENT:
181 | case NGX_ENOTDIR:
182 | case NGX_ENAMETOOLONG:
183 |
184 | level = NGX_LOG_ERR;
185 | rc = NGX_HTTP_NOT_FOUND;
186 | break;
187 |
188 | case NGX_EACCES:
189 |
190 | level = NGX_LOG_ERR;
191 | rc = NGX_HTTP_FORBIDDEN;
192 | break;
193 |
194 | default:
195 |
196 | level = NGX_LOG_CRIT;
197 | rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
198 | break;
199 | }
200 |
201 | if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
202 | ngx_log_error(level, log, of.err,
203 | "%s \"%s\" failed", of.failed, path.data);
204 | }
205 |
206 | return rc;
207 | }
208 |
209 | if (!of.is_file) {
210 |
211 | if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
212 | ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
213 | ngx_close_file_n " \"%s\" failed", path.data);
214 | }
215 |
216 | return NGX_DECLINED;
217 | }
218 |
219 | r->root_tested = !r->error_page;
220 |
221 | begin = 0;
222 | end = of.size;
223 |
224 | if (r->args.len) {
225 |
226 | if (ngx_http_arg(r, slcf->begin.data, slcf->begin.len, &value)
227 | == NGX_OK)
228 | {
229 | begin = ngx_atoof(value.data, value.len);
230 |
231 | if (begin == NGX_ERROR || begin >= of.size) {
232 | begin = 0;
233 | }
234 | }
235 |
236 | if (ngx_http_arg(r, slcf->end.data, slcf->end.len, &value) == NGX_OK) {
237 |
238 | end = ngx_atoof(value.data, value.len);
239 |
240 | if (end == NGX_ERROR || end >= of.size) {
241 | end = of.size;
242 | }
243 | }
244 | }
245 |
246 | end = end < begin ? of.size : end;
247 |
248 | len = (end == begin) ? 0 : ((end - begin)
249 | + ((begin == 0 && slcf->header_first) ? slcf->header.len : 0)
250 | + ((end == of.size && slcf->footer_last) ? slcf->footer.len : 0));
251 |
252 | log->action = "sending slice to client";
253 |
254 | r->headers_out.status = NGX_HTTP_OK;
255 | r->headers_out.content_length_n = len;
256 | r->headers_out.last_modified_time = of.mtime;
257 |
258 | if (ngx_http_set_content_type(r) != NGX_OK) {
259 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
260 | }
261 |
262 | if (len == 0) {
263 | r->header_only = 1;
264 | return ngx_http_send_header(r);
265 | }
266 |
267 | /*
268 | * add header when the first header is not denied
269 | */
270 | if (slcf->header.len
271 | && !(begin == 0 && !slcf->header_first))
272 | {
273 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
274 | if (b == NULL) {
275 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
276 | }
277 |
278 | b->pos = slcf->header.data;
279 | b->last = slcf->header.data + slcf->header.len;
280 | b->memory = 1;
281 |
282 | out[0].buf = b;
283 | out[0].next = &out[1];
284 |
285 | i = 0;
286 | } else {
287 | i = 1;
288 | }
289 |
290 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
291 | if (b == NULL) {
292 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
293 | }
294 |
295 | b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
296 | if (b->file == NULL) {
297 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
298 | }
299 |
300 | r->allow_ranges = 1;
301 |
302 | rc = ngx_http_send_header(r);
303 |
304 | if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
305 | return rc;
306 | }
307 |
308 | b->file_pos = begin;
309 | b->file_last = end;
310 |
311 | b->in_file = b->file_last ? 1: 0;
312 | b->last_buf = 1;
313 | b->last_in_chain = 1;
314 |
315 | b->file->fd = of.fd;
316 | b->file->name = path;
317 | b->file->log = log;
318 | b->file->directio = of.is_directio;
319 |
320 | out[1].buf = b;
321 | out[1].next = NULL;
322 |
323 | /*
324 | * add footer when the last footer is not denied
325 | */
326 | if (slcf->footer.len
327 | && !(end == of.size && !slcf->footer_last))
328 | {
329 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
330 | if (b == NULL) {
331 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
332 | }
333 |
334 | b->pos = slcf->footer.data;
335 | b->last = slcf->footer.data + slcf->footer.len;
336 | b->memory = 1;
337 | b->last_buf = 1;
338 | b->last_in_chain = 1;
339 |
340 | out[2].buf = b;
341 | out[2].next = NULL;
342 |
343 | out[1].buf->last_buf = 0;
344 | out[1].buf->last_in_chain = 0;
345 | out[1].next = &out[2];
346 | }
347 |
348 | return ngx_http_output_filter(r, &out[i]);
349 | }
350 |
351 |
352 | static void *
353 | ngx_http_slice_create_loc_conf(ngx_conf_t *cf)
354 | {
355 | ngx_http_slice_loc_conf_t *conf;
356 |
357 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_slice_loc_conf_t));
358 | if (conf == NULL) {
359 | return NULL;
360 | }
361 |
362 | /*
363 | * set by ngx_pcalloc() :
364 | *
365 | * conf->begin = { 0, NULL }
366 | * conf->end = { 0, NULL }
367 | * conf->header = { 0, NULL }
368 | * conf->footer = { 0, NULL }
369 | */
370 |
371 | conf->header_first = NGX_CONF_UNSET;
372 | conf->footer_last = NGX_CONF_UNSET;
373 |
374 | return conf;
375 | }
376 |
377 |
378 | static char *
379 | ngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
380 | {
381 | ngx_http_slice_loc_conf_t *prev = parent;
382 | ngx_http_slice_loc_conf_t *conf = child;
383 |
384 | ngx_conf_merge_str_value(conf->begin, prev->begin, "begin");
385 | ngx_conf_merge_str_value(conf->end, prev->end, "end");
386 | ngx_conf_merge_str_value(conf->header, prev->header, "");
387 | ngx_conf_merge_str_value(conf->footer, prev->footer, "");
388 | ngx_conf_merge_value(conf->header_first, prev->header_first, 1);
389 | ngx_conf_merge_value(conf->footer_last, prev->footer_last, 1);
390 |
391 | return NGX_CONF_OK;
392 | }
393 |
394 |
395 | static char *
396 | ngx_http_slice(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
397 | {
398 | ngx_http_core_loc_conf_t *clcf;
399 |
400 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
401 | clcf->handler = ngx_http_slice_handler;
402 |
403 | return NGX_CONF_OK;
404 | }
405 |
--------------------------------------------------------------------------------