-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathclient.lua
114 lines (93 loc) · 2.5 KB
/
client.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
local tonumber = tonumber
local concat = table.concat
local setmetatable = setmetatable
local error = error
local pcall = pcall
local type = type
local parse = require "luaweb.parse"
local request = require "luaweb.request"
module "luaweb.client"
local function httpAssert(a, message, code)
if not a then
error({message = message, code = code}, 2)
end
return a
end
_M = {}
local client = _M
client.__index = client
local handlers
function new(socket, callback)
return setmetatable({
socket = socket;
handler = handlers.requestLine;
parser = parse.requestLine;
headers = {};
receiver = function(s) return s:receive("*l") end;
callback = callback;
}, client)
end
function client:think()
local data, err = self.receiver(self.socket)
if data then
local succ, err = pcall(self.handle, self, data)
if not succ then
self:handleError(err)
return false
end
elseif err ~= "timeout" then
return false
end
return true
end
handlers = {
requestLine = function(self, method, path, version)
httpAssert(method and path and version, "invalid request-line", 400)
version = httpAssert(tonumber(version), "invalid HTTP version", 400)
httpAssert(version >= 1.1, "client must be HTTP/1.1 compatible", 505)
self.method = method
self.path = path
self.version = version
return handlers.header, parse.header
end;
header = function(self, name, value)
if not name then
local contentLength = self.headers["Content-Length"]
if contentLength then
self.receiver = function(s) return s:receive(httpAssert(tonumber(contentLength), "invalid Content-Length", 400)) end
return handlers.body, function(data) return data end
else
self:finalize()
return nil, function() error{message = "body not expected", code = 400} end
end
end
self.headers[name] = value
return handlers.header, parse.header
end;
body = function(self, data)
self.body = data
self:finalize()
end;
}
function client:handle(data)
self.handler, self.parser = self:handler(self.parser(data))
end
function client:handleError(err)
if type(err) == "string" then
error(err, 3)
else
self:sendError(err.code, err.message)
end
end
local errorTemplate = "HTTP/1.1 %i %s\r\n"
function client:sendError(code, message)
self.socket:write(errorTemplate:format(code, message))
end
function client:finalize()
local req = request.new{
body = self.body, method = self.method, path = self.path, headers = self.headers;
sink = function(data) self.socket:send(data) end;
}
self.callback(req)
self.socket:close()
end