Klaus Demo bjoern / 38c1a6d
Revert "get rid of cStringIO". Py3k support will be a separate development branch. Using conditional compilation (#ifdef ON_PY3K cascades) ends up in unreadable mess, particularly for module initialization and unicode/bytes processing. So if a Python 3 version happens at all (I'm rather discouraged right now), that will be a separate branch. This reverts commit 6b48d96f543a4b5c834a18e795bf6166fe4271d6. Jonas Haag 9 years ago
9 changed file(s) with 73 addition(s) and 163 deletion(s). Raw diff Collapse all Expand all
00 #include <Python.h>
11 #include "server.h"
22 #include "wsgi.h"
3 #include "stream.h"
43 #include "bjoernmodule.h"
54
65
7372 static PyMethodDef Bjoern_FunctionTable[] = {
7473 {"run", run, METH_VARARGS, run_doc},
7574 {"listen", listen, METH_VARARGS, listen_doc},
76 {NULL, NULL, 0, NULL}
75 {NULL, NULL, 0, NULL}
7776 };
7877
7978 PyMODINIT_FUNC initbjoern()
8079 {
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);
80 _initialize_wsgi_module();
8581 _initialize_static_strings();
8682
8783 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;
108
119 enum http_status { HTTP_BAD_REQUEST = 1, HTTP_LENGTH_REQUIRED, HTTP_SERVER_ERROR };
1210
00 #include <Python.h>
1 #include <cStringIO.h>
12 #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(string header);
5 static PyObject* wsgi_http_header(Request*, const char*, const size_t);
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;
816
917 Request* Request_new(int client_fd, const char* client_addr)
1018 {
2533 {
2634 memset(&request->state, 0, sizeof(Request) - (size_t)&((Request*)NULL)->state);
2735 request->state.response_length_unknown = true;
28 request->parser.body = (string){NULL, 0};
2936 }
3037
3138 void Request_free(Request* request)
8087 * [old header data ] ...stuff... [ new header data ]
8188 * ^-------------- A -------------^--------B--------^
8289 *
83 * A = XXX- PARSER->XXX.data
90 * A = XXX- PARSER->XXX_start
8491 * B = len
8592 * A + B = old header start to new header end
8693 */ \
87 do { PARSER->name.len = (name - PARSER->name.data) + len; } while(0)
94 do { PARSER->name##_len = (name - PARSER->name##_start) + len; } while(0)
8895
8996 #define _set_header(k, v) PyDict_SetItem(REQUEST->headers, k, v);
9097 #define _set_header_free_value(k, v) \
105112 static int on_message_begin(http_parser* parser)
106113 {
107114 REQUEST->headers = PyDict_New();
108 PARSER->field = (string){NULL, 0};
109 PARSER->value = (string){NULL, 0};
115 PARSER->field_start = NULL;
116 PARSER->field_len = 0;
117 PARSER->value_start = NULL;
118 PARSER->value_len = 0;
110119 return 0;
111120 }
112121
126135
127136 static int on_header_field(http_parser* parser, const char* field, size_t len)
128137 {
129 if(PARSER->value.data) {
138 if(PARSER->value_start) {
130139 /* Store previous header and start a new one */
131140 _set_header_free_both(
132 wsgi_http_header(PARSER->field),
133 PyString_FromStringAndSize(PARSER->value.data, PARSER->value.len)
134 );
135 } else if(PARSER->field.data) {
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) {
136145 UPDATE_LENGTH(field);
137146 return 0;
138147 }
139 PARSER->field = (string){(char*)field, len};
140 PARSER->value = (string){NULL, 0};
148 PARSER->field_start = field;
149 PARSER->field_len = len;
150 PARSER->value_start = NULL;
151 PARSER->value_len = 0;
141152 return 0;
142153 }
143154
144155 static int
145156 on_header_value(http_parser* parser, const char* value, size_t len)
146157 {
147 if(PARSER->value.data) {
158 if(PARSER->value_start) {
148159 UPDATE_LENGTH(value);
149160 } else {
150161 /* Start a new value */
151 PARSER->value = (string){(char*)value, len};
162 PARSER->value_start = value;
163 PARSER->value_len = len;
152164 }
153165 return 0;
154166 }
156168 static int
157169 on_headers_complete(http_parser* parser)
158170 {
159 if(PARSER->field.data) {
171 if(PARSER->field_start) {
160172 _set_header_free_both(
161 wsgi_http_header(PARSER->field),
162 PyString_FromStringAndSize(PARSER->value.data, PARSER->value.len)
173 wsgi_http_header(REQUEST, PARSER->field_start, PARSER->field_len),
174 PyString_FromStringAndSize(PARSER->value_start, PARSER->value_len)
163175 );
164176 }
165177 return 0;
168180 static int
169181 on_body(http_parser* parser, const char* data, const size_t len)
170182 {
171 /* XXX does the body copying really make the code more readable? */
172 string body = PARSER->body;
173 if(body.data == NULL) {
183 Iobject* body;
184
185 body = (Iobject*)PyDict_GetItem(REQUEST->headers, _wsgi_input);
186 if(body == NULL) {
174187 if(!parser->content_length) {
175188 REQUEST->state.error_code = HTTP_LENGTH_REQUIRED;
176189 return 1;
177190 }
178 body.data = malloc(parser->content_length);
179 if(body.data == NULL)
191 PyObject* buf = PyString_FromStringAndSize(NULL, parser->content_length);
192 body = (Iobject*)PycStringIO->NewInput(buf);
193 Py_XDECREF(buf);
194 if(body == NULL)
180195 return 1;
181 }
182 memcpy(body.data + body.len, data, len);
183 body.len += len;
184 PARSER->body = body;
196 _set_header(_wsgi_input, (PyObject*)body);
197 Py_DECREF(body);
198 }
199 memcpy(body->buf + body->pos, data, len);
200 body->pos += len;
185201 return 0;
186202 }
187203
207223 /* REMOTE_ADDR */
208224 _set_header(_REMOTE_ADDR, REQUEST->client_addr);
209225
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);
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 }
214235
215236 PyDict_Update(REQUEST->headers, wsgi_base_dict);
216237
220241
221242
222243 static PyObject*
223 wsgi_http_header(string header)
224 {
225 PyObject* obj = PyString_FromStringAndSize(NULL, header.len+strlen("HTTP_"));
244 wsgi_http_header(Request* request, const char* data, size_t len)
245 {
246 PyObject* obj = PyString_FromStringAndSize(NULL, len+strlen("HTTP_"));
226247 char* dest = PyString_AS_STRING(obj);
227248
228249 *dest++ = 'H';
231252 *dest++ = 'P';
232253 *dest++ = '_';
233254
234 while(header.len--) {
235 char c = *header.data++;
255 while(len--) {
256 char c = *data++;
236257 if(c == '-')
237258 *dest++ = '_';
238259 else if(c >= 'a' && c <= 'z')
266287 void _initialize_request_module(const char* server_host, const int server_port)
267288 {
268289 if(wsgi_base_dict == NULL) {
290 PycString_IMPORT;
269291 wsgi_base_dict = PyDict_New();
270292
271293 /* dct['SCRIPT_NAME'] = '' */
1818
1919 typedef struct {
2020 http_parser parser;
21 string field;
22 string value;
23 string body;
21 const char* field_start;
22 size_t field_len;
23 const char* value_start;
24 size_t value_len;
2425 } bj_parser;
2526
2627 typedef struct {
+0
-81
bjoern/stream.c less more
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
-11
bjoern/stream.h less more
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);
2223 StartResponse* start_response = PyObject_NEW(StartResponse, &StartResponse_Type);
2324 start_response->request = request;
2425
377378 assert(new_chunk_p == PyString_AS_STRING(new_chunk) + n + chunklen + 1);
378379 return new_chunk;
379380 }
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();
34 bool wsgi_call_application(Request*);
45 PyObject* wsgi_iterable_get_next_chunk(Request*);
56 PyObject* wrap_http_chunk_cruft_around(PyObject* chunk);
+0
-23
tests/readinput.py less more
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)