Source

luaeio / eio.lua

Full commit
local libeio = require("libeio")

local http = {}

function http:serve(port, handlers)
	self.fds = {}
	self.crs = {}
	self.handlers = handlers

	local f = libeio.listen(port)
	if f < 0 then error("listen(): "..f) end
	self.fd = f

	while true do 
		local n
		local rfds, wfds = {}, {}
		-- add listening socket
		table.insert(self.fds, self.fd, {fd=self.fd, r=true})

		-- add other file descriptors to rfds/wfds
		for _, fd in pairs(self.fds) do
			if fd.r then 
				table.insert(rfds, fd.fd)
			end
			if fd.w then
				table.insert(wfds, fd.fd)
			end
		end

		-- wait for the events
		n, rfds, wfds = libeio.select(rfds, wfds)
		if n < 0 then error("select(): "..n) end

		-- special case: read on listening socket (accept event)
		if rfds[self.fd] ~= nil then
			self:accept()
		end

		-- dispatch the rest of the events
		for fd, cr in pairs(self.crs) do
			self.fds[fd].can_read = (rfds[fd] ~= nil)
			self.fds[fd].can_write = (wfds[fd] ~= nil)
			coroutine.resume(cr)
			if coroutine.status(cr) == 'dead' then
				libeio.close(fd)
				self.crs[fd] = nil
				self.fds[fd] = nil
			end
		end
	end
end

function http:accept()
	local fd = libeio.accept(self.fd)
	print('accept')

	self.fds[fd] = {fd = fd}

	local read_header = function()
		local request = ""

		while request:find("\r\n\r\n") == nil do
		--while request:find("\n\n") == nil do
			local n, buf = self:read(fd, 1)
			if n > 0 then 
				request = request..tostring(buf)
			elseif n == 0 then
				return -- connection was closed
			end
		end

		local r = {
			req = request,
			read = function(self,n) return self:read(fd, n) end,
			write = function(self,s) return libeio.write(fd, s) end
		}
		print(request)

		local handler = function(r) r:write("404 Not Found\r\n") end
		for uri, h in pairs(self.handlers) do
			if uri == "/" then
				handler = h
				break
			end
		end

		handler(r)
	end

	self.crs[fd] = coroutine.create(read_header)
end

function http:read(fd, size)
	if self.fds[fd].can_read then
		self.fds[fd].can_read = false
		self.fds[fd].r = false
		return libeio.read(fd, size)
	else
		self.fds[fd].r = true
		coroutine.yield()
		return -1, nil
	end
end

--------------------------

function http_handler(r)
	print(r.req)
	r:write("hello")
end

h = {}
http:serve(8080, {["/"] = http_handler})