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
Version bump and continued work on Python binding

Slow but steady progress on the WSGI binding as I learn python. Currently 
the
test program runs and outputs the environ variable. Not sure yet how to 
trap
signals in a PythonVM-friendly manner so you must kill it.

Working on the Python is giving me a lot of ideas to bring back to Ruby. 
For
example, there is no point in implementing an Ebb::Server class which
instantiates. No one will ever want to run more than one Server per 
virtual
machine. Using a global ebb_server structure could simplify ebb_ruby.c a 
lot.

The Python type structure is inane.
Ryan Dahl (author)
2 months ago
commit  38aebaab6cc2ec4144c080a94abfab2cdfd71ade
tree    52942b4a34997b675cd553a1d97e917fdf0ca2a1
parent  eb823db6bb07b9c2f411f0aa62b923d3f8198838
...
79
80
81
82
83
 
 
84
85
86
...
79
80
81
 
 
82
83
84
85
86
0
@@ -79,8 +79,8 @@ spec = Gem::Specification.new do |s|
0
   s.description = ''
0
   s.name = 'ebb'
0
   s.author = 'ry dahl'
0
- s.email = 'ry@tinyclouds.org'
0
- s.homepage = 'http://repo.or.cz/w/ebb.git'
0
+ s.email = 'ry at tiny clouds dot org'
0
+ s.homepage = 'http://ebb.rubyforge.org'
0
   s.version = File.read(dir("VERSION")).gsub(/\s/,'')
0
   s.requirements << 'none'
0
   s.rubyforge_project = 'ebb'
...
1
 
...
 
1
0
@@ -1 +1 @@
0
-0.0.3
0
+0.0.4
...
15
16
17
18
19
20
21
...
15
16
17
 
18
19
20
0
@@ -15,7 +15,6 @@ module Ebb
0
       'SCRIPT_NAME' => '',
0
       'SERVER_SOFTWARE' => "Ebb #{Ebb::VERSION}",
0
       'SERVER_PROTOCOL' => 'HTTP/1.1',
0
- 'GATEWAY_INTERFACE' => 'CGI/1.2',
0
       'rack.version' => [0, 1],
0
       'rack.errors' => STDERR,
0
       'rack.url_scheme' => 'http',
...
9
10
11
12
13
14
15
 
 
 
 
16
17
18
...
20
21
22
23
 
24
25
...
9
10
11
 
 
 
 
12
13
14
15
16
17
18
...
20
21
22
 
23
24
25
0
@@ -9,10 +9,10 @@ def pkgconfig(*packages, **kw):
0
         kw.setdefault(flag_map.get(token[:2]), []).append(token[2:])
0
     return kw
0
 
0
-ebb_ext = Extension( "ebb_ext"
0
- , ["src/ebb_python.c"]
0
- , **pkgconfig('glib-2.0', include_dirs = ['libev'])
0
- )
0
+ebb = Extension( "ebb"
0
+ , ["src/ebb_python.c", "src/ebb.c", "src/parser.c"]
0
+ , **pkgconfig('glib-2.0', include_dirs = ['libev'])
0
+ )
0
 
0
 setup( name = "Ebb"
0
      , description = "a wsgi web server"
0
@@ -20,5 +20,5 @@ setup( name = "Ebb"
0
      , author = "ry dahl"
0
      , author_email = "ry at tiny clouds dot org"
0
      , url = "http://ebb.rubyforge.org/"
0
- , ext_modules = [ebb_ext]
0
+ , ext_modules = [ebb]
0
      )
0
\ No newline at end of file
...
88
89
90
91
 
92
93
94
...
88
89
90
 
91
92
93
94
0
@@ -88,7 +88,7 @@ struct ebb_client {
0
 
0
 typedef void (*ebb_request_cb)(ebb_client*, void*);
0
 
0
-ebb_server* ebb_server_alloc();
0
+ebb_server* ebb_server_alloc(void);
0
 void ebb_server_free(ebb_server*);
0
 void ebb_server_init( ebb_server *server
0
                     , struct ev_loop *loop
...
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
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
...
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
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
120
121
122
123
124
125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
 
 
155
156
157
 
 
158
159
160
161
162
163
164
165
 
 
 
166
167
168
169
170
171
172
173
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
0
@@ -3,89 +3,199 @@
0
  * License. See README file for details.
0
  */
0
 #include <Python.h>
0
-#include <ebb.h>
0
+#include "ebb.h"
0
 #include <ev.h>
0
+#include <assert.h>
0
 
0
+#define PyDict_SetStringString(dict, str1, str2) PyDict_SetItemString(dict, str1, PyString_FromString(str2))
0
+#define ASCII_UPPER(ch) ('a' <= ch && ch <= 'z' ? ch - 'a' + 'A' : ch)
0
+
0
+/* Why is there a global ebb_server variable instead of a wrapping it in a
0
+ * class? Because you would for no conceivable reason want to run more than
0
+ * one ebb_server per python VM instance.
0
+ */
0
+static ebb_server server;
0
+struct ev_loop *loop = NULL;
0
+static PyObject *application = NULL;
0
+static PyObject *base_env = NULL;
0
+static PyObject *global_http_prefix;
0
+static PyObject *global_request_method;
0
+static PyObject *global_request_uri;
0
+static PyObject *global_fragment;
0
+static PyObject *global_request_path;
0
+static PyObject *global_query_string;
0
+static PyObject *global_http_version;
0
+static PyObject *global_request_body;
0
+static PyObject *global_server_name;
0
+static PyObject *global_server_port;
0
+static PyObject *global_path_info;
0
+static PyObject *global_content_length;
0
+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
+ */
0
 typedef struct {
0
- PyObject_HEAD
0
- ebb_server *server;
0
-} Server;
0
+ PyObject_HEAD
0
+ ebb_client *client;
0
+} ebb_Client;
0
+
0
+
0
+static PyObject* env_field(struct ebb_env_item *item)
0
+{
0
+ PyObject* f = NULL;
0
+ int i;
0
+
0
+ switch(item->type) {
0
+ case EBB_FIELD_VALUE_PAIR:
0
+ f = PyString_FromStringAndSize(NULL, PyString_GET_SIZE(global_http_prefix) + item->field_length);
0
+ memcpy( PyString_AS_STRING(f)
0
+ , PyString_AS_STRING(global_http_prefix)
0
+ , PyString_GET_SIZE(global_http_prefix)
0
+ );
0
+ for(i = 0; i < item->field_length; i++) {
0
+ char *ch = PyString_AS_STRING(f) + PyString_GET_SIZE(global_http_prefix) + i;
0
+ *ch = item->field[i] == '-' ? '_' : ASCII_UPPER(item->field[i]);
0
+ }
0
+ break;
0
+ case EBB_REQUEST_METHOD: f = global_request_method; break;
0
+ case EBB_REQUEST_URI: f = global_request_uri; break;
0
+ case EBB_FRAGMENT: f = global_fragment; break;
0
+ case EBB_REQUEST_PATH: f = global_request_path; break;
0
+ case EBB_QUERY_STRING: f = global_query_string; break;
0
+ case EBB_HTTP_VERSION: f = global_http_version; break;
0
+ case EBB_SERVER_NAME: f = global_server_name; break;
0
+ case EBB_SERVER_PORT: f = global_server_port; break;
0
+ case EBB_CONTENT_LENGTH: f = global_content_length; break;
0
+ default: assert(FALSE);
0
+ }
0
+ Py_INCREF(f);
0
+ return f;
0
+}
0
 
0
-void Server_alloc()
0
+
0
+static PyObject* env_value(struct ebb_env_item *item)
0
+{
0
+ if(item->value_length > 0)
0
+ return PyString_FromStringAndSize(item->value, item->value_length);
0
+ else
0
+ return Py_None; // XXX need to increase ref count? :/
0
+}
0
+
0
+
0
+static PyObject* client_env(ebb_client *client)
0
 {
0
+ PyObject *env = PyDict_Copy(base_env);
0
+ int i;
0
   
0
+ for(i=0; i < client->env_size; i++) {
0
+ PyDict_SetItem(env, env_field(&client->env[i])
0
+ , env_value(&client->env[i])
0
+ );
0
+ }
0
+ // PyDict_SetStringString(hash, global_path_info, rb_hash_aref(hash, global_request_path));
0
+
0
+ return env;
0
 }
0
 
0
-static void
0
-Server_dealloc(Server* self)
0
+const char *test_response = "Hello World!\r\n";
0
+
0
+void request_cb(ebb_client *client, void *ignore)
0
 {
0
- ebb_server_free(self->server);
0
- self->ob_type->tp_free((PyObject*)self);
0
+ PyObject *env = client_env(client);
0
+ PyObject *start_response = Py_None;
0
+ PyObject *arglist;
0
+ PyObject *result;
0
+
0
+ printf("hello world\n");
0
+
0
+ arglist = Py_BuildValue("OO", env, start_response);
0
+ result = PyEval_CallObject(application, arglist);
0
+
0
+ Py_DECREF(arglist);
0
+ Py_DECREF(env);
0
+
0
+ ebb_client_write(client, test_response, strlen(test_response));
0
+ ebb_client_finished(client);
0
+
0
+ printf("finished calling\n");
0
 }
0
 
0
 
0
-static PyMethodDef Server_methods[] = {
0
- {"name", (PyCFunction)Noddy_name, METH_NOARGS,
0
- "Return the name, combining the first and last name"
0
- },
0
- {NULL} /* Sentinel */
0
-};
0
-
0
-static PyTypeObject ServerType = {
0
- PyObject_HEAD_INIT(NULL)
0
- 0, /*ob_size*/
0
- "ebb.Server", /*tp_name*/
0
- sizeof(Server), /*tp_basicsize*/
0
- 0, /*tp_itemsize*/
0
- (destructor)Server_dealloc, /*tp_dealloc*/
0
- 0, /*tp_print*/
0
- 0, /*tp_getattr*/
0
- 0, /*tp_setattr*/
0
- 0, /*tp_compare*/
0
- 0, /*tp_repr*/
0
- 0, /*tp_as_number*/
0
- 0, /*tp_as_sequence*/
0
- 0, /*tp_as_mapping*/
0
- 0, /*tp_hash */
0
- 0, /*tp_call*/
0
- 0, /*tp_str*/
0
- 0, /*tp_getattro*/
0
- 0, /*tp_setattro*/
0
- 0, /*tp_as_buffer*/
0
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
0
- "Ebb Server", /* tp_doc */
0
- 0, /* tp_traverse */
0
- 0, /* tp_clear */
0
- 0, /* tp_richcompare */
0
- 0, /* tp_weaklistoffset */
0
- 0, /* tp_iter */
0
- 0, /* tp_iternext */
0
- Server_methods, /* tp_methods */
0
- 0, /* tp_members */
0
- 0, /* tp_getset */
0
- 0, /* tp_base */
0
- 0, /* tp_dict */
0
- 0, /* tp_descr_get */
0
- 0, /* tp_descr_set */
0
- 0, /* tp_dictoffset */
0
- (initproc)Server_init, /* tp_init */
0
- Server_alloc, /* tp_alloc */
0
- Server_new, /* tp_new */
0
-};
0
-
0
-initebb(void)
0
+static PyObject *start_server(PyObject *self, PyObject *args)
0
+{
0
+ PyObject *application_temp;
0
+ int port;
0
+
0
+ if(!PyArg_ParseTuple(args, "Oi", &application_temp, &port))
0
+ return NULL;
0
+ if(!PyCallable_Check(application_temp)) {
0
+ PyErr_SetString(PyExc_TypeError, "parameter must be callable");
0
+ return NULL;
0
+ }
0
+ Py_XINCREF(application_temp); /* Add a reference to new callback */
0
+ Py_XDECREF(application); /* Dispose of previous callback */
0
+ application = application_temp; /* Remember new callback */
0
+
0
+ loop = ev_default_loop(0);
0
+ ebb_server_init(&server, loop, request_cb, NULL);
0
+ ebb_server_listen_on_port(&server, port);
0
+
0
+ //ev_loop(loop, EVLOOP_ONESHOT);
0
+ ev_loop(loop, 0);
0
+
0
+ Py_XDECREF(application);
0
+
0
+ Py_RETURN_NONE;
0
+}
0
+
0
+static PyObject *stop_server(PyObject *self)
0
 {
0
- PyObject *m, *d, *tmp;
0
- ServerType.ob_type = &PyType_Type;
0
+ Py_RETURN_NONE;
0
+}
0
 
0
- m = Py_InitModule("Ebb", GeoIP_Class_methods);
0
- d = PyModule_GetDict(m);
0
+static PyMethodDef Ebb_methods[] =
0
+ { {"start_server" , (PyCFunction)start_server, METH_VARARGS,
0
+ "Listen on port supplied. Start the server event loop." }
0
+ , {"stop_server" , (PyCFunction)stop_server, METH_NOARGS,
0
+ "Unlisten." }
0
+ , {NULL, NULL, 0, NULL}
0
+ };
0
 
0
- tmp = PyInt_FromLong(0);
0
- PyDict_SetItemString(d, "GEOIP_STANDARD", tmp);
0
- Py_DECREF(tmp);
0
+PyMODINIT_FUNC initebb(void)
0
+{
0
+ ebb_server_init(&server, loop, request_cb, NULL);
0
+
0
+
0
+ PyObject *m;
0
+
0
+ m = Py_InitModule("ebb", Ebb_methods);
0
 
0
- tmp = PyInt_FromLong(1);
0
- PyDict_SetItemString(d, "GEOIP_MEMORY_CACHE", tmp);
0
- Py_DECREF(tmp);
0
+ base_env = PyDict_New();
0
+ PyDict_SetStringString(base_env, "SCRIPT_NAME", "");
0
+ PyDict_SetStringString(base_env, "SERVER_SOFTWARE", "Ebb 0.0.4");
0
+ PyDict_SetStringString(base_env, "SERVER_PROTOCOL", "HTTP/1.1");
0
+ PyDict_SetStringString(base_env, "wsgi.url_scheme", "http");
0
+ PyDict_SetItemString(base_env, "wsgi.multithread", Py_False);
0
+ PyDict_SetItemString(base_env, "wsgi.multiprocess", Py_False);
0
+ PyDict_SetItemString(base_env, "wsgi.run_once", Py_False);
0
+ //PyDict_SetItemString(base_env, "wsgi.version", (0,1));
0
+ //PyDict_SetItemString(base_env, "wsgi.errors", STDERR);
0
+
0
+#define DEF_GLOBAL(N, val) global_##N = PyString_FromString(val)
0
+ DEF_GLOBAL(http_prefix, "HTTP_");
0
+ DEF_GLOBAL(request_method, "REQUEST_METHOD");
0
+ DEF_GLOBAL(request_uri, "REQUEST_URI");
0
+ DEF_GLOBAL(fragment, "FRAGMENT");
0
+ DEF_GLOBAL(request_path, "REQUEST_PATH");
0
+ DEF_GLOBAL(query_string, "QUERY_STRING");
0
+ DEF_GLOBAL(http_version, "HTTP_VERSION");
0
+ DEF_GLOBAL(request_body, "REQUEST_BODY");
0
+ DEF_GLOBAL(server_name, "SERVER_NAME");
0
+ DEF_GLOBAL(server_port, "SERVER_PORT");
0
+ DEF_GLOBAL(path_info, "PATH_INFO");
0
+ DEF_GLOBAL(content_length, "CONTENT_LENGTH");
0
+ DEF_GLOBAL(http_host, "HTTP_HOST");
0
+
0
 }
0
\ No newline at end of file
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0
@@ -0,0 +1,15 @@
0
+import sys
0
+sys.path.append('/Users/ry/projects/ebb/build/lib.macosx-10.3-ppc-2.5')
0
+import ebb
0
+
0
+print "hello"
0
+
0
+def simple_app(environ, start_response):
0
+ """Simplest possible application object"""
0
+ status = '200 OK'
0
+ print repr(environ)
0
+ response_headers = [('Content-type','text/plain')]
0
+ #start_response(status, response_headers)
0
+ return ['Hello world!\n']
0
+
0
+ebb.start_server(simple_app, 4000)
0
\ No newline at end of file

Comments

    No one has commented yet.