Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.11 ]​[ nginx-1.14.2 ]​

0001 
0002 /*
0003  * Copyright (C) Roman Arutyunyan
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 typedef struct {
0014     size_t               size;
0015 } ngx_http_slice_loc_conf_t;
0016 
0017 
0018 typedef struct {
0019     off_t                start;
0020     off_t                end;
0021     ngx_str_t            range;
0022     ngx_str_t            etag;
0023     unsigned             last:1;
0024     unsigned             active:1;
0025     ngx_http_request_t  *sr;
0026 } ngx_http_slice_ctx_t;
0027 
0028 
0029 typedef struct {
0030     off_t                start;
0031     off_t                end;
0032     off_t                complete_length;
0033 } ngx_http_slice_content_range_t;
0034 
0035 
0036 static ngx_int_t ngx_http_slice_header_filter(ngx_http_request_t *r);
0037 static ngx_int_t ngx_http_slice_body_filter(ngx_http_request_t *r,
0038     ngx_chain_t *in);
0039 static ngx_int_t ngx_http_slice_parse_content_range(ngx_http_request_t *r,
0040     ngx_http_slice_content_range_t *cr);
0041 static ngx_int_t ngx_http_slice_range_variable(ngx_http_request_t *r,
0042     ngx_http_variable_value_t *v, uintptr_t data);
0043 static off_t ngx_http_slice_get_start(ngx_http_request_t *r);
0044 static void *ngx_http_slice_create_loc_conf(ngx_conf_t *cf);
0045 static char *ngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent,
0046     void *child);
0047 static ngx_int_t ngx_http_slice_add_variables(ngx_conf_t *cf);
0048 static ngx_int_t ngx_http_slice_init(ngx_conf_t *cf);
0049 
0050 
0051 static ngx_command_t  ngx_http_slice_filter_commands[] = {
0052 
0053     { ngx_string("slice"),
0054       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0055       ngx_conf_set_size_slot,
0056       NGX_HTTP_LOC_CONF_OFFSET,
0057       offsetof(ngx_http_slice_loc_conf_t, size),
0058       NULL },
0059 
0060       ngx_null_command
0061 };
0062 
0063 
0064 static ngx_http_module_t  ngx_http_slice_filter_module_ctx = {
0065     ngx_http_slice_add_variables,          /* preconfiguration */
0066     ngx_http_slice_init,                   /* postconfiguration */
0067 
0068     NULL,                                  /* create main configuration */
0069     NULL,                                  /* init main configuration */
0070 
0071     NULL,                                  /* create server configuration */
0072     NULL,                                  /* merge server configuration */
0073 
0074     ngx_http_slice_create_loc_conf,        /* create location configuration */
0075     ngx_http_slice_merge_loc_conf          /* merge location configuration */
0076 };
0077 
0078 
0079 ngx_module_t  ngx_http_slice_filter_module = {
0080     NGX_MODULE_V1,
0081     &ngx_http_slice_filter_module_ctx,     /* module context */
0082     ngx_http_slice_filter_commands,        /* module directives */
0083     NGX_HTTP_MODULE,                       /* module type */
0084     NULL,                                  /* init master */
0085     NULL,                                  /* init module */
0086     NULL,                                  /* init process */
0087     NULL,                                  /* init thread */
0088     NULL,                                  /* exit thread */
0089     NULL,                                  /* exit process */
0090     NULL,                                  /* exit master */
0091     NGX_MODULE_V1_PADDING
0092 };
0093 
0094 
0095 static ngx_str_t  ngx_http_slice_range_name = ngx_string("slice_range");
0096 
0097 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
0098 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
0099 
0100 
0101 static ngx_int_t
0102 ngx_http_slice_header_filter(ngx_http_request_t *r)
0103 {
0104     off_t                            end;
0105     ngx_int_t                        rc;
0106     ngx_table_elt_t                 *h;
0107     ngx_http_slice_ctx_t            *ctx;
0108     ngx_http_slice_loc_conf_t       *slcf;
0109     ngx_http_slice_content_range_t   cr;
0110 
0111     ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
0112     if (ctx == NULL) {
0113         return ngx_http_next_header_filter(r);
0114     }
0115 
0116     if (r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) {
0117         if (r == r->main) {
0118             ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);
0119             return ngx_http_next_header_filter(r);
0120         }
0121 
0122         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0123                       "unexpected status code %ui in slice response",
0124                       r->headers_out.status);
0125         return NGX_ERROR;
0126     }
0127 
0128     h = r->headers_out.etag;
0129 
0130     if (ctx->etag.len) {
0131         if (h == NULL
0132             || h->value.len != ctx->etag.len
0133             || ngx_strncmp(h->value.data, ctx->etag.data, ctx->etag.len)
0134                != 0)
0135         {
0136             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0137                           "etag mismatch in slice response");
0138             return NGX_ERROR;
0139         }
0140     }
0141 
0142     if (h) {
0143         ctx->etag = h->value;
0144     }
0145 
0146     if (ngx_http_slice_parse_content_range(r, &cr) != NGX_OK) {
0147         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0148                       "invalid range in slice response");
0149         return NGX_ERROR;
0150     }
0151 
0152     if (cr.complete_length == -1) {
0153         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0154                       "no complete length in slice response");
0155         return NGX_ERROR;
0156     }
0157 
0158     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0159                    "http slice response range: %O-%O/%O",
0160                    cr.start, cr.end, cr.complete_length);
0161 
0162     slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);
0163 
0164     end = ngx_min(cr.start + (off_t) slcf->size, cr.complete_length);
0165 
0166     if (cr.start != ctx->start || cr.end != end) {
0167         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0168                       "unexpected range in slice response: %O-%O",
0169                       cr.start, cr.end);
0170         return NGX_ERROR;
0171     }
0172 
0173     ctx->start = end;
0174     ctx->active = 1;
0175 
0176     r->headers_out.status = NGX_HTTP_OK;
0177     r->headers_out.status_line.len = 0;
0178     r->headers_out.content_length_n = cr.complete_length;
0179     r->headers_out.content_offset = cr.start;
0180     r->headers_out.content_range->hash = 0;
0181     r->headers_out.content_range = NULL;
0182 
0183     r->allow_ranges = 1;
0184     r->subrequest_ranges = 1;
0185     r->single_range = 1;
0186 
0187     rc = ngx_http_next_header_filter(r);
0188 
0189     if (r != r->main) {
0190         return rc;
0191     }
0192 
0193     r->preserve_body = 1;
0194 
0195     if (r->headers_out.status == NGX_HTTP_PARTIAL_CONTENT) {
0196         if (ctx->start + (off_t) slcf->size <= r->headers_out.content_offset) {
0197             ctx->start = slcf->size
0198                          * (r->headers_out.content_offset / slcf->size);
0199         }
0200 
0201         ctx->end = r->headers_out.content_offset
0202                    + r->headers_out.content_length_n;
0203 
0204     } else {
0205         ctx->end = cr.complete_length;
0206     }
0207 
0208     return rc;
0209 }
0210 
0211 
0212 static ngx_int_t
0213 ngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
0214 {
0215     ngx_int_t                   rc;
0216     ngx_chain_t                *cl;
0217     ngx_http_slice_ctx_t       *ctx;
0218     ngx_http_slice_loc_conf_t  *slcf;
0219 
0220     ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
0221 
0222     if (ctx == NULL || r != r->main) {
0223         return ngx_http_next_body_filter(r, in);
0224     }
0225 
0226     for (cl = in; cl; cl = cl->next) {
0227         if (cl->buf->last_buf) {
0228             cl->buf->last_buf = 0;
0229             cl->buf->last_in_chain = 1;
0230             cl->buf->sync = 1;
0231             ctx->last = 1;
0232         }
0233     }
0234 
0235     rc = ngx_http_next_body_filter(r, in);
0236 
0237     if (rc == NGX_ERROR || !ctx->last) {
0238         return rc;
0239     }
0240 
0241     if (ctx->sr && !ctx->sr->done) {
0242         return rc;
0243     }
0244 
0245     if (!ctx->active) {
0246         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0247                       "missing slice response");
0248         return NGX_ERROR;
0249     }
0250 
0251     if (ctx->start >= ctx->end) {
0252         ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);
0253         ngx_http_send_special(r, NGX_HTTP_LAST);
0254         return rc;
0255     }
0256 
0257     if (r->buffered) {
0258         return rc;
0259     }
0260 
0261     if (ngx_http_subrequest(r, &r->uri, &r->args, &ctx->sr, NULL,
0262                             NGX_HTTP_SUBREQUEST_CLONE)
0263         != NGX_OK)
0264     {
0265         return NGX_ERROR;
0266     }
0267 
0268     ngx_http_set_ctx(ctx->sr, ctx, ngx_http_slice_filter_module);
0269 
0270     slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);
0271 
0272     ctx->range.len = ngx_sprintf(ctx->range.data, "bytes=%O-%O", ctx->start,
0273                                  ctx->start + (off_t) slcf->size - 1)
0274                      - ctx->range.data;
0275 
0276     ctx->active = 0;
0277 
0278     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0279                    "http slice subrequest: \"%V\"", &ctx->range);
0280 
0281     return rc;
0282 }
0283 
0284 
0285 static ngx_int_t
0286 ngx_http_slice_parse_content_range(ngx_http_request_t *r,
0287     ngx_http_slice_content_range_t *cr)
0288 {
0289     off_t             start, end, complete_length, cutoff, cutlim;
0290     u_char           *p;
0291     ngx_table_elt_t  *h;
0292 
0293     h = r->headers_out.content_range;
0294 
0295     if (h == NULL
0296         || h->value.len < 7
0297         || ngx_strncmp(h->value.data, "bytes ", 6) != 0)
0298     {
0299         return NGX_ERROR;
0300     }
0301 
0302     p = h->value.data + 6;
0303 
0304     cutoff = NGX_MAX_OFF_T_VALUE / 10;
0305     cutlim = NGX_MAX_OFF_T_VALUE % 10;
0306 
0307     start = 0;
0308     end = 0;
0309     complete_length = 0;
0310 
0311     while (*p == ' ') { p++; }
0312 
0313     if (*p < '0' || *p > '9') {
0314         return NGX_ERROR;
0315     }
0316 
0317     while (*p >= '0' && *p <= '9') {
0318         if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
0319             return NGX_ERROR;
0320         }
0321 
0322         start = start * 10 + (*p++ - '0');
0323     }
0324 
0325     while (*p == ' ') { p++; }
0326 
0327     if (*p++ != '-') {
0328         return NGX_ERROR;
0329     }
0330 
0331     while (*p == ' ') { p++; }
0332 
0333     if (*p < '0' || *p > '9') {
0334         return NGX_ERROR;
0335     }
0336 
0337     while (*p >= '0' && *p <= '9') {
0338         if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) {
0339             return NGX_ERROR;
0340         }
0341 
0342         end = end * 10 + (*p++ - '0');
0343     }
0344 
0345     end++;
0346 
0347     while (*p == ' ') { p++; }
0348 
0349     if (*p++ != '/') {
0350         return NGX_ERROR;
0351     }
0352 
0353     while (*p == ' ') { p++; }
0354 
0355     if (*p != '*') {
0356         if (*p < '0' || *p > '9') {
0357             return NGX_ERROR;
0358         }
0359 
0360         while (*p >= '0' && *p <= '9') {
0361             if (complete_length >= cutoff
0362                 && (complete_length > cutoff || *p - '0' > cutlim))
0363             {
0364                 return NGX_ERROR;
0365             }
0366 
0367             complete_length = complete_length * 10 + (*p++ - '0');
0368         }
0369 
0370     } else {
0371         complete_length = -1;
0372         p++;
0373     }
0374 
0375     while (*p == ' ') { p++; }
0376 
0377     if (*p != '\0') {
0378         return NGX_ERROR;
0379     }
0380 
0381     cr->start = start;
0382     cr->end = end;
0383     cr->complete_length = complete_length;
0384 
0385     return NGX_OK;
0386 }
0387 
0388 
0389 static ngx_int_t
0390 ngx_http_slice_range_variable(ngx_http_request_t *r,
0391     ngx_http_variable_value_t *v, uintptr_t data)
0392 {
0393     u_char                     *p;
0394     ngx_http_slice_ctx_t       *ctx;
0395     ngx_http_slice_loc_conf_t  *slcf;
0396 
0397     ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
0398 
0399     if (ctx == NULL) {
0400         if (r != r->main || r->headers_out.status) {
0401             v->not_found = 1;
0402             return NGX_OK;
0403         }
0404 
0405         slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);
0406 
0407         if (slcf->size == 0) {
0408             v->not_found = 1;
0409             return NGX_OK;
0410         }
0411 
0412         ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t));
0413         if (ctx == NULL) {
0414             return NGX_ERROR;
0415         }
0416 
0417         ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);
0418 
0419         p = ngx_pnalloc(r->pool, sizeof("bytes=-") - 1 + 2 * NGX_OFF_T_LEN);
0420         if (p == NULL) {
0421             return NGX_ERROR;
0422         }
0423 
0424         ctx->start = slcf->size * (ngx_http_slice_get_start(r) / slcf->size);
0425 
0426         ctx->range.data = p;
0427         ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start,
0428                                      ctx->start + (off_t) slcf->size - 1)
0429                          - p;
0430     }
0431 
0432     v->data = ctx->range.data;
0433     v->valid = 1;
0434     v->not_found = 0;
0435     v->no_cacheable = 1;
0436     v->len = ctx->range.len;
0437 
0438     return NGX_OK;
0439 }
0440 
0441 
0442 static off_t
0443 ngx_http_slice_get_start(ngx_http_request_t *r)
0444 {
0445     off_t             start, cutoff, cutlim;
0446     u_char           *p;
0447     ngx_table_elt_t  *h;
0448 
0449     if (r->headers_in.if_range) {
0450         return 0;
0451     }
0452 
0453     h = r->headers_in.range;
0454 
0455     if (h == NULL
0456         || h->value.len < 7
0457         || ngx_strncasecmp(h->value.data, (u_char *) "bytes=", 6) != 0)
0458     {
0459         return 0;
0460     }
0461 
0462     p = h->value.data + 6;
0463 
0464     if (ngx_strchr(p, ',')) {
0465         return 0;
0466     }
0467 
0468     while (*p == ' ') { p++; }
0469 
0470     if (*p == '-') {
0471         return 0;
0472     }
0473 
0474     cutoff = NGX_MAX_OFF_T_VALUE / 10;
0475     cutlim = NGX_MAX_OFF_T_VALUE % 10;
0476 
0477     start = 0;
0478 
0479     while (*p >= '0' && *p <= '9') {
0480         if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
0481             return 0;
0482         }
0483 
0484         start = start * 10 + (*p++ - '0');
0485     }
0486 
0487     return start;
0488 }
0489 
0490 
0491 static void *
0492 ngx_http_slice_create_loc_conf(ngx_conf_t *cf)
0493 {
0494     ngx_http_slice_loc_conf_t  *slcf;
0495 
0496     slcf = ngx_palloc(cf->pool, sizeof(ngx_http_slice_loc_conf_t));
0497     if (slcf == NULL) {
0498         return NULL;
0499     }
0500 
0501     slcf->size = NGX_CONF_UNSET_SIZE;
0502 
0503     return slcf;
0504 }
0505 
0506 
0507 static char *
0508 ngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
0509 {
0510     ngx_http_slice_loc_conf_t *prev = parent;
0511     ngx_http_slice_loc_conf_t *conf = child;
0512 
0513     ngx_conf_merge_size_value(conf->size, prev->size, 0);
0514 
0515     return NGX_CONF_OK;
0516 }
0517 
0518 
0519 static ngx_int_t
0520 ngx_http_slice_add_variables(ngx_conf_t *cf)
0521 {
0522     ngx_http_variable_t  *var;
0523 
0524     var = ngx_http_add_variable(cf, &ngx_http_slice_range_name, 0);
0525     if (var == NULL) {
0526         return NGX_ERROR;
0527     }
0528 
0529     var->get_handler = ngx_http_slice_range_variable;
0530 
0531     return NGX_OK;
0532 }
0533 
0534 
0535 static ngx_int_t
0536 ngx_http_slice_init(ngx_conf_t *cf)
0537 {
0538     ngx_http_next_header_filter = ngx_http_top_header_filter;
0539     ngx_http_top_header_filter = ngx_http_slice_header_filter;
0540 
0541     ngx_http_next_body_filter = ngx_http_top_body_filter;
0542     ngx_http_top_body_filter = ngx_http_slice_body_filter;
0543 
0544     return NGX_OK;
0545 }