Lighttpd1.4.20源码分析之状态机(3)返回response2011-12-20 cnblogs kernel_hcy好久没顾这个了,最近比较清闲,重新拾掇一下,有始有终。回到正题,前一篇介绍完了请求的处理,先面lighttpd将会把处理的结果返回给客户端。状态机进入CON_STATE_RESPONST_START。在这个状态中,服务器主要的工作在函数connection_handle_write_prepare。这个函数不算复杂,主要是根据客户端请求的method来设置response的headers,其实就是设置“Content-Length”的值。下面是函数代码,做了一些删减:
1 static int connection_handle_write_prepare(server * srv, connection * con)
2 {
3if (con->mode == DIRECT)
4{
5/*
6 * static files
7 */
8switch (con->request.http_method)
9{
10case HTTP_METHOD_GET:
11case HTTP_METHOD_POST:
12case HTTP_METHOD_HEAD:
13case HTTP_METHOD_PUT:
14case HTTP_METHOD_MKCOL:
15case HTTP_METHOD_DELETE:
16case HTTP_METHOD_COPY:
17case HTTP_METHOD_MOVE:
18case HTTP_METHOD_PROPFIND:
19case HTTP_METHOD_PROPPATCH:
20case HTTP_METHOD_LOCK:
21case HTTP_METHOD_UNLOCK:
22break;
23case HTTP_METHOD_OPTIONS:
24/*
25 * 400 is coming from the request-parser BEFORE uri.path is set
26 * 403 is from the response handler when noone else catched it
27 * */
28if ((!con->http_status || con->http_status == 200)
29&& con->uri.path->used && con->uri.path->ptr[0] != "*")
30{
31response_header_insert(srv, con, CONST_STR_LEN("Allow"),
32 CONST_STR_LEN("OPTIONS, GET, HEAD, POST"));
33
34con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED;
35con->parsed_response &= ~HTTP_CONTENT_LENGTH;
36
37con->http_status = 200;
38con->file_finished = 1;
39
40chunkqueue_reset(con->write_queue);
41}
42break;
43default:
44switch (con->http_status)
45{
46case 400:/* bad request */
47case 414:/* overload request header */
48case 505:/* unknown protocol */
49case 207:/* this was webdav */
50break;
51default:
52con->http_status = 501;
53break;
54}
55break;
56}
57}
58
59if (con->http_status == 0)
60{
61con->http_status = 403;
62}
63
64switch (con->http_status)
65{
66case 204:/* class: header only */
67case 205:
68case 304:
69/*
70 * disable chunked encoding again as we have no body
71 */
72con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED;
73con->parsed_response &= ~HTTP_CONTENT_LENGTH;
74chunkqueue_reset(con->write_queue);
75
76con->file_finished = 1;
77break;
78default:/* class: header + body */
79if (con->mode != DIRECT)
80break;
81
82/*
83 * only custom body for 4xx and 5xx
84 */
85if (con->http_status < 400 || con->http_status >= 600)
86break;
87
88con->file_finished = 0;
89buffer_reset(con->physical.path);
90
91/*
92 * try to send static errorfile
93 */
94if (!buffer_is_empty(con->conf.errorfile_prefix))
95{
96 //设置对应的错误提示文件
97}
98
99if (!con->file_finished)
100{
101//没有对应的错误提示文件,设置默认错误提示。
102}
103break;
104}
105
106if (con->file_finished)
107{
108/*
109 * we have all the content and chunked encoding is not used, set a
110 * content-length
111 */
112if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) &&
113(con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)
114{
115off_t qlen = chunkqueue_length(con->write_queue);
116/**
117 * The Content-Length header only can be sent if we have content:
118 * - HEAD doesn"t have a content-body (but have a content-length)
119 * - 1xx, 204 and 304 don"t have a content-body (RFC 2616 Section 4.3)
120 *
121 * Otherwise generate a Content-Length header as chunked encoding is not
122 * available
123 */
124if ((con->http_status >= 100 && con->http_status < 200) ||
125con->http_status == 204 || con->http_status == 304)
126{
127data_string *ds;
128/*
129 * no Content-Body, no Content-Length
130 */
131if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "Content-Length")))
132{
133buffer_reset(ds->value);/* Headers with empty values
134 * are ignored for output */
135}
136}
137else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD)
138{
139/*
140 * qlen = 0 is important for Redirects (301, ...) as they MAY
141 * have a content. Browsers are waiting for a Content otherwise
142 */
143buffer_copy_off_t(srv->tmp_buf, qlen);
144
145response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf));
146}
147}
148}
149else
150{
151/**
152 * the file isn"t finished yet, but we have all headers
153 *
154 * to get keep-alive we either need:
155 * - Content-Length: ... (HTTP/1.0 and HTTP/1.0) or
156 * - Transfer-Encoding: chunked (HTTP/1.1)
157 */
158
159if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) &&
160((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0))
161{
162con->keep_alive = 0;
163}
164
165/**
166 * if the backend sent a Connection: close, follow the wish
167 *
168 * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we
169 * will close the connection. That"s fine. We can always decide the close
170 * the connection
171 *
172 * FIXME: to be nice we should remove the Connection: ...
173 */
174if (con->parsed_response & HTTP_CONNECTION)
175{
176/*
177 * a subrequest disable keep-alive although the client wanted it
178 */
179if (con->keep_alive && !con->response.keep_alive)
180{
181con->keep_alive = 0;
182}
183}
184}
185
186if (con->request.http_method == HTTP_METHOD_HEAD)
187{
188/**
189 * a HEAD request has the same as a GET
190 * without the content
191 */
192con->file_finished = 1;
193
194chunkqueue_reset(con->write_queue);
195con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED;
196}
197
198http_response_write_header(srv, con);
199
200return 0;
201 }