Annotate

net/websocket.lua @ 10226:77f900bbbf25

Remove COMPAT with temporary luasec fork The changes in the temporary fork were merged into mainline luasec ca 2013 and included in the 0.5 release in 2014.
author Kim Alvefur <zash@zash.se>
date Sun, 25 Aug 2019 23:12:55 +0200
parent 10113:66a9bc2d5c8d
child 10453:926d6086a95a
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
1 -- Prosody IM
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
2 -- Copyright (C) 2012 Florian Zeitz
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
3 -- Copyright (C) 2014 Daurnimator
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
4 --
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
5 -- This project is MIT/X11 licensed. Please see the
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
6 -- COPYING file in the source package for more information.
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
7 --
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
8
6396
17b54f523796 Check Sec-WebSocket-Protocol header
Florian Zeitz <florob@babelmonkeys.de>
parents: 6395
diff changeset
9 local t_concat = table.concat;
17b54f523796 Check Sec-WebSocket-Protocol header
Florian Zeitz <florob@babelmonkeys.de>
parents: 6395
diff changeset
10
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
11 local http = require "net.http";
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
12 local frames = require "net.websocket.frames";
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
13 local base64 = require "util.encodings".base64;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
14 local sha1 = require "util.hashes".sha1;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
15 local random_bytes = require "util.random".bytes;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
16 local timer = require "util.timer";
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
17 local log = require "util.logger".init "websocket";
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
18
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
19 local close_timeout = 3; -- Seconds to wait after sending close frame until closing connection.
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
20
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
21 local websockets = {};
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
22
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
23 local websocket_listeners = {};
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
24 function websocket_listeners.ondisconnect(conn, err)
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
25 local s = websockets[conn];
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
26 websockets[conn] = nil;
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
27 if s.close_timer then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
28 timer.stop(s.close_timer);
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
29 s.close_timer = nil;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
30 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
31 s.readyState = 3;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
32 if s.close_code == nil and s.onerror then s:onerror(err); end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
33 if s.onclose then s:onclose(s.close_code, s.close_message or err); end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
34 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
35
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
36 function websocket_listeners.ondetach(conn)
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
37 websockets[conn] = nil;
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
38 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
39
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
40 local function fail(s, code, reason)
8189
a3565d7dd304 net.websocket: Remove stray module api reference, shouldn't be used in here
Kim Alvefur <zash@zash.se>
parents: 7759
diff changeset
41 log("warn", "WebSocket connection failed, closing. %d %s", code, reason);
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
42 s:close(code, reason);
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
43 s.conn:close();
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
44 return false
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
45 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
46
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
47 function websocket_listeners.onincoming(conn, buffer, err) -- luacheck: ignore 212/err
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
48 local s = websockets[conn];
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
49 s.readbuffer = s.readbuffer..buffer;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
50 while true do
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
51 local frame, len = frames.parse(s.readbuffer);
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
52 if frame == nil then break end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
53 s.readbuffer = s.readbuffer:sub(len+1);
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
54
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
55 log("debug", "Websocket received frame: opcode=%0x, %i bytes", frame.opcode, #frame.data);
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
56
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
57 -- Error cases
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
58 if frame.RSV1 or frame.RSV2 or frame.RSV3 then -- Reserved bits non zero
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
59 return fail(s, 1002, "Reserved bits not zero");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
60 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
61
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
62 if frame.opcode < 0x8 then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
63 local databuffer = s.databuffer;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
64 if frame.opcode == 0x0 then -- Continuation frames
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
65 if not databuffer then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
66 return fail(s, 1002, "Unexpected continuation frame");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
67 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
68 databuffer[#databuffer+1] = frame.data;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
69 elseif frame.opcode == 0x1 or frame.opcode == 0x2 then -- Text or Binary frame
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
70 if databuffer then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
71 return fail(s, 1002, "Continuation frame expected");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
72 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
73 databuffer = {type=frame.opcode, frame.data};
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
74 s.databuffer = databuffer;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
75 else
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
76 return fail(s, 1002, "Reserved opcode");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
77 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
78 if frame.FIN then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
79 s.databuffer = nil;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
80 if s.onmessage then
6396
17b54f523796 Check Sec-WebSocket-Protocol header
Florian Zeitz <florob@babelmonkeys.de>
parents: 6395
diff changeset
81 s:onmessage(t_concat(databuffer), databuffer.type);
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
82 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
83 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
84 else -- Control frame
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
85 if frame.length > 125 then -- Control frame with too much payload
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
86 return fail(s, 1002, "Payload too large");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
87 elseif not frame.FIN then -- Fragmented control frame
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
88 return fail(s, 1002, "Fragmented control frame");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
89 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
90 if frame.opcode == 0x8 then -- Close request
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
91 if frame.length == 1 then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
92 return fail(s, 1002, "Close frame with payload, but too short for status code");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
93 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
94 local status_code, message = frames.parse_close(frame.data);
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
95 if status_code == nil then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
96 --[[ RFC 6455 7.4.1
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
97 1005 is a reserved value and MUST NOT be set as a status code in a
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
98 Close control frame by an endpoint. It is designated for use in
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
99 applications expecting a status code to indicate that no status
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
100 code was actually present.
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
101 ]]
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
102 status_code = 1005
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
103 elseif status_code < 1000 then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
104 return fail(s, 1002, "Closed with invalid status code");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
105 elseif ((status_code > 1003 and status_code < 1007) or status_code > 1011) and status_code < 3000 then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
106 return fail(s, 1002, "Closed with reserved status code");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
107 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
108 s.close_code, s.close_message = status_code, message;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
109 s:close(1000);
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
110 return true;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
111 elseif frame.opcode == 0x9 then -- Ping frame
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
112 frame.opcode = 0xA;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
113 frame.MASK = true; -- RFC 6455 6.1.5: If the data is being sent by the client, the frame(s) MUST be masked
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
114 conn:write(frames.build(frame));
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
115 elseif frame.opcode == 0xA then -- Pong frame
10113
66a9bc2d5c8d net.websocket: Fix log call to pass data via format string instead of concatenation
Kim Alvefur <zash@zash.se>
parents: 10112
diff changeset
116 log("debug", "Received unexpected pong frame: %s", frame.data);
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
117 else
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
118 return fail(s, 1002, "Reserved opcode");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
119 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
120 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
121 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
122 return true;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
123 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
124
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
125 local websocket_methods = {};
7759
69706084bdfe net.websocket: Ignore unused argument warnings [luacheck]
Kim Alvefur <zash@zash.se>
parents: 6455
diff changeset
126 local function close_timeout_cb(now, timerid, s) -- luacheck: ignore 212/now 212/timerid
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
127 s.close_timer = nil;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
128 log("warn", "Close timeout waiting for server to close, closing manually.");
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
129 s.conn:close();
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
130 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
131 function websocket_methods:close(code, reason)
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
132 if self.readyState < 2 then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
133 code = code or 1000;
10112
b327f2870382 net.*: Remove tostring call from logging
Kim Alvefur <zash@zash.se>
parents: 8893
diff changeset
134 log("debug", "closing WebSocket with code %i: %s" , code , reason);
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
135 self.readyState = 2;
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
136 local conn = self.conn;
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
137 conn:write(frames.build_close(code, reason, true));
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
138 -- Do not close socket straight away, wait for acknowledgement from server.
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
139 self.close_timer = timer.add_task(close_timeout, close_timeout_cb, self);
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
140 elseif self.readyState == 2 then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
141 log("debug", "tried to close a closing WebSocket, closing the raw socket.");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
142 -- Stop timer
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
143 if self.close_timer then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
144 timer.stop(self.close_timer);
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
145 self.close_timer = nil;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
146 end
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
147 local conn = self.conn;
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
148 conn:close();
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
149 else
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
150 log("debug", "tried to close a closed WebSocket, ignoring.");
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
151 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
152 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
153 function websocket_methods:send(data, opcode)
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
154 if self.readyState < 1 then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
155 return nil, "WebSocket not open yet, unable to send data.";
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
156 elseif self.readyState >= 2 then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
157 return nil, "WebSocket closed, unable to send data.";
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
158 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
159 if opcode == "text" or opcode == nil then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
160 opcode = 0x1;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
161 elseif opcode == "binary" then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
162 opcode = 0x2;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
163 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
164 local frame = {
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
165 FIN = true;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
166 MASK = true; -- RFC 6455 6.1.5: If the data is being sent by the client, the frame(s) MUST be masked
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
167 opcode = opcode;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
168 data = tostring(data);
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
169 };
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
170 log("debug", "WebSocket sending frame: opcode=%0x, %i bytes", frame.opcode, #frame.data);
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
171 return self.conn:write(frames.build(frame));
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
172 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
173
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
174 local websocket_metatable = {
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
175 __index = websocket_methods;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
176 };
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
177
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
178 local function connect(url, ex, listeners)
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
179 ex = ex or {};
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
180
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
181 --[[RFC 6455 4.1.7:
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
182 The request MUST include a header field with the name
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
183 |Sec-WebSocket-Key|. The value of this header field MUST be a
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
184 nonce consisting of a randomly selected 16-byte value that has
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
185 been base64-encoded (see Section 4 of [RFC4648]). The nonce
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
186 MUST be selected randomly for each connection.
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
187 ]]
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
188 local key = base64.encode(random_bytes(16));
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
189
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
190 -- Either a single protocol string or an array of protocol strings.
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
191 local protocol = ex.protocol;
6396
17b54f523796 Check Sec-WebSocket-Protocol header
Florian Zeitz <florob@babelmonkeys.de>
parents: 6395
diff changeset
192 if type(protocol) == "string" then
6407
4bbd198cf3e6 net.websocket: Fix handling of 'protocol' argument
Kim Alvefur <zash@zash.se>
parents: 6398
diff changeset
193 protocol = { protocol, [protocol] = true };
4bbd198cf3e6 net.websocket: Fix handling of 'protocol' argument
Kim Alvefur <zash@zash.se>
parents: 6398
diff changeset
194 elseif type(protocol) == "table" and protocol[1] then
4bbd198cf3e6 net.websocket: Fix handling of 'protocol' argument
Kim Alvefur <zash@zash.se>
parents: 6398
diff changeset
195 for _, v in ipairs(protocol) do
4bbd198cf3e6 net.websocket: Fix handling of 'protocol' argument
Kim Alvefur <zash@zash.se>
parents: 6398
diff changeset
196 protocol[v] = true;
4bbd198cf3e6 net.websocket: Fix handling of 'protocol' argument
Kim Alvefur <zash@zash.se>
parents: 6398
diff changeset
197 end
4bbd198cf3e6 net.websocket: Fix handling of 'protocol' argument
Kim Alvefur <zash@zash.se>
parents: 6398
diff changeset
198 else
4bbd198cf3e6 net.websocket: Fix handling of 'protocol' argument
Kim Alvefur <zash@zash.se>
parents: 6398
diff changeset
199 protocol = nil;
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
200 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
201
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
202 local headers = {
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
203 ["Upgrade"] = "websocket";
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
204 ["Connection"] = "Upgrade";
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
205 ["Sec-WebSocket-Key"] = key;
6407
4bbd198cf3e6 net.websocket: Fix handling of 'protocol' argument
Kim Alvefur <zash@zash.se>
parents: 6398
diff changeset
206 ["Sec-WebSocket-Protocol"] = protocol and t_concat(protocol, ", ");
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
207 ["Sec-WebSocket-Version"] = "13";
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
208 ["Sec-WebSocket-Extensions"] = ex.extensions;
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
209 }
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
210 if ex.headers then
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
211 for k,v in pairs(ex.headers) do
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
212 headers[k] = v;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
213 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
214 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
215
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
216 local s = setmetatable({
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
217 readbuffer = "";
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
218 databuffer = nil;
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
219 conn = nil;
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
220 close_code = nil;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
221 close_message = nil;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
222 close_timer = nil;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
223 readyState = 0;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
224 protocol = nil;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
225
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
226 url = url;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
227
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
228 onopen = listeners.onopen;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
229 onclose = listeners.onclose;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
230 onmessage = listeners.onmessage;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
231 onerror = listeners.onerror;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
232 }, websocket_metatable);
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
233
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
234 local http_url = url:gsub("^(ws)", "http");
7759
69706084bdfe net.websocket: Ignore unused argument warnings [luacheck]
Kim Alvefur <zash@zash.se>
parents: 6455
diff changeset
235 local http_req = http.request(http_url, { -- luacheck: ignore 211/http_req
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
236 method = "GET";
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
237 headers = headers;
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
238 sslctx = ex.sslctx;
8893
eb710675f7f8 net.websocket: Honour ex.insecure to match net.http's new parameter for that
Matthew Wild <mwild1@gmail.com>
parents: 8892
diff changeset
239 insecure = ex.insecure;
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
240 }, function(b, c, r, http_req)
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
241 if c ~= 101
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
242 or r.headers["connection"]:lower() ~= "upgrade"
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
243 or r.headers["upgrade"] ~= "websocket"
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
244 or r.headers["sec-websocket-accept"] ~= base64.encode(sha1(key .. "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
6407
4bbd198cf3e6 net.websocket: Fix handling of 'protocol' argument
Kim Alvefur <zash@zash.se>
parents: 6398
diff changeset
245 or (protocol and not protocol[r.headers["sec-websocket-protocol"]])
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
246 then
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
247 s.readyState = 3;
10112
b327f2870382 net.*: Remove tostring call from logging
Kim Alvefur <zash@zash.se>
parents: 8893
diff changeset
248 log("warn", "WebSocket connection to %s failed: %s", url, b);
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
249 if s.onerror then s:onerror("connecting-failed"); end
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
250 return;
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
251 end
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
252
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
253 s.protocol = r.headers["sec-websocket-protocol"];
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
254
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
255 -- Take possession of socket from http
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
256 local conn = http_req.conn;
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
257 http_req.conn = nil;
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
258 s.conn = conn;
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
259 websockets[conn] = s;
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
260 conn:setlistener(websocket_listeners);
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
261
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
262 log("debug", "WebSocket connected successfully to %s", url);
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
263 s.readyState = 1;
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
264 if s.onopen then s:onopen(); end
8892
fb38b2c77a72 net.websocket: Fix incompatibility with net.http changes
Matthew Wild <mwild1@gmail.com>
parents: 8189
diff changeset
265 websocket_listeners.onincoming(conn, b);
6395
e0164b0fcafd net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
266 end);
6389
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
267
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
268 return s;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
269 end
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
270
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
271 return {
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
272 connect = connect;
8eccd07b619c net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff changeset
273 };