Mercurial > hg > nginx-vendor-0-5
comparison src/http/ngx_http_header_filter_module.c @ 50:72eb30262aac NGINX_0_1_25
nginx 0.1.25
*) Bugfix: nginx did run on Linux parisc.
*) Feature: nginx now does not start under FreeBSD if the sysctl
kern.ipc.somaxconn value is too big.
*) Bugfix: if a request was internally redirected by the
ngx_http_index_module module to the ngx_http_proxy_module or
ngx_http_fastcgi_module modules, then the index file was not closed
after request completion.
*) Feature: the "proxy_pass" can be used in location with regular
expression.
*) Feature: the ngx_http_rewrite_filter_module module supports the
condition like "if ($HTTP_USER_AGENT ~ MSIE)".
*) Bugfix: nginx started too slow if the large number of addresses and
text values were used in the "geo" directive.
*) Change: a variable name must be declared as "$name" in the "geo"
directive. The previous variant without "$" is still supported, but
will be removed soon.
*) Feature: the "%{VARIABLE}v" logging parameter.
*) Feature: the "set $name value" directive.
*) Bugfix: gcc 4.0 compatibility.
*) Feature: the --with-openssl-opt=OPTIONS autoconfiguration directive.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Sat, 19 Mar 2005 00:00:00 +0300 |
parents | |
children | b55cbf18157e |
comparison
equal
deleted
inserted
replaced
49:93dabbc9efb9 | 50:72eb30262aac |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_http.h> | |
10 #include <nginx.h> | |
11 | |
12 | |
13 static ngx_int_t ngx_http_header_filter_init(ngx_cycle_t *cycle); | |
14 static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r); | |
15 | |
16 | |
17 static ngx_http_module_t ngx_http_header_filter_module_ctx = { | |
18 NULL, /* pre conf */ | |
19 | |
20 NULL, /* create main configuration */ | |
21 NULL, /* init main configuration */ | |
22 | |
23 NULL, /* create server configuration */ | |
24 NULL, /* merge server configuration */ | |
25 | |
26 NULL, /* create location configuration */ | |
27 NULL, /* merge location configuration */ | |
28 }; | |
29 | |
30 | |
31 ngx_module_t ngx_http_header_filter_module = { | |
32 NGX_MODULE, | |
33 &ngx_http_header_filter_module_ctx, /* module context */ | |
34 NULL, /* module directives */ | |
35 NGX_HTTP_MODULE, /* module type */ | |
36 ngx_http_header_filter_init, /* init module */ | |
37 NULL /* init process */ | |
38 }; | |
39 | |
40 | |
41 static char server_string[] = "Server: " NGINX_VER CRLF; | |
42 | |
43 | |
44 static ngx_str_t http_codes[] = { | |
45 | |
46 ngx_string("200 OK"), | |
47 ngx_null_string, /* "201 Created" */ | |
48 ngx_null_string, /* "202 Accepted" */ | |
49 ngx_null_string, /* "203 Non-Authoritative Information" */ | |
50 ngx_null_string, /* "204 No Content" */ | |
51 ngx_null_string, /* "205 Reset Content" */ | |
52 ngx_string("206 Partial Content"), | |
53 | |
54 /* ngx_null_string, */ /* "207 Multi-Status" */ | |
55 | |
56 #define NGX_HTTP_LEVEL_200 7 | |
57 | |
58 /* ngx_null_string, */ /* "300 Multiple Choices" */ | |
59 | |
60 ngx_string("301 Moved Permanently"), | |
61 ngx_string("302 Moved Temporarily"), | |
62 ngx_null_string, /* "303 See Other" */ | |
63 ngx_string("304 Not Modified"), | |
64 | |
65 /* ngx_null_string, */ /* "305 Use Proxy" */ | |
66 /* ngx_null_string, */ /* "306 unused" */ | |
67 /* ngx_null_string, */ /* "307 Temporary Redirect" */ | |
68 | |
69 #define NGX_HTTP_LEVEL_300 4 | |
70 | |
71 ngx_string("400 Bad Request"), | |
72 ngx_string("401 Unauthorized"), | |
73 ngx_string("402 Payment Required"), | |
74 ngx_string("403 Forbidden"), | |
75 ngx_string("404 Not Found"), | |
76 ngx_string("405 Not Allowed"), | |
77 ngx_string("406 Not Acceptable"), | |
78 ngx_null_string, /* "407 Proxy Authentication Required" */ | |
79 ngx_string("408 Request Time-out"), | |
80 ngx_null_string, /* "409 Conflict" */ | |
81 ngx_string("410 Gone"), | |
82 ngx_string("411 Length Required"), | |
83 ngx_null_string, /* "412 Precondition Failed" */ | |
84 ngx_string("413 Request Entity Too Large"), | |
85 ngx_null_string, /* "414 Request-URI Too Large", but we never send it | |
86 * because we treat such requests as the HTTP/0.9 | |
87 * requests and send only a body without a header | |
88 */ | |
89 ngx_null_string, /* "415 Unsupported Media Type" */ | |
90 ngx_string("416 Requested Range Not Satisfiable"), | |
91 | |
92 /* ngx_null_string, */ /* "417 Expectation Failed" */ | |
93 /* ngx_null_string, */ /* "418 unused" */ | |
94 /* ngx_null_string, */ /* "419 unused" */ | |
95 /* ngx_null_string, */ /* "420 unused" */ | |
96 /* ngx_null_string, */ /* "421 unused" */ | |
97 /* ngx_null_string, */ /* "422 Unprocessable Entity" */ | |
98 /* ngx_null_string, */ /* "423 Locked" */ | |
99 /* ngx_null_string, */ /* "424 Failed Dependency" */ | |
100 | |
101 #define NGX_HTTP_LEVEL_400 17 | |
102 | |
103 ngx_string("500 Internal Server Error"), | |
104 ngx_string("501 Method Not Implemented"), | |
105 ngx_string("502 Bad Gateway"), | |
106 ngx_string("503 Service Temporarily Unavailable"), | |
107 ngx_string("504 Gateway Time-out") | |
108 | |
109 /* ngx_null_string, */ /* "505 HTTP Version Not Supported" */ | |
110 /* ngx_null_string, */ /* "506 Variant Also Negotiates" */ | |
111 /* ngx_null_string, */ /* "507 Insufficient Storage" */ | |
112 /* ngx_null_string, */ /* "508 unused" */ | |
113 /* ngx_null_string, */ /* "509 unused" */ | |
114 /* ngx_null_string, */ /* "510 Not Extended" */ | |
115 }; | |
116 | |
117 | |
118 ngx_http_header_t ngx_http_headers_out[] = { | |
119 { ngx_string("Server"), offsetof(ngx_http_headers_out_t, server) }, | |
120 { ngx_string("Date"), offsetof(ngx_http_headers_out_t, date) }, | |
121 { ngx_string("Content-Type"), | |
122 offsetof(ngx_http_headers_out_t, content_type) }, | |
123 { ngx_string("Content-Length"), | |
124 offsetof(ngx_http_headers_out_t, content_length) }, | |
125 { ngx_string("Content-Encoding"), | |
126 offsetof(ngx_http_headers_out_t, content_encoding) }, | |
127 { ngx_string("Location"), offsetof(ngx_http_headers_out_t, location) }, | |
128 { ngx_string("Last-Modified"), | |
129 offsetof(ngx_http_headers_out_t, last_modified) }, | |
130 { ngx_string("Accept-Ranges"), | |
131 offsetof(ngx_http_headers_out_t, accept_ranges) }, | |
132 { ngx_string("Expires"), offsetof(ngx_http_headers_out_t, expires) }, | |
133 { ngx_string("Cache-Control"), | |
134 offsetof(ngx_http_headers_out_t, cache_control) }, | |
135 { ngx_string("ETag"), offsetof(ngx_http_headers_out_t, etag) }, | |
136 | |
137 { ngx_null_string, 0 } | |
138 }; | |
139 | |
140 | |
141 static ngx_int_t | |
142 ngx_http_header_filter(ngx_http_request_t *r) | |
143 { | |
144 u_char *p; | |
145 size_t len; | |
146 ngx_uint_t status, i; | |
147 ngx_buf_t *b; | |
148 ngx_chain_t out; | |
149 ngx_list_part_t *part; | |
150 ngx_table_elt_t *header; | |
151 ngx_http_core_loc_conf_t *clcf; | |
152 | |
153 if (r->http_version < NGX_HTTP_VERSION_10) { | |
154 return NGX_OK; | |
155 } | |
156 | |
157 if (r->method == NGX_HTTP_HEAD) { | |
158 r->header_only = 1; | |
159 } | |
160 | |
161 if (r->headers_out.last_modified_time != -1) { | |
162 if (r->headers_out.status != NGX_HTTP_OK | |
163 && r->headers_out.status != NGX_HTTP_NOT_MODIFIED | |
164 && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) | |
165 { | |
166 r->headers_out.last_modified_time = -1; | |
167 r->headers_out.last_modified = NULL; | |
168 } | |
169 } | |
170 | |
171 len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1 | |
172 /* the end of the header */ | |
173 + sizeof(CRLF) - 1; | |
174 | |
175 /* status line */ | |
176 | |
177 if (r->headers_out.status_line.len) { | |
178 len += r->headers_out.status_line.len; | |
179 #if (NGX_SUPPRESS_WARN) | |
180 status = NGX_INVALID_ARRAY_INDEX; | |
181 #endif | |
182 | |
183 } else { | |
184 | |
185 if (r->headers_out.status < NGX_HTTP_MOVED_PERMANENTLY) { | |
186 /* 2XX */ | |
187 status = r->headers_out.status - NGX_HTTP_OK; | |
188 | |
189 } else if (r->headers_out.status < NGX_HTTP_BAD_REQUEST) { | |
190 /* 3XX */ | |
191 status = r->headers_out.status - NGX_HTTP_MOVED_PERMANENTLY | |
192 + NGX_HTTP_LEVEL_200; | |
193 | |
194 if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { | |
195 r->header_only = 1; | |
196 } | |
197 | |
198 } else if (r->headers_out.status < NGX_HTTP_INTERNAL_SERVER_ERROR) { | |
199 /* 4XX */ | |
200 status = r->headers_out.status - NGX_HTTP_BAD_REQUEST | |
201 + NGX_HTTP_LEVEL_200 | |
202 + NGX_HTTP_LEVEL_300; | |
203 | |
204 } else { | |
205 /* 5XX */ | |
206 status = r->headers_out.status - NGX_HTTP_INTERNAL_SERVER_ERROR | |
207 + NGX_HTTP_LEVEL_200 | |
208 + NGX_HTTP_LEVEL_300 | |
209 + NGX_HTTP_LEVEL_400; | |
210 } | |
211 | |
212 len += http_codes[status].len; | |
213 } | |
214 | |
215 if (r->headers_out.server && r->headers_out.server->key.len) { | |
216 len += r->headers_out.server->key.len | |
217 + r->headers_out.server->value.len + 2; | |
218 } else { | |
219 len += sizeof(server_string) - 1; | |
220 } | |
221 | |
222 if (r->headers_out.date && r->headers_out.date->key.len) { | |
223 len += r->headers_out.date->key.len | |
224 + r->headers_out.date->value.len + 2; | |
225 } else { | |
226 len += sizeof("Date: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; | |
227 } | |
228 | |
229 if (r->headers_out.content_length == NULL) { | |
230 if (r->headers_out.content_length_n >= 0) { | |
231 len += sizeof("Content-Length: ") - 1 + NGX_OFF_T_LEN + 2; | |
232 } | |
233 } | |
234 | |
235 if (r->headers_out.content_type && r->headers_out.content_type->value.len) { | |
236 r->headers_out.content_type->key.len = 0; | |
237 len += sizeof("Content-Type: ") - 1 | |
238 + r->headers_out.content_type->value.len + 2; | |
239 | |
240 if (r->headers_out.charset.len) { | |
241 len += sizeof("; charset=") - 1 + r->headers_out.charset.len; | |
242 } | |
243 } | |
244 | |
245 if (r->headers_out.location | |
246 && r->headers_out.location->value.len | |
247 && r->headers_out.location->value.data[0] == '/') | |
248 { | |
249 r->headers_out.location->key.len = 0; | |
250 len += sizeof("Location: http://") - 1 | |
251 + r->server_name.len + r->headers_out.location->value.len + 2; | |
252 | |
253 if (r->port != 80) { | |
254 len += r->port_text->len; | |
255 } | |
256 } | |
257 | |
258 if (r->headers_out.last_modified && r->headers_out.last_modified->key.len) { | |
259 len += r->headers_out.last_modified->key.len | |
260 + r->headers_out.last_modified->value.len + 2; | |
261 | |
262 } else if (r->headers_out.last_modified_time != -1) { | |
263 len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; | |
264 } | |
265 | |
266 if (r->chunked) { | |
267 len += sizeof("Transfer-Encoding: chunked" CRLF) - 1; | |
268 } | |
269 | |
270 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
271 | |
272 if (r->keepalive) { | |
273 len += sizeof("Connection: keep-alive" CRLF) - 1; | |
274 | |
275 /* | |
276 * MSIE and Opera ignore the "Keep-Alive: timeout=<N>" header. | |
277 * MSIE keeps the connection alive for about 60-65 seconds. | |
278 * Opera keeps the connection alive very long. | |
279 * Mozilla keeps the connection alive for N plus about 1-10 seconds. | |
280 * Konqueror keeps the connection alive for about N seconds. | |
281 */ | |
282 | |
283 if (clcf->keepalive_header) { | |
284 len += sizeof("Keep-Alive: timeout=") - 1 + NGX_TIME_T_LEN + 2; | |
285 } | |
286 | |
287 } else { | |
288 len += sizeof("Connection: closed" CRLF) - 1; | |
289 } | |
290 | |
291 part = &r->headers_out.headers.part; | |
292 header = part->elts; | |
293 | |
294 for (i = 0; /* void */; i++) { | |
295 | |
296 if (i >= part->nelts) { | |
297 if (part->next == NULL) { | |
298 break; | |
299 } | |
300 | |
301 part = part->next; | |
302 header = part->elts; | |
303 i = 0; | |
304 } | |
305 | |
306 if (header[i].key.len == 0) { | |
307 continue; | |
308 } | |
309 | |
310 len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len | |
311 + sizeof(CRLF) - 1; | |
312 } | |
313 | |
314 b = ngx_create_temp_buf(r->pool, len); | |
315 if (b == NULL) { | |
316 return NGX_ERROR; | |
317 } | |
318 | |
319 /* "HTTP/1.x " */ | |
320 b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1); | |
321 | |
322 /* status line */ | |
323 if (r->headers_out.status_line.len) { | |
324 b->last = ngx_cpymem(b->last, r->headers_out.status_line.data, | |
325 r->headers_out.status_line.len); | |
326 | |
327 } else { | |
328 b->last = ngx_cpymem(b->last, http_codes[status].data, | |
329 http_codes[status].len); | |
330 } | |
331 *b->last++ = CR; *b->last++ = LF; | |
332 | |
333 if (!(r->headers_out.server && r->headers_out.server->key.len)) { | |
334 b->last = ngx_cpymem(b->last, server_string, sizeof(server_string) - 1); | |
335 } | |
336 | |
337 if (!(r->headers_out.date && r->headers_out.date->key.len)) { | |
338 b->last = ngx_cpymem(b->last, "Date: ", sizeof("Date: ") - 1); | |
339 b->last = ngx_cpymem(b->last, ngx_cached_http_time.data, | |
340 ngx_cached_http_time.len); | |
341 | |
342 *b->last++ = CR; *b->last++ = LF; | |
343 } | |
344 | |
345 if (r->headers_out.content_length == NULL) { | |
346 if (r->headers_out.content_length_n >= 0) { | |
347 b->last = ngx_sprintf(b->last, "Content-Length: %O" CRLF, | |
348 r->headers_out.content_length_n); | |
349 } | |
350 } | |
351 | |
352 if (r->headers_out.content_type && r->headers_out.content_type->value.len) { | |
353 b->last = ngx_cpymem(b->last, "Content-Type: ", | |
354 sizeof("Content-Type: ") - 1); | |
355 p = b->last; | |
356 b->last = ngx_cpymem(b->last, r->headers_out.content_type->value.data, | |
357 r->headers_out.content_type->value.len); | |
358 | |
359 if (r->headers_out.charset.len) { | |
360 b->last = ngx_cpymem(b->last, "; charset=", | |
361 sizeof("; charset=") - 1); | |
362 b->last = ngx_cpymem(b->last, r->headers_out.charset.data, | |
363 r->headers_out.charset.len); | |
364 | |
365 r->headers_out.content_type->value.len = b->last - p; | |
366 r->headers_out.content_type->value.data = p; | |
367 } | |
368 | |
369 *b->last++ = CR; *b->last++ = LF; | |
370 } | |
371 | |
372 if (r->headers_out.location | |
373 && r->headers_out.location->value.len | |
374 && r->headers_out.location->value.data[0] == '/') | |
375 { | |
376 p = b->last + sizeof("Location: ") - 1; | |
377 b->last = ngx_cpymem(b->last, "Location: http://", | |
378 sizeof("Location: http://") - 1); | |
379 b->last = ngx_cpymem(b->last, r->server_name.data, | |
380 r->server_name.len); | |
381 if (r->port != 80) { | |
382 b->last = ngx_cpymem(b->last, r->port_text->data, | |
383 r->port_text->len); | |
384 } | |
385 | |
386 b->last = ngx_cpymem(b->last, r->headers_out.location->value.data, | |
387 r->headers_out.location->value.len); | |
388 | |
389 r->headers_out.location->value.len = b->last - p; | |
390 r->headers_out.location->value.data = p; | |
391 | |
392 *b->last++ = CR; *b->last++ = LF; | |
393 } | |
394 | |
395 if (!(r->headers_out.last_modified && r->headers_out.last_modified->key.len) | |
396 && r->headers_out.last_modified_time != -1) | |
397 { | |
398 b->last = ngx_cpymem(b->last, "Last-Modified: ", | |
399 sizeof("Last-Modified: ") - 1); | |
400 b->last = ngx_http_time(b->last, r->headers_out.last_modified_time); | |
401 | |
402 *b->last++ = CR; *b->last++ = LF; | |
403 } | |
404 | |
405 if (r->chunked) { | |
406 b->last = ngx_cpymem(b->last, "Transfer-Encoding: chunked" CRLF, | |
407 sizeof("Transfer-Encoding: chunked" CRLF) - 1); | |
408 } | |
409 | |
410 if (r->keepalive) { | |
411 b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF, | |
412 sizeof("Connection: keep-alive" CRLF) - 1); | |
413 | |
414 if (clcf->keepalive_header) { | |
415 b->last = ngx_sprintf(b->last, "Keep-Alive: timeout=%T" CRLF, | |
416 clcf->keepalive_header); | |
417 } | |
418 | |
419 } else { | |
420 b->last = ngx_cpymem(b->last, "Connection: close" CRLF, | |
421 sizeof("Connection: close" CRLF) - 1); | |
422 } | |
423 | |
424 part = &r->headers_out.headers.part; | |
425 header = part->elts; | |
426 | |
427 for (i = 0; /* void */; i++) { | |
428 | |
429 if (i >= part->nelts) { | |
430 if (part->next == NULL) { | |
431 break; | |
432 } | |
433 | |
434 part = part->next; | |
435 header = part->elts; | |
436 i = 0; | |
437 } | |
438 | |
439 if (header[i].key.len == 0) { | |
440 continue; | |
441 } | |
442 | |
443 b->last = ngx_cpymem(b->last, header[i].key.data, header[i].key.len); | |
444 *b->last++ = ':' ; *b->last++ = ' ' ; | |
445 | |
446 b->last = ngx_cpymem(b->last, header[i].value.data, | |
447 header[i].value.len); | |
448 *b->last++ = CR; *b->last++ = LF; | |
449 } | |
450 | |
451 #if (NGX_DEBUG) | |
452 *b->last = '\0'; | |
453 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "%s\n", b->pos); | |
454 #endif | |
455 | |
456 /* the end of HTTP header */ | |
457 *b->last++ = CR; *b->last++ = LF; | |
458 | |
459 r->header_size = b->last - b->pos; | |
460 | |
461 if (r->header_only) { | |
462 b->last_buf = 1; | |
463 } | |
464 | |
465 out.buf = b; | |
466 out.next = NULL; | |
467 | |
468 return ngx_http_write_filter(r, &out); | |
469 } | |
470 | |
471 | |
472 static ngx_int_t | |
473 ngx_http_header_filter_init(ngx_cycle_t *cycle) | |
474 { | |
475 ngx_http_top_header_filter = ngx_http_header_filter; | |
476 | |
477 return NGX_OK; | |
478 } |