Klaus Demo ~jonashaag/bjoern / 3b3941d
User 2-space indentation rather than 2-space intendation everywhere (I like it) Jonas Haag 9 years ago
7 changed file(s) with 908 addition(s) and 932 deletion(s). Raw diff Collapse all Expand all
1111 static PyObject*
1212 listen(PyObject* self, PyObject* args)
1313 {
14 const char* host;
15 int port;
14 const char* host;
15 int port;
1616
17 if(wsgi_app) {
18 PyErr_SetString(
19 PyExc_RuntimeError,
20 "Only one bjoern server per Python interpreter is allowed"
21 );
22 return NULL;
23 }
17 if(wsgi_app) {
18 PyErr_SetString(
19 PyExc_RuntimeError,
20 "Only one bjoern server per Python interpreter is allowed"
21 );
22 return NULL;
23 }
2424
25 if(!PyArg_ParseTuple(args, "Osi:run/listen", &wsgi_app, &host, &port))
26 return NULL;
25 if(!PyArg_ParseTuple(args, "Osi:run/listen", &wsgi_app, &host, &port))
26 return NULL;
2727
28 _initialize_request_module(host, port);
28 _initialize_request_module(host, port);
2929
30 if(!server_init(host, port)) {
31 PyErr_Format(
32 PyExc_RuntimeError,
33 "Could not start server on %s:%d", host, port
34 );
35 return NULL;
36 }
30 if(!server_init(host, port)) {
31 PyErr_Format(
32 PyExc_RuntimeError,
33 "Could not start server on %s:%d", host, port
34 );
35 return NULL;
36 }
3737
38 Py_RETURN_NONE;
38 Py_RETURN_NONE;
3939 }
4040
4141 PyDoc_STRVAR(run_doc,
4848 static PyObject*
4949 run(PyObject* self, PyObject* args)
5050 {
51 if(PyTuple_GET_SIZE(args) == 0) {
52 /* bjoern.run() */
53 if(!wsgi_app) {
54 PyErr_SetString(
55 PyExc_RuntimeError,
56 "Must call bjoern.listen(app, host, port) before "
57 "calling bjoern.run() without arguments."
58 );
59 return NULL;
60 }
61 } else {
62 /* bjoern.run(app, host, port) */
63 if(!listen(self, args))
64 return NULL;
51 if(PyTuple_GET_SIZE(args) == 0) {
52 /* bjoern.run() */
53 if(!wsgi_app) {
54 PyErr_SetString(
55 PyExc_RuntimeError,
56 "Must call bjoern.listen(app, host, port) before "
57 "calling bjoern.run() without arguments."
58 );
59 return NULL;
6560 }
61 } else {
62 /* bjoern.run(app, host, port) */
63 if(!listen(self, args))
64 return NULL;
65 }
6666
67 server_run();
68 wsgi_app = NULL;
69 Py_RETURN_NONE;
67 server_run();
68 wsgi_app = NULL;
69 Py_RETURN_NONE;
7070 }
7171
7272 static PyMethodDef Bjoern_FunctionTable[] = {
73 {"run", run, METH_VARARGS, run_doc},
74 {"listen", listen, METH_VARARGS, listen_doc},
75 {NULL, NULL, 0, NULL}
73 {"run", run, METH_VARARGS, run_doc},
74 {"listen", listen, METH_VARARGS, listen_doc},
75 {NULL, NULL, 0, NULL}
7676 };
7777
7878 PyMODINIT_FUNC initbjoern()
7979 {
80 _initialize_wsgi_module();
81 _initialize_static_strings();
80 _initialize_wsgi_module();
81 _initialize_static_strings();
8282
83 PyObject* bjoern_module = Py_InitModule("bjoern", Bjoern_FunctionTable);
84 PyModule_AddObject(bjoern_module, "version", Py_BuildValue("(ii)", 1, 0));
83 PyObject* bjoern_module = Py_InitModule("bjoern", Bjoern_FunctionTable);
84 PyModule_AddObject(bjoern_module, "version", Py_BuildValue("(ii)", 1, 0));
8585 }
66
77 size_t unquote_url_inplace(char* url, size_t len)
88 {
9 for(char *p=url, *end=url+len; url != end; ++url, ++p) {
10 if(*url == '%') {
11 if(url >= end-2) {
12 /* Less than two characters left after the '%' */
13 return 0;
14 }
15 char a = UNHEX(url[1]);
16 char b = UNHEX(url[2]);
17 if(a == NOHEX || b == NOHEX) return 0;
18 *p = a*16 + b;
19 url += 2;
20 len -= 2;
21 } else {
22 *p = *url;
23 }
9 for(char *p=url, *end=url+len; url != end; ++url, ++p) {
10 if(*url == '%') {
11 if(url >= end-2) {
12 /* Less than two characters left after the '%' */
13 return 0;
14 }
15 char a = UNHEX(url[1]);
16 char b = UNHEX(url[2]);
17 if(a == NOHEX || b == NOHEX) return 0;
18 *p = a*16 + b;
19 url += 2;
20 len -= 2;
21 } else {
22 *p = *url;
2423 }
25 return len;
24 }
25 return len;
2626 }
2727
2828 void _initialize_static_strings()
2929 {
30 #define _(name) _##name = PyString_FromString(#name)
31 _(REMOTE_ADDR); _(PATH_INFO); _(QUERY_STRING);
32 _(REQUEST_METHOD); _(SERVER_PROTOCOL); _(GET);
33 _(HTTP_CONTENT_LENGTH); _(CONTENT_LENGTH); _(HTTP_CONTENT_TYPE); _(CONTENT_TYPE);
34 _HTTP_1_1 = PyString_FromString("HTTP/1.1");
35 _HTTP_1_0 = PyString_FromString("HTTP/1.0");
36 _wsgi_input = PyString_FromString("wsgi.input");
37 _empty_string = PyString_FromString("");
38 #undef _
30 #define _(name) _##name = PyString_FromString(#name)
31 _(REMOTE_ADDR); _(PATH_INFO); _(QUERY_STRING);
32 _(REQUEST_METHOD); _(SERVER_PROTOCOL); _(GET);
33 _(HTTP_CONTENT_LENGTH); _(CONTENT_LENGTH); _(HTTP_CONTENT_TYPE); _(CONTENT_TYPE);
34 _HTTP_1_1 = PyString_FromString("HTTP/1.1");
35 _HTTP_1_0 = PyString_FromString("HTTP/1.0");
36 _wsgi_input = PyString_FromString("wsgi.input");
37 _empty_string = PyString_FromString("");
38 #undef _
3939 }
1616 *_SERVER_PROTOCOL, *_HTTP_1_1, *_HTTP_1_0, *_wsgi_input, *_empty_string;
1717
1818 #ifdef DEBUG
19 #define DBG_REQ(request, ...) \
20 do { \
21 printf("[DEBUG Req %ld] ", request->id); \
22 DBG(__VA_ARGS__); \
23 } while(0)
24 #define DBG(...) \
25 do { \
26 printf(__VA_ARGS__); \
27 printf("\n"); \
28 } while(0)
19 #define DBG_REQ(request, ...) \
20 do { \
21 printf("[DEBUG Req %ld] ", request->id); \
22 DBG(__VA_ARGS__); \
23 } while(0)
24 #define DBG(...) \
25 do { \
26 printf(__VA_ARGS__); \
27 printf("\n"); \
28 } while(0)
2929 #else
30 #define DBG(...) do{}while(0)
31 #define DBG_REQ(...) DBG(__VA_ARGS__)
30 #define DBG(...) do{}while(0)
31 #define DBG_REQ(...) DBG(__VA_ARGS__)
3232 #endif
3333
3434 #define DBG_REFCOUNT(obj) \
35 DBG(#obj "->obj_refcnt: %d", obj->ob_refcnt)
35 DBG(#obj "->obj_refcnt: %d", obj->ob_refcnt)
3636
3737 #define DBG_REFCOUNT_REQ(request, obj) \
38 DBG_REQ(request, #obj "->ob_refcnt: %d", obj->ob_refcnt)
38 DBG_REQ(request, #obj "->ob_refcnt: %d", obj->ob_refcnt)
3939
4040 #ifdef WITHOUT_ASSERTS
41 #undef assert
42 #define assert(...) do{}while(0)
41 #undef assert
42 #define assert(...) do{}while(0)
4343 #endif
4444
4545 #endif
88
99 Request* Request_new(int client_fd, const char* client_addr)
1010 {
11 Request* request = malloc(sizeof(Request));
11 Request* request = malloc(sizeof(Request));
1212 #ifdef DEBUG
13 static unsigned long request_id = 0;
14 request->id = request_id++;
13 static unsigned long request_id = 0;
14 request->id = request_id++;
1515 #endif
16 request->client_fd = client_fd;
17 request->client_addr = PyString_FromString(client_addr);
18 http_parser_init((http_parser*)&request->parser, HTTP_REQUEST);
19 request->parser.parser.data = request;
20 Request_reset(request);
21 return request;
16 request->client_fd = client_fd;
17 request->client_addr = PyString_FromString(client_addr);
18 http_parser_init((http_parser*)&request->parser, HTTP_REQUEST);
19 request->parser.parser.data = request;
20 Request_reset(request);
21 return request;
2222 }
2323
2424 void Request_reset(Request* request)
2525 {
26 memset(&request->state, 0, sizeof(Request) - (size_t)&((Request*)NULL)->state);
27 request->state.response_length_unknown = true;
26 memset(&request->state, 0, sizeof(Request) - (size_t)&((Request*)NULL)->state);
27 request->state.response_length_unknown = true;
2828 }
2929
3030 void Request_free(Request* request)
3131 {
32 Request_clean(request);
33 free(request);
32 Request_clean(request);
33 free(request);
3434 }
3535
3636 void Request_clean(Request* request)
3737 {
38 Py_XDECREF(request->iterable);
39 Py_XDECREF(request->body);
40 if(request->headers)
41 assert(request->headers->ob_refcnt >= 1);
42 if(request->status)
43 assert(request->status->ob_refcnt >= 1);
44 Py_XDECREF(request->headers);
45 Py_XDECREF(request->status);
38 Py_XDECREF(request->iterable);
39 Py_XDECREF(request->body);
40 if(request->headers)
41 assert(request->headers->ob_refcnt >= 1);
42 if(request->status)
43 assert(request->status->ob_refcnt >= 1);
44 Py_XDECREF(request->headers);
45 Py_XDECREF(request->status);
4646 }
4747
4848 /* Parse stuff */
4949
50 void Request_parse(Request* request,
51 const char* data,
52 const size_t data_len) {
53 assert(data_len);
54 size_t nparsed = http_parser_execute((http_parser*)&request->parser,
55 &parser_settings, data, data_len);
56 if(nparsed != data_len)
57 request->state.error_code = HTTP_BAD_REQUEST;
58 }
59
60 static int a;
61 static void x() {}
50 void Request_parse(Request* request, const char* data, const size_t data_len)
51 {
52 assert(data_len);
53 size_t nparsed = http_parser_execute((http_parser*)&request->parser,
54 &parser_settings, data, data_len);
55 if(nparsed != data_len)
56 request->state.error_code = HTTP_BAD_REQUEST;
57 }
6258
6359 #define REQUEST ((Request*)parser->data)
6460 #define PARSER ((bj_parser*)parser)
65 #define _update_length(name) \
66 /* Update the len of a header field/value.
67 *
68 * Short explaination of the pointer arithmetics fun used here:
69 *
70 * [old header data ] ...stuff... [ new header data ]
71 * ^-------------- A -------------^--------B--------^
72 *
73 * A = XXX_start - PARSER->XXX_start
74 * B = XXX_len
75 * A + B = old header start to new header end
76 */ \
77 do {\
78 PARSER->name##_len = (name##_start - PARSER->name##_start) \
79 + name##_len; \
80 } while(0)
61 #define UPDATE_LENGTH(name) \
62 /* Update the len of a header field/value.
63 *
64 * Short explaination of the pointer arithmetics fun used here:
65 *
66 * [old header data ] ...stuff... [ new header data ]
67 * ^-------------- A -------------^--------B--------^
68 *
69 * A = XXX- PARSER->XXX_start
70 * B = len
71 * A + B = old header start to new header end
72 */ \
73 do { PARSER->name##_len = (name - PARSER->name##_start) + len; } while(0)
8174
8275 #define _set_header(k, v) PyDict_SetItem(REQUEST->headers, k, v);
8376 #define _set_header_free_value(k, v) \
84 do { \
85 PyObject* val = (v); \
86 _set_header(k, val); \
87 Py_DECREF(val); \
88 } while(0)
77 do { \
78 PyObject* val = (v); \
79 _set_header(k, val); \
80 Py_DECREF(val); \
81 } while(0)
8982 #define _set_header_free_both(k, v) \
90 do { \
91 PyObject* key = (k); \
92 PyObject* val = (v); \
93 _set_header(key, val); \
94 Py_DECREF(key); \
95 Py_DECREF(val); \
96 } while(0)
83 do { \
84 PyObject* key = (k); \
85 PyObject* val = (v); \
86 _set_header(key, val); \
87 Py_DECREF(key); \
88 Py_DECREF(val); \
89 } while(0)
9790
9891 static int on_message_begin(http_parser* parser)
9992 {
100 REQUEST->headers = PyDict_New();
101 PARSER->field_start = NULL;
102 PARSER->field_len = 0;
103 PARSER->value_start = NULL;
104 PARSER->value_len = 0;
93 REQUEST->headers = PyDict_New();
94 PARSER->field_start = NULL;
95 PARSER->field_len = 0;
96 PARSER->value_start = NULL;
97 PARSER->value_len = 0;
98 return 0;
99 }
100
101 static int on_path(http_parser* parser, char* path, size_t len)
102 {
103 if(!(len = unquote_url_inplace(path, len)))
104 return 1;
105 _set_header_free_value(_PATH_INFO, PyString_FromStringAndSize(path, len));
106 return 0;
107 }
108
109 static int on_query_string(http_parser* parser, const char* query, size_t len)
110 {
111 _set_header_free_value(_QUERY_STRING, PyString_FromStringAndSize(query, len));
112 return 0;
113 }
114
115 static int on_header_field(http_parser* parser, const char* field, size_t len)
116 {
117 if(PARSER->value_start) {
118 /* Store previous header and start a new one */
119 _set_header_free_both(
120 wsgi_http_header(REQUEST, PARSER->field_start, PARSER->field_len),
121 PyString_FromStringAndSize(PARSER->value_start, PARSER->value_len)
122 );
123 } else if(PARSER->field_start) {
124 UPDATE_LENGTH(field);
105125 return 0;
106 }
107
108 static int on_path(http_parser* parser,
109 char* path_start,
110 size_t path_len) {
111 if(!(path_len = unquote_url_inplace(path_start, path_len)))
112 return 1;
113 _set_header_free_value(
114 _PATH_INFO,
115 PyString_FromStringAndSize(path_start, path_len)
116 );
117 return 0;
118 }
119
120 static int on_query_string(http_parser* parser,
121 const char* query_start,
122 const size_t query_len) {
123 _set_header_free_value(
124 _QUERY_STRING,
125 PyString_FromStringAndSize(query_start, query_len)
126 );
127 return 0;
128 }
129
130 static int on_header_field(http_parser* parser,
131 const char* field_start,
132 const size_t field_len) {
133 if(PARSER->value_start) {
134 /* Store previous header and start a new one */
135 _set_header_free_both(
136 wsgi_http_header(REQUEST, PARSER->field_start, PARSER->field_len),
137 PyString_FromStringAndSize(PARSER->value_start, PARSER->value_len)
138 );
139
140 } else if(PARSER->field_start) {
141 _update_length(field);
142 return 0;
126 }
127 PARSER->field_start = field;
128 PARSER->field_len = len;
129 PARSER->value_start = NULL;
130 PARSER->value_len = 0;
131 return 0;
132 }
133
134 static int
135 on_header_value(http_parser* parser, const char* value, size_t len)
136 {
137 if(PARSER->value_start) {
138 UPDATE_LENGTH(value);
139 } else {
140 /* Start a new value */
141 PARSER->value_start = value;
142 PARSER->value_len = len;
143 }
144 return 0;
145 }
146
147 static int
148 on_headers_complete(http_parser* parser)
149 {
150 if(PARSER->field_start) {
151 _set_header_free_both(
152 wsgi_http_header(REQUEST, PARSER->field_start, PARSER->field_len),
153 PyString_FromStringAndSize(PARSER->value_start, PARSER->value_len)
154 );
155 }
156 return 0;
157 }
158
159 static int
160 on_body(http_parser* parser, const char* body, const size_t len)
161 {
162 if(!REQUEST->body) {
163 if(!parser->content_length) {
164 REQUEST->state.error_code = HTTP_LENGTH_REQUIRED;
165 return 1;
143166 }
144
145 PARSER->field_start = field_start;
146 PARSER->field_len = field_len;
147 PARSER->value_start = NULL;
148 PARSER->value_len = 0;
149
150 return 0;
151 }
152
153 static int on_header_value(http_parser* parser,
154 const char* value_start,
155 const size_t value_len) {
156 if(PARSER->value_start) {
157 _update_length(value);
158 } else {
159 /* Start a new value */
160 PARSER->value_start = value_start;
161 PARSER->value_len = value_len;
162 }
163 return 0;
164 }
165
166 static int on_headers_complete(http_parser* parser)
167 {
168 if(PARSER->field_start) {
169 _set_header_free_both(
170 wsgi_http_header(REQUEST, PARSER->field_start, PARSER->field_len),
171 PyString_FromStringAndSize(PARSER->value_start, PARSER->value_len)
172 );
173 }
174 return 0;
175 }
176
177 static int on_body(http_parser* parser,
178 const char* body_start,
179 const size_t body_len) {
180 if(!REQUEST->body) {
181 if(!parser->content_length) {
182 REQUEST->state.error_code = HTTP_LENGTH_REQUIRED;
183 return 1;
184 }
185 REQUEST->body = PycStringIO->NewOutput(parser->content_length);
186 }
187
188 if(PycStringIO->cwrite(REQUEST->body, body_start, body_len) < 0) {
189 REQUEST->state.error_code = HTTP_SERVER_ERROR;
190 return 1;
191 }
192
193 return 0;
194 }
195
196 static int on_message_complete(http_parser* parser)
197 {
198 /* HTTP_CONTENT_{LENGTH,TYPE} -> CONTENT_{LENGTH,TYPE} */
199 PyDict_ReplaceKey(REQUEST->headers, _HTTP_CONTENT_LENGTH, _CONTENT_LENGTH);
200 PyDict_ReplaceKey(REQUEST->headers, _HTTP_CONTENT_TYPE, _CONTENT_TYPE);
201
202 /* SERVER_PROTOCOL (REQUEST_PROTOCOL) */
203 _set_header(_SERVER_PROTOCOL, parser->http_minor == 1 ? _HTTP_1_1 : _HTTP_1_0);
204
205 /* REQUEST_METHOD */
206 if(parser->method == HTTP_GET) {
207 /* I love useless micro-optimizations. */
208 _set_header(_REQUEST_METHOD, _GET);
209 } else {
167 REQUEST->body = PycStringIO->NewOutput(parser->content_length);
168 }
169 if(PycStringIO->cwrite(REQUEST->body, body, len) < 0) {
170 REQUEST->state.error_code = HTTP_SERVER_ERROR;
171 return 1;
172 }
173 return 0;
174 }
175
176 static int
177 on_message_complete(http_parser* parser)
178 {
179 /* HTTP_CONTENT_{LENGTH,TYPE} -> CONTENT_{LENGTH,TYPE} */
180 PyDict_ReplaceKey(REQUEST->headers, _HTTP_CONTENT_LENGTH, _CONTENT_LENGTH);
181 PyDict_ReplaceKey(REQUEST->headers, _HTTP_CONTENT_TYPE, _CONTENT_TYPE);
182
183 /* SERVER_PROTOCOL (REQUEST_PROTOCOL) */
184 _set_header(_SERVER_PROTOCOL, parser->http_minor == 1 ? _HTTP_1_1 : _HTTP_1_0);
185
186 /* REQUEST_METHOD */
187 if(parser->method == HTTP_GET) {
188 /* I love useless micro-optimizations. */
189 _set_header(_REQUEST_METHOD, _GET);
190 } else {
210191 _set_header_free_value(_REQUEST_METHOD,
211 PyString_FromString(http_method_str(parser->method)));
212 }
213
214 /* REMOTE_ADDR */
215 _set_header(_REMOTE_ADDR, REQUEST->client_addr);
216
217 /* wsgi.input */
218 _set_header_free_value(_wsgi_input,
219 PycStringIO->NewInput(
220 REQUEST->body ? PycStringIO->cgetvalue(REQUEST->body)
221 : _empty_string));
222
223 PyDict_Update(REQUEST->headers, wsgi_base_dict);
224
225 REQUEST->state.parse_finished = true;
226 return 0;
192 PyString_FromString(http_method_str(parser->method)));
193 }
194
195 /* REMOTE_ADDR */
196 _set_header(_REMOTE_ADDR, REQUEST->client_addr);
197
198 /* wsgi.input */
199 _set_header_free_value(
200 _wsgi_input,
201 PycStringIO->NewInput(REQUEST->body ? PycStringIO->cgetvalue(REQUEST->body)
202 : _empty_string)
203 );
204
205 PyDict_Update(REQUEST->headers, wsgi_base_dict);
206
207 REQUEST->state.parse_finished = true;
208 return 0;
227209 }
228210
229211
230212 static PyObject*
231213 wsgi_http_header(Request* request, const char* data, size_t len)
232214 {
233 PyObject* obj = PyString_FromStringAndSize(/* empty string */ NULL,
234 len+strlen("HTTP_"));
235 char* dest = PyString_AS_STRING(obj);
236
237 *dest++ = 'H';
238 *dest++ = 'T';
239 *dest++ = 'T';
240 *dest++ = 'P';
241 *dest++ = '_';
242
243 while(len--) {
244 char c = *data++;
245 if(c == '-')
246 *dest++ = '_';
247 else if(c >= 'a' && c <= 'z')
248 *dest++ = c - ('a'-'A');
249 else
250 *dest++ = c;
251 }
252 return obj;
215 PyObject* obj = PyString_FromStringAndSize(NULL, len+strlen("HTTP_"));
216 char* dest = PyString_AS_STRING(obj);
217
218 *dest++ = 'H';
219 *dest++ = 'T';
220 *dest++ = 'T';
221 *dest++ = 'P';
222 *dest++ = '_';
223
224 while(len--) {
225 char c = *data++;
226 if(c == '-')
227 *dest++ = '_';
228 else if(c >= 'a' && c <= 'z')
229 *dest++ = c - ('a'-'A');
230 else
231 *dest++ = c;
232 }
233
234 return obj;
253235 }
254236
255237 static inline void
256238 PyDict_ReplaceKey(PyObject* dict, PyObject* old_key, PyObject* new_key)
257239 {
258 PyObject* value = PyDict_GetItem(dict, old_key);
259 if(value) {
260 Py_INCREF(value);
261 PyDict_DelItem(dict, old_key);
262 PyDict_SetItem(dict, new_key, value);
263 Py_DECREF(value);
264 }
240 PyObject* value = PyDict_GetItem(dict, old_key);
241 if(value) {
242 Py_INCREF(value);
243 PyDict_DelItem(dict, old_key);
244 PyDict_SetItem(dict, new_key, value);
245 Py_DECREF(value);
246 }
265247 }
266248
267249
268250 static http_parser_settings
269251 parser_settings = {
270 .on_message_begin = on_message_begin,
271 .on_path = on_path,
272 .on_query_string = on_query_string,
273 .on_url = NULL,
274 .on_fragment = NULL,
275 .on_header_field = on_header_field,
276 .on_header_value = on_header_value,
277 .on_headers_complete = on_headers_complete,
278 .on_body = on_body,
279 .on_message_complete = on_message_complete
252 on_message_begin, on_path, on_query_string, NULL, NULL, on_header_field,
253 on_header_value, on_headers_complete, on_body, on_message_complete
280254 };
281255
282256 void _initialize_request_module(const char* server_host, const int server_port)
283257 {
284 if(wsgi_base_dict == NULL) {
285 PycString_IMPORT;
286 wsgi_base_dict = PyDict_New();
287
288 /* dct['wsgi.version'] = (1, 0) */
289 PyDict_SetItemString(
290 wsgi_base_dict,
291 "wsgi.version",
292 PyTuple_Pack(2, PyInt_FromLong(1), PyInt_FromLong(0))
293 );
294
295 /* dct['wsgi.url_scheme'] = 'http'
296 * (This can be hard-coded as there is no TLS support in bjoern.) */
297 PyDict_SetItemString(
298 wsgi_base_dict,
299 "wsgi.url_scheme",
300 PyString_FromString("http")
301 );
302
303 /* dct['wsgi.errors'] = sys.stderr */
304 PyDict_SetItemString(
305 wsgi_base_dict,
306 "wsgi.errors",
307 PySys_GetObject("stderr")
308 );
309
310 /* dct['wsgi.multithread'] = True
311 * If I correctly interpret the WSGI specs, this means
312 * "Can the server be ran in a thread?" */
313 PyDict_SetItemString(
314 wsgi_base_dict,
315 "wsgi.multithread",
316 Py_True
317 );
318
319 /* dct['wsgi.multiprocess'] = True
320 * ... and this one "Can the server process be forked?" */
321 PyDict_SetItemString(
322 wsgi_base_dict,
323 "wsgi.multiprocess",
324 Py_True
325 );
326
327 /* dct['wsgi.run_once'] = False (bjoern is no CGI gateway) */
328 PyDict_SetItemString(
329 wsgi_base_dict,
330 "wsgi.run_once",
331 Py_False
332 );
333 }
334
335 PyDict_SetItemString(
336 wsgi_base_dict,
337 "SERVER_NAME",
338 PyString_FromString(server_host)
339 );
340
341 PyDict_SetItemString(
342 wsgi_base_dict,
343 "SERVER_PORT",
344 PyString_FromFormat("%d", server_port)
345 );
346 }
258 if(wsgi_base_dict == NULL) {
259 PycString_IMPORT;
260 wsgi_base_dict = PyDict_New();
261
262 /* dct['wsgi.version'] = (1, 0) */
263 PyDict_SetItemString(
264 wsgi_base_dict,
265 "wsgi.version",
266 PyTuple_Pack(2, PyInt_FromLong(1), PyInt_FromLong(0))
267 );
268
269 /* dct['wsgi.url_scheme'] = 'http'
270 * (This can be hard-coded as there is no TLS support in bjoern.) */
271 PyDict_SetItemString(
272 wsgi_base_dict,
273 "wsgi.url_scheme",
274 PyString_FromString("http")
275 );
276
277 /* dct['wsgi.errors'] = sys.stderr */
278 PyDict_SetItemString(
279 wsgi_base_dict,
280 "wsgi.errors",
281 PySys_GetObject("stderr")
282 );
283
284 /* dct['wsgi.multithread'] = True
285 * If I correctly interpret the WSGI specs, this means
286 * "Can the server be ran in a thread?" */
287 PyDict_SetItemString(
288 wsgi_base_dict,
289 "wsgi.multithread",
290 Py_True
291 );
292
293 /* dct['wsgi.multiprocess'] = True
294 * ... and this one "Can the server process be forked?" */
295 PyDict_SetItemString(
296 wsgi_base_dict,
297 "wsgi.multiprocess",
298 Py_True
299 );
300
301 /* dct['wsgi.run_once'] = False (bjoern is no CGI gateway) */
302 PyDict_SetItemString(
303 wsgi_base_dict,
304 "wsgi.run_once",
305 Py_False
306 );
307 }
308
309 PyDict_SetItemString(
310 wsgi_base_dict,
311 "SERVER_NAME",
312 PyString_FromString(server_host)
313 );
314
315 PyDict_SetItemString(
316 wsgi_base_dict,
317 "SERVER_PORT",
318 PyString_FromFormat("%d", server_port)
319 );
320 }
77 void _initialize_request_module(const char* host, const int port);
88
99 typedef struct {
10 unsigned error_code : 2;
11 unsigned parse_finished : 1;
12 unsigned start_response_called : 1;
13 unsigned wsgi_call_done : 1;
14 unsigned keep_alive : 1;
15 unsigned response_length_unknown : 1;
16 unsigned chunked_response : 1;
10 unsigned error_code : 2;
11 unsigned parse_finished : 1;
12 unsigned start_response_called : 1;
13 unsigned wsgi_call_done : 1;
14 unsigned keep_alive : 1;
15 unsigned response_length_unknown : 1;
16 unsigned chunked_response : 1;
1717 } request_state;
1818
1919 typedef struct {
20 http_parser parser;
21 const char* field_start;
22 size_t field_len;
23 const char* value_start;
24 size_t value_len;
20 http_parser parser;
21 const char* field_start;
22 size_t field_len;
23 const char* value_start;
24 size_t value_len;
2525 } bj_parser;
2626
2727 typedef struct {
2828 #ifdef DEBUG
29 unsigned long id;
29 unsigned long id;
3030 #endif
31 bj_parser parser;
32 ev_io ev_watcher;
31 bj_parser parser;
32 ev_io ev_watcher;
3333
34 int client_fd;
35 PyObject* client_addr;
34 int client_fd;
35 PyObject* client_addr;
3636
37 request_state state;
37 request_state state;
3838
39 PyObject* headers;
40 PyObject* body;
41 PyObject* current_chunk;
42 Py_ssize_t current_chunk_p;
43 PyObject* iterable;
44 PyObject* status;
39 PyObject* headers;
40 PyObject* body;
41 PyObject* current_chunk;
42 Py_ssize_t current_chunk_p;
43 PyObject* iterable;
44 PyObject* status;
4545 } Request;
4646
4747 #define REQUEST_FROM_WATCHER(watcher) \
48 (Request*)((size_t)watcher - (size_t)(&(((Request*)NULL)->ev_watcher)));
48 (Request*)((size_t)watcher - (size_t)(&(((Request*)NULL)->ev_watcher)));
4949
5050 Request* Request_new(int client_fd, const char* client_addr);
5151 void Request_parse(Request*, const char*, const size_t);
1717
1818 static int sockfd;
1919 static const char* http_error_messages[4] = {
20 NULL, /* Error codes start at 1 because 0 means "no error" */
21 "HTTP/1.1 400 Bad Request\r\n\r\n",
22 "HTTP/1.1 406 Length Required\r\n\r\n",
23 "HTTP/1.1 500 Internal Server Error\r\n\r\n"
20 NULL, /* Error codes start at 1 because 0 means "no error" */
21 "HTTP/1.1 400 Bad Request\r\n\r\n",
22 "HTTP/1.1 406 Length Required\r\n\r\n",
23 "HTTP/1.1 500 Internal Server Error\r\n\r\n"
2424 };
2525
2626 typedef void ev_io_callback(struct ev_loop*, ev_io*, const int);
3535
3636 void server_run(const char* hostaddr, const int port)
3737 {
38 struct ev_loop* mainloop = ev_default_loop(0);
39
40 ev_io accept_watcher;
41 ev_io_init(&accept_watcher, ev_io_on_request, sockfd, EV_READ);
42 ev_io_start(mainloop, &accept_watcher);
38 struct ev_loop* mainloop = ev_default_loop(0);
39
40 ev_io accept_watcher;
41 ev_io_init(&accept_watcher, ev_io_on_request, sockfd, EV_READ);
42 ev_io_start(mainloop, &accept_watcher);
4343
4444 #if WANT_SIGINT_HANDLING
45 ev_signal signal_watcher;
46 ev_signal_init(&signal_watcher, ev_signal_on_sigint, SIGINT);
47 ev_signal_start(mainloop, &signal_watcher);
48 #endif
49
50 /* This is the program main loop */
51 Py_BEGIN_ALLOW_THREADS
52 ev_loop(mainloop, 0);
53 Py_END_ALLOW_THREADS
45 ev_signal signal_watcher;
46 ev_signal_init(&signal_watcher, ev_signal_on_sigint, SIGINT);
47 ev_signal_start(mainloop, &signal_watcher);
48 #endif
49
50 /* This is the program main loop */
51 Py_BEGIN_ALLOW_THREADS
52 ev_loop(mainloop, 0);
53 Py_END_ALLOW_THREADS
5454 }
5555
5656 #if WANT_SIGINT_HANDLING
5757 static void
5858 ev_signal_on_sigint(struct ev_loop* mainloop, ev_signal* watcher, const int events)
5959 {
60 /* Clean up and shut down this thread.
61 * (Shuts down the Python interpreter if this is the main thread) */
62 ev_unloop(mainloop, EVUNLOOP_ALL);
63 PyErr_SetInterrupt();
60 /* Clean up and shut down this thread.
61 * (Shuts down the Python interpreter if this is the main thread) */
62 ev_unloop(mainloop, EVUNLOOP_ALL);
63 PyErr_SetInterrupt();
6464 }
6565 #endif
6666
6767 bool server_init(const char* hostaddr, const int port)
6868 {
69 if((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
70 return false;
71
72 struct sockaddr_in sockaddr;
73 sockaddr.sin_family = PF_INET;
74 inet_pton(AF_INET, hostaddr, &sockaddr.sin_addr);
75 sockaddr.sin_port = htons(port);
76
77 /* Set SO_REUSEADDR t make the IP address available for reuse */
78 int optval = true;
79 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
80
81 if(bind(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0)
82 return false;
83
84 if(listen(sockfd, LISTEN_BACKLOG) < 0)
85 return false;
86
87 DBG("Listening on %s:%d...", hostaddr, port);
88 return true;
69 if((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
70 return false;
71
72 struct sockaddr_in sockaddr;
73 sockaddr.sin_family = PF_INET;
74 inet_pton(AF_INET, hostaddr, &sockaddr.sin_addr);
75 sockaddr.sin_port = htons(port);
76
77 /* Set SO_REUSEADDR t make the IP address available for reuse */
78 int optval = true;
79 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
80
81 if(bind(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0)
82 return false;
83
84 if(listen(sockfd, LISTEN_BACKLOG) < 0)
85 return false;
86
87 DBG("Listening on %s:%d...", hostaddr, port);
88 return true;
8989 }
9090
9191 static void
9292 ev_io_on_request(struct ev_loop* mainloop, ev_io* watcher, const int events)
9393 {
94 int client_fd;
95 struct sockaddr_in sockaddr;
96 socklen_t addrlen;
97
98 addrlen = sizeof(struct sockaddr_in);
99 client_fd = accept(watcher->fd, (struct sockaddr*)&sockaddr, &addrlen);
100 if(client_fd < 0) {
101 DBG("Could not accept() client: errno %d", errno);
102 return;
103 }
104
105 int flags = fcntl(client_fd, F_GETFL, 0);
106 if(fcntl(client_fd, F_SETFL, (flags < 0 ? 0 : flags) | O_NONBLOCK) == -1) {
107 DBG("Could not set_nonblocking() client %d: errno %d", client_fd, errno);
108 return;
109 }
110
111 GIL_LOCK(0);
112 Request* request = Request_new(client_fd, inet_ntoa(sockaddr.sin_addr));
113 GIL_UNLOCK(0);
114
115 DBG_REQ(request, "Accepted client %s:%d on fd %d",
116 inet_ntoa(sockaddr.sin_addr), ntohs(sockaddr.sin_port), client_fd);
117
118 ev_io_init(&request->ev_watcher, &ev_io_on_read,
119 client_fd, EV_READ);
120 ev_io_start(mainloop, &request->ev_watcher);
94 int client_fd;
95 struct sockaddr_in sockaddr;
96 socklen_t addrlen;
97
98 addrlen = sizeof(struct sockaddr_in);
99 client_fd = accept(watcher->fd, (struct sockaddr*)&sockaddr, &addrlen);
100 if(client_fd < 0) {
101 DBG("Could not accept() client: errno %d", errno);
102 return;
103 }
104
105 int flags = fcntl(client_fd, F_GETFL, 0);
106 if(fcntl(client_fd, F_SETFL, (flags < 0 ? 0 : flags) | O_NONBLOCK) == -1) {
107 DBG("Could not set_nonblocking() client %d: errno %d", client_fd, errno);
108 return;
109 }
110
111 GIL_LOCK(0);
112 Request* request = Request_new(client_fd, inet_ntoa(sockaddr.sin_addr));
113 GIL_UNLOCK(0);
114
115 DBG_REQ(request, "Accepted client %s:%d on fd %d",
116 inet_ntoa(sockaddr.sin_addr), ntohs(sockaddr.sin_port), client_fd);
117
118 ev_io_init(&request->ev_watcher, &ev_io_on_read,
119 client_fd, EV_READ);
120 ev_io_start(mainloop, &request->ev_watcher);
121121 }
122122
123123 static void
124124 ev_io_on_read(struct ev_loop* mainloop, ev_io* watcher, const int events)
125125 {
126 static char read_buf[READ_BUFFER_SIZE];
127 Request* request = REQUEST_FROM_WATCHER(watcher);
128
129 ssize_t read_bytes = read(
130 request->client_fd,
131 read_buf,
132 READ_BUFFER_SIZE
133 );
134
135 GIL_LOCK(0);
136
137 if(read_bytes <= 0) {
138 if(errno != EAGAIN && errno != EWOULDBLOCK) {
139 if(read_bytes == 0)
140 DBG_REQ(request, "Client disconnected");
141 else
142 DBG_REQ(request, "Hit errno %d while read()ing", errno);
143 close(request->client_fd);
144 Request_free(request);
145 ev_io_stop(mainloop, &request->ev_watcher);
146 }
147 goto out;
148 }
149
150 Request_parse(request, read_buf, read_bytes);
151
152 if(request->state.error_code) {
153 DBG_REQ(request, "Parse error");
154 request->current_chunk = PyString_FromString(
155 http_error_messages[request->state.error_code]);
156 assert(request->iterable == NULL);
157 }
158 else if(request->state.parse_finished) {
159 if(!wsgi_call_application(request)) {
160 assert(PyErr_Occurred());
161 PyErr_Print();
162 assert(!request->state.chunked_response);
163 Py_XCLEAR(request->iterable);
164 request->current_chunk = PyString_FromString(
165 http_error_messages[HTTP_SERVER_ERROR]);
166 }
126 static char read_buf[READ_BUFFER_SIZE];
127
128 Request* request = REQUEST_FROM_WATCHER(watcher);
129
130 ssize_t read_bytes = read(
131 request->client_fd,
132 read_buf,
133 READ_BUFFER_SIZE
134 );
135
136 GIL_LOCK(0);
137
138 if(read_bytes <= 0) {
139 if(errno != EAGAIN && errno != EWOULDBLOCK) {
140 if(read_bytes == 0)
141 DBG_REQ(request, "Client disconnected");
142 else
143 DBG_REQ(request, "Hit errno %d while read()ing", errno);
144 close(request->client_fd);
145 Request_free(request);
146 ev_io_stop(mainloop, &request->ev_watcher);
147 }
148 goto out;
149 }
150
151 Request_parse(request, read_buf, read_bytes);
152
153 if(request->state.error_code) {
154 DBG_REQ(request, "Parse error");
155 request->current_chunk = PyString_FromString(
156 http_error_messages[request->state.error_code]);
157 assert(request->iterable == NULL);
158 }
159 else if(request->state.parse_finished) {
160 if(!wsgi_call_application(request)) {
161 assert(PyErr_Occurred());
162 PyErr_Print();
163 assert(!request->state.chunked_response);
164 Py_XCLEAR(request->iterable);
165 request->current_chunk = PyString_FromString(
166 http_error_messages[HTTP_SERVER_ERROR]);
167 }
168 } else {
169 /* Wait for more data */
170 goto out;
171 }
172
173 ev_io_stop(mainloop, &request->ev_watcher);
174 ev_io_init(&request->ev_watcher, &ev_io_on_write,
175 request->client_fd, EV_WRITE);
176 ev_io_start(mainloop, &request->ev_watcher);
177
178 out:
179 GIL_UNLOCK(0);
180 return;
181 }
182
183 static void
184 ev_io_on_write(struct ev_loop* mainloop, ev_io* watcher, const int events)
185 {
186 Request* request = REQUEST_FROM_WATCHER(watcher);
187
188 GIL_LOCK(0);
189 assert(request->current_chunk);
190
191 if(send_chunk(request))
192 goto out;
193
194 if(request->iterable) {
195 PyObject* next_chunk;
196 next_chunk = wsgi_iterable_get_next_chunk(request);
197 if(next_chunk) {
198 if(request->state.chunked_response) {
199 request->current_chunk = wrap_http_chunk_cruft_around(next_chunk);
200 Py_DECREF(next_chunk);
201 } else {
202 request->current_chunk = next_chunk;
203 }
204 assert(request->current_chunk_p == 0);
205 goto out;
167206 } else {
168 /* Wait for more data */
169 goto out;
170 }
171
172 ev_io_stop(mainloop, &request->ev_watcher);
173 ev_io_init(&request->ev_watcher, &ev_io_on_write,
174 request->client_fd, EV_WRITE);
175 ev_io_start(mainloop, &request->ev_watcher);
176
177 out:
178 GIL_UNLOCK(0);
179 return;
180 }
181
182 static void
183 ev_io_on_write(struct ev_loop* mainloop, ev_io* watcher, const int events)
184 {
185 GIL_LOCK(0);
186
187 Request* request = REQUEST_FROM_WATCHER(watcher);
188 assert(request->current_chunk);
189
190 if(send_chunk(request))
191 goto out;
192
193 if(request->iterable) {
194 PyObject* next_chunk;
195 next_chunk = wsgi_iterable_get_next_chunk(request);
196 if(next_chunk) {
197 if(request->state.chunked_response) {
198 request->current_chunk = wrap_http_chunk_cruft_around(next_chunk);
199 Py_DECREF(next_chunk);
200 } else {
201 request->current_chunk = next_chunk;
202 }
203 assert(request->current_chunk_p == 0);
204 goto out;
205 } else {
206 if(PyErr_Occurred()) {
207 PyErr_Print();
208 /* We can't do anything graceful here because at least one
209 * chunk is already sent... just close the connection */
210 DBG_REQ(request, "Exception in iterator, can not recover");
211 ev_io_stop(mainloop, &request->ev_watcher);
212 close(request->client_fd);
213 Request_free(request);
214 goto out;
215 }
216 Py_DECREF(request->iterable);
217 request->iterable = NULL;
218 }
219 }
220
221 if(request->state.chunked_response) {
222 /* We have to send a terminating empty chunk + \r\n */
223 request->current_chunk = PyString_FromString("0\r\n\r\n");
224 assert(request->current_chunk_p == 0);
225 request->state.chunked_response = false;
226 goto out;
227 }
228
229 ev_io_stop(mainloop, &request->ev_watcher);
230 if(request->state.keep_alive) {
231 DBG_REQ(request, "done, keep-alive");
232 Request_clean(request);
233 Request_reset(request);
234 ev_io_init(&request->ev_watcher, &ev_io_on_read,
235 request->client_fd, EV_READ);
236 ev_io_start(mainloop, &request->ev_watcher);
237 } else {
238 DBG_REQ(request, "done, close");
207 if(PyErr_Occurred()) {
208 PyErr_Print();
209 /* We can't do anything graceful here because at least one
210 * chunk is already sent... just close the connection */
211 DBG_REQ(request, "Exception in iterator, can not recover");
212 ev_io_stop(mainloop, &request->ev_watcher);
239213 close(request->client_fd);
240214 Request_free(request);
241 }
215 goto out;
216 }
217 Py_DECREF(request->iterable);
218 request->iterable = NULL;
219 }
220 }
221
222 if(request->state.chunked_response) {
223 /* We have to send a terminating empty chunk + \r\n */
224 request->current_chunk = PyString_FromString("0\r\n\r\n");
225 assert(request->current_chunk_p == 0);
226 request->state.chunked_response = false;
227 goto out;
228 }
229
230 ev_io_stop(mainloop, &request->ev_watcher);
231 if(request->state.keep_alive) {
232 DBG_REQ(request, "done, keep-alive");
233 Request_clean(request);
234 Request_reset(request);
235 ev_io_init(&request->ev_watcher, &ev_io_on_read,
236 request->client_fd, EV_READ);
237 ev_io_start(mainloop, &request->ev_watcher);
238 } else {
239 DBG_REQ(request, "done, close");
240 close(request->client_fd);
241 Request_free(request);
242 }
242243
243244 out:
244 GIL_UNLOCK(0);
245 GIL_UNLOCK(0);
245246 }
246247
247248 static bool
248249 send_chunk(Request* request)
249250 {
250 ssize_t chunk_length;
251 ssize_t sent_bytes;
252
253 assert(request->current_chunk != NULL);
254 assert(!(request->current_chunk_p == PyString_GET_SIZE(request->current_chunk)
255 && PyString_GET_SIZE(request->current_chunk) != 0));
256
257 sent_bytes = write(
258 request->client_fd,
259 PyString_AS_STRING(request->current_chunk) + request->current_chunk_p,
260 PyString_GET_SIZE(request->current_chunk) - request->current_chunk_p
261 );
262
263 if(sent_bytes == -1) {
264 error:
265 if(errno == EAGAIN || errno == EWOULDBLOCK) {
266 /* Try again later */
267 return true;
268 } else {
269 /* Serious transmission failure. Hang up. */
270 fprintf(stderr, "Client %d hit errno %d\n", request->client_fd, errno);
271 Py_DECREF(request->current_chunk);
272 Py_XCLEAR(request->iterable);
273 request->state.keep_alive = false;
274 return false;
275 }
276 }
277
278 request->current_chunk_p += sent_bytes;
279 if(request->current_chunk_p == PyString_GET_SIZE(request->current_chunk)) {
280 Py_DECREF(request->current_chunk);
281 request->current_chunk = NULL;
282 request->current_chunk_p = 0;
283 return false;
284 }
285 return true;
286 }
251 ssize_t chunk_length;
252 ssize_t sent_bytes;
253
254 assert(request->current_chunk != NULL);
255 assert(!(request->current_chunk_p == PyString_GET_SIZE(request->current_chunk)
256 && PyString_GET_SIZE(request->current_chunk) != 0));
257
258 sent_bytes = write(
259 request->client_fd,
260 PyString_AS_STRING(request->current_chunk) + request->current_chunk_p,
261 PyString_GET_SIZE(request->current_chunk) - request->current_chunk_p
262 );
263
264 if(sent_bytes == -1) {
265 error:
266 if(errno == EAGAIN || errno == EWOULDBLOCK) {
267 /* Try again later */
268 return true;
269 } else {
270 /* Serious transmission failure. Hang up. */
271 fprintf(stderr, "Client %d hit errno %d\n", request->client_fd, errno);
272 Py_DECREF(request->current_chunk);
273 Py_XCLEAR(request->iterable);
274 request->state.keep_alive = false;
275 return false;
276 }
277 }
278
279 request->current_chunk_p += sent_bytes;
280 if(request->current_chunk_p == PyString_GET_SIZE(request->current_chunk)) {
281 Py_DECREF(request->current_chunk);
282 request->current_chunk = NULL;
283 request->current_chunk_p = 0;
284 return false;
285 }
286 return true;
287 }
22 #include "wsgi.h"
33
44 #define TYPE_ERROR_INNER(what, expected, ...) \
5 PyErr_Format(PyExc_TypeError, what " must be " expected " " __VA_ARGS__)
5 PyErr_Format(PyExc_TypeError, what " must be " expected " " __VA_ARGS__)
66 #define TYPE_ERROR(what, expected, got) \
7 TYPE_ERROR_INNER(what, expected, "(got '%.200s' object instead)", Py_TYPE(got)->tp_name)
7 TYPE_ERROR_INNER(what, expected, "(got '%.200s' object instead)", Py_TYPE(got)->tp_name)
88
99 static PyObject* (start_response)(PyObject* self, PyObject* args, PyObject *kwargs);
1010 static Py_ssize_t wsgi_getheaders(Request*, PyObject* buf);
1212 static inline bool should_keep_alive(Request*);
1313
1414 typedef struct {
15 PyObject_HEAD
16 Request* request;
15 PyObject_HEAD
16 Request* request;
1717 } StartResponse;
1818
1919 bool
2020 wsgi_call_application(Request* request)
2121 {
22 assert(StartResponse_Type.tp_flags & Py_TPFLAGS_READY);
23 StartResponse* start_response = PyObject_NEW(StartResponse, &StartResponse_Type);
24 start_response->request = request;
25
26 /* From now on, `headers` stores the _response_ headers
27 * (passed by the WSGI app) rather than the _request_ headers */
28 PyObject* request_headers = request->headers;
29 request->headers = NULL;
30
31 /* application(environ, start_response) call */
32 PyObject* retval = PyObject_CallFunctionObjArgs(
33 wsgi_app,
34 request_headers,
35 start_response,
36 NULL /* sentinel */
22 assert(StartResponse_Type.tp_flags & Py_TPFLAGS_READY);
23 StartResponse* start_response = PyObject_NEW(StartResponse, &StartResponse_Type);
24 start_response->request = request;
25
26 /* From now on, `headers` stores the _response_ headers
27 * (passed by the WSGI app) rather than the _request_ headers */
28 PyObject* request_headers = request->headers;
29 request->headers = NULL;
30
31 /* application(environ, start_response) call */
32 PyObject* retval = PyObject_CallFunctionObjArgs(
33 wsgi_app,
34 request_headers,
35 start_response,
36 NULL /* sentinel */
37 );
38
39 Py_DECREF(request_headers);
40 Py_DECREF(start_response);
41
42 if(retval == NULL)
43 return false;
44
45 /* The following code is somewhat magic, so worth an explanation.
46 *
47 * If the application we called was a generator, we have to call .next() on
48 * it before we do anything else because that may execute code that
49 * invokes `start_response` (which might not have been invoked yet).
50 * Think of the following scenario:
51 *
52 * def app(environ, start_response):
53 * start_response('200 Ok', ...)
54 * yield 'Hello World'
55 *
56 * That would make `app` return an iterator (more precisely, a generator).
57 * Unfortunately, `start_response` wouldn't be called until the first item
58 * of that iterator is requested; `start_response` however has to be called
59 * _before_ the wsgi body is sent, because it passes the HTTP headers.
60 *
61 * If the application returned a list this would not be required of course,
62 * but special-handling is painful - especially in C - so here's one generic
63 * way to solve the problem:
64 *
65 * Look into the returned iterator in any case. This allows us to do other
66 * optimizations, for example if the returned value is a list with exactly
67 * one string in it, we can pick the string and throw away the list so bjoern
68 * does not have to come back again and look into the iterator a second time.
69 */
70 PyObject* first_chunk;
71
72 if(PyList_Check(retval) && PyList_GET_SIZE(retval) == 1 &&
73 PyString_Check(PyList_GET_ITEM(retval, 0)))
74 {
75 /* Optimize the most common case, a single string in a list: */
76 PyObject* tmp = PyList_GET_ITEM(retval, 0);
77 Py_INCREF(tmp);
78 Py_DECREF(retval);
79 retval = tmp;
80 goto string; /* eeevil */
81 } else if(PyString_Check(retval)) {
82 /* According to PEP 333 strings should be handled like any other iterable,
83 * i.e. sending the response item for item. "item for item" means
84 * "char for char" if you have a string. -- I'm not that stupid. */
85 string:
86 if(PyString_GET_SIZE(retval)) {
87 first_chunk = retval;
88 } else {
89 Py_DECREF(retval);
90 first_chunk = NULL;
91 }
92 } else {
93 /* Generic iterable (list of length != 1, generator, ...) */
94 PyObject* iter = PyObject_GetIter(retval);
95 Py_DECREF(retval);
96 if(iter == NULL)
97 return false;
98 request->iterable = iter;
99 first_chunk = wsgi_iterable_get_next_chunk(request);
100 if(first_chunk == NULL && PyErr_Occurred())
101 return false;
102 }
103
104 if(request->headers == NULL) {
105 /* It is important that this check comes *after* the call to
106 * wsgi_iterable_get_next_chunk(), because in case the WSGI application
107 * was an iterator, there's no chance start_response could be called
108 * before. See above if you don't understand what I say. */
109 PyErr_SetString(
110 PyExc_RuntimeError,
111 "wsgi application returned before start_response was called"
37112 );
38
39 Py_DECREF(request_headers);
40 Py_DECREF(start_response);
41
42 if(retval == NULL)
43 return false;
44
45 /* The following code is somewhat magic, so worth an explanation.
46 *
47 * If the application we called was a generator, we have to call .next() on
48 * it before we do anything else because that may execute code that
49 * invokes `start_response` (which might not have been invoked yet).
50 * Think of the following scenario:
51 *
52 * def app(environ, start_response):
53 * start_response('200 Ok', ...)
54 * yield 'Hello World'
55 *
56 * That would make `app` return an iterator (more precisely, a generator).
57 * Unfortunately, `start_response` wouldn't be called until the first item
58 * of that iterator is requested; `start_response` however has to be called
59 * _before_ the wsgi body is sent, because it passes the HTTP headers.
60 *
61 * If the application returned a list this would not be required of course,
62 * but special-handling is painful - especially in C - so here's one generic
63 * way to solve the problem:
64 *
65 * Look into the returned iterator in any case. This allows us to do other
66 * optimizations, for example if the returned value is a list with exactly
67 * one string in it, we can pick the string and throw away the list so bjoern
68 * does not have to come back again and look into the iterator a second time.
69 */
70 PyObject* first_chunk;
71
72 if(PyList_Check(retval) && PyList_GET_SIZE(retval) == 1 &&
73 PyString_Check(PyList_GET_ITEM(retval, 0)))
74 {
75 /* Optimize the most common case, a single string in a list: */
76 PyObject* tmp = PyList_GET_ITEM(retval, 0);
77 Py_INCREF(tmp);
78 Py_DECREF(retval);
79 retval = tmp;
80 goto string; /* eeevil */
81 } else if(PyString_Check(retval)) {
82 /* According to PEP 333 strings should be handled like any other iterable,
83 * i.e. sending the response item for item. "item for item" means
84 * "char for char" if you have a string. -- I'm not that stupid. */
85 string:
86 if(PyString_GET_SIZE(retval)) {
87 first_chunk = retval;
88 } else {
89 Py_DECREF(retval);
90 first_chunk = NULL;
91 }
92 } else {
93 /* Generic iterable (list of length != 1, generator, ...) */
94 PyObject* iter = PyObject_GetIter(retval);
95 Py_DECREF(retval);
96 if(iter == NULL)
97 return false;
98 request->iterable = iter;
99 first_chunk = wsgi_iterable_get_next_chunk(request);
100 if(first_chunk == NULL && PyErr_Occurred())
101 return false;
102 }
103
104 if(request->headers == NULL) {
105 /* It is important that this check comes *after* the call to
106 * wsgi_iterable_get_next_chunk(), because in case the WSGI application
107 * was an iterator, there's no chance start_response could be called
108 * before. See above if you don't understand what I say. */
109 PyErr_SetString(
110 PyExc_RuntimeError,
111 "wsgi application returned before start_response was called"
112 );
113 Py_DECREF(first_chunk);
114 return false;
115 }
116
117 if(should_keep_alive(request)) {
118 request->state.chunked_response = request->state.response_length_unknown;
119 request->state.keep_alive = true;
120 } else {
121 request->state.keep_alive = false;
122 }
123
124 /* Get the headers and concatenate the first body chunk to them.
125 * In the first place this makes the code more simple because afterwards
126 * we can throw away the first chunk PyObject; but it also is an optimization:
127 * At least for small responses, the complete response could be sent with
128 * one send() call (in server.c:ev_io_on_write) which is a (tiny) performance
129 * booster because less kernel calls means less kernel call overhead. */
130 PyObject* buf = PyString_FromStringAndSize(NULL, 1024);
131 Py_ssize_t length = wsgi_getheaders(request, buf);
132 if(length == 0) {
133 Py_DECREF(first_chunk);
134 Py_DECREF(buf);
135 return false;
136 }
137
138 if(first_chunk == NULL) {
139 _PyString_Resize(&buf, length);
140 goto out;
141 }
142
143 if(request->state.chunked_response) {
144 PyObject* new_chunk = wrap_http_chunk_cruft_around(first_chunk);
145 Py_DECREF(first_chunk);
146 assert(PyString_GET_SIZE(new_chunk) >= PyString_GET_SIZE(first_chunk) + 5);
147 first_chunk = new_chunk;
148 }
149
150 assert(buf);
151 _PyString_Resize(&buf, length + PyString_GET_SIZE(first_chunk));
152 memcpy(PyString_AS_STRING(buf)+length, PyString_AS_STRING(first_chunk),
153 PyString_GET_SIZE(first_chunk));
154
155113 Py_DECREF(first_chunk);
114 return false;
115 }
116
117 if(should_keep_alive(request)) {
118 request->state.chunked_response = request->state.response_length_unknown;
119 request->state.keep_alive = true;
120 } else {
121 request->state.keep_alive = false;
122 }
123
124 /* Get the headers and concatenate the first body chunk to them.
125 * In the first place this makes the code more simple because afterwards
126 * we can throw away the first chunk PyObject; but it also is an optimization:
127 * At least for small responses, the complete response could be sent with
128 * one send() call (in server.c:ev_io_on_write) which is a (tiny) performance
129 * booster because less kernel calls means less kernel call overhead. */
130 PyObject* buf = PyString_FromStringAndSize(NULL, 1024);
131 Py_ssize_t length = wsgi_getheaders(request, buf);
132 if(length == 0) {
133 Py_DECREF(first_chunk);
134 Py_DECREF(buf);
135 return false;
136 }
137
138 if(first_chunk == NULL) {
139 _PyString_Resize(&buf, length);
140 goto out;
141 }
142
143 if(request->state.chunked_response) {
144 PyObject* new_chunk = wrap_http_chunk_cruft_around(first_chunk);
145 Py_DECREF(first_chunk);
146 assert(PyString_GET_SIZE(new_chunk) >= PyString_GET_SIZE(first_chunk) + 5);
147 first_chunk = new_chunk;
148 }
149
150 assert(buf);
151 _PyString_Resize(&buf, length + PyString_GET_SIZE(first_chunk));
152 memcpy(PyString_AS_STRING(buf)+length, PyString_AS_STRING(first_chunk),
153 PyString_GET_SIZE(first_chunk));
154
155 Py_DECREF(first_chunk);
156156
157157 out:
158 request->state.wsgi_call_done = true;
159 request->current_chunk = buf;
160 request->current_chunk_p = 0;
161 return true;
158 request->state.wsgi_call_done = true;
159 request->current_chunk = buf;
160 request->current_chunk_p = 0;
161 return true;
162162 }
163163
164164 static inline bool
165165 inspect_headers(Request* request)
166166 {
167 Py_ssize_t i;
168 PyObject* tuple;
169
170 for(i=0; i<PyList_GET_SIZE(request->headers); ++i) {
171 tuple = PyList_GET_ITEM(request->headers, i);
172
173 if(!PyTuple_Check(tuple) || PyTuple_GET_SIZE(tuple) != 2)
174 goto err;
175
176 PyObject* field = PyTuple_GET_ITEM(tuple, 0);
177 PyObject* value = PyTuple_GET_ITEM(tuple, 1);
178
179 if(!PyString_Check(field) || !PyString_Check(value))
180 goto err;
181
182 if(!strncasecmp(PyString_AS_STRING(field), "Content-Length", PyString_GET_SIZE(field)))
183 request->state.response_length_unknown = false;
184 }
185 return true;
167 Py_ssize_t i;
168 PyObject* tuple;
169
170 for(i=0; i<PyList_GET_SIZE(request->headers); ++i) {
171 tuple = PyList_GET_ITEM(request->headers, i);
172
173 if(!PyTuple_Check(tuple) || PyTuple_GET_SIZE(tuple) != 2)
174 goto err;
175
176 PyObject* field = PyTuple_GET_ITEM(tuple, 0);
177 PyObject* value = PyTuple_GET_ITEM(tuple, 1);
178
179 if(!PyString_Check(field) || !PyString_Check(value))
180 goto err;
181
182 if(!strncasecmp(PyString_AS_STRING(field), "Content-Length", PyString_GET_SIZE(field)))
183 request->state.response_length_unknown = false;
184 }
185 return true;
186186
187187 err:
188 TYPE_ERROR_INNER("start_response argument 2", "a list of 2-tuples",
189 "(found invalid '%.200s' object at position %d)", Py_TYPE(tuple)->tp_name, i);
190 return false;
188 TYPE_ERROR_INNER("start_response argument 2", "a list of 2-tuples",
189 "(found invalid '%.200s' object at position %d)", Py_TYPE(tuple)->tp_name, i);
190 return false;
191191 }
192192
193193 static Py_ssize_t
194194 wsgi_getheaders(Request* request, PyObject* buf)
195195 {
196 char* bufp = PyString_AS_STRING(buf);
197
198 #define buf_write(src, len) \
199 do { \
200 size_t n = len; \
201 const char* s = src; \
202 while(n--) *bufp++ = *s++; \
203 } while(0)
204 #define buf_write2(src) buf_write(src, strlen(src))
205
206 buf_write2("HTTP/1.1 ");
207 buf_write(PyString_AS_STRING(request->status),
208 PyString_GET_SIZE(request->status));
209
210 for(Py_ssize_t i=0; i<PyList_GET_SIZE(request->headers); ++i) {
211 PyObject *tuple = PyList_GET_ITEM(request->headers, i);
212 PyObject *field = PyTuple_GET_ITEM(tuple, 0),
213 *value = PyTuple_GET_ITEM(tuple, 1);
214 buf_write2("\r\n");
215 buf_write(PyString_AS_STRING(field), PyString_GET_SIZE(field));
216 buf_write2(": ");
217 buf_write(PyString_AS_STRING(value), PyString_GET_SIZE(value));
218 }
219 if(request->state.chunked_response)
220 buf_write2("\r\nTransfer-Encoding: chunked");
221 buf_write2("\r\n\r\n");
222
223 return bufp - PyString_AS_STRING(buf);
196 char* bufp = PyString_AS_STRING(buf);
197
198 #define buf_write(src, len) \
199 do { \
200 size_t n = len; \
201 const char* s = src; \
202 while(n--) *bufp++ = *s++; \
203 } while(0)
204 #define buf_write2(src) buf_write(src, strlen(src))
205
206 buf_write2("HTTP/1.1 ");
207 buf_write(PyString_AS_STRING(request->status),
208 PyString_GET_SIZE(request->status));
209
210 for(Py_ssize_t i=0; i<PyList_GET_SIZE(request->headers); ++i) {
211 PyObject *tuple = PyList_GET_ITEM(request->headers, i);
212 PyObject *field = PyTuple_GET_ITEM(tuple, 0),
213 *value = PyTuple_GET_ITEM(tuple, 1);
214 buf_write2("\r\n");
215 buf_write(PyString_AS_STRING(field), PyString_GET_SIZE(field));
216 buf_write2(": ");
217 buf_write(PyString_AS_STRING(value), PyString_GET_SIZE(value));
218 }
219 if(request->state.chunked_response)
220 buf_write2("\r\nTransfer-Encoding: chunked");
221 buf_write2("\r\n\r\n");
222
223 return bufp - PyString_AS_STRING(buf);
224224 }
225225
226226 inline PyObject*
227227 wsgi_iterable_get_next_chunk(Request* request)
228228 {
229 /* Get the next item out of ``request->iterable``, skipping empty ones. */
230 PyObject* next;
231 while(true) {
232 next = PyIter_Next(request->iterable);
233 if(next == NULL)
234 return NULL;
235 if(!PyString_Check(next)) {
236 TYPE_ERROR("wsgi iterable items", "strings", next);
237 Py_DECREF(next);
238 return NULL;
239 }
240 if(PyString_GET_SIZE(next))
241 return next;
242 Py_DECREF(next);
229 /* Get the next item out of ``request->iterable``, skipping empty ones. */
230 PyObject* next;
231 while(true) {
232 next = PyIter_Next(request->iterable);
233 if(next == NULL)
234 return NULL;
235 if(!PyString_Check(next)) {
236 TYPE_ERROR("wsgi iterable items", "strings", next);
237 Py_DECREF(next);
238 return NULL;
243239 }
240 if(PyString_GET_SIZE(next))
241 return next;
242 Py_DECREF(next);
243 }
244244 }
245245
246246 static inline void
247247 restore_exception_tuple(PyObject* exc_info, bool incref_items)
248248 {
249 if(incref_items) {
250 Py_INCREF(PyTuple_GET_ITEM(exc_info, 0));
251 Py_INCREF(PyTuple_GET_ITEM(exc_info, 1));
252 Py_INCREF(PyTuple_GET_ITEM(exc_info, 2));
253 }
254 PyErr_Restore(
255 PyTuple_GET_ITEM(exc_info, 0),
256 PyTuple_GET_ITEM(exc_info, 1),
257 PyTuple_GET_ITEM(exc_info, 2)
258 );
249 if(incref_items) {
250 Py_INCREF(PyTuple_GET_ITEM(exc_info, 0));
251 Py_INCREF(PyTuple_GET_ITEM(exc_info, 1));
252 Py_INCREF(PyTuple_GET_ITEM(exc_info, 2));
253 }
254 PyErr_Restore(
255 PyTuple_GET_ITEM(exc_info, 0),
256 PyTuple_GET_ITEM(exc_info, 1),
257 PyTuple_GET_ITEM(exc_info, 2)
258 );
259259 }
260260
261261 static PyObject*
262262 start_response(PyObject* self, PyObject* args, PyObject* kwargs)
263263 {
264 Request* request = ((StartResponse*)self)->request;
265
266 if(request->state.start_response_called) {
267 /* not the first call of start_response --
268 * throw away any previous status and headers. */
269 Py_CLEAR(request->status);
270 Py_CLEAR(request->headers);
271 request->state.response_length_unknown = false;
264 Request* request = ((StartResponse*)self)->request;
265
266 if(request->state.start_response_called) {
267 /* not the first call of start_response --
268 * throw away any previous status and headers. */
269 Py_CLEAR(request->status);
270 Py_CLEAR(request->headers);
271 request->state.response_length_unknown = false;
272 }
273
274 PyObject* status = NULL;
275 PyObject* headers = NULL;
276 PyObject* exc_info = NULL;
277 if(!PyArg_UnpackTuple(args, "start_response", 2, 3, &status, &headers, &exc_info))
278 return NULL;
279
280 if(exc_info) {
281 if(!PyTuple_Check(exc_info) || PyTuple_GET_SIZE(exc_info) != 3) {
282 TYPE_ERROR("start_response argument 3", "a 3-tuple", exc_info);
283 return NULL;
272284 }
273285
274 PyObject* status = NULL;
275 PyObject* headers = NULL;
276 PyObject* exc_info = NULL;
277 if(!PyArg_UnpackTuple(args, "start_response", 2, 3, &status, &headers, &exc_info))
278 return NULL;
279
280 if(exc_info) {
281 if(!PyTuple_Check(exc_info) || PyTuple_GET_SIZE(exc_info) != 3) {
282 TYPE_ERROR("start_response argument 3", "a 3-tuple", exc_info);
283 return NULL;
284 }
285
286 restore_exception_tuple(exc_info, /* incref items? */ true);
287
288 if(request->state.wsgi_call_done) {
289 /* Too late to change headers. According to PEP 333, we should let
290 * the exception propagate in this case. */
291 return NULL;
292 }
293
294 /* Headers not yet sent; handle this start_response call as if 'exc_info'
295 * would not have been passed, but print and clear the exception. */
296 PyErr_Print();
286 restore_exception_tuple(exc_info, /* incref items? */ true);
287
288 if(request->state.wsgi_call_done) {
289 /* Too late to change headers. According to PEP 333, we should let
290 * the exception propagate in this case. */
291 return NULL;
297292 }
298 else if(request->state.start_response_called) {
299 PyErr_SetString(PyExc_TypeError, "'start_response' called twice without "
300 "passing 'exc_info' the second time");
301 return NULL;
302 }
303
304 if(!PyString_Check(status)) {
305 TYPE_ERROR("start_response argument 1", "a 'status reason' string", status);
306 return NULL;
307 }
308 if(!PyList_Check(headers)) {
309 TYPE_ERROR("start response argument 2", "a list of 2-tuples", headers);
310 return NULL;
311 }
312
313 request->headers = headers;
314
315 if(!inspect_headers(request)) {
316 request->headers = NULL;
317 return NULL;
318 }
319
320 request->status = status;
321
322 Py_INCREF(request->status);
323 Py_INCREF(request->headers);
324
325 request->state.start_response_called = true;
326
327 Py_RETURN_NONE;
293
294 /* Headers not yet sent; handle this start_response call as if 'exc_info'
295 * would not have been passed, but print and clear the exception. */
296 PyErr_Print();
297 }
298 else if(request->state.start_response_called) {
299 PyErr_SetString(PyExc_TypeError, "'start_response' called twice without "
300 "passing 'exc_info' the second time");
301 return NULL;
302 }
303
304 if(!PyString_Check(status)) {
305 TYPE_ERROR("start_response argument 1", "a 'status reason' string", status);
306 return NULL;
307 }
308 if(!PyList_Check(headers)) {
309 TYPE_ERROR("start response argument 2", "a list of 2-tuples", headers);
310 return NULL;
311 }
312
313 request->headers = headers;
314
315 if(!inspect_headers(request)) {
316 request->headers = NULL;
317 return NULL;
318 }
319
320 request->status = status;
321
322 Py_INCREF(request->status);
323 Py_INCREF(request->headers);
324
325 request->state.start_response_called = true;
326
327 Py_RETURN_NONE;
328328 }
329329
330330 PyTypeObject StartResponse_Type = {
331 PyObject_HEAD_INIT(NULL)
332 0, /* ob_size (deprecated) */
333 "start_response", /* tp_name (__name__) */
334 sizeof(StartResponse), /* tp_basicsize */
335 0, /* tp_itemsize */
336 (destructor)PyObject_FREE, /* tp_dealloc */
337 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_{print,getattr,setattr,compare,...} */
338 start_response /* tp_call (__call__) */
331 PyObject_HEAD_INIT(NULL)
332 0, /* ob_size (deprecated) */
333 "start_response", /* tp_name (__name__) */
334 sizeof(StartResponse), /* tp_basicsize */
335 0, /* tp_itemsize */
336 (destructor)PyObject_FREE, /* tp_dealloc */
337 0, 0, 0, 0, 0, 0, 0, 0, 0, /* tp_{print,getattr,setattr,compare,...} */
338 start_response /* tp_call (__call__) */
339339 };
340340
341341 #define F_KEEP_ALIVE 1<<1
344344 static inline bool
345345 should_keep_alive(Request* request)
346346 {
347 if(!(request->parser.parser.flags & F_KEEP_ALIVE)) {
348 /* Only keep-alive if the client requested it explicitly */
349 return false;
350 }
351 if(request->state.response_length_unknown) {
352 /* If the client wants to keep-alive the connection but we don't know
353 * the response length, we can use Transfer-Encoding: chunked on HTTP/1.1.
354 * On HTTP/1.0 no such thing exists so there's no other option than closing
355 * the connection to indicate the response end. */
356 return have_http11(request->parser.parser);
357 } else {
358 /* If the response length is known we can keep-alive for both 1.0 and 1.1 */
359 return true;
360 }
347 if(!(request->parser.parser.flags & F_KEEP_ALIVE)) {
348 /* Only keep-alive if the client requested it explicitly */
349 return false;
350 }
351 if(request->state.response_length_unknown) {
352 /* If the client wants to keep-alive the connection but we don't know
353 * the response length, we can use Transfer-Encoding: chunked on HTTP/1.1.
354 * On HTTP/1.0 no such thing exists so there's no other option than closing
355 * the connection to indicate the response end. */
356 return have_http11(request->parser.parser);
357 } else {
358 /* If the response length is known we can keep-alive for both 1.0 and 1.1 */
359 return true;
360 }
361361 }
362362
363363 PyObject*
364 wrap_http_chunk_cruft_around(PyObject* chunk) {
365 /* Who the hell decided to use decimal representation for Content-Length
366 * but hexadecimal representation for chunk lengths btw!?! Fuck W3C */
367 size_t chunklen = PyString_GET_SIZE(chunk);
368 assert(chunklen);
369 char buf[strlen("ffffffffffffffff") + 2];
370 size_t n = sprintf(buf, "%x\r\n", chunklen);
371 PyObject* new_chunk = PyString_FromStringAndSize(NULL, n + chunklen + 2);
372 char* new_chunk_p = PyString_AS_STRING(new_chunk);
373 memcpy(new_chunk_p, buf, n);
374 new_chunk_p += n;
375 memcpy(new_chunk_p, PyString_AS_STRING(chunk), chunklen);
376 new_chunk_p += chunklen;
377 *new_chunk_p++ = '\r'; *new_chunk_p = '\n';
378 assert(new_chunk_p == PyString_AS_STRING(new_chunk) + n + chunklen + 1);
379 return new_chunk;
364 wrap_http_chunk_cruft_around(PyObject* chunk)
365 {
366 /* Who the hell decided to use decimal representation for Content-Length
367 * but hexadecimal representation for chunk lengths btw!?! Fuck W3C */
368 size_t chunklen = PyString_GET_SIZE(chunk);
369 assert(chunklen);
370 char buf[strlen("ffffffffffffffff") + 2];
371 size_t n = sprintf(buf, "%x\r\n", chunklen);
372 PyObject* new_chunk = PyString_FromStringAndSize(NULL, n + chunklen + 2);
373 char* new_chunk_p = PyString_AS_STRING(new_chunk);
374 memcpy(new_chunk_p, buf, n);
375 new_chunk_p += n;
376 memcpy(new_chunk_p, PyString_AS_STRING(chunk), chunklen);
377 new_chunk_p += chunklen;
378 *new_chunk_p++ = '\r'; *new_chunk_p = '\n';
379 assert(new_chunk_p == PyString_AS_STRING(new_chunk) + n + chunklen + 1);
380 return new_chunk;
380381 }
381382
382383 void _initialize_wsgi_module() {
383 int ready = PyType_Ready(&StartResponse_Type);
384 assert(ready == 0);
385 assert(StartResponse_Type.tp_flags & Py_TPFLAGS_READY);
386 }
384 int ready = PyType_Ready(&StartResponse_Type);
385 assert(ready == 0);
386 assert(StartResponse_Type.tp_flags & Py_TPFLAGS_READY);
387 }