Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.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     if (r->headers_out.status == NGX_HTTP_PARTIAL_CONTENT) {
0194         if (ctx->start + (off_t) slcf->size <= r->headers_out.content_offset) {
0195             ctx->start = slcf->size
0196                          * (r->headers_out.content_offset / slcf->size);
0197         }
0198 
0199         ctx->end = r->headers_out.content_offset
0200                    + r->headers_out.content_length_n;
0201 
0202     } else {
0203         ctx->end = cr.complete_length;
0204     }
0205 
0206     return rc;
0207 }
0208 
0209 
0210 static ngx_int_t
0211 ngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
0212 {
0213     ngx_int_t                   rc;
0214     ngx_chain_t                *cl;
0215     ngx_http_slice_ctx_t       *ctx;
0216     ngx_http_slice_loc_conf_t  *slcf;
0217 
0218     ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
0219 
0220     if (ctx == NULL || r != r->main) {
0221         return ngx_http_next_body_filter(r, in);
0222     }
0223 
0224     for (cl = in; cl; cl = cl->next) {
0225         if (cl->buf->last_buf) {
0226             cl->buf->last_buf = 0;
0227             cl->buf->last_in_chain = 1;
0228             cl->buf->sync = 1;
0229             ctx->last = 1;
0230         }
0231     }
0232 
0233     rc = ngx_http_next_body_filter(r, in);
0234 
0235     if (rc == NGX_ERROR || !ctx->last) {
0236         return rc;
0237     }
0238 
0239     if (ctx->sr && !ctx->sr->done) {
0240         return rc;
0241     }
0242 
0243     if (!ctx->active) {
0244         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0245                       "missing slice response");
0246         return NGX_ERROR;
0247     }
0248 
0249     if (ctx->start >= ctx->end) {
0250         ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);
0251         ngx_http_send_special(r, NGX_HTTP_LAST);
0252         return rc;
0253     }
0254 
0255     if (r->buffered) {
0256         return rc;
0257     }
0258 
0259     if (ngx_http_subrequest(r, &r->uri, &r->args, &ctx->sr, NULL,
0260                             NGX_HTTP_SUBREQUEST_CLONE)
0261         != NGX_OK)
0262     {
0263         return NGX_ERROR;
0264     }
0265 
0266     ngx_http_set_ctx(ctx->sr, ctx, ngx_http_slice_filter_module);
0267 
0268     slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);
0269 
0270     ctx->range.len = ngx_sprintf(ctx->range.data, "bytes=%O-%O", ctx->start,
0271                                  ctx->start + (off_t) slcf->size - 1)
0272                      - ctx->range.data;
0273 
0274     ctx->active = 0;
0275 
0276     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0277                    "http slice subrequest: \"%V\"", &ctx->range);
0278 
0279     return rc;
0280 }
0281 
0282 
0283 static ngx_int_t
0284 ngx_http_slice_parse_content_range(ngx_http_request_t *r,
0285     ngx_http_slice_content_range_t *cr)
0286 {
0287     off_t             start, end, complete_length, cutoff, cutlim;
0288     u_char           *p;
0289     ngx_table_elt_t  *h;
0290 
0291     h = r->headers_out.content_range;
0292 
0293     if (h == NULL
0294         || h->value.len < 7
0295         || ngx_strncmp(h->value.data, "bytes ", 6) != 0)
0296     {
0297         return NGX_ERROR;
0298     }
0299 
0300     p = h->value.data + 6;
0301 
0302     cutoff = NGX_MAX_OFF_T_VALUE / 10;
0303     cutlim = NGX_MAX_OFF_T_VALUE % 10;
0304 
0305     start = 0;
0306     end = 0;
0307     complete_length = 0;
0308 
0309     while (*p == ' ') { p++; }
0310 
0311     if (*p < '0' || *p > '9') {
0312         return NGX_ERROR;
0313     }
0314 
0315     while (*p >= '0' && *p <= '9') {
0316         if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
0317             return NGX_ERROR;
0318         }
0319 
0320         start = start * 10 + *p++ - '0';
0321     }
0322 
0323     while (*p == ' ') { p++; }
0324 
0325     if (*p++ != '-') {
0326         return NGX_ERROR;
0327     }
0328 
0329     while (*p == ' ') { p++; }
0330 
0331     if (*p < '0' || *p > '9') {
0332         return NGX_ERROR;
0333     }
0334 
0335     while (*p >= '0' && *p <= '9') {
0336         if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) {
0337             return NGX_ERROR;
0338         }
0339 
0340         end = end * 10 + *p++ - '0';
0341     }
0342 
0343     end++;
0344 
0345     while (*p == ' ') { p++; }
0346 
0347     if (*p++ != '/') {
0348         return NGX_ERROR;
0349     }
0350 
0351     while (*p == ' ') { p++; }
0352 
0353     if (*p != '*') {
0354         if (*p < '0' || *p > '9') {
0355             return NGX_ERROR;
0356         }
0357 
0358         while (*p >= '0' && *p <= '9') {
0359             if (complete_length >= cutoff
0360                 && (complete_length > cutoff || *p - '0' > cutlim))
0361             {
0362                 return NGX_ERROR;
0363             }
0364 
0365             complete_length = complete_length * 10 + *p++ - '0';
0366         }
0367 
0368     } else {
0369         complete_length = -1;
0370         p++;
0371     }
0372 
0373     while (*p == ' ') { p++; }
0374 
0375     if (*p != '\0') {
0376         return NGX_ERROR;
0377     }
0378 
0379     cr->start = start;
0380     cr->end = end;
0381     cr->complete_length = complete_length;
0382 
0383     return NGX_OK;
0384 }
0385 
0386 
0387 static ngx_int_t
0388 ngx_http_slice_range_variable(ngx_http_request_t *r,
0389     ngx_http_variable_value_t *v, uintptr_t data)
0390 {
0391     u_char                     *p;
0392     ngx_http_slice_ctx_t       *ctx;
0393     ngx_http_slice_loc_conf_t  *slcf;
0394 
0395     ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
0396 
0397     if (ctx == NULL) {
0398         if (r != r->main || r->headers_out.status) {
0399             v->not_found = 1;
0400             return NGX_OK;
0401         }
0402 
0403         slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);
0404 
0405         if (slcf->size == 0) {
0406             v->not_found = 1;
0407             return NGX_OK;
0408         }
0409 
0410         ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t));
0411         if (ctx == NULL) {
0412             return NGX_ERROR;
0413         }
0414 
0415         ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);
0416 
0417         p = ngx_pnalloc(r->pool, sizeof("bytes=-") - 1 + 2 * NGX_OFF_T_LEN);
0418         if (p == NULL) {
0419             return NGX_ERROR;
0420         }
0421 
0422         ctx->start = slcf->size * (ngx_http_slice_get_start(r) / slcf->size);
0423 
0424         ctx->range.data = p;
0425         ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start,
0426                                      ctx->start + (off_t) slcf->size - 1)
0427                          - p;
0428     }
0429 
0430     v->data = ctx->range.data;
0431     v->valid = 1;
0432     v->not_found = 0;
0433     v->no_cacheable = 1;
0434     v->len = ctx->range.len;
0435 
0436     return NGX_OK;
0437 }
0438 
0439 
0440 static off_t
0441 ngx_http_slice_get_start(ngx_http_request_t *r)
0442 {
0443     off_t             start, cutoff, cutlim;
0444     u_char           *p;
0445     ngx_table_elt_t  *h;
0446 
0447     if (r->headers_in.if_range) {
0448         return 0;
0449     }
0450 
0451     h = r->headers_in.range;
0452 
0453     if (h == NULL
0454         || h->value.len < 7
0455         || ngx_strncasecmp(h->value.data, (u_char *) "bytes=", 6) != 0)
0456     {
0457         return 0;
0458     }
0459 
0460     p = h->value.data + 6;
0461 
0462     if (ngx_strchr(p, ',')) {
0463         return 0;
0464     }
0465 
0466     while (*p == ' ') { p++; }
0467 
0468     if (*p == '-') {
0469         return 0;
0470     }
0471 
0472     cutoff = NGX_MAX_OFF_T_VALUE / 10;
0473     cutlim = NGX_MAX_OFF_T_VALUE % 10;
0474 
0475     start = 0;
0476 
0477     while (*p >= '0' && *p <= '9') {
0478         if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
0479             return 0;
0480         }
0481 
0482         start = start * 10 + *p++ - '0';
0483     }
0484 
0485     return start;
0486 }
0487 
0488 
0489 static void *
0490 ngx_http_slice_create_loc_conf(ngx_conf_t *cf)
0491 {
0492     ngx_http_slice_loc_conf_t  *slcf;
0493 
0494     slcf = ngx_palloc(cf->pool, sizeof(ngx_http_slice_loc_conf_t));
0495     if (slcf == NULL) {
0496         return NULL;
0497     }
0498 
0499     slcf->size = NGX_CONF_UNSET_SIZE;
0500 
0501     return slcf;
0502 }
0503 
0504 
0505 static char *
0506 ngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
0507 {
0508     ngx_http_slice_loc_conf_t *prev = parent;
0509     ngx_http_slice_loc_conf_t *conf = child;
0510 
0511     ngx_conf_merge_size_value(conf->size, prev->size, 0);
0512 
0513     return NGX_CONF_OK;
0514 }
0515 
0516 
0517 static ngx_int_t
0518 ngx_http_slice_add_variables(ngx_conf_t *cf)
0519 {
0520     ngx_http_variable_t  *var;
0521 
0522     var = ngx_http_add_variable(cf, &ngx_http_slice_range_name, 0);
0523     if (var == NULL) {
0524         return NGX_ERROR;
0525     }
0526 
0527     var->get_handler = ngx_http_slice_range_variable;
0528 
0529     return NGX_OK;
0530 }
0531 
0532 
0533 static ngx_int_t
0534 ngx_http_slice_init(ngx_conf_t *cf)
0535 {
0536     ngx_http_next_header_filter = ngx_http_top_header_filter;
0537     ngx_http_top_header_filter = ngx_http_slice_header_filter;
0538 
0539     ngx_http_next_body_filter = ngx_http_top_body_filter;
0540     ngx_http_top_body_filter = ngx_http_slice_body_filter;
0541 
0542     return NGX_OK;
0543 }