forked from loicortola/nodemcu-espress
-
Notifications
You must be signed in to change notification settings - Fork 0
/
espress.lua
153 lines (146 loc) · 4.69 KB
/
espress.lua
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
------------------------------------------------------------------------------
-- HTTP server module
-- LICENCE: http://opensource.org/licenses/MIT
-- Author: Loic Ortola https://github.com/loicortola
------------------------------------------------------------------------------
-- HTTP status codes as defined in RFC 2616 + common ones along with their message
do
------------------------------------------------------------------------------
-- HTTP parser
------------------------------------------------------------------------------
-- Request prototype functions
local reqprototype
-- Response prototype functions
local resprototype
-- Request handler chain
local handlers
-- Chain processor
local processrequest
-- Request buffer
local requestbuffer
local getondisconnect = function(holder)
return function(res, conn)
tmr.wdclr()
print("Finished chain for element " .. holder.id)
requestbuffer:remove(holder.id)
holder:destructor()
holder = nil
if (requestbuffer:hasnext()) then
processrequest(requestbuffer:next())
end
collectgarbage("collect")
if (node ~= nil) then
print("Garbage Collector is sweeping. Available memory is now " .. node.heap() .. " bytes.")
end
end
end
local getonreceive = function(holder)
holder.tmp = {}
holder.tmp.buf = ""
holder.tmp.parsedlines = 0
holder.tmp.headerslength = 0
holder.tmp.bodylength = 0
-- Metadata parser
return function(conn, chunk)
tmr.wdclr()
-- concat chunks in buffer
holder.tmp.buf = holder.tmp.buf .. chunk
-- this will be used to remove headers from request body
-- Read line from chunk
while #holder.tmp.buf > 0 do
-- Ensure current line is complete
local e = holder.tmp.buf:find("\r\n", 1, true)
-- Leave if line not done
if not e then break end
-- Parse current line
local line = holder.tmp.buf:sub(1, e - 1)
holder.tmp.buf = holder.tmp.buf:sub(e + 2)
holder.tmp.headerslength = holder.tmp.headerslength + e + 1
if holder.tmp.parsedlines == 0 then
-- FIRST LINE
local f = loadfile('http_request.lc')
holder.req = f()(reqprototype, line)
f = nil
local f = loadfile('http_response.lc')
holder.res = f()(resprototype, conn, getondisconnect(holder))
f = nil
collectgarbage("collect")
elseif #line > 0 then
-- HEADER LINES
-- Parse header
local _, _, k, v = line:find("^([%w-]+):%s*(.+)")
if k then
-- Valid header
k = k:lower()
--print("Adding header " .. k)
if k == "content-length" then
holder.tmp.bodylength = tonumber(v)
end
if not (k == "pragma" or k == "cache-control" or k == "connection") then
-- Add header into request (keep out some useless headers for memory)
holder.req:addheader(k, v)
end
end
else
-- BODY
local body = chunk:sub(holder.tmp.headerslength + 1)
-- Buffer no longer needed
chunk = nil
holder.tmp.buf = nil
if holder.tmp.bodylength == 0 then
-- Clean unneeded methods
holder.req.parseparams = nil
holder.req.addheader = nil
holder.tmp = nil
collectgarbage("collect")
-- Handle request if no body present
requestbuffer:push(holder)
if not (requestbuffer:isbusy()) then
processrequest(requestbuffer:next())
else
print("Stored request into buffer." .. ((node ~= nil) and ' Available memory: ' .. node.heap() .. ' bytes' or ''))
end
else
-- If we are sending a form, we need to parse it
if holder.req.headers["content-type"] == "application/x-www-form-urlencoded" then
holder.req:parseparams(body)
holder.req.parseparams = nil
holder.req.addheader = nil
end
-- Change receive hook to body parser if body present
local f = loadfile("http_getondata.lc")
local onreceive = f()(requestbuffer, processrequest)(holder)
f = nil
conn:on("receive", onreceive)
onreceive(conn, body)
onreceive = nil
collectgarbage("collect")
end
break
end
holder.tmp.parsedlines = holder.tmp.parsedlines + 1
end
end
end
------------------------------------------------------------------------------
-- HTTP server
------------------------------------------------------------------------------
local srv
local createserver = function()
local f = loadfile('http_prototypes.lc')
reqprototype, resprototype = f()
f = loadfile('espress_init.lc')
local hdlr = f()(getonreceive)
handlers = hdlr.handlers
f = loadfile('http_request_processor.lc')
processrequest = f()(handlers)
f = loadfile('http_request_buffer.lc')
requestbuffer = f()
f = nil
collectgarbage("collect")
return hdlr
end
return {
createserver = createserver
}
end