1 /* A python binding to the ebb web server
2 * Copyright (c) 2008 Ry Dahl. This software is released under the MIT
3 * License. See README file for details.
10 #define PyDict_SetStringString(dict, str1, str2) PyDict_SetItemString(dict, str1, PyString_FromString(str2))
11 #define ASCII_UPPER(ch) ('a' <= ch && ch <= 'z' ? ch - 'a' + 'A' : ch)
13 /* Why is there a global ebb_server variable instead of a wrapping it in a
14 * class? Because you would for no conceivable reason want to run more than
15 * one ebb_server per python VM instance.
17 static ebb_server server
;
19 static PyObject
*application
;
20 static PyObject
*base_env
;
21 static PyObject
*global_http_prefix
;
22 static PyObject
*global_request_method
;
23 static PyObject
*global_request_uri
;
24 static PyObject
*global_fragment
;
25 static PyObject
*global_request_path
;
26 static PyObject
*global_query_string
;
27 static PyObject
*global_http_version
;
28 static PyObject
*global_request_body
;
29 static PyObject
*global_server_name
;
30 static PyObject
*global_server_port
;
31 static PyObject
*global_path_info
;
32 static PyObject
*global_content_length
;
33 static PyObject
*global_http_host
;
35 /* A callable type called Client. __call__(status, response_headers)
36 * is the second argument to an appplcation
37 * Client.environ is the first argument.
45 py_start_response(py_client
*self
, PyObject
*args
, PyObject
*kw
);
47 static void py_client_dealloc(PyObject
*obj
)
49 py_client
*self
= (py_client
*) obj
;
50 ebb_client_release(self
->client
);
51 obj
->ob_type
->tp_free(obj
);
52 printf("dealloc called!\n");
55 static PyMethodDef client_methods
[] =
56 { {"start_response" , (PyCFunction
)py_start_response
, METH_VARARGS
, NULL
}
57 // , {"write" , (PyCFunction)write, METH_VARARGS, NULL }
58 , {NULL
, NULL
, 0, NULL
}
61 static PyTypeObject py_client_t
=
63 , tp_name
: "ebb.Client"
64 , tp_doc
: "a wrapper around ebb_client"
65 , tp_basicsize
: sizeof(py_client
)
66 , tp_flags
: Py_TPFLAGS_DEFAULT
67 , tp_methods
: client_methods
68 , tp_dealloc
: py_client_dealloc
72 py_start_response(py_client
*self
, PyObject
*args
, PyObject
*kw
)
74 PyObject
*response_headers
;
78 if(!PyArg_ParseTuple(args
, "sO", &status_string
, &response_headers
))
81 /* do this goofy split(' ') operation. wsgi is such a terrible api. */
82 status
= 100 * (status_string
[0] - '0')
83 + 10 * (status_string
[1] - '0')
84 + 1 * (status_string
[2] - '0');
85 assert(0 <= status
&& status
< 1000);
87 ebb_client_write_status(self
->client
, status
, status_string
+4);
89 PyObject
*iterator
= PyObject_GetIter(response_headers
);
90 PyObject
*header_pair
;
92 while(header_pair
= PyIter_Next(iterator
)) {
93 if(!PyArg_ParseTuple(header_pair
, "ss", &field
, &value
))
95 ebb_client_write_header(self
->client
, field
, value
);
96 Py_DECREF(header_pair
);
98 ebb_client_write(self
->client
, "\r\n", 2);
103 static PyObject
* env_field(struct ebb_env_item
*item
)
109 case EBB_FIELD_VALUE_PAIR
:
110 f
= PyString_FromStringAndSize(NULL
, PyString_GET_SIZE(global_http_prefix
) + item
->field_length
);
111 memcpy( PyString_AS_STRING(f
)
112 , PyString_AS_STRING(global_http_prefix
)
113 , PyString_GET_SIZE(global_http_prefix
)
115 for(i
= 0; i
< item
->field_length
; i
++) {
116 char *ch
= PyString_AS_STRING(f
) + PyString_GET_SIZE(global_http_prefix
) + i
;
117 *ch
= item
->field
[i
] == '-' ? '_' : ASCII_UPPER(item
->field
[i
]);
120 case EBB_REQUEST_METHOD
: f
= global_request_method
; break;
121 case EBB_REQUEST_URI
: f
= global_request_uri
; break;
122 case EBB_FRAGMENT
: f
= global_fragment
; break;
123 case EBB_REQUEST_PATH
: f
= global_request_path
; break;
124 case EBB_QUERY_STRING
: f
= global_query_string
; break;
125 case EBB_HTTP_VERSION
: f
= global_http_version
; break;
126 case EBB_SERVER_PORT
: f
= global_server_port
; break;
127 case EBB_CONTENT_LENGTH
: f
= global_content_length
; break;
128 default: assert(FALSE
);
135 static PyObject
* env_value(struct ebb_env_item
*item
)
137 if(item
->value_length
> 0)
138 return PyString_FromStringAndSize(item
->value
, item
->value_length
);
140 return Py_None
; // XXX need to increase ref count? :/
144 static PyObject
* py_client_env(ebb_client
*client
)
146 PyObject
*env
= PyDict_Copy(base_env
);
149 for(i
=0; i
< client
->env_size
; i
++) {
150 PyDict_SetItem(env
, env_field(&client
->env
[i
])
151 , env_value(&client
->env
[i
])
154 // PyDict_SetStringString(hash, global_path_info, rb_hash_aref(hash, global_request_path));
159 static py_client
* py_client_new(ebb_client
*client
)
161 py_client
*self
= PyObject_New(py_client
, &py_client_t
);
162 if(self
== NULL
) return NULL
;
163 self
->client
= client
;
165 //if(0 < PyObject_SetAttrString((PyObject*)self, "environ", py_client_env(client)))
171 const char *test_response
= "test_response defined in ebb_python.c\r\n";
173 void request_cb(ebb_client
*client
, void *ignore
)
176 PyObject
*environ
, *start_response
;
178 py_client
*pclient
= py_client_new(client
);
179 assert(pclient
!= NULL
);
180 //environ = PyObject_GetAttrString((PyObject*)pclient, "environ");
181 environ
= py_client_env(client
);
182 assert(environ
!= NULL
);
184 start_response
= PyObject_GetAttrString((PyObject
*)pclient
, "start_response");
185 assert(start_response
!= NULL
);
187 PyObject
*arglist
= Py_BuildValue("OO", environ
, start_response
);
188 assert(arglist
!= NULL
);
189 assert(application
!= NULL
);
190 PyObject
*body
= PyEval_CallObject(application
, arglist
);
191 assert(body
!= NULL
);
196 PyObject
*iterator
= PyObject_GetIter(body
);
198 while (body_item
= PyIter_Next(iterator
)) {
199 char *body_string
= PyString_AsString(body_item
);
200 int body_length
= PyString_Size(body_item
);
201 /* Todo support streaming! */
202 ebb_client_write(pclient
->client
, body_string
, body_length
);
203 Py_DECREF(body_item
);
206 ebb_client_begin_transmission(client
);
212 static PyObject
*start_server(PyObject
*self
, PyObject
*args
)
214 PyObject
*application_temp
;
217 if(!PyArg_ParseTuple(args
, "Oi", &application_temp
, &port
))
219 if(!PyCallable_Check(application_temp
)) {
220 PyErr_SetString(PyExc_TypeError
, "parameter must be callable");
223 Py_XINCREF(application_temp
); /* Add a reference to new callback */
224 Py_XDECREF(application
); /* Dispose of previous callback */
225 application
= application_temp
; /* Remember new callback */
227 loop
= ev_default_loop(0);
228 ebb_server_init(&server
, loop
, request_cb
, NULL
);
229 ebb_server_listen_on_port(&server
, port
);
231 //ev_loop(loop, EVLOOP_ONESHOT);
234 Py_XDECREF(application
);
239 static PyObject
*stop_server(PyObject
*self
)
245 static PyMethodDef ebb_module_methods
[] =
246 { {"start_server" , (PyCFunction
)start_server
, METH_VARARGS
, NULL
}
247 , {"stop_server" , (PyCFunction
)stop_server
, METH_NOARGS
, NULL
}
248 , {NULL
, NULL
, 0, NULL
}
251 PyMODINIT_FUNC
initebb(void)
253 PyObject
*m
= Py_InitModule("ebb", ebb_module_methods
);
255 base_env
= PyDict_New();
256 PyDict_SetStringString(base_env
, "SCRIPT_NAME", "");
257 PyDict_SetStringString(base_env
, "SERVER_SOFTWARE", EBB_VERSION
);
258 PyDict_SetStringString(base_env
, "SERVER_NAME", "0.0.0.0");
259 PyDict_SetStringString(base_env
, "SERVER_PROTOCOL", "HTTP/1.1");
260 PyDict_SetStringString(base_env
, "wsgi.url_scheme", "http");
261 PyDict_SetItemString(base_env
, "wsgi.multithread", Py_False
);
262 PyDict_SetItemString(base_env
, "wsgi.multiprocess", Py_False
);
263 PyDict_SetItemString(base_env
, "wsgi.run_once", Py_False
);
264 //PyDict_SetItemString(base_env, "wsgi.version", (0,1));
265 //PyDict_SetItemString(base_env, "wsgi.errors", STDERR);
269 py_client_t
.tp_new
= PyType_GenericNew
;
270 if (PyType_Ready(&py_client_t
) < 0) return;
271 Py_INCREF(&py_client_t
);
272 PyModule_AddObject(m
, "Client", (PyObject
*)&py_client_t
);
274 #define DEF_GLOBAL(N, val) global_##N = PyString_FromString(val)
275 DEF_GLOBAL(http_prefix
, "HTTP_");
276 DEF_GLOBAL(request_method
, "REQUEST_METHOD");
277 DEF_GLOBAL(request_uri
, "REQUEST_URI");
278 DEF_GLOBAL(fragment
, "FRAGMENT");
279 DEF_GLOBAL(request_path
, "REQUEST_PATH");
280 DEF_GLOBAL(query_string
, "QUERY_STRING");
281 DEF_GLOBAL(http_version
, "HTTP_VERSION");
282 DEF_GLOBAL(request_body
, "REQUEST_BODY");
283 DEF_GLOBAL(server_name
, "SERVER_NAME");
284 DEF_GLOBAL(server_port
, "SERVER_PORT");
285 DEF_GLOBAL(path_info
, "PATH_INFO");
286 DEF_GLOBAL(content_length
, "CONTENT_LENGTH");
287 DEF_GLOBAL(http_host
, "HTTP_HOST");
289 ebb_server_init(&server
, loop
, request_cb
, NULL
);