├── 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 | --------------------------------------------------------------------------------