applecodervinegar
applecodervinegar

Reputation: 123

how to send https request from lua in haproxy before routing request?

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).

  1. How can I make an https request to the service?
  2. Is it possible to specify a domain name instead of IP address of the service to connect to?

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

Answers (1)

knownUnknown
knownUnknown

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

Related Questions