Mercurial > hg > nginx-vendor-1-0
comparison src/http/ngx_http_header_filter.c @ 0:f0b350454894 NGINX_0_1_0
nginx 0.1.0
*) The first public version.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Mon, 04 Oct 2004 00:00:00 +0400 |
parents | |
children | cc9f381affaa |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f0b350454894 |
---|---|
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 child */ | |
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 ngx_null_string, /* "207 Multi-Status" */ | |
54 | |
55 #if 0 | |
56 ngx_null_string, /* "300 Multiple Choices" */ | |
57 #endif | |
58 | |
59 ngx_string("301 Moved Permanently"), | |
60 #if 0 | |
61 ngx_string("302 Moved Temporarily"), | |
62 #else | |
63 ngx_string("302 Found"), | |
64 #endif | |
65 ngx_null_string, /* "303 See Other" */ | |
66 ngx_string("304 Not Modified"), | |
67 | |
68 ngx_string("400 Bad Request"), | |
69 ngx_string("401 Unauthorized"), | |
70 ngx_null_string, /* "402 Payment Required" */ | |
71 ngx_string("403 Forbidden"), | |
72 ngx_string("404 Not Found"), | |
73 ngx_string("405 Not Allowed"), | |
74 ngx_null_string, /* "406 Not Acceptable" */ | |
75 ngx_null_string, /* "407 Proxy Authentication Required" */ | |
76 ngx_string("408 Request Time-out"), | |
77 ngx_null_string, /* "409 Conflict" */ | |
78 ngx_null_string, /* "410 Gone" */ | |
79 ngx_string("411 Length Required"), | |
80 ngx_null_string, /* "412 Precondition Failed" */ | |
81 ngx_string("413 Request Entity Too Large"), | |
82 ngx_null_string, /* "414 Request-URI Too Large" but we never send it | |
83 * because we treat such requests as the HTTP/0.9 | |
84 * requests and send only a body without a header | |
85 */ | |
86 ngx_null_string, /* "415 Unsupported Media Type" */ | |
87 ngx_string("416 Requested Range Not Satisfiable"), | |
88 | |
89 ngx_string("500 Internal Server Error"), | |
90 ngx_string("501 Method Not Implemented"), | |
91 ngx_string("502 Bad Gateway"), | |
92 ngx_string("503 Service Temporarily Unavailable"), | |
93 ngx_string("504 Gateway Time-out") | |
94 }; | |
95 | |
96 | |
97 ngx_http_header_t ngx_http_headers_out[] = { | |
98 { ngx_string("Server"), offsetof(ngx_http_headers_out_t, server) }, | |
99 { ngx_string("Date"), offsetof(ngx_http_headers_out_t, date) }, | |
100 { ngx_string("Content-Type"), | |
101 offsetof(ngx_http_headers_out_t, content_type) }, | |
102 { ngx_string("Content-Length"), | |
103 offsetof(ngx_http_headers_out_t, content_length) }, | |
104 { ngx_string("Content-Encoding"), | |
105 offsetof(ngx_http_headers_out_t, content_encoding) }, | |
106 { ngx_string("Location"), offsetof(ngx_http_headers_out_t, location) }, | |
107 { ngx_string("Last-Modified"), | |
108 offsetof(ngx_http_headers_out_t, last_modified) }, | |
109 { ngx_string("Accept-Ranges"), | |
110 offsetof(ngx_http_headers_out_t, accept_ranges) }, | |
111 { ngx_string("Expires"), offsetof(ngx_http_headers_out_t, expires) }, | |
112 { ngx_string("Cache-Control"), | |
113 offsetof(ngx_http_headers_out_t, cache_control) }, | |
114 { ngx_string("ETag"), offsetof(ngx_http_headers_out_t, etag) }, | |
115 | |
116 { ngx_null_string, 0 } | |
117 }; | |
118 | |
119 | |
120 static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r) | |
121 { | |
122 u_char *p; | |
123 size_t len; | |
124 ngx_uint_t status, i; | |
125 ngx_buf_t *b; | |
126 ngx_chain_t *ln; | |
127 ngx_list_part_t *part; | |
128 ngx_table_elt_t *header; | |
129 ngx_http_core_loc_conf_t *clcf; | |
130 | |
131 if (r->http_version < NGX_HTTP_VERSION_10) { | |
132 return NGX_OK; | |
133 } | |
134 | |
135 if (r->method == NGX_HTTP_HEAD) { | |
136 r->header_only = 1; | |
137 } | |
138 | |
139 if (r->headers_out.last_modified_time != -1) { | |
140 if (r->headers_out.status != NGX_HTTP_OK | |
141 && r->headers_out.status != NGX_HTTP_NOT_MODIFIED | |
142 && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) | |
143 { | |
144 r->headers_out.last_modified_time = -1; | |
145 r->headers_out.last_modified = NULL; | |
146 } | |
147 } | |
148 | |
149 /* 2 is for trailing "\r\n" and 2 is for "\r\n" in the end of header */ | |
150 len = sizeof("HTTP/1.x ") - 1 + 2 + 2; | |
151 | |
152 /* status line */ | |
153 if (r->headers_out.status_line.len) { | |
154 len += r->headers_out.status_line.len; | |
155 #if (NGX_SUPPRESS_WARN) | |
156 status = NGX_INVALID_ARRAY_INDEX; | |
157 #endif | |
158 | |
159 } else { | |
160 | |
161 if (r->headers_out.status < NGX_HTTP_MOVED_PERMANENTLY) { | |
162 /* 2XX */ | |
163 status = r->headers_out.status - NGX_HTTP_OK; | |
164 | |
165 } else if (r->headers_out.status < NGX_HTTP_BAD_REQUEST) { | |
166 /* 3XX */ | |
167 status = r->headers_out.status - NGX_HTTP_MOVED_PERMANENTLY + 8; | |
168 | |
169 if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { | |
170 r->header_only = 1; | |
171 } | |
172 | |
173 } else if (r->headers_out.status < NGX_HTTP_INTERNAL_SERVER_ERROR) { | |
174 /* 4XX */ | |
175 status = r->headers_out.status - NGX_HTTP_BAD_REQUEST + 8 + 4; | |
176 | |
177 } else { | |
178 /* 5XX */ | |
179 status = r->headers_out.status | |
180 - NGX_HTTP_INTERNAL_SERVER_ERROR + 8 + 4 + 17; | |
181 } | |
182 | |
183 len += http_codes[status].len; | |
184 } | |
185 | |
186 if (r->headers_out.server && r->headers_out.server->key.len) { | |
187 len += r->headers_out.server->key.len | |
188 + r->headers_out.server->value.len + 2; | |
189 } else { | |
190 len += sizeof(server_string) - 1; | |
191 } | |
192 | |
193 if (r->headers_out.date && r->headers_out.date->key.len) { | |
194 len += r->headers_out.date->key.len | |
195 + r->headers_out.date->value.len + 2; | |
196 } else { | |
197 len += sizeof("Date: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; | |
198 } | |
199 | |
200 if (r->headers_out.content_length == NULL) { | |
201 if (r->headers_out.content_length_n >= 0) { | |
202 len += sizeof("Content-Length: ") - 1 + NGX_OFF_T_LEN + 2; | |
203 } | |
204 } | |
205 | |
206 if (r->headers_out.content_type && r->headers_out.content_type->value.len) { | |
207 r->headers_out.content_type->key.len = 0; | |
208 len += sizeof("Content-Type: ") - 1 | |
209 + r->headers_out.content_type->value.len + 2; | |
210 | |
211 if (r->headers_out.charset.len) { | |
212 len += sizeof("; charset=") - 1 + r->headers_out.charset.len; | |
213 } | |
214 } | |
215 | |
216 if (r->headers_out.location | |
217 && r->headers_out.location->value.len | |
218 && r->headers_out.location->value.data[0] == '/') | |
219 { | |
220 r->headers_out.location->key.len = 0; | |
221 len += sizeof("Location: http://") - 1 | |
222 + r->server_name->len + r->headers_out.location->value.len + 2; | |
223 | |
224 if (r->port != 80) { | |
225 len += r->port_text->len; | |
226 } | |
227 } | |
228 | |
229 if (r->headers_out.last_modified && r->headers_out.last_modified->key.len) { | |
230 len += r->headers_out.last_modified->key.len | |
231 + r->headers_out.last_modified->value.len + 2; | |
232 | |
233 } else if (r->headers_out.last_modified_time != -1) { | |
234 len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; | |
235 } | |
236 | |
237 if (r->chunked) { | |
238 len += sizeof("Transfer-Encoding: chunked" CRLF) - 1; | |
239 } | |
240 | |
241 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
242 | |
243 if (r->keepalive) { | |
244 len += sizeof("Connection: keep-alive" CRLF) - 1; | |
245 | |
246 /* | |
247 * MSIE and Opera ignore the "Keep-Alive: timeout=<N>" header. | |
248 * MSIE keeps the connection alive for about 60-65 seconds. | |
249 * Opera keeps the connection alive very long. | |
250 * Mozilla keeps the connection alive for N plus about 1-10 seconds. | |
251 * Konqueror keeps the connection alive for about N seconds. | |
252 */ | |
253 | |
254 if (clcf->keepalive_header | |
255 && (r->headers_in.gecko || r->headers_in.konqueror)) | |
256 { | |
257 len += sizeof("Keep-Alive: timeout=") - 1 + TIME_T_LEN + 2; | |
258 } | |
259 | |
260 } else { | |
261 len += sizeof("Connection: closed" CRLF) - 1; | |
262 } | |
263 | |
264 part = &r->headers_out.headers.part; | |
265 header = part->elts; | |
266 | |
267 for (i = 0; /* void */; i++) { | |
268 | |
269 if (i >= part->nelts) { | |
270 if (part->next == NULL) { | |
271 break; | |
272 } | |
273 | |
274 part = part->next; | |
275 header = part->elts; | |
276 i = 0; | |
277 } | |
278 | |
279 if (header[i].key.len == 0) { | |
280 continue; | |
281 } | |
282 | |
283 /* 2 is for ": " and 2 is for "\r\n" */ | |
284 len += header[i].key.len + 2 + header[i].value.len + 2; | |
285 } | |
286 | |
287 if (!(b = ngx_create_temp_buf(r->pool, len))) { | |
288 return NGX_ERROR; | |
289 } | |
290 | |
291 /* "HTTP/1.x " */ | |
292 b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1); | |
293 | |
294 /* status line */ | |
295 if (r->headers_out.status_line.len) { | |
296 b->last = ngx_cpymem(b->last, r->headers_out.status_line.data, | |
297 r->headers_out.status_line.len); | |
298 | |
299 } else { | |
300 b->last = ngx_cpymem(b->last, http_codes[status].data, | |
301 http_codes[status].len); | |
302 } | |
303 *(b->last++) = CR; *(b->last++) = LF; | |
304 | |
305 if (!(r->headers_out.server && r->headers_out.server->key.len)) { | |
306 b->last = ngx_cpymem(b->last, server_string, sizeof(server_string) - 1); | |
307 } | |
308 | |
309 if (!(r->headers_out.date && r->headers_out.date->key.len)) { | |
310 b->last = ngx_cpymem(b->last, "Date: ", sizeof("Date: ") - 1); | |
311 b->last = ngx_cpymem(b->last, ngx_cached_http_time.data, | |
312 ngx_cached_http_time.len); | |
313 | |
314 *(b->last++) = CR; *(b->last++) = LF; | |
315 } | |
316 | |
317 if (r->headers_out.content_length == NULL) { | |
318 if (r->headers_out.content_length_n >= 0) { | |
319 b->last += ngx_snprintf((char *) b->last, | |
320 sizeof("Content-Length: ") + NGX_OFF_T_LEN + 2, | |
321 "Content-Length: " OFF_T_FMT CRLF, | |
322 r->headers_out.content_length_n); | |
323 } | |
324 } | |
325 | |
326 if (r->headers_out.content_type && r->headers_out.content_type->value.len) { | |
327 b->last = ngx_cpymem(b->last, "Content-Type: ", | |
328 sizeof("Content-Type: ") - 1); | |
329 p = b->last; | |
330 b->last = ngx_cpymem(b->last, r->headers_out.content_type->value.data, | |
331 r->headers_out.content_type->value.len); | |
332 | |
333 if (r->headers_out.charset.len) { | |
334 b->last = ngx_cpymem(b->last, "; charset=", | |
335 sizeof("; charset=") - 1); | |
336 b->last = ngx_cpymem(b->last, r->headers_out.charset.data, | |
337 r->headers_out.charset.len); | |
338 | |
339 r->headers_out.content_type->value.len = b->last - p; | |
340 r->headers_out.content_type->value.data = p; | |
341 } | |
342 | |
343 *(b->last++) = CR; *(b->last++) = LF; | |
344 } | |
345 | |
346 if (r->headers_out.location | |
347 && r->headers_out.location->value.len | |
348 && r->headers_out.location->value.data[0] == '/') | |
349 { | |
350 p = b->last + sizeof("Location: ") - 1; | |
351 b->last = ngx_cpymem(b->last, "Location: http://", | |
352 sizeof("Location: http://") - 1); | |
353 b->last = ngx_cpymem(b->last, r->server_name->data, | |
354 r->server_name->len); | |
355 if (r->port != 80) { | |
356 b->last = ngx_cpymem(b->last, r->port_text->data, | |
357 r->port_text->len); | |
358 } | |
359 | |
360 b->last = ngx_cpymem(b->last, r->headers_out.location->value.data, | |
361 r->headers_out.location->value.len); | |
362 | |
363 r->headers_out.location->value.len = b->last - p; | |
364 r->headers_out.location->value.data = p; | |
365 | |
366 *(b->last++) = CR; *(b->last++) = LF; | |
367 } | |
368 | |
369 if (!(r->headers_out.last_modified && r->headers_out.last_modified->key.len) | |
370 && r->headers_out.last_modified_time != -1) | |
371 { | |
372 b->last = ngx_cpymem(b->last, "Last-Modified: ", | |
373 sizeof("Last-Modified: ") - 1); | |
374 b->last += ngx_http_time(b->last, r->headers_out.last_modified_time); | |
375 | |
376 *(b->last++) = CR; *(b->last++) = LF; | |
377 } | |
378 | |
379 if (r->chunked) { | |
380 b->last = ngx_cpymem(b->last, "Transfer-Encoding: chunked" CRLF, | |
381 sizeof("Transfer-Encoding: chunked" CRLF) - 1); | |
382 } | |
383 | |
384 if (r->keepalive) { | |
385 b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF, | |
386 sizeof("Connection: keep-alive" CRLF) - 1); | |
387 | |
388 if (clcf->keepalive_header | |
389 && (r->headers_in.gecko || r->headers_in.konqueror)) | |
390 { | |
391 b->last += ngx_snprintf((char *) b->last, | |
392 sizeof("Keep-Alive: timeout=") + TIME_T_LEN + 2, | |
393 "Keep-Alive: timeout=" TIME_T_FMT CRLF, | |
394 clcf->keepalive_header); | |
395 } | |
396 | |
397 } else { | |
398 b->last = ngx_cpymem(b->last, "Connection: close" CRLF, | |
399 sizeof("Connection: close" CRLF) - 1); | |
400 } | |
401 | |
402 part = &r->headers_out.headers.part; | |
403 header = part->elts; | |
404 | |
405 for (i = 0; /* void */; i++) { | |
406 | |
407 if (i >= part->nelts) { | |
408 if (part->next == NULL) { | |
409 break; | |
410 } | |
411 | |
412 part = part->next; | |
413 header = part->elts; | |
414 i = 0; | |
415 } | |
416 | |
417 if (header[i].key.len == 0) { | |
418 continue; | |
419 } | |
420 | |
421 b->last = ngx_cpymem(b->last, header[i].key.data, header[i].key.len); | |
422 *(b->last++) = ':' ; *(b->last++) = ' ' ; | |
423 | |
424 b->last = ngx_cpymem(b->last, header[i].value.data, | |
425 header[i].value.len); | |
426 *(b->last++) = CR; *(b->last++) = LF; | |
427 } | |
428 | |
429 #if (NGX_DEBUG) | |
430 *(b->last) = '\0'; | |
431 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "%s\n", b->pos); | |
432 #endif | |
433 | |
434 /* the end of HTTP header */ | |
435 *(b->last++) = CR; *(b->last++) = LF; | |
436 | |
437 r->header_size = b->last - b->pos; | |
438 | |
439 if (r->header_only) { | |
440 b->last_buf = 1; | |
441 } | |
442 | |
443 if (!(ln = ngx_alloc_chain_link(r->pool))) { | |
444 return NGX_ERROR; | |
445 } | |
446 | |
447 ln->buf = b; | |
448 ln->next = NULL; | |
449 | |
450 return ngx_http_write_filter(r, ln); | |
451 } | |
452 | |
453 | |
454 static ngx_int_t ngx_http_header_filter_init(ngx_cycle_t *cycle) | |
455 { | |
456 ngx_http_top_header_filter = ngx_http_header_filter; | |
457 | |
458 return NGX_OK; | |
459 } |