Reputation: 123
I used haproxy Socket class as outlined here https://www.haproxy.com/blog/5-ways-to-extend-haproxy-with-lua/#actions to make http request to external service from lua code (See code below).
Any help is appreciated.
local function http_request(txn, data)
local addr = <external-IP>
local port = 80
-- Set up a request to the service
local hdrs = {
[1] = string.format('host: %s:%s', addr, port),
[2] = 'accept: */*',
[3] = 'connection: close'
}
local req = {
[1] = string.format('GET %s HTTP/1.1', data.path),
[2] = table.concat(hdrs, '\r\n'),
[3] = '\r\n'
}
req = table.concat(req, '\r\n')
-- Use core.tcp to get an instance of the Socket class
local socket = core.tcp()
socket:settimeout(data.timeout)
-- Connect to the service and send the request
if socket:connect(addr, port) then
if socket:send(req) then
-- Skip response headers
while true do
local line, _ = socket:receive('*l')
if not line then break end
if line == '' then break end
end
-- Get response body, if any
local content = socket:receive('*a')
return content
else
core.Alert('Could not connect to server (send)')
end
socket:close()
else
core.Alert('Could not connect to server (connect)')
end
end
Upvotes: 2
Views: 1914
Reputation: 909
Recently while working on a problem I figured out that we cannot pass domain name. I was using http.lua lib. This http.lua lib uses Socket class as you are doing in your code.
Also after searching a lot I was unable to find a dns resolver lib. One was there something related to nginx lua, but it requires installing lots of different lua libs, so I skipped it.
The work around I did is, created my own dns resolver service http://127.0.0.1:53535 in HAProxy as below
listen lua_dns
bind 127.0.0.1:53535
http-request do-resolve(txn.dstip,mydns,ipv4) hdr(ResolveHost),lower
http-request return status 200 content-type text/plain lf-string OK hdr ResolvedIp "%[var(txn.dstip)]"
To this service I pass the domain name in request header ResolveHost
and get the IP in response header ResolvedIp
.
Now the lua functions to parse the domain from URL and call dns resolver service is as below
local function parse_domain(url)
local schema, host, _ = url:match("^(.*)://(.-)[?/](.*)$")
if not schema then
-- maybe path (request uri) is missing
schema, host = url:match("^(.*)://(.-)$")
if not schema then
core.Info("ERROR :: Could not parse URL: "..url)
return nil
end
end
return host
end
local function resolve_domain(domain)
local d = parse_domain(domain)
local r, msg = http.get{ url = "http://127.0.0.1:53535", headers={ResolveHost=d} }
if r == nil then
core.Info("ERROR: "..msg..". While resolving doamin: "..d)
return msg
end
return r.headers['resolvedip']
end
Now replace the resolved IP with the domain name in the URL using gsub()
url = string:gsub(domain_name, resolved_ip)
and then call your API using http.lua
local res, msg = http.get{ url=url, headers=headers }
Here the http.lua lib will handle HTTP and HTTPS urls.
Upvotes: 3