Skip to content

Commit

Permalink
Refactoring of @k3d3's initial Unix socket patch.
Browse files Browse the repository at this point in the history
The 'port' argument of 'bjoern.run' may now be omitted if using
Unix sockets.
  • Loading branch information
jonashaag committed Jan 14, 2012
1 parent 59c44f2 commit 741f169
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 61 deletions.
27 changes: 20 additions & 7 deletions bjoern/bjoernmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ static PyObject*
listen(PyObject* self, PyObject* args)
{
const char* host;
int port;
int port = 0;

if(wsgi_app) {
PyErr_SetString(
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion bjoern/request.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}
60 changes: 37 additions & 23 deletions bjoern/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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
Expand All @@ -55,42 +56,51 @@ 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();
}
#endif

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;
Expand All @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion http-parser
Submodule http-parser updated from 3cf68f to 1786fd
3 changes: 2 additions & 1 deletion tests/hello.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
38 changes: 10 additions & 28 deletions tests/hello_unix.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 741f169

Please sign in to comment.