Software /
code /
prosody
Annotate
net/websocket.lua @ 11111:55d8612ac357 0.11
mod_websocket: Continue to process data already in the buffer after an error occurs
Previously any error, or even a normal websocket close frame, would return early,
leaving potentially entire frames in the buffer unprocessed and then discarded.
This change stops processing new data, but returns an existing processed data up
to the point of the error/close.
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Mon, 28 Sep 2020 16:36:12 +0100 |
parent | 8893:eb710675f7f8 |
child | 10112:b327f2870382 |
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 |
8eccd07b619c
net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff
changeset
|
116 log("debug", "Received unexpected pong frame: " .. tostring(frame.data)); |
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; |
8eccd07b619c
net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff
changeset
|
134 log("debug", "closing WebSocket with code %i: %s" , code , tostring(reason)); |
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; |
e0164b0fcafd
net/websocket: Add new websocket client code
daurnimator <quae@daurnimator.com>
parents:
diff
changeset
|
248 log("warn", "WebSocket connection to %s failed: %s", url, tostring(b)); |
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 }; |