Klaus Demo bjoern / 6b48d96
Python 3 support, step 1: Get rid of cStringIO. Jonas Haag 9 years ago
9 changed file(s) with 163 addition(s) and 73 deletion(s). Raw diff Collapse all Expand all
00 #include <Python.h>
11 #include "server.h"
22 #include "wsgi.h"
3 #include "stream.h"
34 #include "bjoernmodule.h"
45
56
7273 static PyMethodDef Bjoern_FunctionTable[] = {
7374 {"run", run, METH_VARARGS, run_doc},
7475 {"listen", listen, METH_VARARGS, listen_doc},
75 {NULL, NULL, 0, NULL}
76 {NULL, NULL, 0, NULL}
7677 };
7778
7879 PyMODINIT_FUNC initbjoern()
7980 {
80 _initialize_wsgi_module();
81 PyType_Ready(&StartResponse_Type);
82 PyType_Ready(&StreamType);
83 assert(StreamType.tp_flags & Py_TPFLAGS_READY);
84 assert(StartResponse_Type.tp_flags & Py_TPFLAGS_READY);
8185 _initialize_static_strings();
8286
8387 PyObject* bjoern_module = Py_InitModule("bjoern", Bjoern_FunctionTable);
55 #include <stddef.h>
66 #include <stdbool.h>
77 #include <string.h>
8
9 typedef struct { char* data; size_t len; } string;
810
911 enum http_status { HTTP_BAD_REQUEST = 1, HTTP_LENGTH_REQUIRED, HTTP_SERVER_ERROR };
1012
00 #include <Python.h>
1 #include <cStringIO.h>
21 #include "request.h"
2 #include "stream.h"
33
44 static inline void PyDict_ReplaceKey(PyObject* dict, PyObject* k1, PyObject* k2);
5 static PyObject* wsgi_http_header(Request*, const char*, const size_t);
5 static PyObject* wsgi_http_header(string header);
66 static http_parser_settings parser_settings;
77 static PyObject* wsgi_base_dict = NULL;
8
9 /* Non-public type from cStringIO I abuse in on_body */
10 typedef struct {
11 PyObject_HEAD
12 char *buf;
13 Py_ssize_t pos, string_size;
14 PyObject *pbuf;
15 } Iobject;
168
179 Request* Request_new(int client_fd, const char* client_addr)
1810 {
3325 {
3426 memset(&request->state, 0, sizeof(Request) - (size_t)&((Request*)NULL)->state);
3527 request->state.response_length_unknown = true;
28 request->parser.body = (string){NULL, 0};
3629 }
3730
3831 void Request_free(Request* request)
8780 * [old header data ] ...stuff... [ new header data ]
8881 * ^-------------- A -------------^--------B--------^
8982 *
90 * A = XXX- PARSER->XXX_start
83 * A = XXX- PARSER->XXX.data
9184 * B = len
9285 * A + B = old header start to new header end
9386 */ \
94 do { PARSER->name##_len = (name - PARSER->name##_start) + len; } while(0)
87 do { PARSER->name.len = (name - PARSER->name.data) + len; } while(0)
9588
9689 #define _set_header(k, v) PyDict_SetItem(REQUEST->headers, k, v);
9790 #define _set_header_free_value(k, v) \
112105 static int on_message_begin(http_parser* parser)
113106 {
114107 REQUEST->headers = PyDict_New();
115 PARSER->field_start = NULL;
116 PARSER->field_len = 0;
117 PARSER->value_start = NULL;
118 PARSER->value_len = 0;
108 PARSER->field = (string){NULL, 0};
109 PARSER->value = (string){NULL, 0};
119110 return 0;
120111 }
121112
135126
136127 static int on_header_field(http_parser* parser, const char* field, size_t len)
137128 {
138 if(PARSER->value_start) {
129 if(PARSER->value.data) {
139130 /* Store previous header and start a new one */
140131 _set_header_free_both(
141 wsgi_http_header(REQUEST, PARSER->field_start, PARSER->field_len),
142 PyString_FromStringAndSize(PARSER->value_start, PARSER->value_len)
143 );
144 } else if(PARSER->field_start) {
132 wsgi_http_header(PARSER->field),
133 PyString_FromStringAndSize(PARSER->value.data, PARSER->value.len)
134 );
135 } else if(PARSER->field.data) {
145136 UPDATE_LENGTH(field);
146137 return 0;
147138 }
148 PARSER->field_start = field;
149 PARSER->field_len = len;
150 PARSER->value_start = NULL;
151 PARSER->value_len = 0;
139 PARSER->field = (string){(char*)field, len};
140 PARSER->value = (string){NULL, 0};
152141 return 0;
153142 }
154143
155144 static int
156145 on_header_value(http_parser* parser, const char* value, size_t len)
157146 {
158 if(PARSER->value_start) {
147 if(PARSER->value.data) {
159148 UPDATE_LENGTH(value);
160149 } else {
161150 /* Start a new value */
162 PARSER->value_start = value;
163 PARSER->value_len = len;
151 PARSER->value = (string){(char*)value, len};
164152 }
165153 return 0;
166154 }
168156 static int
169157 on_headers_complete(http_parser* parser)
170158 {
171 if(PARSER->field_start) {
159 if(PARSER->field.data) {
172160 _set_header_free_both(
173 wsgi_http_header(REQUEST, PARSER->field_start, PARSER->field_len),
174 PyString_FromStringAndSize(PARSER->value_start, PARSER->value_len)
161 wsgi_http_header(PARSER->field),
162 PyString_FromStringAndSize(PARSER->value.data, PARSER->value.len)
175163 );
176164 }
177165 return 0;
180168 static int
181169 on_body(http_parser* parser, const char* data, const size_t len)
182170 {
183 Iobject* body;
184
185 body = (Iobject*)PyDict_GetItem(REQUEST->headers, _wsgi_input);
186 if(body == NULL) {
171 /* XXX does the body copying really make the code more readable? */
172 string body = PARSER->body;
173 if(body.data == NULL) {
187174 if(!parser->content_length) {
188175 REQUEST->state.error_code = HTTP_LENGTH_REQUIRED;
189176 return 1;
190177 }
191 PyObject* buf = PyString_FromStringAndSize(NULL, parser->content_length);
192 body = (Iobject*)PycStringIO->NewInput(buf);
193 Py_XDECREF(buf);
194 if(body == NULL)
178 body.data = malloc(parser->content_length);
179 if(body.data == NULL)
195180 return 1;
196 _set_header(_wsgi_input, (PyObject*)body);
197 Py_DECREF(body);
198 }
199 memcpy(body->buf + body->pos, data, len);
200 body->pos += len;
181 }
182 memcpy(body.data + body.len, data, len);
183 body.len += len;
184 PARSER->body = body;
201185 return 0;
202186 }
203187
223207 /* REMOTE_ADDR */
224208 _set_header(_REMOTE_ADDR, REQUEST->client_addr);
225209
226 PyObject* body = PyDict_GetItem(REQUEST->headers, _wsgi_input);
227 if(body) {
228 /* We abused the `pos` member for tracking the amount of data copied from
229 * the buffer in on_body, so reset it to zero here. */
230 ((Iobject*)body)->pos = 0;
231 } else {
232 /* Request has no body */
233 _set_header_free_value(_wsgi_input, PycStringIO->NewInput(_empty_string));
234 }
210 Stream* body = PyObject_NEW(Stream, &StreamType);
211 body->data = PARSER->body;
212 body->pos = 0;
213 _set_header_free_value(_wsgi_input, (PyObject*)body);
235214
236215 PyDict_Update(REQUEST->headers, wsgi_base_dict);
237216
241220
242221
243222 static PyObject*
244 wsgi_http_header(Request* request, const char* data, size_t len)
245 {
246 PyObject* obj = PyString_FromStringAndSize(NULL, len+strlen("HTTP_"));
223 wsgi_http_header(string header)
224 {
225 PyObject* obj = PyString_FromStringAndSize(NULL, header.len+strlen("HTTP_"));
247226 char* dest = PyString_AS_STRING(obj);
248227
249228 *dest++ = 'H';
252231 *dest++ = 'P';
253232 *dest++ = '_';
254233
255 while(len--) {
256 char c = *data++;
234 while(header.len--) {
235 char c = *header.data++;
257236 if(c == '-')
258237 *dest++ = '_';
259238 else if(c >= 'a' && c <= 'z')
287266 void _initialize_request_module(const char* server_host, const int server_port)
288267 {
289268 if(wsgi_base_dict == NULL) {
290 PycString_IMPORT;
291269 wsgi_base_dict = PyDict_New();
292270
293271 /* dct['SCRIPT_NAME'] = '' */
1818
1919 typedef struct {
2020 http_parser parser;
21 const char* field_start;
22 size_t field_len;
23 const char* value_start;
24 size_t value_len;
21 string field;
22 string value;
23 string body;
2524 } bj_parser;
2625
2726 typedef struct {
0 #include "stream.h"
1
2 static PyObject*
3 _Stream_read(Stream* stream, size_t howmuch)
4 {
5 size_t bytesleft;
6 PyObject* chunk;
7 if(stream->data.data == NULL)
8 return NULL;
9 bytesleft = stream->data.len - stream->pos;
10 if(howmuch > bytesleft)
11 howmuch = bytesleft;
12 if(howmuch == 0)
13 return NULL;
14 chunk = PyString_FromStringAndSize(stream->data.data + stream->pos, howmuch);
15 stream->pos += howmuch;
16 return chunk;
17 }
18
19 static PyObject*
20 Stream_read(PyObject* self, PyObject* args)
21 {
22 size_t howmuch;
23 if(!PyArg_ParseTuple(args, "I", &howmuch))
24 return NULL;
25 PyObject* chunk = _Stream_read((Stream*)self, howmuch);
26 if(chunk == NULL) {
27 Py_INCREF(_empty_string);
28 chunk = _empty_string;
29 }
30 return chunk;
31 }
32
33 static PyObject*
34 Stream_notimplemented(PyObject* self, PyObject* args)
35 {
36 PyErr_SetString(PyExc_NotImplementedError,
37 "Nobody would ever use readline(s) on a stream of random bytes, right?");
38 return NULL;
39 }
40
41 static PyObject*
42 Stream_iternew(PyObject* self) {
43 Py_INCREF(self);
44 return self;
45 }
46
47 static PyObject*
48 Stream_iternext(PyObject* self)
49 {
50 return _Stream_read((Stream*)self, STREAM_CHUNK_SIZE);
51 }
52
53 static void
54 Stream_dealloc(PyObject* self)
55 {
56 free(((Stream*)self)->data.data);
57 self->ob_type->tp_free(self);
58 }
59
60 static PyMethodDef Stream_methods[] = {
61 {"read", Stream_read, METH_VARARGS, NULL},
62 {"readline", Stream_notimplemented, METH_NOARGS, NULL},
63 {"readlines", Stream_notimplemented, METH_VARARGS, NULL},
64 {NULL, NULL, 0, NULL}
65 };
66
67 PyTypeObject StreamType = {
68 PyObject_HEAD_INIT(NULL)
69 0,
70 "StringStream",
71 sizeof(Stream),
72 0,
73 Stream_dealloc,
74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75 Py_TPFLAGS_HAVE_CLASS | Py_TPFLAGS_HAVE_ITER,
76 0, 0, 0, 0, 0,
77 Stream_iternew,
78 Stream_iternext,
79 Stream_methods
80 };
0 #include "common.h"
1
2 #define STREAM_CHUNK_SIZE 4*1024 /* XXX this is kind of random */
3
4 PyTypeObject StreamType;
5
6 typedef struct {
7 PyObject_HEAD
8 string data;
9 size_t pos;
10 } Stream;
1919 bool
2020 wsgi_call_application(Request* request)
2121 {
22 assert(StartResponse_Type.tp_flags & Py_TPFLAGS_READY);
2322 StartResponse* start_response = PyObject_NEW(StartResponse, &StartResponse_Type);
2423 start_response->request = request;
2524
378377 assert(new_chunk_p == PyString_AS_STRING(new_chunk) + n + chunklen + 1);
379378 return new_chunk;
380379 }
381
382 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 }
00 #include <Python.h>
11 #include "request.h"
22
3 void _initialize_wsgi_module();
43 bool wsgi_call_application(Request*);
54 PyObject* wsgi_iterable_get_next_chunk(Request*);
65 PyObject* wrap_http_chunk_cruft_around(PyObject* chunk);
0 from __future__ import print_function
1 import pprint
2 import bjoern
3 import sys
4
5 if 'silenceillkillyou' in sys.argv:
6 def print(*args): pass
7
8 def app(env, start_response):
9 print('--')
10 stream = env['wsgi.input']
11 print(stream)
12 print(dir(stream))
13 print(repr(stream.read(2)))
14 print(repr(stream.read(10)))
15 print(repr(stream.read(10)))
16 print(repr(stream.read(10)))
17 print(repr(stream.read(10)))
18 print(repr(stream.read(10)))
19 start_response('200 yo', [])
20 return []
21
22 bjoern.run(app, '0.0.0.0', 8080)