Klaus Demo bjoern / 741f169
Refactoring of @k3d3's initial Unix socket patch. The 'port' argument of 'bjoern.run' may now be omitted if using Unix sockets. Jonas Haag 8 years ago
6 changed file(s) with 116 addition(s) and 92 deletion(s). Raw diff Collapse all Expand all
1313 listen(PyObject* self, PyObject* args)
1414 {
1515 const char* host;
16 int port;
16 int port = 0;
1717
1818 if(wsgi_app) {
1919 PyErr_SetString(
2323 return NULL;
2424 }
2525
26 if(!PyArg_ParseTuple(args, "Osi:run/listen", &wsgi_app, &host, &port))
26 if(!PyArg_ParseTuple(args, "Os|i:run/listen", &wsgi_app, &host, &port))
2727 return NULL;
2828
2929 _initialize_request_module(host, port);
3030
31 if(!port) {
32 /* Unix socket: "unix:/tmp/foo.sock" or "unix:@abstract-socket" (Linux) */
33 if(strncmp("unix:", host, 5)) {
34 PyErr_Format(PyExc_ValueError, "'port' missing but 'host' is not a Unix socket");
35 goto err;
36 }
37 host += 5;
38 }
39
3140 if(!server_init(host, port)) {
32 PyErr_Format(
33 PyExc_RuntimeError,
34 "Could not start server on %s:%d", host, port
35 );
36 return NULL;
41 if(port)
42 PyErr_Format(PyExc_RuntimeError, "Could not start server on %s:%d", host, port);
43 else
44 PyErr_Format(PyExc_RuntimeError, "Could not start server on %s", host);
45 goto err;
3746 }
3847
3948 Py_RETURN_NONE;
49
50 err:
51 wsgi_app = NULL;
52 return NULL;
4053 }
4154
4255 PyDoc_STRVAR(run_doc,
353353 PyDict_SetItemString(
354354 wsgi_base_dict,
355355 "SERVER_PORT",
356 PyString_FromFormat("%d", server_port)
356 server_port ? PyString_FromFormat("%d", server_port) : _empty_string
357357 );
358358 }
1717 #define GIL_LOCK(n) PyGILState_STATE _gilstate_##n = PyGILState_Ensure()
1818 #define GIL_UNLOCK(n) PyGILState_Release(_gilstate_##n)
1919
20 static int sockfd;
2120 static const char* http_error_messages[4] = {
2221 NULL, /* Error codes start at 1 because 0 means "no error" */
2322 "HTTP/1.1 400 Bad Request\r\n\r\n",
3736 static bool do_sendfile(Request*);
3837 static bool handle_nonzero_errno(Request*);
3938
39 static struct { int fd; const char* filename; } sockinfo;
40
4041 void server_run(void)
4142 {
4243 struct ev_loop* mainloop = ev_default_loop(0);
4344
4445 ev_io accept_watcher;
45 ev_io_init(&accept_watcher, ev_io_on_request, sockfd, EV_READ);
46 ev_io_init(&accept_watcher, ev_io_on_request, sockinfo.fd, EV_READ);
4647 ev_io_start(mainloop, &accept_watcher);
4748
4849 #if WANT_SIGINT_HANDLING
5455 /* This is the program main loop */
5556 Py_BEGIN_ALLOW_THREADS
5657 ev_loop(mainloop, 0);
58 ev_default_destroy();
5759 Py_END_ALLOW_THREADS
60 }
61
62 static void cleanup() {
63 close(sockinfo.fd);
64 if(sockinfo.filename)
65 unlink(sockinfo.filename);
5866 }
5967
6068 #if WANT_SIGINT_HANDLING
6371 {
6472 /* Clean up and shut down this thread.
6573 * (Shuts down the Python interpreter if this is the main thread) */
74 cleanup();
6675 ev_unloop(mainloop, EVUNLOOP_ALL);
6776 PyErr_SetInterrupt();
6877 }
7079
7180 bool server_init(const char* hostaddr, const int port)
7281 {
73 if(strncmp("unix:", hostaddr, 5) == 0) {
74 if((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
82 if(!port) {
83 /* Unix socket */
84 if((sockinfo.fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
7585 return false;
7686
7787 struct sockaddr_un sockaddr;
7888 sockaddr.sun_family = PF_UNIX;
79 strcpy(sockaddr.sun_path, hostaddr+5);
80 if(hostaddr[5] == '@') sockaddr.sun_path[0] = '\0'; /* Use @ for abstract */
81
82 if(bind(sockfd, (struct sockaddr*)&sockaddr, strlen(hostaddr+5)+2) < 0)
83 return false;
84
85 if(listen(sockfd, LISTEN_BACKLOG) < 0)
86 return false;
87
88 DBG("Listening on %s...", hostaddr);
89 return true;
89 strcpy(sockaddr.sun_path, hostaddr);
90
91 /* Use @ for abstract */
92 if(hostaddr[0] == '@')
93 sockaddr.sun_path[0] = '\0';
94 else
95 sockinfo.filename = hostaddr;
96
97 if(bind(sockinfo.fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0)
98 goto err;
9099 }
91100 else {
92 if((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
101 /* IP bind */
102 if((sockinfo.fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
93103 return false;
94104
95105 struct sockaddr_in sockaddr;
99109
100110 /* Set SO_REUSEADDR t make the IP address available for reuse */
101111 int optval = true;
102 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
103
104 if(bind(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0)
105 return false;
106
107 if(listen(sockfd, LISTEN_BACKLOG) < 0)
108 return false;
109
110 DBG("Listening on %s:%d...", hostaddr, port);
111 return true;
112 }
112 setsockopt(sockinfo.fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
113
114 if(bind(sockinfo.fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0)
115 goto err;
116 }
117
118 if(listen(sockinfo.fd, LISTEN_BACKLOG) < 0)
119 goto err;
120
121 DBG("Listening on %s:%d...", hostaddr, port);
122 return true;
123
124 err:
125 cleanup();
126 return false;
113127 }
114128
115129 static void
204218 return;
205219 }
206220
207 /* XXX too much gotos */
221 /* XXX too many gotos */
208222 static void
209223 ev_io_on_write(struct ev_loop* mainloop, ev_io* watcher, const int events)
210224 {
1717 #define GIL_LOCK(n) PyGILState_STATE _gilstate_##n = PyGILState_Ensure()
1818 #define GIL_UNLOCK(n) PyGILState_Release(_gilstate_##n)
1919
20 static int sockfd;
2120 static const char* http_error_messages[4] = {
2221 NULL, /* Error codes start at 1 because 0 means "no error" */
2322 "HTTP/1.1 400 Bad Request\r\n\r\n",
3736 static bool do_sendfile(Request*);
3837 static bool handle_nonzero_errno(Request*);
3938
39 static struct { int fd; const char* filename; } sockinfo;
40
4041 void server_run(void)
4142 {
4243 struct ev_loop* mainloop = ev_default_loop(0);
4344
4445 ev_io accept_watcher;
45 ev_io_init(&accept_watcher, ev_io_on_request, sockfd, EV_READ);
46 ev_io_init(&accept_watcher, ev_io_on_request, sockinfo.fd, EV_READ);
4647 ev_io_start(mainloop, &accept_watcher);
4748
4849 #if WANT_SIGINT_HANDLING
5455 /* This is the program main loop */
5556 Py_BEGIN_ALLOW_THREADS
5657 ev_loop(mainloop, 0);
58 ev_default_destroy();
5759 Py_END_ALLOW_THREADS
60 }
61
62 static void cleanup() {
63 close(sockinfo.fd);
64 if(sockinfo.filename)
65 unlink(sockinfo.filename);
5866 }
5967
6068 #if WANT_SIGINT_HANDLING
6371 {
6472 /* Clean up and shut down this thread.
6573 * (Shuts down the Python interpreter if this is the main thread) */
74 cleanup();
6675 ev_unloop(mainloop, EVUNLOOP_ALL);
6776 PyErr_SetInterrupt();
6877 }
7079
7180 bool server_init(const char* hostaddr, const int port)
7281 {
73 if(strncmp("unix:", hostaddr, 5) == 0) {
74 if((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
82 if(!port) {
83 /* Unix socket */
84 if((sockinfo.fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
7585 return false;
7686
7787 struct sockaddr_un sockaddr;
7888 sockaddr.sun_family = PF_UNIX;
79 strcpy(sockaddr.sun_path, hostaddr+5);
80 if(hostaddr[5] == '@') sockaddr.sun_path[0] = '\0'; /* Use @ for abstract */
81
82 if(bind(sockfd, (struct sockaddr*)&sockaddr, strlen(hostaddr+5)+2) < 0)
83 return false;
84
85 if(listen(sockfd, LISTEN_BACKLOG) < 0)
86 return false;
87
88 DBG("Listening on %s...", hostaddr);
89 return true;
89 strcpy(sockaddr.sun_path, hostaddr);
90
91 /* Use @ for abstract */
92 if(hostaddr[0] == '@')
93 sockaddr.sun_path[0] = '\0';
94 else
95 sockinfo.filename = hostaddr;
96
97 if(bind(sockinfo.fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0)
98 goto err;
9099 }
91100 else {
92 if((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
101 /* IP bind */
102 if((sockinfo.fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
93103 return false;
94104
95105 struct sockaddr_in sockaddr;
99109
100110 /* Set SO_REUSEADDR t make the IP address available for reuse */
101111 int optval = true;
102 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
103
104 if(bind(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0)
105 return false;
106
107 if(listen(sockfd, LISTEN_BACKLOG) < 0)
108 return false;
109
110 DBG("Listening on %s:%d...", hostaddr, port);
111 return true;
112 }
112 setsockopt(sockinfo.fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
113
114 if(bind(sockinfo.fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0)
115 goto err;
116 }
117
118 if(listen(sockinfo.fd, LISTEN_BACKLOG) < 0)
119 goto err;
120
121 DBG("Listening on %s:%d...", hostaddr, port);
122 return true;
123
124 err:
125 cleanup();
126 return false;
113127 }
114128
115129 static void
204218 return;
205219 }
206220
207 /* XXX too much gotos */
221 /* XXX too many gotos */
208222 static void
209223 ev_io_on_write(struct ev_loop* mainloop, ev_io* watcher, const int events)
210224 {
2424 def wsgi_app(env, start_response):
2525 return choice(apps)(env, start_response)
2626
27 bjoern.run(wsgi_app, '0.0.0.0', 8080)
27 if __name__ == '__main__':
28 bjoern.run(wsgi_app, '0.0.0.0', 8080)
0 import bjoern
1 from random import choice
0 import sys
1 from hello import *
22
3 def app1(env, sr):
4 sr('200 ok', [('Foo', 'Bar'), ('Blah', 'Blubb'), ('Spam', 'Eggs'), ('Blurg', 'asdasjdaskdasdjj asdk jaks / /a jaksdjkas jkasd jkasdj '),
5 ('asd2easdasdjaksdjdkskjkasdjka', 'oasdjkadk kasdk k k k k k ')])
6 return ['hello', 'world']
7
8 def app2(env, sr):
9 sr('200 ok', [])
10 return 'hello'
11
12 def app3(env, sr):
13 sr('200 abc', [('Content-Length', '12')])
14 yield 'Hello'
15 yield ' World'
16 yield '\n'
17
18 def app4(e, s):
19 s('200 ok', [])
20 return ['hello\n']
21
22 apps = (app1, app2, app3, app4)
23
24 def wsgi_app(env, start_response):
25 return choice(apps)(env, start_response)
26
27 bjoern.run(wsgi_app, 'unix:@hello_unix', 0)
3 if __name__ == '__main__':
4 try:
5 host = sys.argv[1]
6 except IndexError:
7 host = 'hello_unix'
8 host = 'unix:' + host
9 bjoern.run(wsgi_app, host)