diff --git a/core/protocol.c b/core/protocol.c index 9afe8d39..43fd6525 100644 --- a/core/protocol.c +++ b/core/protocol.c @@ -412,6 +412,12 @@ static int uwsgi_proto_check_11(struct wsgi_request *wsgi_req, char *key, char * return 0; } + if (!uwsgi_proto_key("HTTP_ORIGIN", 11)) { + wsgi_req->http_origin = buf; + wsgi_req->http_origin_len = len; + return 0; + } + return 0; } @@ -589,9 +595,28 @@ static int uwsgi_proto_check_22(struct wsgi_request *wsgi_req, char *key, char * wsgi_req->if_modified_since_len = len; return 0; } + + if (!uwsgi_proto_key("HTTP_SEC_WEBSOCKET_KEY", 22)) { + wsgi_req->http_sec_websocket_key = buf; + wsgi_req->http_sec_websocket_key_len = len; + return 0; + } + return 0; } +static int uwsgi_proto_check_27(struct wsgi_request *wsgi_req, char *key, char *buf, uint16_t len) { + + if (!uwsgi_proto_key("HTTP_SEC_WEBSOCKET_PROTOCOL", 27)) { + wsgi_req->http_sec_websocket_protocol = buf; + wsgi_req->http_sec_websocket_protocol_len = len; + return 0; + } + + return 0; +} + + void uwsgi_proto_hooks_setup() { int i = 0; for(i=0;ihttp_sec_websocket_key; + key_len = wsgi_req->http_sec_websocket_key_len; + } + if (key_len == 0) return -1; + char sha1[20]; if (uwsgi_response_prepare_headers(wsgi_req, "101 Web Socket Protocol Handshake", 33)) return -1; if (uwsgi_response_add_header(wsgi_req, "Upgrade", 7, "WebSocket", 9)) return -1; if (uwsgi_response_add_header(wsgi_req, "Connection", 10, "Upgrade", 7)) return -1; - if (origin_len > 0) { + + // if origin was requested or proto_len is specified, send it back + if (wsgi_req->http_origin_len > 0 || origin_len > 0) { + if (!origin_len) { + origin = wsgi_req->http_origin; + origin_len = wsgi_req->http_origin_len; + } if (uwsgi_response_add_header(wsgi_req, "Sec-WebSocket-Origin", 20, origin, origin_len)) return -1; } else { if (uwsgi_response_add_header(wsgi_req, "Sec-WebSocket-Origin", 20, "*", 1)) return -1; } + + // if protocol was requested or proto_len is specified, send it back + if (wsgi_req->http_sec_websocket_protocol_len > 0 || proto_len > 0) { + if (!proto_len) { + proto = wsgi_req->http_sec_websocket_protocol; + proto_len = wsgi_req->http_sec_websocket_protocol_len; + } + if (uwsgi_response_add_header(wsgi_req, "Sec-WebSocket-Protocol", 22, proto, proto_len)) return -1; + } // generate websockets sha1 and encode it to base64 if (!uwsgi_sha1_2n(key, key_len, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", 36, sha1)) return -1; size_t b64_len = 0; diff --git a/plugins/lua/lua_plugin.c b/plugins/lua/lua_plugin.c index c7445c19..730676c6 100644 --- a/plugins/lua/lua_plugin.c +++ b/plugins/lua/lua_plugin.c @@ -376,18 +376,22 @@ static int uwsgi_api_ready_fd(lua_State *L) { static int uwsgi_api_websocket_handshake(lua_State *L) { uint8_t argc = lua_gettop(L); - if (argc == 0) goto error; - const char *key = NULL, *origin = NULL; - size_t key_len = 0, origin_len = 0; + const char *key = NULL, *origin = NULL, *proto = NULL; + size_t key_len = 0, origin_len = 0, proto_len = 0; - key = lua_tolstring(L, 1, &key_len); - if (argc > 1) { - origin = lua_tolstring(L, 2, &origin_len); + if (argc > 0) { + key = lua_tolstring(L, 1, &key_len); + if (argc > 1) { + origin = lua_tolstring(L, 2, &origin_len); + if (argc > 2) { + proto = lua_tolstring(L, 3, &proto_len); + } + } } struct wsgi_request *wsgi_req = current_wsgi_req(); - if (uwsgi_websocket_handshake(wsgi_req, (char *)key, key_len, (char *)origin, origin_len)) { + if (uwsgi_websocket_handshake(wsgi_req, (char *)key, key_len, (char *)origin, origin_len, (char *) proto, proto_len)) { goto error; } @@ -786,7 +790,7 @@ static int uwsgi_lua_request(struct wsgi_request *wsgi_req) { uwsgi_log("%s\n", lua_tostring(L, -1)); lua_pop(L, 1); lua_pushvalue(L, -1); - goto clear; + goto clear2; } //uwsgi_log("%d %s %s %s\n",i,lua_typename(L, lua_type(L, -3)), lua_typename(L, lua_type(L, -2)) , lua_typename(L, lua_type(L, -1))); @@ -827,6 +831,7 @@ static int uwsgi_lua_request(struct wsgi_request *wsgi_req) { } clear: lua_pop(L, 4); +clear2: // set frequency if (!ulua.gc_freq || uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].requests % ulua.gc_freq == 0) { lua_gc(L, LUA_GCCOLLECT, 0); diff --git a/plugins/psgi/uwsgi_plmodule.c b/plugins/psgi/uwsgi_plmodule.c index d7e65890..4471e591 100644 --- a/plugins/psgi/uwsgi_plmodule.c +++ b/plugins/psgi/uwsgi_plmodule.c @@ -473,16 +473,24 @@ XS(XS_websocket_handshake) { char *origin = NULL; STRLEN origin_len = 0; - psgi_check_args(1); - - key = SvPV(ST(0), key_len); + char *proto = NULL; + STRLEN proto_len = 0; - if (items > 1) { - origin = SvPV(ST(0), origin_len); + psgi_check_args(0); + + if (items > 0) { + key = SvPV(ST(0), key_len); + if (items > 1) { + origin = SvPV(ST(1), origin_len); + if (items > 2) { + proto = SvPV(ST(2), proto_len); + } + } + } struct wsgi_request *wsgi_req = current_wsgi_req(); - if (uwsgi_websocket_handshake(wsgi_req, key, key_len, origin, origin_len)) { + if (uwsgi_websocket_handshake(wsgi_req, key, key_len, origin, origin_len, proto, proto_len)) { croak("unable to complete websocket handshake"); } diff --git a/plugins/pypy/pypy_setup.py b/plugins/pypy/pypy_setup.py index 87726abf..be27ae86 100644 --- a/plugins/pypy/pypy_setup.py +++ b/plugins/pypy/pypy_setup.py @@ -224,7 +224,7 @@ int async_add_fd_write(struct wsgi_request *, int, int); int async_add_fd_read(struct wsgi_request *, int, int); int uwsgi_connect(char *, int, int); -int uwsgi_websocket_handshake(struct wsgi_request *, char *, uint16_t, char *, uint16_t); +int uwsgi_websocket_handshake(struct wsgi_request *, char *, uint16_t, char *, uint16_t, char *, uint16_t); int uwsgi_websocket_send(struct wsgi_request *, char *, size_t); struct uwsgi_buffer *uwsgi_websocket_recv(struct wsgi_request *); struct uwsgi_buffer *uwsgi_websocket_recv_nb(struct wsgi_request *); @@ -839,9 +839,12 @@ uwsgi.websocket_recv_nb = uwsgi_pypy_websocket_recv_nb """ uwsgi.websocket_handshake(key, origin) """ -def uwsgi_pypy_websocket_handshake(key, origin=''): +def uwsgi_pypy_websocket_handshake(key='', origin='', proto=''): wsgi_req = uwsgi_pypy_current_wsgi_req(); - if lib.uwsgi_websocket_handshake(wsgi_req, ffi.new('char[]', key), len(key), ffi.new('char[]',origin), len(origin)) < 0: + c_key = ffi.new('char[]', key) + c_origin = ffi.new('char[]', origin) + c_proto = ffi.new('char[]', proto) + if lib.uwsgi_websocket_handshake(wsgi_req, c_key, len(key), c_origin, len(origin), c_proto, len(proto)) < 0: raise IOError("unable to complete websocket handshake") uwsgi.websocket_handshake = uwsgi_pypy_websocket_handshake diff --git a/plugins/python/uwsgi_pymodule.c b/plugins/python/uwsgi_pymodule.c index 55fdc336..7f577260 100644 --- a/plugins/python/uwsgi_pymodule.c +++ b/plugins/python/uwsgi_pymodule.c @@ -965,14 +965,17 @@ PyObject *py_uwsgi_websocket_handshake(PyObject * self, PyObject * args) { char *origin = NULL; Py_ssize_t origin_len = 0; - if (!PyArg_ParseTuple(args, "s#|s#:websocket_handshake", &key, &key_len, &origin, &origin_len)) { + char *proto = NULL; + Py_ssize_t proto_len = 0; + + if (!PyArg_ParseTuple(args, "|s#s#s#:websocket_handshake", &key, &key_len, &origin, &origin_len, &proto, &proto_len)) { return NULL; } struct wsgi_request *wsgi_req = py_current_wsgi_req(); UWSGI_RELEASE_GIL - int ret = uwsgi_websocket_handshake(wsgi_req, key, key_len, origin, origin_len); + int ret = uwsgi_websocket_handshake(wsgi_req, key, key_len, origin, origin_len, proto, proto_len); UWSGI_GET_GIL if (ret) { diff --git a/plugins/rack/rack_api.c b/plugins/rack/rack_api.c index 3fb8a847..c027d51b 100644 --- a/plugins/rack/rack_api.c +++ b/plugins/rack/rack_api.c @@ -1048,25 +1048,26 @@ static VALUE uwsgi_ruby_websocket_handshake(int argc, VALUE *argv, VALUE *class) struct wsgi_request *wsgi_req = current_wsgi_req(); - if (argc < 1) { - rb_raise(rb_eRuntimeError, "you neeto specify a valid websocket key"); - return Qnil; + char *key = NULL, *origin = NULL, *proto = NULL; + size_t key_len = 0, origin_len = 0, proto_len = 0; + + if (argc > 0) { + Check_Type(argv[0], T_STRING); + key = RSTRING_PTR(argv[0]); + key_len = RSTRING_LEN(argv[0]); + if (argc > 1) { + Check_Type(argv[1], T_STRING); + origin = RSTRING_PTR(argv[1]); + origin_len = RSTRING_LEN(argv[1]); + if (argc > 2) { + Check_Type(argv[2], T_STRING); + proto = RSTRING_PTR(argv[2]); + proto_len = RSTRING_LEN(argv[2]); + } + } } - Check_Type(argv[0], T_STRING); - char *key = RSTRING_PTR(argv[0]); - size_t key_len = RSTRING_LEN(argv[0]); - - char *origin = NULL; - size_t origin_len = 0; - - if (argc > 1) { - Check_Type(argv[1], T_STRING); - origin = RSTRING_PTR(argv[1]); - origin_len = RSTRING_LEN(argv[1]); - } - - if (uwsgi_websocket_handshake(wsgi_req, key, key_len, origin, origin_len)) { + if (uwsgi_websocket_handshake(wsgi_req, key, key_len, origin, origin_len, proto, proto_len)) { rb_raise(rb_eRuntimeError, "unable to complete websocket handshake"); } return Qnil; diff --git a/tests/websockets_echo.lua b/tests/websockets_echo.lua new file mode 100644 index 00000000..47ec4d65 --- /dev/null +++ b/tests/websockets_echo.lua @@ -0,0 +1,63 @@ +#!./uwsgi --https :8443,foobar.crt,foobar.key --http-modifier1 6 --http-raw-body --threads 100 --lua tests/websocket_echo.lua + +function app(env) + local function html() + coroutine.yield(string.format([[ + + + + + +

WebSocket

+ + +
+
+ + + ]], ws_scheme, env['HTTP_HOST'])) + end + + ws_scheme = 'ws' + if env['HTTPS'] ~= nil then + ws_scheme = 'wss' + end + + if env['PATH_INFO'] == '/' then + return 200, { ["Content-type"] = "text/html" }, coroutine.wrap(html) + + elseif env['PATH_INFO'] == '/foobar/' then + uwsgi.websocket_handshake(nil, nil, 'echo') + print("websockets...") + while 1 do + msg = uwsgi.websocket_recv() + uwsgi.websocket_send(string.format("[%s] %s", os.time(), msg)) + end + end +end + +return app diff --git a/tests/websockets_echo.ru b/tests/websockets_echo.ru index ad2f2880..0cefc9a1 100644 --- a/tests/websockets_echo.ru +++ b/tests/websockets_echo.ru @@ -4,13 +4,13 @@ class WebsocketEcho def call(env) - ws_scheme = 'ws'; + ws_scheme = 'ws' if env.has_key?('HTTPS') or env['rack.url_scheme'] == 'https' - ws_scheme = 'wss'; + ws_scheme = 'wss' end if env['PATH_INFO'] == '/' - body = <