Klaus Demo ~jonashaag/bjoern / 4827db8
Fixed #51: Default to Keep-Alive for HTTP/1.1. Thanks to @jameslittle for the report, investigation and initial patch! Jonas Haag 7 years ago
2 changed file(s) with 74 addition(s) and 35 deletion(s). Raw diff Collapse all Expand all
114114 return false;
115115 }
116116
117 /* Let http-parser decide if connection should be keep-alive: */
117 /* keep-alive cruft */
118118 if(http_should_keep_alive(&request->parser.parser)) {
119 request->state.chunked_response = request->state.response_length_unknown;
120 request->state.keep_alive = true;
119 if(request->state.response_length_unknown) {
120 if(request->parser.parser.http_major > 0 && request->parser.parser.http_minor > 0) {
121 /* On HTTP 1.1, we can use Transfer-Encoding: chunked. */
122 request->state.chunked_response = true;
123 request->state.keep_alive = true;
124 } else {
125 /* On HTTP 1.0, we can only resort to closing the connection. */
126 request->state.keep_alive = false;
127 }
128 } else {
129 /* We know the content-length. Can always keep-alive. */
130 request->state.keep_alive = true;
131 }
121132 } else {
133 /* Explicit "Connection: close" (HTTP 1.1) or missing "Connection: keep-alive" (HTTP 1.0) */
122134 request->state.keep_alive = false;
123135 }
124136
196208 wsgi_getheaders(Request* request, PyObject* buf)
197209 {
198210 char* bufp = PyString_AS_STRING(buf);
199 int have_http11 = (request->parser.parser.http_major > 0 && request->parser.parser.http_minor > 0);
200211
201212 #define buf_write(src, len) \
202213 do { \
206217 } while(0)
207218 #define buf_write2(src) buf_write(src, strlen(src))
208219
220 /* First line, e.g. "HTTP/1.1 200 Ok" */
209221 buf_write2("HTTP/1.1 ");
210222 buf_write(PyString_AS_STRING(request->status),
211223 PyString_GET_SIZE(request->status));
212224
225 /* Headers, from the `request->headers` mapping.
226 * [("Header1", "value1"), ("Header2", "value2")]
227 * --> "Header1: value1\r\nHeader2: value2"
228 */
213229 for(Py_ssize_t i=0; i<PyList_GET_SIZE(request->headers); ++i) {
214230 PyObject *tuple = PyList_GET_ITEM(request->headers, i);
215231 PyObject *field = PyTuple_GET_ITEM(tuple, 0),
219235 buf_write2(": ");
220236 buf_write(PyString_AS_STRING(value), PyString_GET_SIZE(value));
221237 }
222 if(!have_http11) {
223 if(request->state.chunked_response)
224 /* Can't do chunked with HTTP 1.0 */
225 buf_write2("\r\nConnection: close");
226 else if (request->state.keep_alive)
227 /* Need to be explicit with HTTP 1.0 */
228 buf_write2("\r\nConnection: Keep-Alive");
229 }
230 else if(request->state.chunked_response)
231 buf_write2("\r\nTransfer-Encoding: chunked");
238
239 /* See `wsgi_call_application` */
240 if(request->state.keep_alive) {
241 buf_write2("\r\nConnection: Keep-Alive");
242 if(request->state.chunked_response) {
243 buf_write2("\r\nTransfer-Encoding: chunked");
244 }
245 } else {
246 buf_write2("\r\nConnection: close");
247 }
248
232249 buf_write2("\r\n\r\n");
233250
234251 return bufp - PyString_AS_STRING(buf);
00 import os
1 import re
21 import random
32 import httplib
43 import socket
6059
6160 if still_alive(self.conn.sock):
6261 if not self.expect_keep_alive:
63 raise Fail("Expected connection not be kept-alive")
62 raise Fail("Expected connection not to be kept-alive")
6463 self.request_count -= 1
6564 if self.request_count:
6665 if self.request_count == 1:
8685 return True
8786
8887 def _tinker_request(self):
89 req = 'GET / HTTP/1.%d\r\n' % self.http_minor
90 if self.want_keep_alive:
91 req += 'Connection: Keep-Alive\r\n'
88 if self.http_minor == 0:
89 req = 'GET / HTTP/1.0\r\n'
90 if self.want_keep_alive:
91 req += 'Connection: Keep-Alive\r\n'
92 else:
93 req = 'GET / HTTP/1.1\r\n'
94 if not self.want_keep_alive:
95 req += 'Connection: close\r\n'
9296 req += '\r\n'
9397 return req
9498
119123 import time; time.sleep(0.1)
120124
121125 for index, tpl in enumerate([
122 (0, False, False, False, False, False),
123 (0, False, False, True, False, False),
124 (0, False, False, True, False, False),
125 (0, False, True, True, False, True),
126 (0, True, False, False, False, False),
127 (0, True, False, True, False, False),
128 (0, True, True, False, False, False),
129 (0, True, True, True, False, False),
126 # HTTP 1.0:
127 # - Column "keep-alive" means "keep-alive header sent".
128 # - Never do keep-alive unless requested.
129 # - Never do chunked (not supported).
130 # - Can only keep-alive if content length is given.
131 # => Only "keep-alive" case is:
132 # "no-error AND content-length is given AND keep-alive requested"
130133
131 (1, False, False, False, False, False),
132 (1, False, False, True, True, True),
133 (1, False, True, False, False, False),
134 (1, False, True, True, False, True),
135 (1, True, False, False, False, False),
136 (1, True, False, True, False, False),
137 (1, True, True, False, False, False),
138 (1, True, True, True, False, False)
134 # |----------------------- inputs -----------------| |------- outputs -------|
135 # | HTTP minor error content-length keep-alive | | chunked keep alive |
136 ( 0, 0, 0, 0, 0, 0),
137 ( 0, 0, 0, 1, 0, 0),
138 ( 0, 0, 1, 0, 0, 0),
139 ( 0, 0, 1, 1, 0, 1),
140 ( 0, 1, 0, 0, 0, 0),
141 ( 0, 1, 0, 1, 0, 0),
142 ( 0, 1, 1, 0, 0, 0),
143 ( 0, 1, 1, 1, 0, 0),
144
145 # HTTP 1.1:
146 # - Columns "keep-alive" means "no close header sent"
147 # - Do keep-alive by default
148 # - Do chunked if content length is missing and keep-alive
149 # => Only "close" in case of error.
150
151 # |----------------------- inputs -----------------| |------- outputs -------|
152 # | HTTP minor error content-length keep-alive | | chunked keep alive |
153 ( 1, 0, 0, 0, 0, 0),
154 ( 1, 0, 0, 1, 1, 1),
155 ( 1, 0, 1, 0, 0, 0),
156 ( 1, 0, 1, 1, 0, 1),
157 ( 1, 1, 0, 0, 0, 0),
158 ( 1, 1, 0, 1, 0, 0),
159 ( 1, 1, 1, 0, 0, 0),
160 ( 1, 1, 1, 1, 0, 0)
139161 ]):
140162 print 'Running test %d: %r' % (index, tpl)
141163 class _test(Testcase):