Klaus Demo bjoern / 5e6e6d4
bjoern Python3 Port (tests/hello.py working) Introduced a single header file "py2py3.h" to handle python2/3 difference in bjoern and enable full python3 support on top of python2 support. This port heavily referenced the existing bjoern-py3 implementation: https://github.com/isaiah/bjoern-py3 but removed the bytesio.c/h completely. Hua Yanghao authored 3 years ago Jonas Haag committed 3 years ago
21 changed file(s) with 214 addition(s) and 139 deletion(s). Raw diff Collapse all Expand all
00 *.pyc
1 *.swp
12 *~
23 build/
34 dist/
45 MANIFEST
6 bjoern.egg-info/
00 SOURCE_DIR = bjoern
11 BUILD_DIR = build
2 PYTHON ?= python2
23
3 PYTHON_INCLUDE = $(shell python2-config --include)
4 PYTHON_LDFLAGS = $(shell python2-config --ldflags)
4 PYTHON_INCLUDE = $(shell ${PYTHON}-config --includes)
5 PYTHON_LDFLAGS = $(shell ${PYTHON}-config --ldflags)
56
67 HTTP_PARSER_DIR = http-parser
78 HTTP_PARSER_OBJ = $(HTTP_PARSER_DIR)/http_parser.o
3940
4041 _bjoernmodule:
4142 @$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(objects) -o $(BUILD_DIR)/_bjoern.so
42 @PYTHONPATH=$$PYTHONPATH:$(BUILD_DIR) python2 -c "import bjoern"
43 @PYTHONPATH=$$PYTHONPATH:$(BUILD_DIR) ${PYTHON} -c "import bjoern"
4344
4445 again: clean all
4546
8081 wget -O - -q -S $(TEST_URL)
8182
8283 valgrind:
83 valgrind --leak-check=full --show-reachable=yes python2 tests/empty.py
84 valgrind --leak-check=full --show-reachable=yes ${PYTHON} tests/empty.py
8485
8586 callgrind:
86 valgrind --tool=callgrind python2 tests/wsgitest-round-robin.py
87 valgrind --tool=callgrind ${PYTHON} tests/wsgitest-round-robin.py
8788
8889 memwatch:
8990 watch -n 0.5 \
90 'cat /proc/$$(pgrep -n python2)/cmdline | tr "\0" " " | head -c -1; \
91 'cat /proc/$$(pgrep -n ${PYTHON})/cmdline | tr "\0" " " | head -c -1; \
9192 echo; echo; \
92 tail -n +25 /proc/$$(pgrep -n python2)/smaps'
93 tail -n +25 /proc/$$(pgrep -n ${PYTHON})/smaps'
9394
9495 upload:
95 python2 setup.py sdist upload
96 ${PYTHON} setup.py sdist upload
9697
9798 $(HTTP_PARSER_OBJ):
9899 $(MAKE) -C $(HTTP_PARSER_DIR) http_parser.o CFLAGS_DEBUG_EXTRA=-fPIC CFLAGS_FAST_EXTRA=-fPIC
+0
-5
bjoern/25compat.h less more
0 #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
1 #define NOP do{}while(0)
2 #define PyFile_IncUseCount(file) NOP
3 #define PyFile_DecUseCount(file) NOP
4 #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
11 #include "server.h"
22 #include "wsgi.h"
33 #include "filewrapper.h"
4
54
65 static PyObject*
76 run(PyObject* self, PyObject* args)
4342 {NULL, NULL, 0, NULL}
4443 };
4544
46 PyMODINIT_FUNC init_bjoern(void)
45 #if PY_MAJOR_VERSION >= 3
46 static struct PyModuleDef module = {
47 PyModuleDef_HEAD_INIT,
48 "bjoern",
49 NULL,
50 -1, /* size of per-interpreter state of the module,
51 or -1 if the module keeps state in global variables. */
52 Bjoern_FunctionTable,
53 NULL, NULL, NULL, NULL,
54 };
55 #endif
56
57 #if PY_MAJOR_VERSION >= 3
58 #define INIT_BJOERN PyInit__bjoern
59 #else
60 #define INIT_BJOERN init_bjoern
61 #endif
62
63 PyMODINIT_FUNC INIT_BJOERN(void)
4764 {
4865 _init_common();
4966 _init_filewrapper();
5269 assert(FileWrapper_Type.tp_flags & Py_TPFLAGS_READY);
5370 PyType_Ready(&StartResponse_Type);
5471 assert(StartResponse_Type.tp_flags & Py_TPFLAGS_READY);
72 Py_INCREF(&FileWrapper_Type);
73 Py_INCREF(&StartResponse_Type);
5574
75 #if PY_MAJOR_VERSION >= 3
76 PyObject* bjoern_module = PyModule_Create(&module);
77 if (bjoern_module == NULL) {
78 return NULL;
79 }
80
81 PyModule_AddObject(bjoern_module, "version", Py_BuildValue("(iii)", 1, 4, 3));
82 return bjoern_module;
83 #else
5684 PyObject* bjoern_module = Py_InitModule("_bjoern", Bjoern_FunctionTable);
5785 PyModule_AddObject(bjoern_module, "version", Py_BuildValue("(iii)", 1, 4, 3));
86 #endif
87
5888 }
00 #include "common.h"
1 #include "py2py3.h"
12
23 #define UNHEX(c) ((c >= '0' && c <= '9') ? (c - '0') : \
34 (c >= 'a' && c <= 'f') ? (c - 'a' + 10) : \
2728
2829 void _init_common()
2930 {
30 #define _(name) _##name = PyString_FromString(#name)
31
32 #define _(name) _##name = _Unicode_FromString(#name)
3133 _(REMOTE_ADDR);
3234 _(PATH_INFO);
3335 _(QUERY_STRING);
4244 _(CONTENT_LENGTH);
4345 _(HTTP_CONTENT_TYPE);
4446 _(CONTENT_TYPE);
47
48 _(BytesIO);
49 _(write);
50 _(read);
51 _(seek);
4552 #undef _
4653
47 _HTTP_1_1 = PyString_FromString("HTTP/1.1");
48 _HTTP_1_0 = PyString_FromString("HTTP/1.0");
49 _wsgi_input = PyString_FromString("wsgi.input");
50 _empty_string = PyString_FromString("");
54 _HTTP_1_1 = _Unicode_FromString("HTTP/1.1");
55 _HTTP_1_0 = _Unicode_FromString("HTTP/1.0");
56 _wsgi_input = _Unicode_FromString("wsgi.input");
57 _empty_string = _Unicode_FromString("");
58 _empty_bytes = _Bytes_FromString("");
5159 }
55 #include <stddef.h>
66 #include <stdbool.h>
77 #include <string.h>
8
9 #if PY_MINOR_VERSION < 6
10 # include "25compat.h"
11 #endif
128
139 #define TYPE_ERROR_INNER(what, expected, ...) \
1410 PyErr_Format(PyExc_TypeError, what " must be " expected " " __VA_ARGS__)
2521 PyObject *_REMOTE_ADDR, *_PATH_INFO, *_QUERY_STRING, *_REQUEST_METHOD, *_GET,
2622 *_HTTP_CONTENT_LENGTH, *_CONTENT_LENGTH, *_HTTP_CONTENT_TYPE,
2723 *_CONTENT_TYPE, *_SERVER_PROTOCOL, *_SERVER_NAME, *_SERVER_PORT,
28 *_HTTP_1_1, *_HTTP_1_0, *_wsgi_input, *_close, *_empty_string;
24 *_HTTP_1_1, *_HTTP_1_0, *_wsgi_input, *_close, *_empty_string,
25 *_empty_bytes, *_BytesIO, *_write, *_read, *_seek;
2926
3027 #ifdef DEBUG
3128 #define DBG_REQ(request, ...) \
00 #include "filewrapper.h"
1 #include "py2py3.h"
12
23 static PyObject*
34 FileWrapper_New(PyTypeObject* cls, PyObject* args, PyObject* kwargs)
89 if(!PyArg_ParseTuple(args, "O|I:FileWrapper", &file, &ignored_blocksize))
910 return NULL;
1011
11 if(!PyFile_Check(file)) {
12 if(!_File_Check(file)) {
1213 TYPE_ERROR("FileWrapper argument", "file", file);
1314 return NULL;
1415 }
0 #ifndef _PY2PY3_H
1 #define _PY2PY3_H
2
3 #if PY_MAJOR_VERSION >= 3
4 #define NOP do{}while(0)
5 #define PyFile_IncUseCount(file) NOP
6 #define PyFile_DecUseCount(file) NOP
7 #define _Bytes_AS_DATA(obj) PyBytes_AS_STRING(obj)
8 #define _Unicode_AS_DATA(obj) PyUnicode_AsUTF8(obj)
9 #define _Bytes_FromString(name) PyBytes_FromString(name)
10 #define _Unicode_FromString(name) PyUnicode_FromString(name)
11 #define _Bytes_FromStringAndSize(data, len) PyBytes_FromStringAndSize(data, len)
12 #define _Unicode_FromStringAndSize(data, len) PyUnicode_FromStringAndSize(data, len)
13 #define _Bytes_GET_SIZE(obj) PyBytes_GET_SIZE(obj)
14 #define _Unicode_GET_SIZE(obj) PyUnicode_GET_LENGTH(obj)
15 #define _Bytes_Check(obj) PyBytes_Check(obj)
16 #define _Unicode_Check(obj) PyUnicode_Check(obj)
17 #define _Bytes_Resize(obj, len) _PyBytes_Resize(obj, len)
18 #define _Unicode_Resize(obj, len) PyUnicode_Resize(obj, len)
19 #define _FromLong(n) PyLong_FromLong(n)
20 #define _File_Check(file) (PyObject_HasAttrString(file, "fileno") && \
21 PyCallable_Check(PyObject_GetAttrString(file, "fileno")))
22 #else
23 #define _Bytes_AS_DATA(obj) PyString_AS_STRING(obj)
24 #define _Unicode_AS_DATA(obj) PyString_AS_STRING(obj)
25 #define _Bytes_FromString(name) PyString_FromString(name)
26 #define _Unicode_FromString(name) PyString_FromString(name)
27 #define _Bytes_FromStringAndSize(data, len) PyString_FromStringAndSize(data, len)
28 #define _Unicode_FromStringAndSize(data, len) PyString_FromStringAndSize(data, len)
29 #define _Bytes_GET_SIZE(obj) PyString_GET_SIZE(obj)
30 #define _Unicode_GET_SIZE(obj) PyString_GET_SIZE(obj)
31 #define _Bytes_Check(obj) PyString_Check(obj)
32 #define _Unicode_Check(obj) PyString_Check(obj)
33 #define _Bytes_Resize(obj, len) _PyString_Resize(obj, len)
34 #define _Unicode_Resize(obj, len) _PyString_Resize(obj, len)
35 #define _FromLong(n) PyInt_FromLong(n)
36 #define _File_Check(file) PyFile_Check(file)
37 #endif
38
39 #endif /* _PY2PY3_H */
00 #include <Python.h>
1 #include <cStringIO.h>
21 #include "request.h"
32 #include "filewrapper.h"
3
4 #include "py2py3.h"
45
56 static inline void PyDict_ReplaceKey(PyObject* dict, PyObject* k1, PyObject* k2);
67 static PyObject* wsgi_http_header(string header);
78 static http_parser_settings parser_settings;
89 static PyObject* wsgi_base_dict = NULL;
910
10 /* Non-public type from cStringIO I abuse in on_body */
11 typedef struct {
12 PyObject_HEAD
13 char *buf;
14 Py_ssize_t pos, string_size;
15 PyObject *pbuf;
16 } Iobject;
11 static PyObject *IO_module;
1712
1813 Request* Request_new(ServerInfo* server_info, int client_fd, const char* client_addr)
1914 {
2419 #endif
2520 request->server_info = server_info;
2621 request->client_fd = client_fd;
27 request->client_addr = PyString_FromString(client_addr);
22 request->client_addr = _Unicode_FromString(client_addr);
2823 http_parser_init((http_parser*)&request->parser, HTTP_REQUEST);
2924 request->parser.parser.data = request;
3025 Request_reset(request);
9388 do { PARSER->name.len = (name - PARSER->name.data) + len; } while(0)
9489
9590 #define _set_header(k, v) PyDict_SetItem(REQUEST->headers, k, v);
91 /* PyDict_SetItem() increases the ref-count for value */
9692 #define _set_header_free_value(k, v) \
9793 do { \
9894 PyObject* val = (v); \
10096 Py_DECREF(val); \
10197 } while(0)
10298
99
103100 #define _set_header_from_http_header() \
104101 do { \
105102 PyObject* key = wsgi_http_header(PARSER->field); \
106103 if (key) { \
107 _set_header_free_value(key, PyString_FromStringAndSize(PARSER->value.data, PARSER->value.len)); \
104 _set_header_free_value(key, _Unicode_FromStringAndSize(PARSER->value.data, PARSER->value.len)); \
108105 Py_DECREF(key); \
109106 } \
110107 } while(0) \
123120 {
124121 if(!(len = unquote_url_inplace((char*)path, len)))
125122 return 1;
126 _set_header_free_value(_PATH_INFO, PyString_FromStringAndSize(path, len));
123 _set_header_free_value(_PATH_INFO, _Unicode_FromStringAndSize(path, len));
127124 return 0;
128125 }
129126
130127 static int
131128 on_query_string(http_parser* parser, const char* query, size_t len)
132129 {
133 _set_header_free_value(_QUERY_STRING, PyString_FromStringAndSize(query, len));
130 _set_header_free_value(_QUERY_STRING, _Unicode_FromStringAndSize(query, len));
134131 return 0;
135132 }
136133
173170 static int
174171 on_body(http_parser* parser, const char* data, const size_t len)
175172 {
176 Iobject* body;
177
178 body = (Iobject*)PyDict_GetItem(REQUEST->headers, _wsgi_input);
179 if(body == NULL) {
173 PyObject *body;
174
175 body = PyDict_GetItem(REQUEST->headers, _wsgi_input);
176 if (body == NULL) {
180177 if(!parser->content_length) {
181178 REQUEST->state.error_code = HTTP_LENGTH_REQUIRED;
182179 return 1;
183180 }
184 PyObject* buf = PyString_FromStringAndSize(NULL, parser->content_length);
185 body = (Iobject*)PycStringIO->NewInput(buf);
186 Py_XDECREF(buf);
187 if(body == NULL)
181 body = PyObject_CallMethodObjArgs(IO_module, _BytesIO, NULL);
182 if (body == NULL) {
188183 return 1;
189 _set_header(_wsgi_input, (PyObject*)body);
190 Py_DECREF(body);
191 }
192 memcpy(body->buf + body->pos, data, len);
193 body->pos += len;
184 }
185 _set_header_free_value(_wsgi_input, body);
186 }
187 PyObject *temp_data = _Bytes_FromStringAndSize(data, len);
188 PyObject *tmp = PyObject_CallMethodObjArgs(body, _write, temp_data, NULL);
189 Py_DECREF(tmp); /* Never throw away return objects from py-api */
190 Py_DECREF(temp_data);
194191 return 0;
195192 }
196193
216213 _set_header(_REQUEST_METHOD, _GET);
217214 } else {
218215 _set_header_free_value(_REQUEST_METHOD,
219 PyString_FromString(http_method_str(parser->method)));
216 _Unicode_FromString(http_method_str(parser->method))
217 );
220218 }
221219
222220 /* REMOTE_ADDR */
224222
225223 PyObject* body = PyDict_GetItem(REQUEST->headers, _wsgi_input);
226224 if(body) {
227 /* We abused the `pos` member for tracking the amount of data copied from
228 * the buffer in on_body, so reset it to zero here. */
229 ((Iobject*)body)->pos = 0;
225 /* first do a seek(0) and then read() returns all data */
226 PyObject *buf = PyObject_CallMethodObjArgs(body, _seek, _FromLong(0), NULL);
227 Py_DECREF(buf); /* Discard the return value */
230228 } else {
231229 /* Request has no body */
232 _set_header_free_value(_wsgi_input, PycStringIO->NewInput(_empty_string));
230 body = PyObject_CallMethodObjArgs(IO_module, _BytesIO, NULL);
231 _set_header_free_value(_wsgi_input, body);
233232 }
234233
235234 PyDict_Update(REQUEST->headers, wsgi_base_dict);
242241 static PyObject*
243242 wsgi_http_header(string header)
244243 {
245 PyObject* obj = PyString_FromStringAndSize(NULL, header.len+strlen("HTTP_"));
246 char* dest = PyString_AS_STRING(obj);
247
248 *dest++ = 'H';
249 *dest++ = 'T';
250 *dest++ = 'T';
251 *dest++ = 'P';
252 *dest++ = '_';
244 const char *http_ = "HTTP_";
245 int size = header.len+strlen(http_);
246 char dest[size];
247 int i = 5;
248
249 memcpy(dest, http_, i);
253250
254251 while(header.len--) {
255252 char c = *header.data++;
256253 if (c == '_') {
257254 // CVE-2015-0219
258 Py_DECREF(obj);
259255 return NULL;
260256 }
261257 else if(c == '-')
262 *dest++ = '_';
258 dest[i++] = '_';
263259 else if(c >= 'a' && c <= 'z')
264 *dest++ = c - ('a'-'A');
260 dest[i++] = c - ('a'-'A');
265261 else
266 *dest++ = c;
267 }
268
269 return obj;
262 dest[i++] = c;
263 }
264
265 return (PyObject *)_Unicode_FromStringAndSize(dest, size);
270266 }
271267
272268 static inline void
290286
291287 void _initialize_request_module()
292288 {
289 IO_module = PyImport_ImportModule("io");
290 if (IO_module == NULL) {
291 /* PyImport_ImportModule should have exception set already */
292 return;
293 }
294
293295 if(wsgi_base_dict == NULL) {
294 PycString_IMPORT;
295296 wsgi_base_dict = PyDict_New();
296297
297298 /* dct['wsgi.file_wrapper'] = FileWrapper */
312313 PyDict_SetItemString(
313314 wsgi_base_dict,
314315 "wsgi.version",
315 PyTuple_Pack(2, PyInt_FromLong(1), PyInt_FromLong(0))
316 PyTuple_Pack(2, _FromLong(1), _FromLong(0))
316317 );
317318
318319 /* dct['wsgi.url_scheme'] = 'http'
320321 PyDict_SetItemString(
321322 wsgi_base_dict,
322323 "wsgi.url_scheme",
323 PyString_FromString("http")
324 _Unicode_FromString("http")
324325 );
325326
326327 /* dct['wsgi.errors'] = sys.stderr */
1515 #include "common.h"
1616 #include "wsgi.h"
1717 #include "server.h"
18
19 #include "py2py3.h"
1820
1921 #define READ_BUFFER_SIZE 64*1024
2022 #define Py_XCLEAR(obj) do { if(obj) { Py_DECREF(obj); obj = NULL; } } while(0)
174176 /* HTTP parse error */
175177 read_state = done;
176178 DBG_REQ(request, "Parse error");
177 request->current_chunk = PyString_FromString(
179 request->current_chunk = _Bytes_FromString(
178180 http_error_messages[request->state.error_code]);
179181 assert(request->iterator == NULL);
180182 } else if(request->state.parse_finished) {
188190 PyErr_Print();
189191 assert(!request->state.chunked_response);
190192 Py_XCLEAR(request->iterator);
191 request->current_chunk = PyString_FromString(
193 request->current_chunk = _Bytes_FromString(
192194 http_error_messages[HTTP_SERVER_ERROR]);
193195 }
194196 } else {
355357 send_terminator_chunk:
356358 if(request->state.chunked_response) {
357359 /* We have to send a terminating empty chunk + \r\n */
358 request->current_chunk = PyString_FromString("0\r\n\r\n");
360 request->current_chunk = _Bytes_FromString("0\r\n\r\n");
359361 assert(request->current_chunk_p == 0);
360362 // Next time we get here, don't send the terminating empty chunk again.
361363 // XXX This is kind of a hack and should be refactored for easier understanding.
373375 Py_ssize_t bytes_sent;
374376
375377 assert(request->current_chunk != NULL);
376 assert(!(request->current_chunk_p == PyString_GET_SIZE(request->current_chunk)
377 && PyString_GET_SIZE(request->current_chunk) != 0));
378 assert(!(request->current_chunk_p == _Bytes_GET_SIZE(request->current_chunk)
379 && _Bytes_GET_SIZE(request->current_chunk) != 0));
378380
379381 bytes_sent = write(
380382 request->client_fd,
381 PyString_AS_STRING(request->current_chunk) + request->current_chunk_p,
382 PyString_GET_SIZE(request->current_chunk) - request->current_chunk_p
383 _Bytes_AS_DATA(request->current_chunk) + request->current_chunk_p,
384 _Bytes_GET_SIZE(request->current_chunk) - request->current_chunk_p
383385 );
384386
385387 if(bytes_sent == -1)
386388 return handle_nonzero_errno(request);
387389
388390 request->current_chunk_p += bytes_sent;
389 if(request->current_chunk_p == PyString_GET_SIZE(request->current_chunk)) {
391 if(request->current_chunk_p == _Bytes_GET_SIZE(request->current_chunk)) {
390392 Py_CLEAR(request->current_chunk);
391393 request->current_chunk_p = 0;
392394 return false;
00 #include "common.h"
11 #include "filewrapper.h"
22 #include "wsgi.h"
3 #include "py2py3.h"
34
45 static size_t wsgi_getheaders(Request*, PyObject* buf);
56
6162 PyObject* first_chunk;
6263
6364 if(PyList_Check(retval) && PyList_GET_SIZE(retval) == 1 &&
64 PyString_Check(PyList_GET_ITEM(retval, 0)))
65 _Bytes_Check(PyList_GET_ITEM(retval, 0)))
6566 {
6667 /* Optimize the most common case, a single string in a list: */
6768 PyObject* tmp = PyList_GET_ITEM(retval, 0);
6970 Py_DECREF(retval);
7071 retval = tmp;
7172 goto string; /* eeevil */
72 } else if(PyString_Check(retval)) {
73 } else if(_Bytes_Check(retval)) {
7374 /* According to PEP 333 strings should be handled like any other iterable,
7475 * i.e. sending the response item for item. "item for item" means
7576 * "char for char" if you have a string. -- I'm not that stupid. */
7677 string:
77 if(PyString_GET_SIZE(retval)) {
78 if(_Bytes_GET_SIZE(retval)) {
7879 first_chunk = retval;
7980 } else {
8081 Py_DECREF(retval);
137138 * At least for small responses, the complete response could be sent with
138139 * one send() call (in server.c:ev_io_on_write) which is a (tiny) performance
139140 * booster because less kernel calls means less kernel call overhead. */
140 PyObject* buf = PyString_FromStringAndSize(NULL, 1024);
141 PyObject* buf = _Bytes_FromStringAndSize(NULL, 1024);
141142 Py_ssize_t length = wsgi_getheaders(request, buf);
142143
143144 if(first_chunk == NULL) {
144 _PyString_Resize(&buf, length);
145 _Bytes_Resize(&buf, length);
145146 goto out;
146147 }
147148
148149 if(request->state.chunked_response) {
149150 PyObject* new_chunk = wrap_http_chunk_cruft_around(first_chunk);
150151 Py_DECREF(first_chunk);
151 assert(PyString_GET_SIZE(new_chunk) >= PyString_GET_SIZE(first_chunk) + 5);
152 assert(_Bytes_GET_SIZE(new_chunk) >= _Bytes_GET_SIZE(first_chunk) + 5);
152153 first_chunk = new_chunk;
153154 }
154155
155156 assert(buf);
156 _PyString_Resize(&buf, length + PyString_GET_SIZE(first_chunk));
157 memcpy(PyString_AS_STRING(buf)+length, PyString_AS_STRING(first_chunk),
158 PyString_GET_SIZE(first_chunk));
157 _Bytes_Resize(&buf, length + _Bytes_GET_SIZE(first_chunk));
158 memcpy((void *)(_Bytes_AS_DATA(buf)+length), _Bytes_AS_DATA(first_chunk),
159 _Bytes_GET_SIZE(first_chunk));
159160
160161 Py_DECREF(first_chunk);
161162
181182 PyObject* field = PyTuple_GET_ITEM(tuple, 0);
182183 PyObject* value = PyTuple_GET_ITEM(tuple, 1);
183184
184 if(!PyString_Check(field) || !PyString_Check(value))
185 if(!_Unicode_Check(field) || !_Unicode_Check(value))
185186 goto err;
186187
187 if(!strncasecmp(PyString_AS_STRING(field), "Content-Length", PyString_GET_SIZE(field)))
188 if(!strncasecmp(_Unicode_AS_DATA(field), "Content-Length", _Unicode_GET_SIZE(field)))
188189 request->state.response_length_unknown = false;
189190 }
190191 return true;
199200 static size_t
200201 wsgi_getheaders(Request* request, PyObject* buf)
201202 {
202 char* bufp = PyString_AS_STRING(buf);
203 char* bufp = (char *)_Bytes_AS_DATA(buf);
203204
204205 #define buf_write(src, len) \
205206 do { \
211212
212213 /* First line, e.g. "HTTP/1.1 200 Ok" */
213214 buf_write2("HTTP/1.1 ");
214 buf_write(PyString_AS_STRING(request->status),
215 PyString_GET_SIZE(request->status));
215 buf_write(_Unicode_AS_DATA(request->status),
216 _Unicode_GET_SIZE(request->status));
216217
217218 /* Headers, from the `request->headers` mapping.
218219 * [("Header1", "value1"), ("Header2", "value2")]
223224 PyObject *field = PyTuple_GET_ITEM(tuple, 0),
224225 *value = PyTuple_GET_ITEM(tuple, 1);
225226 buf_write2("\r\n");
226 buf_write(PyString_AS_STRING(field), PyString_GET_SIZE(field));
227 buf_write(_Unicode_AS_DATA(field), _Unicode_GET_SIZE(field));
227228 buf_write2(": ");
228 buf_write(PyString_AS_STRING(value), PyString_GET_SIZE(value));
229 buf_write(_Unicode_AS_DATA(value), _Unicode_GET_SIZE(value));
229230 }
230231
231232 /* See `wsgi_call_application` */
240241
241242 buf_write2("\r\n\r\n");
242243
243 return bufp - PyString_AS_STRING(buf);
244 return bufp - _Bytes_AS_DATA(buf);
244245 }
245246
246247 inline PyObject*
252253 next = PyIter_Next(request->iterator);
253254 if(next == NULL)
254255 return NULL;
255 if(!PyString_Check(next)) {
256 TYPE_ERROR("wsgi iterable items", "strings", next);
256 if(!_Bytes_Check(next)) {
257 TYPE_ERROR("wsgi iterable items", "bytes", next);
257258 Py_DECREF(next);
258259 return NULL;
259260 }
260 if(PyString_GET_SIZE(next))
261 if(_Bytes_GET_SIZE(next))
261262 return next;
262263 Py_DECREF(next);
263264 }
321322 return NULL;
322323 }
323324
324 if(!PyString_Check(status)) {
325 if(!_Unicode_Check(status)) {
325326 TYPE_ERROR("start_response argument 1", "a 'status reason' string", status);
326327 return NULL;
327328 }
358359 };
359360
360361
361
362
363362 PyObject*
364363 wrap_http_chunk_cruft_around(PyObject* chunk)
365364 {
366365 /* Who the hell decided to use decimal representation for Content-Length
367366 * but hexadecimal representation for chunk lengths btw!?! Fuck W3C */
368 size_t chunklen = PyString_GET_SIZE(chunk);
367 size_t chunklen = _Bytes_GET_SIZE(chunk);
369368 assert(chunklen);
370369 char buf[strlen("ffffffff") + 2];
371370 size_t n = sprintf(buf, "%x\r\n", (unsigned int)chunklen);
372 PyObject* new_chunk = PyString_FromStringAndSize(NULL, n + chunklen + 2);
373 char* new_chunk_p = PyString_AS_STRING(new_chunk);
371 PyObject* new_chunk = _Bytes_FromStringAndSize(NULL, n + chunklen + 2);
372 char * new_chunk_p = (char *)_Bytes_AS_DATA(new_chunk);
374373 memcpy(new_chunk_p, buf, n);
375374 new_chunk_p += n;
376 memcpy(new_chunk_p, PyString_AS_STRING(chunk), chunklen);
375 memcpy(new_chunk_p, _Bytes_AS_DATA(chunk), chunklen);
377376 new_chunk_p += chunklen;
378377 *new_chunk_p++ = '\r'; *new_chunk_p = '\n';
379 assert(new_chunk_p == PyString_AS_STRING(new_chunk) + n + chunklen + 1);
378 assert(new_chunk_p == _Bytes_AS_DATA(new_chunk) + n + chunklen + 1);
380379 return new_chunk;
381380 }
1111 include_dirs = ['http-parser', '/usr/include/libev'],
1212 define_macros = [('WANT_SENDFILE', '1'),
1313 ('WANT_SIGINT_HANDLING', '1')],
14 extra_compile_args = ['-std=c99', '-fno-strict-aliasing', '-fcommon',
14 extra_compile_args = ['-std=c99', '-fno-strict-aliasing', '-fcommon'
1515 '-fPIC', '-Wall', '-Wextra', '-Wno-unused-parameter',
1616 '-Wno-missing-field-initializers', '-g']
1717 )
00 def app(e, s):
11 s('200 ok', [])
2 return ''
2 return b''
33
44 import bjoern
55 bjoern.run(app, '0.0.0.0', 8080)
77
88 def app(environ, start_response):
99 start_response('200 OK', [])
10 yield 'Hello world from worker %d' % os.getpid()
10 yield b'Hello world from worker %d' % os.getpid()
1111
1212
1313 bjoern.listen(app, '0.0.0.0', 8080)
14 for _ in xrange(NUM_WORKERS):
14 for _ in range(NUM_WORKERS):
1515 pid = os.fork()
1616 if pid > 0:
1717 # in master
2525 exit()
2626
2727 try:
28 for _ in xrange(NUM_WORKERS):
28 for _ in range(NUM_WORKERS):
2929 os.wait()
3030 except KeyboardInterrupt:
3131 for pid in worker_pids:
11 from random import choice
22
33 def app1(env, sr):
4 print env
4 #print(env)
55 sr('200 ok', [('Foo', 'Bar'), ('Blah', 'Blubb'), ('Spam', 'Eggs'), ('Blurg', 'asdasjdaskdasdjj asdk jaks / /a jaksdjkas jkasd jkasdj '),
66 ('asd2easdasdjaksdjdkskjkasdjka', 'oasdjkadk kasdk k k k k k ')])
7 return ['hello', 'world']
7 return [b'hello', b'world']
88
99 def app2(env, sr):
10 print env
10 #print(env)
1111 sr('200 ok', [])
12 return 'hello'
12 return b'hello'
1313
1414 def app3(env, sr):
15 print env
15 #print(env)
1616 sr('200 abc', [('Content-Length', '12')])
17 yield 'Hello'
18 yield ' World'
19 yield '\n'
17 yield b'Hello'
18 yield b' World'
19 yield b'\n'
2020
21 def app4(e, s):
22 print e
23 s('200 ok', [])
24 return ['hello\n']
21 def app4(e, sr):
22 sr('200 ok', [])
23 return [b'hello there ... \n']
2524
2625 apps = (app1, app2, app3, app4)
2726
00 N = 1024
1 CHUNK = 'a' * 1024
1 CHUNK = b'a' * 1024
22 DATA_LEN = N * len(CHUNK)
33
44 class _iter(object):
55 def __iter__(self):
6 for i in xrange(N):
6 for i in range(N):
77 yield CHUNK
88
99 def app(e, s):
1010
1111 def application(environ, start_response):
1212 start_response('200 ok', [])
13 yield "chunk1"
13 yield b"chunk1"
1414 os.kill(os.getpid(), signal.SIGINT)
15 yield "chunk2"
16 yield "chunk3"
15 yield b"chunk2"
16 yield b"chunk3"
1717 global request_completed
1818 request_completed = True
1919
22
33 def app(environ, start_response):
44 start_response('200 OK', [])
5 yield 'Hello world'
6 yield ''
5 yield b'Hello world'
6 yield b''
77
88 bjoern.listen(app, '0.0.0.0', 8080)
99 bjoern.run()
3939 def cmd_app():
4040 def app(environ, start_response):
4141 start_response('200 OK', [])
42 return ["Hello from process %d\n" % os.getpid()]
42 return [b"Hello from process %d\n" % os.getpid()]
4343
4444 import bjoern
4545 bjoern.run(app, "localhost", 8080, True)
77 def return_tuple(environ, start_response):
88 start_response('200 OK', [('Content-Type','text/plain')])
99 print 'tuple'
10 return ('Hello,', " it's me, ", 'Bob!')
10 return (b'Hello,', b" it's me, ", b'Bob!')
1111
1212 def return_huge_answer(environ, start_response):
1313 start_response('200 OK', [('Content-Type','text/plain')])
14 return ['x'*(1024*1024)]
14 return [b'x'*(1024*1024)]
1515
1616 def return_404(environ, start_response):
1717 start_response('404 Not Found', (('Content-Type','text/plain'), ))
18 return "URL %s not found" % environ.get('PATH_INFO', 'UNKNOWN')
18 return b"URL %s not found" % environ.get('PATH_INFO', 'UNKNOWN')
1919
2020 dispatch = {
2121 '/tuple': return_tuple,
2121 import sys
2222 x = sys.exc_info()
2323 start_response('500 error', alist, x)
24 return ['hello']
24 return [b'hello']
2525
2626 import bjoern
2727 bjoern.run(app, '0.0.0.0', 8080)