I am trying to improve the user experience by having haproxy handle multiple redirects.
haproxy 2.8
lua 5.6
The initial request is identified by a leading A in the path, and if it starts with then, I want it to follow all the redirects starting with the URL I pull from a JSON service. Haproxy should still proxy normal requests that don’t match the /A pattern to the backend loadbalancer.
The lua script loops through each request and if it’s a 301/302 then it follows that request until it’s not or it hits the max redirects limit. Then it should send the final response as the response to the initial request.
global
log 127.0.0.1 local2
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
chroot /var/lib/haproxy
maxconn 4000
lua-load /etc/haproxy/follow_redirects.lua
daemon
defaults
mode http
option httplog
log global
option dontlognull
option http-server-close
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
frontend http_front
bind *:80
bind *:443 ssl crt /etc/haproxy/ssl
# Use the Lua script for initial requests starting with /A
http-request lua.follow_redirects if { path_beg /A }
default_backend web_backend
backend web_backend
option forwardfor
server webserver loadbalancer.com:443 ssl verify none
Then in the lua script I have this.
http = require("http")
core.register_service("follow_redirects", "http", function(applet)
local max_redirects = 2 -- You can change this to your preferred maximum number of redirects
local user_id = applet.path:sub(3) -- assuming the path starts with '/A'
local url = "http://backendsystem.com/find_user?user_id=" .. user_id
local headers = {}
local count = 0
local response, status_code, response_headers, status_line = http.request {
url = url,
sink = ltn12.sink.table(response.body),
}
while count < max_redirects do
-- Sending the request
local response, err = http.get({url = url, headers = headers})
if not response then
applet:set_status(500)
applet:add_header("Content-Type", "text/plain")
applet:start_response()
applet:send(err)
return
end
-- If the response is not a redirect, break the loop
if response.status ~= 301 and response.status ~= 302 then
break
end
-- Updating URL for the next request
url = response.headers["location"]
count = count + 1
end
-- Responding with the final fetched content or redirect
applet:set_status(response.status)
for k, v in pairs(response.headers) do
applet:add_header(k, v)
end
applet:start_response()
if response.body then
applet:send(response.body)
end
end)
It never completes the chain and gets to the last redirect. What am I doing wrong?