clients.lua

changeset 6
4e46ef3035ba
parent 0
d363a6692a10
child 7
59655d6c45b3
equal deleted inserted replaced
5:4a3caf5d0f4b 6:4e46ef3035ba
1 local socket = require "socket"; 1 local socket = require "socket";
2 local server = require "net.server_select"; 2 local server = require "net.server_select";
3 local http_server = require"net.http.server"; 3 local http_server = require"net.http.server";
4 local new_uuid = require "util.uuid".generate;
4 local log = require "util.logger".init("clients"); 5 local log = require "util.logger".init("clients");
5 6
6 local response_head = table.concat({ 7 local response_head = table.concat({
7 "HTTP/1.1 200 OK"; 8 "HTTP/1.1 200 OK";
8 "Max-Age: 0"; 9 "Max-Age: 0";
11 "Pragma: no-cache"; 12 "Pragma: no-cache";
12 "Content-Type: multipart/x-mixed-replace; boundary=--BoundaryString"; 13 "Content-Type: multipart/x-mixed-replace; boundary=--BoundaryString";
13 "Keep-Alive: timeout=5, max=99"; 14 "Keep-Alive: timeout=5, max=99";
14 "Connection: Keep-Alive"; 15 "Connection: Keep-Alive";
15 "Transfer-Encoding: chunked"; 16 "Transfer-Encoding: chunked";
17 "Set-Cookie: COOKIE_STRING";
16 ""; 18 "";
17 ""; 19 "";
18 }, "\r\n"); 20 }, "\r\n");
19 21
20 local listener = { onconnect = function () end; onincoming = function () end; } 22 local listener = { onconnect = function () end; onincoming = function () end; }
21 23
22 local last_chunk; 24 local last_chunk;
23 25
26 local have_clients = false;
27
28 -- [conn] = cookie
24 local clients = {}; 29 local clients = {};
30 -- [cookie] = conn
31 local client_by_cookie = {};
32 -- [conn] = last_active_timestamp
33 local active_clients = {};
34
35 local activity_timeout = 20;
36
37 local function update_have_clients()
38 if have_clients and not next(active_clients) then
39 have_clients = false;
40 log("debug", "No more clients");
41 events.fire_event("no-clients");
42 elseif not have_clients and next(active_clients) then
43 have_clients = true;
44 log("debug", "Active clients");
45 events.fire_event("have-clients");
46 end
47 end
48
25 49
26 -- Called when a HTTP stream client closes 50 -- Called when a HTTP stream client closes
27 function listener.ondisconnect(conn) 51 function listener.ondisconnect(conn)
52 client_by_cookie[clients[conn]] = nil;
28 clients[conn] = nil; 53 clients[conn] = nil;
29 if not next(clients) then 54 active_clients[conn] = nil;
30 log("debug", "No more clients"); 55 update_have_clients();
31 events.fire_event("no-clients");
32 end
33 end 56 end
34 listener.ondetach = listener.ondisconnect; 57 listener.ondetach = listener.ondisconnect;
35 58
36 function handle_request(event, path) 59 function handle_request(event, path)
37 local path = event.request.url.path; 60 local path = event.request.url.path;
38 if path ~= "/cam" then 61 if path ~= "/cam" then
39 return 404; 62 return 404;
40 end 63 end
41 64
42 if not next(clients) then
43 log("debug", "Have clients now");
44 events.fire_event("have-clients");
45 end
46
47 local conn = event.response.conn; 65 local conn = event.response.conn;
48 66
49 conn:write(response_head); 67 local cookie = event.request.headers.cookie;
50 clients[conn] = true; 68 if cookie then
69 log("debug", "Client %s connected", cookie);
70 else
71 cookie = new_uuid();
72 log("debug", "New client connected, assigned %s", cookie);
73 end
74 conn:write((response_head:gsub("COOKIE_STRING", cookie)));
75 clients[conn] = cookie;
76 active_clients[conn] = os.time();
77 client_by_cookie[cookie] = conn;
78
79 update_have_clients();
51 80
52 if last_chunk then 81 if last_chunk then
53 conn:write(last_chunk); 82 conn:write(last_chunk);
54 end 83 end
55 84
56 conn:setlistener(listener); 85 conn:setlistener(listener);
57 86
58 return true; 87 return true;
88 end
89
90 local function mark_active(request)
91 local cookie = event.request.headers.cookie;
92 if not cookie then
93 log("warn", "Active client with no cookie");
94 return;
95 end
96 local conn = client_by_cookie[cookie];
97 if not conn then
98 log("warn", "Active client with no connection");
99 return;
100 end
101 active_clients[conn] = os.time();
102 update_have_clients();
103 end
104
105 function handle_active(event, path)
106 mark_active(event.request);
107 return "OK";
59 end 108 end
60 109
61 events.add_handler("image-changed", function (event) 110 events.add_handler("image-changed", function (event)
62 log("debug", "New image"); 111 log("debug", "New image");
63 local chunk_data = table.concat({ 112 local chunk_data = table.concat({
67 ""; 116 "";
68 event.image; 117 event.image;
69 }, "\r\n"); 118 }, "\r\n");
70 last_chunk = ("%x\r\n%s\r\n"):format(#chunk_data, chunk_data); 119 last_chunk = ("%x\r\n%s\r\n"):format(#chunk_data, chunk_data);
71 120
121 local time_now = os.time();
72 for client in pairs(clients) do 122 for client in pairs(clients) do
73 client:write(last_chunk); 123 local active_time = active_clients[client];
124 if active_time and time_now - active_time < activity_timeout then
125 client:write(last_chunk);
126 else
127 active_clients[client] = nil;
128 end
74 end 129 end
130 update_have_clients();
75 end); 131 end);
76 132
77 http_server.add_host("localhost"); 133 http_server.add_host("localhost");
78 http_server.set_default_host("localhost"); 134 http_server.set_default_host("localhost");
79 http_server.add_handler("GET localhost/*", handle_request); 135 http_server.add_handler("GET localhost/*", handle_request);
136 http_server.add_handler("GET localhost/active", handle_active);
80 http_server.listen_on(8006); 137 http_server.listen_on(8006);

mercurial