moonbridge
diff example_application.lua @ 121:4997e742c81c
Added multiuser chat to example
author | bsw |
---|---|
date | Sat Apr 11 00:10:45 2015 +0200 (2015-04-11) |
parents | 7014436d88ea |
children | 20e0d4f51381 |
line diff
1.1 --- a/example_application.lua Fri Apr 10 13:17:29 2015 +0200 1.2 +++ b/example_application.lua Sat Apr 11 00:10:45 2015 +0200 1.3 @@ -7,18 +7,159 @@ 1.4 1.5 -- preparation before forking: 1.6 local documents = {} 1.7 -for i, document_name in ipairs{"example_webpage.html", "example_webpage.css"} do 1.8 +for i, document_name in ipairs{"example_webpage.html", "example_chat.html", "example_webpage.css"} do 1.9 local file = assert(io.open(document_name)) 1.10 documents[document_name] = file:read("*a") -- store file contents in memory 1.11 file:close() 1.12 end 1.13 1.14 +local function chat_server(request) 1.15 + 1.16 + 1.17 + local listener = assert(moonbridge_io.tcplisten("localhost", 8081)) 1.18 + local clients = {} 1.19 + local io_listener = { [listener] = true } 1.20 + local io_writer = {} 1.21 + local sessions = {} 1.22 + local msgs = {} 1.23 + local last_msg = 0 1.24 + 1.25 + while true do 1.26 + 1.27 + local new_conn = listener:accept_nb() 1.28 + 1.29 + if new_conn then 1.30 + clients[new_conn] = { 1.31 + read_buffer = "" 1.32 + } 1.33 + io_listener[new_conn] = true 1.34 + end 1.35 + 1.36 + for conn, client in pairs(clients) do 1.37 + 1.38 + repeat 1.39 + local line = conn:read_nb(nil, "\n") 1.40 + if not line then 1.41 + clients[conn] = nil 1.42 + io_listener[conn] = nil 1.43 + 1.44 + elseif line ~= "" then 1.45 + local line, terminator = line:match("([^\n]*)(\n?)") 1.46 + 1.47 + if terminator ~= "\n" then 1.48 + client.read_buffer = client.read_buffer .. line 1.49 + else 1.50 + local line = client.read_buffer .. line 1.51 + client.read_buffer = "" 1.52 + 1.53 + if not client.type then 1.54 + if line == "events" then 1.55 + client.type = "events" 1.56 + client.session = "sesam" .. math.random(10000000,99999999) 1.57 + client.name = "user".. math.random(10000000,99999999) 1.58 + client.last_msg = 0 1.59 + client.send_session = true 1.60 + client.send_name = true 1.61 + client.last_msg = #msgs 1.62 + sessions[client.session] = conn 1.63 + io_listener[conn] = nil 1.64 + else 1.65 + client.type = "chat" 1.66 + if sessions[line] then 1.67 + client.session = line 1.68 + io_listener[conn] = true 1.69 + else 1.70 + conn:close() 1.71 + clients[conn] = nil 1.72 + end 1.73 + end 1.74 + else 1.75 + if client.type == "chat" then 1.76 + local event_client = clients[sessions[client.session]] 1.77 + local command, arg = line:match("([^:]+):(.*)") 1.78 + if not command then 1.79 + elseif command == "NAME" then 1.80 + local name = arg 1.81 + local success 1.82 + repeat 1.83 + success = true 1.84 + for conn2, client2 in pairs(clients) do 1.85 + if client2.name == name then 1.86 + name = name .. math.random(0,9) 1.87 + success = false 1.88 + break 1.89 + end 1.90 + end 1.91 + until success 1.92 + last_msg = last_msg + 1 1.93 + msgs[last_msg] = { 1.94 + name = event_client.name, 1.95 + msg = "is now known as " .. name 1.96 + } 1.97 + event_client.name = name 1.98 + event_client.send_name = true 1.99 + elseif command == "MSG" then 1.100 + if #arg > 0 then 1.101 + last_msg = last_msg + 1 1.102 + msgs[last_msg] = { 1.103 + name = event_client.name, 1.104 + msg = arg 1.105 + } 1.106 + end 1.107 + end 1.108 + end 1.109 + 1.110 + end 1.111 + end 1.112 + end 1.113 + until not line or line == "" 1.114 + end 1.115 + 1.116 + for conn, client in pairs(clients) do 1.117 + if client.type == "events" then 1.118 + if client.send_session then 1.119 + assert(conn:write_nb("SESSION:" .. client.session .. "\n")) 1.120 + client.send_session = false 1.121 + end 1.122 + if client.send_name then 1.123 + assert(conn:write_nb("NAME:" .. client.name .. "\n")) 1.124 + client.send_name = false 1.125 + end 1.126 + if client.last_msg < last_msg then 1.127 + for i = client.last_msg + 1, last_msg do 1.128 + assert(conn:write_nb("MSG:" .. msgs[i].name .. " " .. msgs[i].msg .. "\n")) 1.129 + end 1.130 + client.last_msg = last_msg 1.131 + end 1.132 + assert(conn:write_nb("TIME:" .. os.time() .. "\n")) 1.133 + local bytes_left = assert(conn:flush_nb()) 1.134 + if bytes_left > 0 then 1.135 + io_writer[conn] = true 1.136 + else 1.137 + io_writer[conn] = false 1.138 + end 1.139 + end 1.140 + 1.141 + end 1.142 + 1.143 + moonbridge_io.poll(io_listener, io_writer) 1.144 + 1.145 + end 1.146 + 1.147 +end 1.148 + 1.149 +listen{ 1.150 + { proto = "interval", delay = 1 }, 1.151 + connect = chat_server, 1.152 + max_fork = 1 1.153 +} 1.154 + 1.155 listen{ 1.156 -- listen to a tcp version 4 socket 1.157 - { proto = "tcp4", port = 8080, localhost = true }, 1.158 + { proto = "tcp4", port = 8080, localhost = false }, 1.159 1.160 -- listen to a tcp version 6 socket 1.161 - { proto = "tcp6", port = 8080, localhost = true }, 1.162 + --{ proto = "tcp6", port = 8080, localhost = false }, 1.163 1.164 -- listen to a unix domain socket 1.165 --{ proto = "local", path = 'socket' }, 1.166 @@ -30,7 +171,7 @@ 1.167 pre_fork = 1, -- number of forks 1.168 1.169 -- minimum number of processes 1.170 - min_fork = 2, -- number of forks 1.171 + min_fork = 4, -- number of forks 1.172 1.173 -- maximum number of processes (hard limit) 1.174 max_fork = 16, -- number of forks 1.175 @@ -79,6 +220,22 @@ 1.176 request:send_status("303 See Other") 1.177 request:send_header("Location", "http://" .. request.headers_value.host .. "/example_webpage.html") 1.178 1.179 + elseif request.path == "chat" then 1.180 + request:send_status("200 OK") 1.181 + request:send_header("Content-Type", "text/chat; charset=UTF-8") 1.182 + request:send_data("MULTIUSERCHAT:protocol version 1\n") 1.183 + 1.184 + local conn = assert(moonbridge_io.tcpconnect("localhost", 8081)) 1.185 + 1.186 + conn:write("events\n") 1.187 + conn:flush() 1.188 + 1.189 + while true do 1.190 + local line = conn:read(nil, "\n") 1.191 + request:send_data(line) 1.192 + request:flush() 1.193 + end 1.194 + 1.195 else 1.196 local document_name = request.path 1.197 local document_extension = string.match(document_name, "%.([^.])$") 1.198 @@ -139,6 +296,37 @@ 1.199 request:send_data("<p>Submitted comment: ", http.encode_html(request.post_params.comment), "</p>\n") 1.200 request:send_data("</body>\n</html>\n") 1.201 1.202 + elseif request.path == "chat_send" then 1.203 + local conn = assert(moonbridge_io.tcpconnect("localhost", 8081)) 1.204 + local body = request.body 1.205 + local session 1.206 + for line in body:gmatch("[^\r\n]+") do 1.207 + local command, arg = line:match("([^:]+):(.*)") 1.208 + if not command then 1.209 + -- TODO error handling 1.210 + return 1.211 + elseif command == "SESSION" then 1.212 + session = arg 1.213 + else 1.214 + if not session then 1.215 + -- TODO error handling 1.216 + return 1.217 + end 1.218 + conn:write(session .. "\n") 1.219 + if command == "NAME" then 1.220 + local name = arg:gsub(" ", "_") 1.221 + conn:write("NAME:" .. name .. "\n") 1.222 + elseif command == "MSG" then 1.223 + local msg = arg 1.224 + conn:write("MSG:" .. msg .. "\n") 1.225 + end 1.226 + end 1.227 + end 1.228 + conn:flush() 1.229 + conn:close() 1.230 + request:send_status("200 OK") 1.231 + request:send_header("Content-Type", "text/xml; chatset=UTF-8") 1.232 + 1.233 else 1.234 error_response("404 Not Found") 1.235