ngx_http_special_response.c revision e18a033b
1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_http.h>
11#include <nginx.h>
12
13
14static ngx_int_t ngx_http_send_error_page(ngx_http_request_t *r,
15    ngx_http_err_page_t *err_page);
16static ngx_int_t ngx_http_send_special_response(ngx_http_request_t *r,
17    ngx_http_core_loc_conf_t *clcf, ngx_uint_t err);
18static ngx_int_t ngx_http_send_refresh(ngx_http_request_t *r);
19
20
21static u_char ngx_http_error_full_tail[] =
22"<hr><center>" NGINX_VER "</center>" CRLF
23"</body>" CRLF
24"</html>" CRLF
25;
26
27
28static u_char ngx_http_error_build_tail[] =
29"<hr><center>" NGINX_VER_BUILD "</center>" CRLF
30"</body>" CRLF
31"</html>" CRLF
32;
33
34
35static u_char ngx_http_error_tail[] =
36"<hr><center>nginx</center>" CRLF
37"</body>" CRLF
38"</html>" CRLF
39;
40
41
42static u_char ngx_http_msie_padding[] =
43"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
44"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
45"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
46"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
47"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
48"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
49;
50
51
52static u_char ngx_http_msie_refresh_head[] =
53"<html><head><meta http-equiv=\"Refresh\" content=\"0; URL=";
54
55
56static u_char ngx_http_msie_refresh_tail[] =
57"\"></head><body></body></html>" CRLF;
58
59
60static char ngx_http_error_301_page[] =
61"<html>" CRLF
62"<head><title>301 Moved Permanently</title></head>" CRLF
63"<body bgcolor=\"white\">" CRLF
64"<center><h1>301 Moved Permanently</h1></center>" CRLF
65;
66
67
68static char ngx_http_error_302_page[] =
69"<html>" CRLF
70"<head><title>302 Found</title></head>" CRLF
71"<body bgcolor=\"white\">" CRLF
72"<center><h1>302 Found</h1></center>" CRLF
73;
74
75
76static char ngx_http_error_303_page[] =
77"<html>" CRLF
78"<head><title>303 See Other</title></head>" CRLF
79"<body bgcolor=\"white\">" CRLF
80"<center><h1>303 See Other</h1></center>" CRLF
81;
82
83
84static char ngx_http_error_307_page[] =
85"<html>" CRLF
86"<head><title>307 Temporary Redirect</title></head>" CRLF
87"<body bgcolor=\"white\">" CRLF
88"<center><h1>307 Temporary Redirect</h1></center>" CRLF
89;
90
91
92static char ngx_http_error_400_page[] =
93"<html>" CRLF
94"<head><title>400 Bad Request</title></head>" CRLF
95"<body bgcolor=\"white\">" CRLF
96"<center><h1>400 Bad Request</h1></center>" CRLF
97;
98
99
100static char ngx_http_error_401_page[] =
101"<html>" CRLF
102"<head><title>401 Authorization Required</title></head>" CRLF
103"<body bgcolor=\"white\">" CRLF
104"<center><h1>401 Authorization Required</h1></center>" CRLF
105;
106
107
108static char ngx_http_error_402_page[] =
109"<html>" CRLF
110"<head><title>402 Payment Required</title></head>" CRLF
111"<body bgcolor=\"white\">" CRLF
112"<center><h1>402 Payment Required</h1></center>" CRLF
113;
114
115
116static char ngx_http_error_403_page[] =
117"<html>" CRLF
118"<head><title>403 Forbidden</title></head>" CRLF
119"<body bgcolor=\"white\">" CRLF
120"<center><h1>403 Forbidden</h1></center>" CRLF
121;
122
123
124static char ngx_http_error_404_page[] =
125"<html>" CRLF
126"<head><title>404 Not Found</title></head>" CRLF
127"<body bgcolor=\"white\">" CRLF
128"<center><h1>404 Not Found</h1></center>" CRLF
129;
130
131
132static char ngx_http_error_405_page[] =
133"<html>" CRLF
134"<head><title>405 Not Allowed</title></head>" CRLF
135"<body bgcolor=\"white\">" CRLF
136"<center><h1>405 Not Allowed</h1></center>" CRLF
137;
138
139
140static char ngx_http_error_406_page[] =
141"<html>" CRLF
142"<head><title>406 Not Acceptable</title></head>" CRLF
143"<body bgcolor=\"white\">" CRLF
144"<center><h1>406 Not Acceptable</h1></center>" CRLF
145;
146
147
148static char ngx_http_error_408_page[] =
149"<html>" CRLF
150"<head><title>408 Request Time-out</title></head>" CRLF
151"<body bgcolor=\"white\">" CRLF
152"<center><h1>408 Request Time-out</h1></center>" CRLF
153;
154
155
156static char ngx_http_error_409_page[] =
157"<html>" CRLF
158"<head><title>409 Conflict</title></head>" CRLF
159"<body bgcolor=\"white\">" CRLF
160"<center><h1>409 Conflict</h1></center>" CRLF
161;
162
163
164static char ngx_http_error_410_page[] =
165"<html>" CRLF
166"<head><title>410 Gone</title></head>" CRLF
167"<body bgcolor=\"white\">" CRLF
168"<center><h1>410 Gone</h1></center>" CRLF
169;
170
171
172static char ngx_http_error_411_page[] =
173"<html>" CRLF
174"<head><title>411 Length Required</title></head>" CRLF
175"<body bgcolor=\"white\">" CRLF
176"<center><h1>411 Length Required</h1></center>" CRLF
177;
178
179
180static char ngx_http_error_412_page[] =
181"<html>" CRLF
182"<head><title>412 Precondition Failed</title></head>" CRLF
183"<body bgcolor=\"white\">" CRLF
184"<center><h1>412 Precondition Failed</h1></center>" CRLF
185;
186
187
188static char ngx_http_error_413_page[] =
189"<html>" CRLF
190"<head><title>413 Request Entity Too Large</title></head>" CRLF
191"<body bgcolor=\"white\">" CRLF
192"<center><h1>413 Request Entity Too Large</h1></center>" CRLF
193;
194
195
196static char ngx_http_error_414_page[] =
197"<html>" CRLF
198"<head><title>414 Request-URI Too Large</title></head>" CRLF
199"<body bgcolor=\"white\">" CRLF
200"<center><h1>414 Request-URI Too Large</h1></center>" CRLF
201;
202
203
204static char ngx_http_error_415_page[] =
205"<html>" CRLF
206"<head><title>415 Unsupported Media Type</title></head>" CRLF
207"<body bgcolor=\"white\">" CRLF
208"<center><h1>415 Unsupported Media Type</h1></center>" CRLF
209;
210
211
212static char ngx_http_error_416_page[] =
213"<html>" CRLF
214"<head><title>416 Requested Range Not Satisfiable</title></head>" CRLF
215"<body bgcolor=\"white\">" CRLF
216"<center><h1>416 Requested Range Not Satisfiable</h1></center>" CRLF
217;
218
219
220static char ngx_http_error_421_page[] =
221"<html>" CRLF
222"<head><title>421 Misdirected Request</title></head>" CRLF
223"<body bgcolor=\"white\">" CRLF
224"<center><h1>421 Misdirected Request</h1></center>" CRLF
225;
226
227
228static char ngx_http_error_429_page[] =
229"<html>" CRLF
230"<head><title>429 Too Many Requests</title></head>" CRLF
231"<body bgcolor=\"white\">" CRLF
232"<center><h1>429 Too Many Requests</h1></center>" CRLF
233;
234
235
236static char ngx_http_error_494_page[] =
237"<html>" CRLF
238"<head><title>400 Request Header Or Cookie Too Large</title></head>"
239CRLF
240"<body bgcolor=\"white\">" CRLF
241"<center><h1>400 Bad Request</h1></center>" CRLF
242"<center>Request Header Or Cookie Too Large</center>" CRLF
243;
244
245
246static char ngx_http_error_495_page[] =
247"<html>" CRLF
248"<head><title>400 The SSL certificate error</title></head>"
249CRLF
250"<body bgcolor=\"white\">" CRLF
251"<center><h1>400 Bad Request</h1></center>" CRLF
252"<center>The SSL certificate error</center>" CRLF
253;
254
255
256static char ngx_http_error_496_page[] =
257"<html>" CRLF
258"<head><title>400 No required SSL certificate was sent</title></head>"
259CRLF
260"<body bgcolor=\"white\">" CRLF
261"<center><h1>400 Bad Request</h1></center>" CRLF
262"<center>No required SSL certificate was sent</center>" CRLF
263;
264
265
266static char ngx_http_error_497_page[] =
267"<html>" CRLF
268"<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>"
269CRLF
270"<body bgcolor=\"white\">" CRLF
271"<center><h1>400 Bad Request</h1></center>" CRLF
272"<center>The plain HTTP request was sent to HTTPS port</center>" CRLF
273;
274
275
276static char ngx_http_error_500_page[] =
277"<html>" CRLF
278"<head><title>500 Internal Server Error</title></head>" CRLF
279"<body bgcolor=\"white\">" CRLF
280"<center><h1>500 Internal Server Error</h1></center>" CRLF
281;
282
283
284static char ngx_http_error_501_page[] =
285"<html>" CRLF
286"<head><title>501 Not Implemented</title></head>" CRLF
287"<body bgcolor=\"white\">" CRLF
288"<center><h1>501 Not Implemented</h1></center>" CRLF
289;
290
291
292static char ngx_http_error_502_page[] =
293"<html>" CRLF
294"<head><title>502 Bad Gateway</title></head>" CRLF
295"<body bgcolor=\"white\">" CRLF
296"<center><h1>502 Bad Gateway</h1></center>" CRLF
297;
298
299
300static char ngx_http_error_503_page[] =
301"<html>" CRLF
302"<head><title>503 Service Temporarily Unavailable</title></head>" CRLF
303"<body bgcolor=\"white\">" CRLF
304"<center><h1>503 Service Temporarily Unavailable</h1></center>" CRLF
305;
306
307
308static char ngx_http_error_504_page[] =
309"<html>" CRLF
310"<head><title>504 Gateway Time-out</title></head>" CRLF
311"<body bgcolor=\"white\">" CRLF
312"<center><h1>504 Gateway Time-out</h1></center>" CRLF
313;
314
315
316static char ngx_http_error_507_page[] =
317"<html>" CRLF
318"<head><title>507 Insufficient Storage</title></head>" CRLF
319"<body bgcolor=\"white\">" CRLF
320"<center><h1>507 Insufficient Storage</h1></center>" CRLF
321;
322
323
324static ngx_str_t ngx_http_error_pages[] = {
325
326    ngx_null_string,                     /* 201, 204 */
327
328#define NGX_HTTP_LAST_2XX  202
329#define NGX_HTTP_OFF_3XX   (NGX_HTTP_LAST_2XX - 201)
330
331    /* ngx_null_string, */               /* 300 */
332    ngx_string(ngx_http_error_301_page),
333    ngx_string(ngx_http_error_302_page),
334    ngx_string(ngx_http_error_303_page),
335    ngx_null_string,                     /* 304 */
336    ngx_null_string,                     /* 305 */
337    ngx_null_string,                     /* 306 */
338    ngx_string(ngx_http_error_307_page),
339
340#define NGX_HTTP_LAST_3XX  308
341#define NGX_HTTP_OFF_4XX   (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)
342
343    ngx_string(ngx_http_error_400_page),
344    ngx_string(ngx_http_error_401_page),
345    ngx_string(ngx_http_error_402_page),
346    ngx_string(ngx_http_error_403_page),
347    ngx_string(ngx_http_error_404_page),
348    ngx_string(ngx_http_error_405_page),
349    ngx_string(ngx_http_error_406_page),
350    ngx_null_string,                     /* 407 */
351    ngx_string(ngx_http_error_408_page),
352    ngx_string(ngx_http_error_409_page),
353    ngx_string(ngx_http_error_410_page),
354    ngx_string(ngx_http_error_411_page),
355    ngx_string(ngx_http_error_412_page),
356    ngx_string(ngx_http_error_413_page),
357    ngx_string(ngx_http_error_414_page),
358    ngx_string(ngx_http_error_415_page),
359    ngx_string(ngx_http_error_416_page),
360    ngx_null_string,                     /* 417 */
361    ngx_null_string,                     /* 418 */
362    ngx_null_string,                     /* 419 */
363    ngx_null_string,                     /* 420 */
364    ngx_string(ngx_http_error_421_page),
365    ngx_null_string,                     /* 422 */
366    ngx_null_string,                     /* 423 */
367    ngx_null_string,                     /* 424 */
368    ngx_null_string,                     /* 425 */
369    ngx_null_string,                     /* 426 */
370    ngx_null_string,                     /* 427 */
371    ngx_null_string,                     /* 428 */
372    ngx_string(ngx_http_error_429_page),
373
374#define NGX_HTTP_LAST_4XX  430
375#define NGX_HTTP_OFF_5XX   (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)
376
377    ngx_string(ngx_http_error_494_page), /* 494, request header too large */
378    ngx_string(ngx_http_error_495_page), /* 495, https certificate error */
379    ngx_string(ngx_http_error_496_page), /* 496, https no certificate */
380    ngx_string(ngx_http_error_497_page), /* 497, http to https */
381    ngx_string(ngx_http_error_404_page), /* 498, canceled */
382    ngx_null_string,                     /* 499, client has closed connection */
383
384    ngx_string(ngx_http_error_500_page),
385    ngx_string(ngx_http_error_501_page),
386    ngx_string(ngx_http_error_502_page),
387    ngx_string(ngx_http_error_503_page),
388    ngx_string(ngx_http_error_504_page),
389    ngx_null_string,                     /* 505 */
390    ngx_null_string,                     /* 506 */
391    ngx_string(ngx_http_error_507_page)
392
393#define NGX_HTTP_LAST_5XX  508
394
395};
396
397
398ngx_int_t
399ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error)
400{
401    ngx_uint_t                 i, err;
402    ngx_http_err_page_t       *err_page;
403    ngx_http_core_loc_conf_t  *clcf;
404
405    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
406                   "http special response: %i, \"%V?%V\"",
407                   error, &r->uri, &r->args);
408
409    r->err_status = error;
410
411    if (r->keepalive) {
412        switch (error) {
413            case NGX_HTTP_BAD_REQUEST:
414            case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE:
415            case NGX_HTTP_REQUEST_URI_TOO_LARGE:
416            case NGX_HTTP_TO_HTTPS:
417            case NGX_HTTPS_CERT_ERROR:
418            case NGX_HTTPS_NO_CERT:
419            case NGX_HTTP_INTERNAL_SERVER_ERROR:
420            case NGX_HTTP_NOT_IMPLEMENTED:
421                r->keepalive = 0;
422        }
423    }
424
425    if (r->lingering_close) {
426        switch (error) {
427            case NGX_HTTP_BAD_REQUEST:
428            case NGX_HTTP_TO_HTTPS:
429            case NGX_HTTPS_CERT_ERROR:
430            case NGX_HTTPS_NO_CERT:
431                r->lingering_close = 0;
432        }
433    }
434
435    r->headers_out.content_type.len = 0;
436
437    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
438
439    if (!r->error_page && clcf->error_pages && r->uri_changes != 0) {
440
441        if (clcf->recursive_error_pages == 0) {
442            r->error_page = 1;
443        }
444
445        err_page = clcf->error_pages->elts;
446
447        for (i = 0; i < clcf->error_pages->nelts; i++) {
448            if (err_page[i].status == error) {
449                return ngx_http_send_error_page(r, &err_page[i]);
450            }
451        }
452    }
453
454    r->expect_tested = 1;
455
456    if (ngx_http_discard_request_body(r) != NGX_OK) {
457        r->keepalive = 0;
458    }
459
460    if (clcf->msie_refresh
461        && r->headers_in.msie
462        && (error == NGX_HTTP_MOVED_PERMANENTLY
463            || error == NGX_HTTP_MOVED_TEMPORARILY))
464    {
465        return ngx_http_send_refresh(r);
466    }
467
468    if (error == NGX_HTTP_CREATED) {
469        /* 201 */
470        err = 0;
471
472    } else if (error == NGX_HTTP_NO_CONTENT) {
473        /* 204 */
474        err = 0;
475
476    } else if (error >= NGX_HTTP_MOVED_PERMANENTLY
477               && error < NGX_HTTP_LAST_3XX)
478    {
479        /* 3XX */
480        err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;
481
482    } else if (error >= NGX_HTTP_BAD_REQUEST
483               && error < NGX_HTTP_LAST_4XX)
484    {
485        /* 4XX */
486        err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_OFF_4XX;
487
488    } else if (error >= NGX_HTTP_NGINX_CODES
489               && error < NGX_HTTP_LAST_5XX)
490    {
491        /* 49X, 5XX */
492        err = error - NGX_HTTP_NGINX_CODES + NGX_HTTP_OFF_5XX;
493        switch (error) {
494            case NGX_HTTP_TO_HTTPS:
495            case NGX_HTTPS_CERT_ERROR:
496            case NGX_HTTPS_NO_CERT:
497            case NGX_HTTP_REQUEST_HEADER_TOO_LARGE:
498                r->err_status = NGX_HTTP_BAD_REQUEST;
499        }
500
501    } else {
502        /* unknown code, zero body */
503        err = 0;
504    }
505
506    return ngx_http_send_special_response(r, clcf, err);
507}
508
509
510ngx_int_t
511ngx_http_filter_finalize_request(ngx_http_request_t *r, ngx_module_t *m,
512    ngx_int_t error)
513{
514    void       *ctx;
515    ngx_int_t   rc;
516
517    ngx_http_clean_header(r);
518
519    ctx = NULL;
520
521    if (m) {
522        ctx = r->ctx[m->ctx_index];
523    }
524
525    /* clear the modules contexts */
526    ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
527
528    if (m) {
529        r->ctx[m->ctx_index] = ctx;
530    }
531
532    r->filter_finalize = 1;
533
534    rc = ngx_http_special_response_handler(r, error);
535
536    /* NGX_ERROR resets any pending data */
537
538    switch (rc) {
539
540    case NGX_OK:
541    case NGX_DONE:
542        return NGX_ERROR;
543
544    default:
545        return rc;
546    }
547}
548
549
550void
551ngx_http_clean_header(ngx_http_request_t *r)
552{
553    ngx_memzero(&r->headers_out.status,
554                sizeof(ngx_http_headers_out_t)
555                    - offsetof(ngx_http_headers_out_t, status));
556
557    r->headers_out.headers.part.nelts = 0;
558    r->headers_out.headers.part.next = NULL;
559    r->headers_out.headers.last = &r->headers_out.headers.part;
560
561    r->headers_out.content_length_n = -1;
562    r->headers_out.last_modified_time = -1;
563}
564
565
566static ngx_int_t
567ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)
568{
569    ngx_int_t                  overwrite;
570    ngx_str_t                  uri, args;
571    ngx_table_elt_t           *location;
572    ngx_http_core_loc_conf_t  *clcf;
573
574    overwrite = err_page->overwrite;
575
576    if (overwrite && overwrite != NGX_HTTP_OK) {
577        r->expect_tested = 1;
578    }
579
580    if (overwrite >= 0) {
581        r->err_status = overwrite;
582    }
583
584    if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {
585        return NGX_ERROR;
586    }
587
588    if (uri.len && uri.data[0] == '/') {
589
590        if (err_page->value.lengths) {
591            ngx_http_split_args(r, &uri, &args);
592
593        } else {
594            args = err_page->args;
595        }
596
597        if (r->method != NGX_HTTP_HEAD) {
598            r->method = NGX_HTTP_GET;
599            r->method_name = ngx_http_core_get_method;
600        }
601
602        return ngx_http_internal_redirect(r, &uri, &args);
603    }
604
605    if (uri.len && uri.data[0] == '@') {
606        return ngx_http_named_location(r, &uri);
607    }
608
609    location = ngx_list_push(&r->headers_out.headers);
610
611    if (location == NULL) {
612        return NGX_ERROR;
613    }
614
615    if (overwrite != NGX_HTTP_MOVED_PERMANENTLY
616        && overwrite != NGX_HTTP_MOVED_TEMPORARILY
617        && overwrite != NGX_HTTP_SEE_OTHER
618        && overwrite != NGX_HTTP_TEMPORARY_REDIRECT)
619    {
620        r->err_status = NGX_HTTP_MOVED_TEMPORARILY;
621    }
622
623    location->hash = 1;
624    ngx_str_set(&location->key, "Location");
625    location->value = uri;
626
627    ngx_http_clear_location(r);
628
629    r->headers_out.location = location;
630
631    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
632
633    if (clcf->msie_refresh && r->headers_in.msie) {
634        return ngx_http_send_refresh(r);
635    }
636
637    return ngx_http_send_special_response(r, clcf, r->err_status
638                                                   - NGX_HTTP_MOVED_PERMANENTLY
639                                                   + NGX_HTTP_OFF_3XX);
640}
641
642
643static ngx_int_t
644ngx_http_send_special_response(ngx_http_request_t *r,
645    ngx_http_core_loc_conf_t *clcf, ngx_uint_t err)
646{
647    u_char       *tail;
648    size_t        len;
649    ngx_int_t     rc;
650    ngx_buf_t    *b;
651    ngx_uint_t    msie_padding;
652    ngx_chain_t   out[3];
653
654    if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
655        len = sizeof(ngx_http_error_full_tail) - 1;
656        tail = ngx_http_error_full_tail;
657
658    } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
659        len = sizeof(ngx_http_error_build_tail) - 1;
660        tail = ngx_http_error_build_tail;
661
662    } else {
663        len = sizeof(ngx_http_error_tail) - 1;
664        tail = ngx_http_error_tail;
665    }
666
667    msie_padding = 0;
668
669    if (ngx_http_error_pages[err].len) {
670        r->headers_out.content_length_n = ngx_http_error_pages[err].len + len;
671        if (clcf->msie_padding
672            && (r->headers_in.msie || r->headers_in.chrome)
673            && r->http_version >= NGX_HTTP_VERSION_10
674            && err >= NGX_HTTP_OFF_4XX)
675        {
676            r->headers_out.content_length_n +=
677                                         sizeof(ngx_http_msie_padding) - 1;
678            msie_padding = 1;
679        }
680
681        r->headers_out.content_type_len = sizeof("text/html") - 1;
682        ngx_str_set(&r->headers_out.content_type, "text/html");
683        r->headers_out.content_type_lowcase = NULL;
684
685    } else {
686        r->headers_out.content_length_n = 0;
687    }
688
689    if (r->headers_out.content_length) {
690        r->headers_out.content_length->hash = 0;
691        r->headers_out.content_length = NULL;
692    }
693
694    ngx_http_clear_accept_ranges(r);
695    ngx_http_clear_last_modified(r);
696    ngx_http_clear_etag(r);
697
698    rc = ngx_http_send_header(r);
699
700    if (rc == NGX_ERROR || r->header_only) {
701        return rc;
702    }
703
704    if (ngx_http_error_pages[err].len == 0) {
705        return ngx_http_send_special(r, NGX_HTTP_LAST);
706    }
707
708    b = ngx_calloc_buf(r->pool);
709    if (b == NULL) {
710        return NGX_ERROR;
711    }
712
713    b->memory = 1;
714    b->pos = ngx_http_error_pages[err].data;
715    b->last = ngx_http_error_pages[err].data + ngx_http_error_pages[err].len;
716
717    out[0].buf = b;
718    out[0].next = &out[1];
719
720    b = ngx_calloc_buf(r->pool);
721    if (b == NULL) {
722        return NGX_ERROR;
723    }
724
725    b->memory = 1;
726
727    b->pos = tail;
728    b->last = tail + len;
729
730    out[1].buf = b;
731    out[1].next = NULL;
732
733    if (msie_padding) {
734        b = ngx_calloc_buf(r->pool);
735        if (b == NULL) {
736            return NGX_ERROR;
737        }
738
739        b->memory = 1;
740        b->pos = ngx_http_msie_padding;
741        b->last = ngx_http_msie_padding + sizeof(ngx_http_msie_padding) - 1;
742
743        out[1].next = &out[2];
744        out[2].buf = b;
745        out[2].next = NULL;
746    }
747
748    if (r == r->main) {
749        b->last_buf = 1;
750    }
751
752    b->last_in_chain = 1;
753
754    return ngx_http_output_filter(r, &out[0]);
755}
756
757
758static ngx_int_t
759ngx_http_send_refresh(ngx_http_request_t *r)
760{
761    u_char       *p, *location;
762    size_t        len, size;
763    uintptr_t     escape;
764    ngx_int_t     rc;
765    ngx_buf_t    *b;
766    ngx_chain_t   out;
767
768    len = r->headers_out.location->value.len;
769    location = r->headers_out.location->value.data;
770
771    escape = 2 * ngx_escape_uri(NULL, location, len, NGX_ESCAPE_REFRESH);
772
773    size = sizeof(ngx_http_msie_refresh_head) - 1
774           + escape + len
775           + sizeof(ngx_http_msie_refresh_tail) - 1;
776
777    r->err_status = NGX_HTTP_OK;
778
779    r->headers_out.content_type_len = sizeof("text/html") - 1;
780    ngx_str_set(&r->headers_out.content_type, "text/html");
781    r->headers_out.content_type_lowcase = NULL;
782
783    r->headers_out.location->hash = 0;
784    r->headers_out.location = NULL;
785
786    r->headers_out.content_length_n = size;
787
788    if (r->headers_out.content_length) {
789        r->headers_out.content_length->hash = 0;
790        r->headers_out.content_length = NULL;
791    }
792
793    ngx_http_clear_accept_ranges(r);
794    ngx_http_clear_last_modified(r);
795    ngx_http_clear_etag(r);
796
797    rc = ngx_http_send_header(r);
798
799    if (rc == NGX_ERROR || r->header_only) {
800        return rc;
801    }
802
803    b = ngx_create_temp_buf(r->pool, size);
804    if (b == NULL) {
805        return NGX_ERROR;
806    }
807
808    p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head,
809                   sizeof(ngx_http_msie_refresh_head) - 1);
810
811    if (escape == 0) {
812        p = ngx_cpymem(p, location, len);
813
814    } else {
815        p = (u_char *) ngx_escape_uri(p, location, len, NGX_ESCAPE_REFRESH);
816    }
817
818    b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail,
819                         sizeof(ngx_http_msie_refresh_tail) - 1);
820
821    b->last_buf = (r == r->main) ? 1 : 0;
822    b->last_in_chain = 1;
823
824    out.buf = b;
825    out.next = NULL;
826
827    return ngx_http_output_filter(r, &out);
828}
829