diff --git a/bjoern/bjoernmodule.c b/bjoern/bjoernmodule.c index d6981336..1136bc1b 100644 --- a/bjoern/bjoernmodule.c +++ b/bjoern/bjoernmodule.c @@ -14,7 +14,7 @@ static PyObject* listen(PyObject* self, PyObject* args) { const char* host; - int port; + int port = 0; if(wsgi_app) { PyErr_SetString( @@ -24,20 +24,33 @@ listen(PyObject* self, PyObject* args) return NULL; } - if(!PyArg_ParseTuple(args, "Osi:run/listen", &wsgi_app, &host, &port)) + if(!PyArg_ParseTuple(args, "Os|i:run/listen", &wsgi_app, &host, &port)) return NULL; _initialize_request_module(host, port); + if(!port) { + /* Unix socket: "unix:/tmp/foo.sock" or "unix:@abstract-socket" (Linux) */ + if(strncmp("unix:", host, 5)) { + PyErr_Format(PyExc_ValueError, "'port' missing but 'host' is not a Unix socket"); + goto err; + } + host += 5; + } + if(!server_init(host, port)) { - PyErr_Format( - PyExc_RuntimeError, - "Could not start server on %s:%d", host, port - ); - return NULL; + if(port) + PyErr_Format(PyExc_RuntimeError, "Could not start server on %s:%d", host, port); + else + PyErr_Format(PyExc_RuntimeError, "Could not start server on %s", host); + goto err; } Py_RETURN_NONE; + +err: + wsgi_app = NULL; + return NULL; } PyDoc_STRVAR(run_doc, diff --git a/bjoern/request.c b/bjoern/request.c index 1b4b4832..538fc75d 100644 --- a/bjoern/request.c +++ b/bjoern/request.c @@ -354,6 +354,6 @@ void _initialize_request_module(const char* server_host, const int server_port) PyDict_SetItemString( wsgi_base_dict, "SERVER_PORT", - PyString_FromFormat("%d", server_port) + server_port ? PyString_FromFormat("%d", server_port) : _empty_string ); } diff --git a/bjoern/server.c b/bjoern/server.c index 14a5636e..9b530142 100644 --- a/bjoern/server.c +++ b/bjoern/server.c @@ -18,7 +18,6 @@ #define GIL_LOCK(n) PyGILState_STATE _gilstate_##n = PyGILState_Ensure() #define GIL_UNLOCK(n) PyGILState_Release(_gilstate_##n) -static int sockfd; static const char* http_error_messages[4] = { NULL, /* Error codes start at 1 because 0 means "no error" */ "HTTP/1.1 400 Bad Request\r\n\r\n", @@ -38,12 +37,14 @@ static bool send_chunk(Request*); static bool do_sendfile(Request*); static bool handle_nonzero_errno(Request*); +static struct { int fd; const char* filename; } sockinfo; + void server_run(void) { struct ev_loop* mainloop = ev_default_loop(0); ev_io accept_watcher; - ev_io_init(&accept_watcher, ev_io_on_request, sockfd, EV_READ); + ev_io_init(&accept_watcher, ev_io_on_request, sockinfo.fd, EV_READ); ev_io_start(mainloop, &accept_watcher); #if WANT_SIGINT_HANDLING @@ -55,15 +56,23 @@ void server_run(void) /* This is the program main loop */ Py_BEGIN_ALLOW_THREADS ev_loop(mainloop, 0); + ev_default_destroy(); Py_END_ALLOW_THREADS } +static void cleanup() { + close(sockinfo.fd); + if(sockinfo.filename) + unlink(sockinfo.filename); +} + #if WANT_SIGINT_HANDLING static void ev_signal_on_sigint(struct ev_loop* mainloop, ev_signal* watcher, const int events) { /* Clean up and shut down this thread. * (Shuts down the Python interpreter if this is the main thread) */ + cleanup(); ev_unloop(mainloop, EVUNLOOP_ALL); PyErr_SetInterrupt(); } @@ -71,26 +80,27 @@ ev_signal_on_sigint(struct ev_loop* mainloop, ev_signal* watcher, const int even bool server_init(const char* hostaddr, const int port) { - if(strncmp("unix:", hostaddr, 5) == 0) { - if((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) + if(!port) { + /* Unix socket */ + if((sockinfo.fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) return false; struct sockaddr_un sockaddr; sockaddr.sun_family = PF_UNIX; - strcpy(sockaddr.sun_path, hostaddr+5); - if(hostaddr[5] == '@') sockaddr.sun_path[0] = '\0'; /* Use @ for abstract */ + strcpy(sockaddr.sun_path, hostaddr); - if(bind(sockfd, (struct sockaddr*)&sockaddr, strlen(hostaddr+5)+2) < 0) - return false; + /* Use @ for abstract */ + if(hostaddr[0] == '@') + sockaddr.sun_path[0] = '\0'; + else + sockinfo.filename = hostaddr; - if(listen(sockfd, LISTEN_BACKLOG) < 0) - return false; - - DBG("Listening on %s...", hostaddr); - return true; + if(bind(sockinfo.fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) + goto err; } else { - if((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + /* IP bind */ + if((sockinfo.fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) return false; struct sockaddr_in sockaddr; @@ -100,17 +110,21 @@ bool server_init(const char* hostaddr, const int port) /* Set SO_REUSEADDR t make the IP address available for reuse */ int optval = true; - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + setsockopt(sockinfo.fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - if(bind(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) - return false; + if(bind(sockinfo.fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) + goto err; + } - if(listen(sockfd, LISTEN_BACKLOG) < 0) - return false; + if(listen(sockinfo.fd, LISTEN_BACKLOG) < 0) + goto err; - DBG("Listening on %s:%d...", hostaddr, port); - return true; - } + DBG("Listening on %s:%d...", hostaddr, port); + return true; + +err: + cleanup(); + return false; } static void @@ -205,7 +219,7 @@ ev_io_on_read(struct ev_loop* mainloop, ev_io* watcher, const int events) return; } -/* XXX too much gotos */ +/* XXX too many gotos */ static void ev_io_on_write(struct ev_loop* mainloop, ev_io* watcher, const int events) { diff --git a/http-parser b/http-parser index 3cf68f9a..1786fdae 160000 --- a/http-parser +++ b/http-parser @@ -1 +1 @@ -Subproject commit 3cf68f9a7065325bfbc69917a16245579940c226 +Subproject commit 1786fdae36d3d40d59463dacab1cfb4165cf9f1d diff --git a/tests/hello.py b/tests/hello.py index bf93e2ad..6b11463c 100644 --- a/tests/hello.py +++ b/tests/hello.py @@ -25,4 +25,5 @@ def app4(e, s): def wsgi_app(env, start_response): return choice(apps)(env, start_response) -bjoern.run(wsgi_app, '0.0.0.0', 8080) +if __name__ == '__main__': + bjoern.run(wsgi_app, '0.0.0.0', 8080) diff --git a/tests/hello_unix.py b/tests/hello_unix.py index e670b45e..6087fe28 100644 --- a/tests/hello_unix.py +++ b/tests/hello_unix.py @@ -1,28 +1,10 @@ -import bjoern -from random import choice - -def app1(env, sr): - sr('200 ok', [('Foo', 'Bar'), ('Blah', 'Blubb'), ('Spam', 'Eggs'), ('Blurg', 'asdasjdaskdasdjj asdk jaks / /a jaksdjkas jkasd jkasdj '), - ('asd2easdasdjaksdjdkskjkasdjka', 'oasdjkadk kasdk k k k k k ')]) - return ['hello', 'world'] - -def app2(env, sr): - sr('200 ok', []) - return 'hello' - -def app3(env, sr): - sr('200 abc', [('Content-Length', '12')]) - yield 'Hello' - yield ' World' - yield '\n' - -def app4(e, s): - s('200 ok', []) - return ['hello\n'] - -apps = (app1, app2, app3, app4) - -def wsgi_app(env, start_response): - return choice(apps)(env, start_response) - -bjoern.run(wsgi_app, 'unix:@hello_unix', 0) +import sys +from hello import * + +if __name__ == '__main__': + try: + host = sys.argv[1] + except IndexError: + host = 'hello_unix' + host = 'unix:' + host + bjoern.run(wsgi_app, host)