Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.19.2 ]​[ nginx-1.18.0 ]​

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