Mercurial > hg > nginx
annotate src/http/modules/ngx_http_degradation_module.c @ 9300:5be23505292b default tip
SSI: fixed incorrect or duplicate stub output.
Following 3518:eb3aaf8bd2a9 (0.8.37), r->request_output is only set
if there are data in the first buffer sent in the subrequest. As a
result, following the change mentioned this flag cannot be used to
prevent duplicate ngx_http_ssi_stub_output() calls, since it is not
set if there was already some output, but the first buffer was empty.
Still, when there are multiple subrequests, even an empty subrequest
response might be delayed by the postpone filter, leading to a second
call of ngx_http_ssi_stub_output() during finalization from
ngx_http_writer() the subreqest buffers are released by the postpone
filter. Since r->request_output is not set after the first call, this
resulted in duplicate stub output.
Additionally, checking only the first buffer might be wrong in some
unusual cases. For example, the first buffer might be empty if
$r->flush() is called before printing any data in the embedded Perl
module.
Depending on the postpone_output value and corresponding sizes, this
issue can result in either duplicate or unexpected stub output, or
"zero size buf in writer" alerts.
Following 8124:f5515e727656 (1.23.4), it became slightly easier to
reproduce the issue, as empty static files and empty cache items now
result in a response with an empty buffer. Before the change, an empty
proxied response can be used to reproduce the issue.
Fix is check all buffers and set r->request_output if any non-empty
buffers are sent. This ensures that all unusual cases of non-empty
responses are covered, and also that r->request_output will be set
after the first stub output, preventing duplicate output.
Reported by Jan Gassen.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Thu, 04 Jul 2024 17:41:28 +0300 |
parents | 67653855682e |
children |
rev | line source |
---|---|
3326 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4412 | 4 * Copyright (C) Nginx, Inc. |
3326 | 5 */ |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_http.h> | |
11 | |
12 | |
13 typedef struct { | |
14 size_t sbrk_size; | |
15 } ngx_http_degradation_main_conf_t; | |
16 | |
17 | |
18 typedef struct { | |
19 ngx_uint_t degrade; | |
20 } ngx_http_degradation_loc_conf_t; | |
21 | |
22 | |
23 static ngx_conf_enum_t ngx_http_degrade[] = { | |
24 { ngx_string("204"), 204 }, | |
25 { ngx_string("444"), 444 }, | |
26 { ngx_null_string, 0 } | |
27 }; | |
28 | |
29 | |
30 static void *ngx_http_degradation_create_main_conf(ngx_conf_t *cf); | |
31 static void *ngx_http_degradation_create_loc_conf(ngx_conf_t *cf); | |
32 static char *ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent, | |
33 void *child); | |
34 static char *ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd, | |
35 void *conf); | |
36 static ngx_int_t ngx_http_degradation_init(ngx_conf_t *cf); | |
37 | |
38 | |
39 static ngx_command_t ngx_http_degradation_commands[] = { | |
40 | |
41 { ngx_string("degradation"), | |
42 NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, | |
43 ngx_http_degradation, | |
44 NGX_HTTP_MAIN_CONF_OFFSET, | |
45 0, | |
46 NULL }, | |
47 | |
48 { ngx_string("degrade"), | |
49 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
50 ngx_conf_set_enum_slot, | |
51 NGX_HTTP_LOC_CONF_OFFSET, | |
52 offsetof(ngx_http_degradation_loc_conf_t, degrade), | |
53 &ngx_http_degrade }, | |
54 | |
55 ngx_null_command | |
56 }; | |
57 | |
58 | |
59 static ngx_http_module_t ngx_http_degradation_module_ctx = { | |
60 NULL, /* preconfiguration */ | |
61 ngx_http_degradation_init, /* postconfiguration */ | |
62 | |
63 ngx_http_degradation_create_main_conf, /* create main configuration */ | |
64 NULL, /* init main configuration */ | |
65 | |
66 NULL, /* create server configuration */ | |
67 NULL, /* merge server configuration */ | |
68 | |
69 ngx_http_degradation_create_loc_conf, /* create location configuration */ | |
70 ngx_http_degradation_merge_loc_conf /* merge location configuration */ | |
71 }; | |
72 | |
73 | |
74 ngx_module_t ngx_http_degradation_module = { | |
75 NGX_MODULE_V1, | |
76 &ngx_http_degradation_module_ctx, /* module context */ | |
77 ngx_http_degradation_commands, /* module directives */ | |
78 NGX_HTTP_MODULE, /* module type */ | |
79 NULL, /* init master */ | |
80 NULL, /* init module */ | |
81 NULL, /* init process */ | |
82 NULL, /* init thread */ | |
83 NULL, /* exit thread */ | |
84 NULL, /* exit process */ | |
85 NULL, /* exit master */ | |
86 NGX_MODULE_V1_PADDING | |
87 }; | |
88 | |
89 | |
90 static ngx_int_t | |
91 ngx_http_degradation_handler(ngx_http_request_t *r) | |
92 { | |
3772 | 93 ngx_http_degradation_loc_conf_t *dlcf; |
3326 | 94 |
95 dlcf = ngx_http_get_module_loc_conf(r, ngx_http_degradation_module); | |
96 | |
3772 | 97 if (dlcf->degrade && ngx_http_degraded(r)) { |
98 return dlcf->degrade; | |
3326 | 99 } |
100 | |
3772 | 101 return NGX_DECLINED; |
102 } | |
103 | |
104 | |
105 ngx_uint_t | |
106 ngx_http_degraded(ngx_http_request_t *r) | |
107 { | |
108 time_t now; | |
109 ngx_uint_t log; | |
110 static size_t sbrk_size; | |
111 static time_t sbrk_time; | |
112 ngx_http_degradation_main_conf_t *dmcf; | |
113 | |
3326 | 114 dmcf = ngx_http_get_module_main_conf(r, ngx_http_degradation_module); |
115 | |
116 if (dmcf->sbrk_size) { | |
117 | |
3772 | 118 log = 0; |
3326 | 119 now = ngx_time(); |
120 | |
121 /* lock mutex */ | |
122 | |
123 if (now != sbrk_time) { | |
124 | |
125 /* | |
126 * ELF/i386 is loaded at 0x08000000, 128M | |
127 * ELF/amd64 is loaded at 0x00400000, 4M | |
128 * | |
4572
67653855682e
Fixed spelling in multiline C comments.
Ruslan Ermilov <ru@nginx.com>
parents:
4412
diff
changeset
|
129 * use a function address to subtract the loading address |
3326 | 130 */ |
131 | |
132 sbrk_size = (size_t) sbrk(0) - ((uintptr_t) ngx_palloc & ~0x3FFFFF); | |
133 sbrk_time = now; | |
3772 | 134 log = 1; |
3326 | 135 } |
136 | |
137 /* unlock mutex */ | |
138 | |
139 if (sbrk_size >= dmcf->sbrk_size) { | |
3772 | 140 if (log) { |
141 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, | |
142 "degradation sbrk:%uzM", | |
143 sbrk_size / (1024 * 1024)); | |
144 } | |
145 | |
146 return 1; | |
3326 | 147 } |
148 } | |
149 | |
3772 | 150 return 0; |
3326 | 151 } |
152 | |
153 | |
154 static void * | |
155 ngx_http_degradation_create_main_conf(ngx_conf_t *cf) | |
156 { | |
157 ngx_http_degradation_main_conf_t *dmcf; | |
158 | |
159 dmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_degradation_main_conf_t)); | |
160 if (dmcf == NULL) { | |
161 return NULL; | |
162 } | |
163 | |
164 return dmcf; | |
165 } | |
166 | |
167 | |
168 static void * | |
169 ngx_http_degradation_create_loc_conf(ngx_conf_t *cf) | |
170 { | |
171 ngx_http_degradation_loc_conf_t *conf; | |
172 | |
173 conf = ngx_palloc(cf->pool, sizeof(ngx_http_degradation_loc_conf_t)); | |
174 if (conf == NULL) { | |
175 return NULL; | |
176 } | |
177 | |
178 conf->degrade = NGX_CONF_UNSET_UINT; | |
179 | |
180 return conf; | |
181 } | |
182 | |
183 | |
184 static char * | |
185 ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) | |
186 { | |
187 ngx_http_degradation_loc_conf_t *prev = parent; | |
188 ngx_http_degradation_loc_conf_t *conf = child; | |
189 | |
190 ngx_conf_merge_uint_value(conf->degrade, prev->degrade, 0); | |
191 | |
192 return NGX_CONF_OK; | |
193 } | |
194 | |
195 | |
196 static char * | |
197 ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
198 { | |
199 ngx_http_degradation_main_conf_t *dmcf = conf; | |
200 | |
201 ngx_str_t *value, s; | |
202 | |
203 value = cf->args->elts; | |
204 | |
205 if (ngx_strncmp(value[1].data, "sbrk=", 5) == 0) { | |
206 | |
207 s.len = value[1].len - 5; | |
208 s.data = value[1].data + 5; | |
209 | |
210 dmcf->sbrk_size = ngx_parse_size(&s); | |
211 if (dmcf->sbrk_size == (size_t) NGX_ERROR) { | |
212 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
213 "invalid sbrk size \"%V\"", &value[1]); | |
214 return NGX_CONF_ERROR; | |
215 } | |
3389 | 216 |
217 return NGX_CONF_OK; | |
3326 | 218 } |
219 | |
3389 | 220 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
221 "invalid parameter \"%V\"", &value[1]); | |
222 | |
223 return NGX_CONF_ERROR; | |
3326 | 224 } |
225 | |
226 | |
227 static ngx_int_t | |
228 ngx_http_degradation_init(ngx_conf_t *cf) | |
229 { | |
230 ngx_http_handler_pt *h; | |
231 ngx_http_core_main_conf_t *cmcf; | |
232 | |
233 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); | |
234 | |
235 h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); | |
236 if (h == NULL) { | |
237 return NGX_ERROR; | |
238 } | |
239 | |
240 *h = ngx_http_degradation_handler; | |
241 | |
242 return NGX_OK; | |
243 } |