Browse Source

Implement partial support for wsgi.* environ

The url_scheme is static to "http" for the moment...
Yann Weber 4 years ago
parent
commit
512580c924
8 changed files with 670 additions and 4 deletions
  1. 5
    0
      include/conf.h
  2. 93
    0
      include/python_ioin.h
  3. 8
    0
      include/python_pyfcgi.h
  4. 2
    2
      src/Makefile.am
  5. 512
    0
      src/python_ioin.c
  6. 31
    1
      src/python_pyfcgi.c
  7. 18
    1
      src/pyutils.c
  8. 1
    0
      src/pyworker.c

+ 5
- 0
include/conf.h View File

@@ -125,8 +125,13 @@ struct pyfcgi_context_s {
125 125
 	/**@brief workers count */
126 126
 	unsigned int n_wrk;
127 127
 
128
+	/**@brief Stores python_path (not dupped by python) */
128 129
 	wchar_t python_path[PATH_MAX];
130
+	/**@brief Stores venv_path for python home (not dupped by python) */
129 131
 	wchar_t venv_path[PATH_MAX];
132
+
133
+	/**@brief Stores a part of the environ (containing wsgi.* keys) */
134
+	PyObject *wsgi_dict;
130 135
 };
131 136
 
132 137
 /**@brief Structure containing configuration

+ 93
- 0
include/python_ioin.h View File

@@ -0,0 +1,93 @@
1
+/*
2
+ * Copyright (C) 2019 Weber Yann
3
+ * 
4
+ * This file is part of PyFCGI.
5
+ * 
6
+ * PyFCGI is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU Affero General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * any later version.
10
+ * 
11
+ * PyFCGI is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU Affero General Public License for more details.
15
+ * 
16
+ * You should have received a copy of the GNU Affero General Public License
17
+ * along with PyFCGI.  If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+
20
+/**@defgroup lib_ioin Defines libpyfcgi.IoIn class
21
+ * @brief The IoIn class implement base IO interface allowing python to read
22
+ * HTTP request from FCGI
23
+ * @ingroup libpyfcgo
24
+ */
25
+/**@file python_ioin.h
26
+ * @ingroup lib_ioin
27
+ */
28
+
29
+#ifndef _PYTHON_IOIN__H__
30
+#define _PYTHON_IOIN__H__
31
+
32
+#include "config.h"
33
+
34
+#include <fcgiapp.h>
35
+#include <fcgi_stdio.h> /* fcgi library; put it first */
36
+
37
+#define PY_SSIZE_T_CLEAN
38
+#include <Python.h>
39
+#include "structmember.h"
40
+
41
+#include <limits.h>
42
+#include <stdlib.h>
43
+
44
+#define IoIn__FromString(self, str) \
45
+(((IoIn*)self)->bin?PyBytes_FromString:PyUnicode_FromString)(str)
46
+
47
+#define IoIn__FromBuff(self) IoIn__FromString(self, ((IoIn*)self)->buff)
48
+
49
+extern PyMethodDef IoIn_methods[];
50
+extern PyMemberDef IoIn_members[];
51
+extern PyTypeObject IoInType;
52
+
53
+typedef struct
54
+{
55
+	PyObject_VAR_HEAD;
56
+	PyObject *closed;
57
+	FCGX_Stream **in_stream;
58
+	char *buff;
59
+	/**@brief buffer size */
60
+	int buff_sz;
61
+	/**@brief 1 if binary stream (sending PyBytes) 0 if sending PyUnicode */
62
+	short bin;
63
+	/**@brief 1 if EOF encountered, else 0 */
64
+	short eof;
65
+} IoIn;
66
+
67
+int pyfcgi_ioin_init(PyObject *self, PyObject *args, PyObject *kwds);
68
+void pyfcgi_ioin_del(IoIn *self);
69
+
70
+PyObject* pyfcgi_ioin_close(PyObject *self, PyObject **argv, Py_ssize_t argc);
71
+PyObject* pyfcgi_ioin_fileno(PyObject *self, PyObject **argv, Py_ssize_t argc);
72
+PyObject* pyfcgi_ioin_flush(PyObject *self, PyObject **argv, Py_ssize_t argc);
73
+PyObject* pyfcgi_ioin_isatty(PyObject *self, PyObject **argv, Py_ssize_t argc);
74
+PyObject* pyfcgi_ioin_readable(PyObject *self, PyObject **argv, Py_ssize_t argc);
75
+PyObject* pyfcgi_ioin_readline(PyObject *self, PyObject **argv, Py_ssize_t argc);
76
+PyObject* pyfcgi_ioin_readlines(PyObject *self, PyObject **argv, Py_ssize_t argc);
77
+PyObject* pyfcgi_ioin_read(PyObject *self, PyObject **argv, Py_ssize_t argc);
78
+PyObject* pyfcgi_ioin_readall(PyObject *self, PyObject **argv, Py_ssize_t argc);
79
+PyObject* pyfcgi_ioin_readinto(PyObject *self, PyObject **argv, Py_ssize_t argc);
80
+
81
+PyObject* pyfcgi_ioin_WriteError(PyObject *self, PyObject **argv, Py_ssize_t argc);
82
+#define pyfcgi_ioin_writelines pyfcgi_ioin_WriteError
83
+#define pyfcgi_ioin_write pyfcgi_ioin_WriteError
84
+
85
+PyObject* pyfcgi_ioin_SeekError(PyObject *self, PyObject **argv, Py_ssize_t argc);
86
+#define pyfcgi_ioin_seek pyfcgi_ioin_SeekError
87
+#define pyfcgi_ioin_tell pyfcgi_ioin_SeekError
88
+
89
+PyObject* pyfcgi_ioin_truncate(PyObject *self, PyObject **argv, Py_ssize_t argc);
90
+PyObject* pyfcgi_ioin_seekable(PyObject *self, PyObject **argv, Py_ssize_t argc);
91
+PyObject* pyfcgi_ioin_writable(PyObject *self, PyObject **argv, Py_ssize_t argc);
92
+
93
+#endif

+ 8
- 0
include/python_pyfcgi.h View File

@@ -43,6 +43,7 @@
43 43
 #include "structmember.h"
44 44
 
45 45
 #include "logger.h"
46
+#include "python_ioin.h"
46 47
 
47 48
 #define LIBPYFCGI_DEFAULT_HEADERS "Content-Type: text/html\r\nStatus: %s\r\n\r\n"
48 49
 #define LIBPYFCGI_DEFAULT_STATUS "200 OK"
@@ -60,12 +61,17 @@ struct libpyfcgi_context_s
60 61
 	/**@brief PEP333 headers ref
61 62
 	 * @ingroup libpyfcgi */
62 63
 	PyObject *headers;
64
+	/**@brief libpyfcgi.IoIn instance */
65
+	IoIn *ioin;
63 66
 	/**@brief Indicate if headers was sent in a PEP333 application
64 67
 	 * @ingroup libpyfcgi */
65 68
 	short headers_sent;
66 69
 	/**@brief Stores the libFCGI stream for PEP333 application
67 70
 	 * @ingroup libpyfcgi */
68 71
 	FCGX_Stream *out;
72
+	/**@brief Stores the libFCGI stream from wich HTTP request can be read
73
+	 * @ingroup libpyfcgi */
74
+	FCGX_Stream *in;
69 75
 	/**@brief Persistent buffer (avoid malloc) for PEP333 headers */
70 76
 	char *heads_buf;
71 77
 	/**@brief Buffer size */
@@ -93,6 +99,8 @@ inline void libpyfcgi_clean_response()
93 99
 	libpyfcgi.headers = NULL;
94 100
 	libpyfcgi.headers_sent = 0;
95 101
 	libpyfcgi.rep_sz = 0;
102
+	libpyfcgi.ioin->eof=0;
103
+	libpyfcgi.ioin->closed=Py_False;
96 104
 }
97 105
 
98 106
 /**@brief Send headers stored in @ref libpyfcgi context

+ 2
- 2
src/Makefile.am View File

@@ -6,13 +6,13 @@ pyfcgi_LDADD = $(PYTHON_LDFLAGS)
6 6
 
7 7
 # libpyfcgi python module
8 8
 lib_LTLIBRARIES = libpyfcgi.la
9
-libpyfcgi_la_SOURCES = python_pyfcgi.c
9
+libpyfcgi_la_SOURCES = python_pyfcgi.c python_ioin.c
10 10
 libpyfcgi_la_CFLAGS = $(PYTHON_SO_CFLAGS)
11 11
 libpyfcgi_la_LDFLAGS = $(PYTHON_SO_LDFLAGS)
12 12
 
13 13
 # static librarie for check
14 14
 noinst_LIBRARIES = libpyfcgi.a
15
-libpyfcgi_a_SOURCES = logger.c pyworker.c responder.c conf.c pyutils.c python_pyfcgi.c
15
+libpyfcgi_a_SOURCES = logger.c pyworker.c responder.c conf.c pyutils.c python_pyfcgi.c python_ioin.c
16 16
 libpyfcgi_a_CFLAGS = $(PYTHON_CFLAGS)
17 17
 
18 18
 

+ 512
- 0
src/python_ioin.c View File

@@ -0,0 +1,512 @@
1
+#include "python_ioin.h"
2
+/**@file python_ioin.c
3
+ * @ingroup lib_ioin
4
+ */
5
+
6
+/**@brief If given object in_stream is NULL set error indicator
7
+ * @return 0 if no error else -1 or -2
8
+ */
9
+static int _check_nullin(PyObject *self);
10
+
11
+/**@brief Request a new buffer size.
12
+ * 
13
+ * @param PyObject* self The IoIn instance
14
+ * @param int nreq number of bytes required in buffer
15
+ * @return 0 on error else buffer size
16
+ */
17
+static int pyfcgi_ioin__reqbuf(PyObject *self, int nreq);
18
+
19
+/**@brief Concat two bytes/unicode given IoIn configuration flag
20
+ */
21
+static PyObject* IoIn__Concat(PyObject *self, PyObject *left, PyObject *right);
22
+
23
+PyMethodDef IoIn_methods[] = {
24
+	{"close", (PyCFunction)pyfcgi_ioin_close, METH_FASTCALL, NULL},
25
+	{"fileno", (PyCFunction)pyfcgi_ioin_fileno, METH_FASTCALL, NULL},
26
+	{"flush", (PyCFunction)pyfcgi_ioin_flush, METH_FASTCALL, NULL},
27
+	{"isatty", (PyCFunction)pyfcgi_ioin_isatty, METH_FASTCALL, NULL},
28
+	{"readable", (PyCFunction)pyfcgi_ioin_readable, METH_FASTCALL, NULL},
29
+	{"readline", (PyCFunction)pyfcgi_ioin_readline, METH_FASTCALL, NULL},
30
+	{"readlines", (PyCFunction)pyfcgi_ioin_readlines, METH_FASTCALL, NULL},
31
+	{"seek", (PyCFunction)pyfcgi_ioin_seek, METH_FASTCALL, NULL},
32
+	{"seekable", (PyCFunction)pyfcgi_ioin_seekable, METH_FASTCALL, NULL},
33
+	{"tell", (PyCFunction)pyfcgi_ioin_tell, METH_FASTCALL, NULL},
34
+	{"truncate", (PyCFunction)pyfcgi_ioin_truncate, METH_FASTCALL, NULL},
35
+	{"writable", (PyCFunction)pyfcgi_ioin_writable, METH_FASTCALL, NULL},
36
+	{"writelines", (PyCFunction)pyfcgi_ioin_writelines, METH_FASTCALL, NULL},
37
+	{"read", (PyCFunction)pyfcgi_ioin_read, METH_FASTCALL, NULL},
38
+	{"readall", (PyCFunction)pyfcgi_ioin_readall, METH_FASTCALL, NULL},
39
+	{"readinto", (PyCFunction)pyfcgi_ioin_readinto, METH_FASTCALL, NULL},
40
+	{"write", (PyCFunction)pyfcgi_ioin_write, METH_FASTCALL, NULL},
41
+	{NULL} //Sentinel
42
+};
43
+
44
+PyMemberDef IoIn_members[] = {
45
+	{"closed", T_OBJECT, offsetof(IoIn, closed), READONLY, "True if the stream is closed"},
46
+	{NULL}
47
+};
48
+
49
+PyTypeObject IoInType = {
50
+	PyVarObject_HEAD_INIT(NULL, 0)
51
+	"libpyfcgi.IoIn",                     /* tp_name */
52
+	sizeof(IoIn),                        /* tp_basicsize */
53
+	0,                                               /* tp_itemsize */
54
+	(destructor)pyfcgi_ioin_del, /* tp_dealloc */
55
+	0,                                               /* tp_print */
56
+	0,                                               /* tp_getattr */
57
+	0,                                               /* tp_setattr */
58
+	0,                                               /* tp_reserved */
59
+	0,                                               /* tp_repr */
60
+	0,                                               /* tp_as_number */
61
+	0,                                               /* tp_as_sequence */
62
+	0,                              /* tp_as_mapping */
63
+	0,                                               /* tp_hash  */
64
+	0,                                               /* tp_call */
65
+	0,                                               /* tp_str */
66
+	0,                                               /* tp_getattro */
67
+	0,                                               /* tp_setattro */
68
+	0,                                               /* tp_as_buffer */
69
+	Py_TPFLAGS_DEFAULT |
70
+	Py_TPFLAGS_BASETYPE,   /* tp_flags */
71
+	"RawIo interface to FCGI input stream",                /* tp_doc */
72
+	0,                                               /* tp_traverse */
73
+	0,                                               /* tp_clear */
74
+	0,                                               /* tp_richcompare */
75
+	0,                                               /* tp_weaklistoffset */
76
+	0,                                               /* tp_iter */
77
+	0,                                               /* tp_iternext */
78
+	IoIn_methods,                        /* tp_methods */
79
+	IoIn_members,                        /* tp_members */
80
+	0,                                               /* tp_getset */
81
+	0,                                               /* tp_base */
82
+	0,                                               /* tp_dict */
83
+	0,                                               /* tp_descr_get */
84
+	0,                                               /* tp_descr_set */
85
+	0,                                               /* tp_dictoffset */
86
+	pyfcgi_ioin_init,          /* tp_init */
87
+	0,                                               /* tp_alloc */
88
+	0,                            /* tp_new */
89
+};
90
+
91
+
92
+int pyfcgi_ioin_init(PyObject *self, PyObject *args, PyObject *kwds)
93
+{
94
+	IoIn *ioin = (void*)self;
95
+	ioin->in_stream = NULL;
96
+	ioin->buff = NULL;
97
+	ioin->buff_sz = 0;
98
+	ioin->eof=0;
99
+	ioin->bin=1;
100
+	return 0;
101
+}
102
+
103
+void pyfcgi_ioin_del(IoIn *self)
104
+{
105
+	IoIn *ioin = self;
106
+	if(ioin->buff) { free(ioin->buff); }
107
+	Py_TYPE(self)->tp_free((PyObject*)self);
108
+}
109
+
110
+PyObject* pyfcgi_ioin_close(PyObject *self, PyObject **argv, Py_ssize_t argc)
111
+{
112
+	if(_check_nullin(self)) { Py_RETURN_NONE; }
113
+	if(argc)
114
+	{
115
+		PyErr_Format(PyExc_ValueError,
116
+			"libpyfcgi.IoIn.close() not expecting any argument but %zd given",
117
+			argc);
118
+		Py_RETURN_NONE;
119
+	}
120
+	if(FCGX_FClose(*((IoIn*)self)->in_stream) < 0)
121
+	{
122
+		PyErr_Format(PyExc_OSError,
123
+			"%A unable to close", self);
124
+	}
125
+	((IoIn*)self)->closed = Py_True;
126
+	Py_RETURN_NONE;
127
+}
128
+
129
+PyObject* pyfcgi_ioin_fileno(PyObject *self, PyObject **argv, Py_ssize_t argc)
130
+{
131
+	PyErr_SetString(PyExc_OSError, "libpyfcgi.IoIn has no fileno");
132
+	Py_RETURN_NONE;
133
+}
134
+
135
+PyObject* pyfcgi_ioin_flush(PyObject *self, PyObject **argv, Py_ssize_t argc)
136
+{
137
+	if(_check_nullin(self)) { Py_RETURN_NONE; }
138
+	if(argc)
139
+	{
140
+		PyErr_Format(PyExc_ValueError,
141
+			"libpyfcgi.IoIn.close() not expecting any argument but %zd given",
142
+			argc);
143
+		Py_RETURN_NONE;
144
+	}
145
+	if(FCGX_FFlush(*((IoIn*)self)->in_stream) < 0)
146
+	{
147
+		PyErr_Format(PyExc_OSError,
148
+			"%A unable to flush", self);
149
+	}
150
+	Py_RETURN_NONE;
151
+}
152
+
153
+PyObject* pyfcgi_ioin_isatty(PyObject *self, PyObject **argv, Py_ssize_t argc)
154
+{
155
+	return Py_False;
156
+}
157
+
158
+PyObject* pyfcgi_ioin_readable(PyObject *self, PyObject **argv, Py_ssize_t argc)
159
+{
160
+	return Py_True;
161
+}
162
+
163
+PyObject* pyfcgi_ioin_readline(PyObject *self, PyObject **argv, Py_ssize_t argc)
164
+{
165
+	int read_n;
166
+	long arg;
167
+	char *ret;
168
+
169
+	if(_check_nullin(self)) { Py_RETURN_NONE; }
170
+	if(argc > 1)
171
+	{
172
+		PyErr_Format(PyExc_ValueError,
173
+			"libpyfcgi.IoIn.close() expecting 0 or 1 argument but %zd given",
174
+			argc);
175
+		Py_RETURN_NONE;
176
+	}
177
+	if(!argc || !argv[0])
178
+	{
179
+		read_n = pyfcgi_ioin__reqbuf(self, 0);
180
+	}
181
+	else
182
+	{
183
+		arg = PyLong_AsLong(argv[0]);
184
+		if(PyErr_Occurred())
185
+		{
186
+			Py_RETURN_NONE;
187
+		}
188
+		read_n = pyfcgi_ioin__reqbuf(self, (arg>INT_MAX)?INT_MAX:arg);
189
+	}
190
+
191
+	ret = ((IoIn*)self)->buff;
192
+	if(!FCGX_GetLine(ret, read_n, *((IoIn*)self)->in_stream))
193
+	{
194
+		((IoIn*)self)->eof = 1;
195
+		ret = "";
196
+	}
197
+	return IoIn__FromString(self, ret);
198
+}
199
+
200
+PyObject* pyfcgi_ioin_readlines(PyObject *self, PyObject **argv, Py_ssize_t argc)
201
+{
202
+	size_t hint, left;
203
+	int read_n, toread;
204
+	size_t sz;
205
+	PyObject *res, *cur_str, *read_str, *tmp;
206
+	char *buff;
207
+
208
+	if(_check_nullin(self)) { Py_RETURN_NONE; }
209
+	if(argc > 1)
210
+	{
211
+		PyErr_Format(PyExc_ValueError,
212
+			"libpyfcgi.IoIn.close() expecting 0 or 1 argument but %zd given",
213
+			argc);
214
+		Py_RETURN_NONE;
215
+	}
216
+	if(!argc || !argv[0])
217
+	{
218
+		hint = 0;
219
+		left = 0;
220
+	}
221
+	else
222
+	{
223
+		hint = PyLong_AsSize_t(argv[0]);
224
+		if(PyErr_Occurred())
225
+		{
226
+			Py_RETURN_NONE;
227
+		}
228
+		left = hint;
229
+	}
230
+
231
+	res = PyList_New(0);
232
+	if(!res)
233
+	{
234
+		Py_RETURN_NONE;
235
+	}
236
+	Py_INCREF(res);
237
+
238
+	read_n = pyfcgi_ioin__reqbuf(self, 0);
239
+	buff = ((IoIn*)self)->buff;
240
+	cur_str = NULL;
241
+
242
+	while((hint && left) || !hint)
243
+	{
244
+		toread = (hint&&((size_t)read_n > left))?(int)left:read_n;
245
+		if(!FCGX_GetLine(buff, toread,
246
+			*((IoIn*)self)->in_stream))
247
+		{
248
+			((IoIn*)self)->eof = 1;
249
+			break;
250
+		}
251
+		sz = strlen(buff);
252
+		left -= sz;
253
+		read_str = IoIn__FromBuff(self);
254
+		if(!read_str)
255
+		{
256
+			Py_RETURN_NONE;
257
+		}
258
+		Py_INCREF(read_str);
259
+		if(cur_str)
260
+		{
261
+			tmp = IoIn__Concat(self, cur_str, read_str);
262
+			Py_INCREF(tmp);
263
+			cur_str = tmp;
264
+		}
265
+		else
266
+		{
267
+			cur_str = read_str;
268
+		}
269
+
270
+		if(buff[sz-1] == '\n')
271
+		{
272
+			if(PyList_Append(res, cur_str))
273
+			{
274
+				Py_DECREF(cur_str);
275
+				Py_RETURN_NONE;
276
+			}
277
+			Py_DECREF(cur_str);
278
+			cur_str = NULL;
279
+		}
280
+	}
281
+	if(cur_str)
282
+	{
283
+		if(PyList_Append(res, cur_str))
284
+		{
285
+			Py_DECREF(cur_str);
286
+			Py_RETURN_NONE;
287
+		}
288
+		Py_DECREF(cur_str);
289
+	}
290
+	Py_DECREF(res);
291
+	return res;
292
+}
293
+
294
+PyObject* pyfcgi_ioin_read(PyObject *self, PyObject **argv, Py_ssize_t argc)
295
+{
296
+	size_t left, sz, max;
297
+	long l;
298
+	int read_n, toread;
299
+	PyObject *res, *read_str;
300
+	char *buff;
301
+
302
+	if(_check_nullin(self)) { Py_RETURN_NONE; }
303
+	if(argc > 1)
304
+	{
305
+		PyErr_Format(PyExc_ValueError,
306
+			"libpyfcgi.IoIn.read() expecting at most 1 argument but %zd given",
307
+			argc);
308
+		Py_RETURN_NONE;
309
+	}
310
+
311
+	if(((IoIn*)self)->eof)
312
+	{
313
+		return IoIn__FromString(self, "");
314
+	}
315
+	max = 0;
316
+	if(argc)
317
+	{
318
+		l = PyLong_AsLong(argv[0]);
319
+		if(PyErr_Occurred() || l > 0)
320
+		{
321
+			PyErr_Clear();
322
+			max = PyLong_AsSize_t(argv[0]);
323
+			if(PyErr_Occurred())
324
+			{
325
+				Py_RETURN_NONE;
326
+			}
327
+		}
328
+	}
329
+	left = max;
330
+	read_n = pyfcgi_ioin__reqbuf(self, 0);
331
+	buff = ((IoIn*)self)->buff;
332
+	if(!(res = IoIn__FromString(self, "")))
333
+	{
334
+		Py_RETURN_NONE;
335
+	}
336
+	while((max && left) || !max)
337
+	{
338
+		toread = (max&&((size_t)read_n > left))?(int)left:read_n;
339
+		if(FCGX_GetStr(buff, toread, *((IoIn*)self)->in_stream) < toread)
340
+		{
341
+			((IoIn*)self)->eof = 1;
342
+			break;
343
+		}
344
+		sz = strlen(buff);
345
+		left -= sz;
346
+		if(!(read_str = IoIn__FromBuff(self)))
347
+		{
348
+			Py_RETURN_NONE;
349
+		}
350
+		Py_INCREF(read_str);
351
+		if(!(res = IoIn__Concat(self, res, read_str)))
352
+		{
353
+			Py_RETURN_NONE;
354
+		}
355
+		Py_INCREF(res);
356
+	}
357
+	Py_DECREF(res);
358
+	return res;
359
+}
360
+
361
+PyObject* pyfcgi_ioin_readall(PyObject *self, PyObject **argv, Py_ssize_t argc)
362
+{
363
+	if(_check_nullin(self)) { Py_RETURN_NONE; }
364
+	if(argc)
365
+	{
366
+		PyErr_Format(PyExc_ValueError,
367
+			"libpyfcgi.IoIn.readall() not expecting any argument but %zd given",
368
+			argc);
369
+		Py_RETURN_NONE;
370
+	}
371
+	return pyfcgi_ioin_read(self, NULL, 0);
372
+}
373
+
374
+PyObject* pyfcgi_ioin_readinto(PyObject *self, PyObject **argv, Py_ssize_t argc)
375
+{
376
+	PyObject *b;
377
+	Py_ssize_t max, left, read_n;
378
+	char *buff, *buff_ptr;
379
+	int toread, ret;
380
+
381
+	if(_check_nullin(self)) { Py_RETURN_NONE; }
382
+	if(argc != 1)
383
+	{
384
+		PyErr_Format(PyExc_ValueError,
385
+			"libpyfcgi.IoIn.readinto() expecting 1 argument but %zd given",
386
+			argc);
387
+		Py_RETURN_NONE;
388
+	}
389
+	b = argv[0];
390
+	if(!PyByteArray_Check(b))
391
+	{
392
+		PyErr_Format(PyExc_ValueError,
393
+			"libpyfcgi.IoIn.readinto() expected bytearray as argument but %A given",
394
+			b);
395
+		Py_RETURN_NONE;
396
+	}
397
+	left = max = PyByteArray_Size(b);
398
+	buff = PyByteArray_AsString(b);
399
+	buff_ptr = buff;
400
+	read_n = pyfcgi_ioin__reqbuf(self, 0);
401
+	while(left)
402
+	{
403
+		toread = left>read_n?read_n:left;
404
+		if((ret = FCGX_GetStr(buff_ptr, toread,
405
+			*((IoIn*)self)->in_stream)) < toread)
406
+		{
407
+			((IoIn*)self)->eof = 1;
408
+			break;
409
+		}
410
+		buff_ptr += ret;
411
+		left -= ret;
412
+
413
+	}
414
+	return b;
415
+}	
416
+
417
+
418
+PyObject* pyfcgi_ioin_truncate(PyObject *self, PyObject **argv, Py_ssize_t argc)
419
+{
420
+	PyErr_SetString(PyExc_OSError, "libpyfcgi.IoIn cannot be truncated");
421
+	Py_RETURN_NONE;
422
+}
423
+
424
+PyObject* pyfcgi_ioin_SeekError(PyObject *self, PyObject **argv, Py_ssize_t argc)
425
+{
426
+	PyErr_SetString(PyExc_OSError, "libpyfcgi.IoIn is not seekable");
427
+	Py_RETURN_NONE;
428
+}
429
+
430
+PyObject* pyfcgi_ioin_WriteError(PyObject *self, PyObject **argv, Py_ssize_t argc)
431
+{
432
+	PyErr_SetString(PyExc_OSError, "libpyfcgi.IoIn is not writable");
433
+	Py_RETURN_NONE;
434
+}
435
+
436
+PyObject* pyfcgi_ioin_seekable(PyObject *self, PyObject **argv, Py_ssize_t argc)
437
+{
438
+	return Py_False;
439
+}
440
+
441
+PyObject* pyfcgi_ioin_writable(PyObject *self, PyObject **argv, Py_ssize_t argc)
442
+{
443
+	return Py_False;
444
+}
445
+
446
+
447
+static int _check_nullin(PyObject *self)
448
+{
449
+	if(!((IoIn*)self)->in_stream)
450
+	{
451
+		PyErr_SetString(PyExc_RuntimeError,
452
+			"pyfcgi.IoIn called in wrong context : FGCI input not set");
453
+		return -1;
454
+	}
455
+	else if(!(*(((IoIn*)self)->in_stream)))
456
+	{
457
+		PyErr_SetString(PyExc_RuntimeError,
458
+			"pyfcgi.IoIn called in wrong context : FGCI input is NULL");
459
+		return -2;
460
+	}
461
+	return 0;
462
+}
463
+
464
+static int pyfcgi_ioin__reqbuf(PyObject *self, int nreq)
465
+{
466
+	IoIn *ioin;
467
+	void *tmp;
468
+	int err;
469
+
470
+	ioin = (IoIn*)self;
471
+
472
+	if(ioin->buff_sz > nreq && ioin->buff)
473
+	{
474
+		return ioin->buff_sz;
475
+	}
476
+	
477
+	ioin->buff_sz = ((nreq>>12)+1)<<12;
478
+	if(ioin->buff_sz < nreq) { ioin->buff_sz = nreq; }
479
+
480
+	if(!(tmp = realloc(ioin->buff, ioin->buff_sz)))
481
+	{
482
+		err = errno;
483
+		PyErr_Format(PyExc_OSError,
484
+			"%A error reallocating internal buffer : %s",
485
+			self, strerror(err));
486
+		errno = err;
487
+		return 0;
488
+	}
489
+	ioin->buff = tmp;
490
+	return ioin->buff_sz;
491
+}
492
+
493
+/**@brief Concat two bytes/unicode given IoIn configuration flag
494
+ * @warn Py_DECREF both arguments
495
+ */
496
+static PyObject* IoIn__Concat(PyObject *self, PyObject *left, PyObject *right)
497
+{
498
+	if(((IoIn*)self)->bin)
499
+	{
500
+		PyBytes_Concat(&left, right);
501
+		Py_DECREF(right);
502
+		return left;
503
+	}
504
+	PyObject *res;
505
+	res = PyUnicode_Concat(left, right);
506
+	Py_DECREF(left);
507
+	Py_DECREF(right);
508
+	return res;
509
+}
510
+
511
+
512
+

+ 31
- 1
src/python_pyfcgi.c View File

@@ -21,8 +21,9 @@
21 21
 
22 22
 /* Globals definitions */
23 23
 /* libpyfcgi context */
24
-libpyfcgi_context_t libpyfcgi = { NULL, NULL, NULL, 0, NULL, NULL, 0 };
24
+libpyfcgi_context_t libpyfcgi = { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0 };
25 25
 /* Python module methods specs */
26
+/**@todo Add doc in last field */
26 27
 PyMethodDef pyfcgimodule_methods[] = {
27 28
 	{"start_response", (PyCFunction)pyfcgi_start_response, METH_FASTCALL, NULL},
28 29
 	{"write_body", (PyCFunction)pyfcgi_write_body, METH_FASTCALL, NULL},
@@ -300,12 +301,41 @@ PyInit_libpyfcgi(void)
300 301
 	{
301 302
 		return libpyfcgi.self;
302 303
 	}
304
+
305
+	// init IoIn type
306
+	IoInType.tp_new = PyType_GenericNew;
307
+	if(PyType_Ready(&IoInType) < 0)
308
+	{
309
+		return NULL;
310
+	}
311
+
303 312
 	// init module & globals
304 313
 	libpyfcgi.status = NULL;
305 314
 	libpyfcgi.headers = NULL;
306 315
 	libpyfcgi.rep_sz = 0;
307 316
 	libpyfcgi.self = PyModule_Create(&pyfcgimodule);
308 317
 	if(libpyfcgi.self == NULL) { return NULL; }
318
+
319
+	// Add type to module (optionnal)
320
+	PyModule_AddObject(libpyfcgi.self, "IoIn", (PyObject*)&IoInType);
321
+
322
+	// Create a new instance of IoIn
323
+	libpyfcgi.ioin = (IoIn*)PyObject_CallObject((PyObject*)&IoInType, NULL);
324
+	Py_INCREF(libpyfcgi.ioin);
325
+	if(!libpyfcgi.ioin)
326
+	{
327
+		return NULL;
328
+	}
329
+	libpyfcgi.ioin->in_stream = &libpyfcgi.in; // point on stream pointer
330
+	libpyfcgi.ioin->bin = 1; // binary stream (bytes for python)
331
+
332
+	// Add it to wsgi dict
333
+	if(PyDict_SetItemString(PyFCGI_conf.context.wsgi_dict, "wsgi.input",
334
+		(PyObject*)libpyfcgi.ioin))
335
+	{
336
+		return NULL;
337
+	}
338
+
309 339
 	return libpyfcgi.self;
310 340
 }
311 341
 

+ 18
- 1
src/pyutils.c View File

@@ -3,7 +3,8 @@
3 3
 void pyinit()
4 4
 {
5 5
 	char *venv_path;
6
-	PyObject *sitemod, *mainfun;
6
+	PyObject *sitemod, *mainfun, *wsgi;
7
+
7 8
 
8 9
 	if( (venv_path = getenv("VIRTUAL_ENV")) )
9 10
 	{
@@ -27,6 +28,14 @@ void pyinit()
27 28
 	//Append "." to sys.path
28 29
 	sitemod = PySys_GetObject("path");
29 30
 	PyList_Append(sitemod, PyUnicode_FromString("."));
31
+
32
+	//Initialize the wsgi PEP333 dict
33
+	wsgi = PyFCGI_conf.context.wsgi_dict = PyDict_New();
34
+	PyDict_SetItemString(wsgi, "wsgi.version", Py_BuildValue("ii", 1,0));
35
+
36
+	PyDict_SetItemString(wsgi, "wsgi.multithread", Py_False);
37
+	PyDict_SetItemString(wsgi, "wsgi.multiprocess", Py_True);
38
+	PyDict_SetItemString(wsgi, "wsgi.run_once", Py_False);
30 39
 }
31 40
 
32 41
 void update_python_path()
@@ -270,6 +279,9 @@ void update_python_fd(int pipe_out[2], int pipe_err[2])
270 279
 		goto update_python_fd_err_args;
271 280
 	}
272 281
 	Py_DECREF(args);
282
+
283
+	PyDict_SetItemString(PyFCGI_conf.context.wsgi_dict, "wsgi.errors", new_fd);
284
+
273 285
 	if(PySys_SetObject("stderr", new_fd))
274 286
 	{
275 287
 		pyfcgi_log(LOG_ERR, "Unable to set sys.stderr");
@@ -367,6 +379,11 @@ PyObject* update_pyenv(PyObject *py_osmod, char **environ)
367 379
 		Py_DECREF(pykey);
368 380
 		cur++;
369 381
 	}
382
+	/**@todo set given headers */
383
+	PyDict_SetItemString(PyFCGI_conf.context.wsgi_dict, "wsgi.url_scheme",
384
+		Py_BuildValue("U", "http"));
385
+
386
+	PyDict_Update(pyenv, PyFCGI_conf.context.wsgi_dict);
370 387
 	PyObject_SetAttrString(py_osmod, "environ", pyenv);
371 388
 	return pyenv;
372 389
 }

+ 1
- 0
src/pyworker.c View File

@@ -90,6 +90,7 @@ int work333(int wrk_id, int semid)
90 90
 
91 91
 		libpyfcgi_clean_response();
92 92
 		libpyfcgi.out = out_stream;
93
+		libpyfcgi.in = in_stream;
93 94
 		args = Py_BuildValue("OO", environ, start_response);
94 95
 		entry_ret = PyObject_CallObject(entry_fun, args);
95 96
 		if(entry_ret && entry_ret != Py_None )

Loading…
Cancel
Save