forked from marcoskirsch/nodemcu-httpserver
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttpserver.lua
More file actions
131 lines (115 loc) · 4.7 KB
/
Copy pathhttpserver.lua
File metadata and controls
131 lines (115 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
-- httpserver
-- Author: Marcos Kirsch
module("httpserver", package.seeall)
-- Functions below aren't part of the public API
-- Clients don't need to worry about them.
-- given an HTTP method, returns it or if invalid returns nil
local function validateMethod(method)
local httpMethods = {GET=true, HEAD=true, POST=true, PUT=true, DELETE=true, TRACE=true, OPTIONS=true, CONNECT=true, PATCH=true}
if httpMethods[method] then return method else return nil end
end
local function parseRequest(request)
local e = request:find("\r\n", 1, true)
if not e then return nil end
local line = request:sub(1, e - 1)
local r = {}
_, i, r.method, r.uri = line:find("^([A-Z]+) (.-) HTTP/[1-9]+.[0-9]+$")
return r
end
local function uriToFilename(uri)
return "http/" .. string.sub(uri, 2, -1)
end
local function parseArgs(args)
local r = {}; i=1
if args == nil or args == "" then return r end
for arg in string.gmatch(args, "([^&]+)") do
local name, value = string.match(arg, "(.*)=(.*)")
if name ~= nil then r[name] = value end
i = i + 1
end
return r
end
local function parseUri(uri)
local r = {}
if uri == nil then return r end
if uri == "/" then uri = "/index.html" end
questionMarkPos, b, c, d, e, f = uri:find("?")
if questionMarkPos == nil then
r.file = uri:sub(1, questionMarkPos)
r.args = {}
else
r.file = uri:sub(1, questionMarkPos - 1)
r.args = parseArgs(uri:sub(questionMarkPos+1, #uri))
end
_, r.ext = r.file:match("(.+)%.(.+)")
r.isScript = r.ext == "lua" or r.ext == "lc"
r.file = uriToFilename(r.file)
return r
end
-- Starts web server in the specified port.
return function (port)
local s = net.createServer(net.TCP, 10) -- 10 seconds client timeout
s:listen(
port,
function (connection)
-- This variable holds the thread used for sending data back to the user.
-- We do it in a separate thread because we need to yield when sending lots
-- of data in order to avoid overflowing the mcu's buffer.
local connectionThread
local function onGet(connection, uri)
local uri = parseUri(uri)
local fileServeFunction = nil
if #(uri.file) > 32 then
-- nodemcu-firmware cannot handle long filenames.
uri.args['code'] = 400
fileServeFunction = dofile("httpserver-error.lc")
else
local fileExists = file.open(uri.file, "r")
file.close()
if not fileExists then
uri.args['code'] = 404
fileServeFunction = dofile("httpserver-error.lc")
elseif uri.isScript then
collectgarbage()
fileServeFunction = dofile(uri.file)
else
uri.args['file'] = uri.file
uri.args['ext'] = uri.ext
fileServeFunction = dofile("httpserver-static.lc")
end
end
connectionThread = coroutine.create(fileServeFunction)
--print("Thread created", connectionThread)
coroutine.resume(connectionThread, connection, uri.args)
end
local function onReceive(connection, payload)
--print(payload) -- for debugging
-- parse payload and decide what to serve.
local req = parseRequest(payload)
print("Requested URI: " .. req.uri)
req.method = validateMethod(req.method)
if req.method == "GET" then onGet(connection, req.uri)
elseif req.method == nil then dofile("httpserver-static.lc")(conection, {code=400})
else dofile("httpserver-static.lc")(conection, {code=501}) end
end
local function onSent(connection, payload)
local connectionThreadStatus = coroutine.status(connectionThread)
-- print (connectionThread, "status is", connectionThreadStatus)
if connectionThreadStatus == "suspended" then
-- Not finished sending file, resume.
-- print("Resume thread", connectionThread)
coroutine.resume(connectionThread)
elseif connectionThreadStatus == "dead" then
-- We're done sending file.
-- print("Done thread", connectionThread)
connection:close()
connectionThread = nil
end
end
connection:on("receive", onReceive)
connection:on("sent", onSent)
end
)
print("nodemcu-httpserver running at http://" .. wifi.sta.getip() .. ":" .. port)
return s
end