ry / ebb fork watch download tarball
public this repo is viewable by everyone
Description: web server
Homepage: http://ebb.rubyforge.org
Clone URL: git://github.com/ry/ebb.git
Python binding is working now

It's crazy fast too. (Compared to the Ruby binding.) I've only implemented

the WSGI 2 interface. It shouldn't be hard to add some python-fu to 
support
the real WSGI. I don't know python so I hope someone else will be able to 
do
that.

Not sure how well concurrent requests are working - and the C code is an 
ugly
mess. I'll try to clean up these things soon.
ryah (author)
about 1 month ago
commit  7393e3658c5b1bc422047df6bf40e0f08568af37
tree    bb2e35105908cd6c32122c5ea9d716857ff19ca6
parent  526f7824a2d26d12f7d890730771378d3dd4b19f
...
3
4
5
 
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
8
9
10
11
 
 
12
13
14
 
 
15
...
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 
36
37
38
 
39
40
41
42
0
@@ -3,11 +3,38 @@
0
 A WSGI web server!
0
 """
0
 import ebb_ffi
0
+from signal import *
0
 
0
+def interupt_handler(signum, frame):
0
+ ebb_ffi.server_stop()
0
+
0
+signal(SIGINT, interupt_handler)
0
+signal(SIGTERM, interupt_handler)
0
+
0
+
0
+def wsgi2_request_cb(app, client):
0
+ status_string, headers, body = app.__call__(client.env())
0
+
0
+ status, status_human = status_string.split(" ")
0
+
0
+ client.write_status(int(status), status_human)
0
+
0
+ for field, value in headers:
0
+ client.write_header(field, value)
0
+
0
+ for part in body:
0
+ client.write_body(part)
0
+
0
+ client.release()
0
+ return True
0
+
0
+
0
 def start_server(app, args = {}):
0
   if args['port']:
0
     ebb_ffi.listen_on_port(int(args['port']))
0
   else:
0
- print "bad!"
0
+ print "no port given!"
0
+ exit(1)
0
   
0
- ebb_ffi.process_connections(app)
0
\ No newline at end of file
0
+ print "Ebb listening on port %d" % args['port']
0
+ ebb_ffi.process_connections(app, wsgi2_request_cb)
0
\ No newline at end of file
...
16
17
18
 
19
20
21
 
22
 
 
 
23
24
25
...
37
38
39
40
 
41
42
43
44
 
45
46
47
48
49
50
51
52
53
 
54
55
56
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
59
60
 
 
 
 
 
61
62
63
...
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
...
164
165
166
167
168
169
 
170
171
172
...
174
175
176
177
178
179
 
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
 
 
 
 
 
 
 
206
207
208
209
 
210
 
211
212
213
 
 
 
 
 
 
 
 
 
 
214
215
216
217
218
 
 
219
220
221
222
223
 
 
 
 
224
 
225
 
 
 
 
 
226
227
228
229
230
 
 
 
231
 
232
 
233
234
235
...
244
245
246
 
 
 
 
 
247
248
249
250
 
251
252
253
...
289
290
291
292
 
293
294
295
...
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...
42
43
44
 
45
46
47
48
49
50
51
52
 
 
 
53
54
55
56
57
58
 
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
 
 
112
113
114
115
116
117
118
119
...
127
128
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
131
132
...
188
189
190
 
 
 
191
192
193
194
...
196
197
198
 
 
 
199
200
201
 
 
 
 
 
 
 
 
 
202
 
 
203
 
 
 
 
 
 
 
 
 
 
 
204
205
206
207
208
209
210
211
212
 
 
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
 
 
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
...
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
...
325
326
327
 
328
329
330
331
0
@@ -16,10 +16,15 @@
0
  */
0
 static ebb_server server;
0
 struct ev_loop *loop;
0
+struct ev_timer check_stop_watcher;
0
 
0
 static PyThreadState *libev_thread_state;
0
 
0
+static PyObject *py_request_cb;
0
 static PyObject *application;
0
+static int server_running;
0
+static int server_error;
0
+
0
 static PyObject *base_env;
0
 static PyObject *global_http_prefix;
0
 static PyObject *global_request_method;
0
@@ -37,27 +42,78 @@ static PyObject *global_http_host;
0
 
0
 /* A callable type called Client. __call__(status, response_headers)
0
  * is the second argument to an appplcation
0
- * Client.environ is the first argument.
0
+ * Client.env is the first argument.
0
  */
0
 typedef struct {
0
   PyObject_HEAD
0
   ebb_client *client;
0
+ PyObject *env;
0
 } py_client;
0
 
0
-static PyObject *
0
-py_start_response(py_client *self, PyObject *args, PyObject *kw);
0
-
0
 static void py_client_dealloc(PyObject *obj)
0
 {
0
   py_client *self = (py_client*) obj;
0
   ebb_client_release(self->client);
0
+ Py_DECREF(self->env);
0
   obj->ob_type->tp_free(obj);
0
- printf("dealloc called!\n");
0
 }
0
 
0
+static PyObject *
0
+write_status(py_client *self, PyObject *args)
0
+{
0
+ char *status_human;
0
+ int status;
0
+
0
+ if(!PyArg_ParseTuple(args, "is", &status, &status_human))
0
+ return NULL;
0
+ ebb_client_write_status(self->client, status, status_human);
0
+ Py_RETURN_NONE;
0
+}
0
+
0
+static PyObject *
0
+write_header(py_client *self, PyObject *args)
0
+{
0
+ char *field, *value;
0
+
0
+ if(!PyArg_ParseTuple(args, "ss", &field, &value))
0
+ return NULL;
0
+ ebb_client_write_header(self->client, field, value);
0
+ Py_RETURN_NONE;
0
+}
0
+
0
+static PyObject *
0
+write_body(py_client *self, PyObject *args)
0
+{
0
+ char *body;
0
+ int body_length;
0
+
0
+ if(!PyArg_ParseTuple(args, "s#", &body, &body_length))
0
+ return NULL;
0
+ ebb_client_write_body(self->client, body, body_length);
0
+ Py_RETURN_NONE;
0
+}
0
+
0
+static PyObject *
0
+client_release(py_client *self)
0
+{
0
+ ebb_client_release(self->client);
0
+ Py_RETURN_NONE;
0
+}
0
+
0
+static PyObject *
0
+client_env(py_client *self)
0
+{
0
+ Py_INCREF(self->env);
0
+ return self->env;
0
+}
0
+
0
+
0
 static PyMethodDef client_methods[] =
0
- { {"start_response" , (PyCFunction)py_start_response, METH_VARARGS, NULL }
0
- // , {"write" , (PyCFunction)write, METH_VARARGS, NULL }
0
+ { {"write_status", (PyCFunction)write_status, METH_VARARGS, NULL }
0
+ , {"write_header", (PyCFunction)write_header, METH_VARARGS, NULL }
0
+ , {"write_body", (PyCFunction)write_body, METH_VARARGS, NULL }
0
+ , {"release", (PyCFunction)client_release, METH_NOARGS, NULL }
0
+ , {"env", (PyCFunction)client_env, METH_NOARGS, NULL }
0
   , {NULL, NULL, 0, NULL}
0
   };
0
 
0
@@ -71,38 +127,6 @@ static PyTypeObject py_client_t =
0
   , tp_dealloc: py_client_dealloc
0
   };
0
 
0
-static PyObject *
0
-py_start_response(py_client *self, PyObject *args, PyObject *kw)
0
-{
0
- PyObject *response_headers;
0
- char *status_string;
0
- int status;
0
-
0
- if(!PyArg_ParseTuple(args, "sO", &status_string, &response_headers))
0
- return NULL;
0
-
0
- /* do this goofy split(' ') operation. wsgi is such a terrible api. */
0
- status = 100 * (status_string[0] - '0')
0
- + 10 * (status_string[1] - '0')
0
- + 1 * (status_string[2] - '0');
0
- assert(0 <= status && status < 1000);
0
-
0
- ebb_client_write_status(self->client, status, status_string+4);
0
-
0
- PyObject *iterator = PyObject_GetIter(response_headers);
0
- PyObject *header_pair;
0
- char *field, *value;
0
- while(( header_pair = PyIter_Next(iterator) )) {
0
- if(!PyArg_ParseTuple(header_pair, "ss", &field, &value))
0
- return NULL;
0
- ebb_client_write_header(self->client, field, value);
0
- Py_DECREF(header_pair);
0
- }
0
-
0
- /* return write object */
0
- Py_RETURN_NONE;
0
-}
0
-
0
 static PyObject* env_field(struct ebb_env_item *item)
0
 {
0
   PyObject* f = NULL;
0
@@ -164,9 +188,7 @@ static py_client* py_client_new(ebb_client *client)
0
   py_client *self = PyObject_New(py_client, &py_client_t);
0
   if(self == NULL) return NULL;
0
   self->client = client;
0
-
0
- //if(0 < PyObject_SetAttrString((PyObject*)self, "environ", py_client_env(client)))
0
- // return NULL;
0
+ self->env = py_client_env(client);
0
   
0
   return self;
0
 }
0
@@ -174,62 +196,70 @@ static py_client* py_client_new(ebb_client *client)
0
 void request_cb(ebb_client *client, void *_)
0
 {
0
   PyEval_RestoreThread(libev_thread_state); /* acquire GIL for this thread */
0
-
0
- PyObject *environ, *start_response;
0
-
0
+
0
   py_client *pclient = py_client_new(client);
0
   assert(pclient != NULL);
0
- //environ = PyObject_GetAttrString((PyObject*)pclient, "environ");
0
- environ = py_client_env(client);
0
- assert(environ != NULL);
0
-
0
- start_response = PyObject_GetAttrString((PyObject*)pclient, "start_response");
0
- assert(start_response != NULL);
0
-
0
- PyObject *arglist = Py_BuildValue("OO", environ, start_response);
0
- assert(arglist != NULL);
0
   assert(application != NULL);
0
- PyObject *body = PyEval_CallObject(application, arglist);
0
- assert(body != NULL);
0
   
0
- Py_DECREF(arglist);
0
- Py_DECREF(environ);
0
-
0
- PyObject *iterator = PyObject_GetIter(body);
0
- PyObject *body_item;
0
- while (body_item = PyIter_Next(iterator)) {
0
- char *body_string = PyString_AsString(body_item);
0
- int body_length = PyString_Size(body_item);
0
- /* Todo support streaming! */
0
- ebb_client_write_body(client, body_string, body_length);
0
- Py_DECREF(body_item);
0
+ PyObject *arglist = Py_BuildValue("(OO)", application, pclient);
0
+ assert(arglist != NULL);
0
+ PyObject *rv = PyEval_CallObject(py_request_cb, arglist);
0
+ if(rv == NULL) {
0
+ server_error = TRUE;
0
+ ebb_client_close(pclient->client);
0
+ return;
0
   }
0
   
0
- ebb_client_release(client);
0
-
0
+ Py_DECREF(arglist);
0
   Py_DECREF(pclient);
0
+
0
   libev_thread_state = PyEval_SaveThread(); /* allow other python threads to run again */
0
 }
0
 
0
+static void
0
+check_stop (struct ev_loop *loop, struct ev_timer *w, int revents)
0
+{
0
+ if(PyErr_CheckSignals() < 0 || server_running == FALSE || server_error == TRUE) {
0
+ ev_timer_stop(loop, w);
0
+ ev_unloop(loop, EVUNLOOP_ALL);
0
+ }
0
+}
0
+
0
+
0
 
0
 static PyObject *process_connections(PyObject *_, PyObject *args)
0
 {
0
- assert(application == NULL);
0
- if(!PyArg_ParseTuple(args, "O", &application))
0
+ assert(py_request_cb == NULL);
0
+ if(!PyArg_ParseTuple(args, "OO", &application, &py_request_cb))
0
     return NULL;
0
   if(!PyCallable_Check(application)) {
0
     PyErr_SetString(PyExc_TypeError, "parameter must be callable");
0
     return NULL;
0
   }
0
+ if(!PyCallable_Check(py_request_cb)) {
0
+ PyErr_SetString(PyExc_TypeError, "parameter must be callable");
0
+ return NULL;
0
+ }
0
   Py_XINCREF(application);
0
+ Py_XINCREF(py_request_cb);
0
   
0
+ server_running = TRUE;
0
+ server_error = FALSE;
0
+
0
+ ev_timer_init (&check_stop_watcher, check_stop, 0.5, 0.5);
0
+ ev_timer_start (loop, &check_stop_watcher);
0
   
0
   libev_thread_state = PyEval_SaveThread(); /* allow other threads to run */
0
   ev_loop(loop, 0);
0
   PyEval_RestoreThread(libev_thread_state); /* reacquire GIL */
0
   
0
+ /* TODO: exit properly */
0
+ if(server_error) return NULL;
0
+ //ebb_server_unlisten(&server);
0
   
0
+ Py_XDECREF(py_request_cb);
0
   Py_XDECREF(application);
0
+ py_request_cb = NULL;
0
   application = NULL;
0
   Py_RETURN_NONE;
0
 }
0
@@ -244,10 +274,16 @@ static PyObject *listen_on_port(PyObject *_, PyObject *args)
0
   Py_RETURN_NONE;
0
 }
0
 
0
+static PyObject *server_stop(PyObject *_)
0
+{
0
+ server_running = FALSE;
0
+ Py_RETURN_NONE;
0
+}
0
 
0
 static PyMethodDef ebb_module_methods[] =
0
   { {"listen_on_port", (PyCFunction)listen_on_port, METH_VARARGS, NULL}
0
   , {"process_connections", (PyCFunction)process_connections, METH_VARARGS, NULL}
0
+ , {"server_stop", (PyCFunction)server_stop, METH_NOARGS, NULL}
0
   , {NULL, NULL, 0, NULL}
0
   };
0
 
0
@@ -289,6 +325,6 @@ PyMODINIT_FUNC initebb_ffi(void)
0
   DEF_GLOBAL(content_length, "CONTENT_LENGTH");
0
   DEF_GLOBAL(http_host, "HTTP_HOST");
0
   
0
- loop = ev_default_loop(0);
0
+ loop = ev_default_loop(0);
0
   ebb_server_init(&server, loop, request_cb, NULL);
0
 }
0
\ No newline at end of file
...
2
3
4
5
6
7
 
8
9
10
11
12
 
 
 
13
14
15
...
2
3
4
 
5
 
6
7
8
 
 
 
9
10
11
12
13
14
0
@@ -2,13 +2,12 @@ import sys
0
 sys.path.append('/Users/ry/projects/ebb/build/lib.macosx-10.3-ppc-2.5')
0
 import ebb
0
 # os.path.abspath(__file__)
0
-print "hello"
0
 
0
-def simple_app(environ, start_response):
0
+def simple_app(environ):
0
   """Simplest possible application object"""
0
   status = '200 OK'
0
- response_headers = [('Content-type','text/plain')]
0
- start_response(status, response_headers)
0
- return ["Hello world!\n", "Something else!", "blllllaaaaaaaah\n"]
0
+ headers = [('Content-type','text/plain')]
0
+ body = ["Hello world!\n", "Something else!", "blllllaaaaaaaah\n"]
0
+ return([status, headers, body])
0
 
0
 ebb.start_server(simple_app, {'port': 4001})
0
\ No newline at end of file

Comments

    No one has commented yet.