-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathtest.http.lua
206 lines (168 loc) · 5.03 KB
/
test.http.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
local uv = require "uv"
local format = string.format
local simpleresponse = [=[
HTTP/1.0 200 OK
Server: Lua
Content-Type: text/html
]=]
local STATUS_CODES = {
[100] = 'Continue',
[101] = 'Switching Protocols',
[102] = 'Processing', -- RFC 2518, obsoleted by RFC 4918
[200] = 'OK',
[201] = 'Created',
[202] = 'Accepted',
[203] = 'Non-Authoritative Information',
[204] = 'No Content',
[205] = 'Reset Content',
[206] = 'Partial Content',
[207] = 'Multi-Status', -- RFC 4918
[300] = 'Multiple Choices',
[301] = 'Moved Permanently',
[302] = 'Moved Temporarily',
[303] = 'See Other',
[304] = 'Not Modified',
[305] = 'Use Proxy',
[307] = 'Temporary Redirect',
[400] = 'Bad Request',
[401] = 'Unauthorized',
[402] = 'Payment Required',
[403] = 'Forbidden',
[404] = 'Not Found',
[405] = 'Method Not Allowed',
[406] = 'Not Acceptable',
[407] = 'Proxy Authentication Required',
[408] = 'Request Time-out',
[409] = 'Conflict',
[410] = 'Gone',
[411] = 'Length Required',
[412] = 'Precondition Failed',
[413] = 'Request Entity Too Large',
[414] = 'Request-URI Too Large',
[415] = 'Unsupported Media Type',
[416] = 'Requested Range Not Satisfiable',
[417] = 'Expectation Failed',
[418] = 'I\'m a teapot', -- RFC 2324
[422] = 'Unprocessable Entity', -- RFC 4918
[423] = 'Locked', -- RFC 4918
[424] = 'Failed Dependency', -- RFC 4918
[425] = 'Unordered Collection', -- RFC 4918
[426] = 'Upgrade Required', -- RFC 2817
[500] = 'Internal Server Error',
[501] = 'Not Implemented',
[502] = 'Bad Gateway',
[503] = 'Service Unavailable',
[504] = 'Gateway Time-out',
[505] = 'HTTP Version not supported',
[506] = 'Variant Also Negotiates', -- RFC 2295
[507] = 'Insufficient Storage', -- RFC 4918
[509] = 'Bandwidth Limit Exceeded',
[510] = 'Not Extended' -- RFC 2774
}
local setheader = function(self, name, value)
self.headers[name] = value
end
local addheader = function(self, name, value)
table.insert(self.headers, { name, value })
end
local sendheaders = function(self, code)
code = code or 200
local reason = STATUS_CODES[code]
assert(reason, "invalid response code")
local head = {
format("HTTP/1.1 %d %s ", code, reason),
}
if self.body == nil then
-- RFC 2616, 10.2.5:
-- The 204 response MUST NOT include a message-body, and thus is always
-- terminated by the first empty line after the header fields.
-- RFC 2616, 10.3.5:
-- The 304 response MUST NOT contain a message-body, and thus is always
-- terminated by the first empty line after the header fields.
-- RFC 2616, 10.1 Informational 1xx:
-- This class of status code indicates a provisional response,
-- consisting only of the Status-Line and optional headers, and is
-- terminated by an empty line.
if code == 204 or code == 304 or (code >= 100 and code < 200) then
self.body = nil
else
self.body = {}
end
end
-- auto-generate values:
self.headers.Server = self.headers.Server or "Lua"
-- This should be RFC 1123 date format
-- IE: Tue, 15 Nov 1994 08:12:31 GMT
self.headers.Date = self.headers.Date or os.date("!%a, %d %b %Y %H:%M:%S GMT")
if self.body then
self.headers["Content-Type"] = self.headers["Content-Type"] or "text/html"
if not self.headers["Content-Length"] then
self.headers["Transfer-Encoding"] = "chunked"
end
end
for field, value in pairs(self.headers) do
head[#head+1] = format("%s: %s", tostring(field), tostring(value))
end
head = table.concat(head, "\r\n") .. "\r\n"
print("sending", head)
self.socket:write(head)
end
local continue = function(self)
self.socket:write("HTTP/1.1 100 Continue\r\n\r\n")
end
local write = function(self, str)
-- chunked:
self.socket:write(format("%x\r\n%s\r\n", #str, str))
end
local finish = function(self, str)
-- chunked:
self.socket:write("0\r\n\r\n")
-- done with session:
self.socket:shutdown()
self.socket:close()
end
local host = "127.0.0.1"
local port = 8080
-- Request and Response extend stream.
function server()
local s = uv.tcp()
s:bind("0.0.0.0", 8080)
s:listen(function()
print("listener received request", s)
local client = uv.tcp()
s:accept(client)
print("accepted session", s, client)
-- create request/response objects
local request = { socket = client }
local response = {
socket = client,
headers = {},
}
client:read_start(function(self, chunk, size)
print("received data", size)
if size < 0 then
-- EOF:
print("session ended", size)
self:close()
return
elseif size == 0 then
return
end
-- handle upgrade mode?
-- parse HTTP chunk
print("chunk", chunk)
print("send", simpleresponse)
self:write(simpleresponse)
--sendheaders(response)
print("sent headers")
--write(response, "<html><body>Hello!</body></html>")
print("sent body")
--finish(response)
print("finished")
end)
end)
print("server configured")
end
coroutine.wrap(server)()
uv.run()
print("done")