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); |