Apologies, the LUA script with the sleep updated for debugging purposes.
Here is my LUA script that I attempted to make use of for the exteranl http client:
--- HAProxy JSON to Form Data Converter
-- @module json_to_form_converter
-- @author Rohan de Jongh
-- @license MIT
-- @description Converts JSON payloads to form-urlencoded data for backend authentication
local cjson = require("cjson")
local http = require("socket.http")
local ltn12 = require("ltn12")
local socket = require("socket")
--- Configuration and Environment Management
-- Centralizes configuration with secure defaults and environment-based overrides
local Config = {
--- Backend Server Configuration
backend = {
host = os.getenv("LUA_JSON_TO_FORM_BACKEND_HOST") or "127.0.0.1",
port = os.getenv("LUA_JSON_TO_FORM_BACKEND_PORT") or "8080"
},
--- Operational Modes
modes = {
debug = os.getenv("LUA_JSON_TO_FORM_DEBUG_MODE") == "true" or false,
remove_fields = os.getenv("LUA_JSON_TO_FORM_REMOVE_FIELDS") == "true" or false
},
--- Fields to Remove from Response
remove_fields_list = (function()
local fields_to_remove = os.getenv("LUA_JSON_TO_FORM_REMOVE_FIELDS_LIST")
if not fields_to_remove then
return {} -- Empty Default list
end
local fields = {}
for field in string.gmatch(fields_to_remove, "[^,]+") do
local trimmed_field = field:gsub("^%s+", ""):gsub("%s+$", "")
if trimmed_field ~= "" then
fields[#fields + 1] = trimmed_field
end
end
return fields
end)(),
--- Performance and Security Timeouts
timeouts = {
client = tonumber(os.getenv("LUA_JSON_TO_FORM_TIMEOUT_CLIENT")) or 30,
connect = tonumber(os.getenv("LUA_JSON_TO_FORM_TIMEOUT_CONNECT")) or 5
}
}
--- Logging Utility
-- Provides conditional logging based on debug mode
-- @param message string The message to log
-- @param level string Optional log level (default: "INFO")
local function log(message, level)
if Config.modes.debug then
level = level or "INFO"
print(string.format("[%s] %s - %s",
os.date("%Y-%m-%d %H:%M:%S"),
level,
message
))
end
end
--- Create a Standardized Error Response
-- Generates a JSON-formatted error message for consistent error handling
-- @param message string Error description
-- @return string JSON error response
local function createErrorResponse(message)
log("Error: " .. message, "ERROR")
return string.format(
'{"error": "%s"}',
message:gsub('"', '\\"')
)
end
--- URL Encoding Utility
-- Safely encodes strings for URL transmission, preserving certain characters
-- @param str string String to encode
-- @return string URL-encoded string
local function urlEncode(str)
if not str then return "" end
local preserve = { ['_'] = true, ['-'] = true, ['.'] = true }
return str:gsub("[^%w]", function(c)
return preserve[c] and c or string.format("%%%02X", string.byte(c))
end)
end
--- JSON to Form Data Converter
-- Transforms JSON payload into form-urlencoded data with robust validation
-- @param jsonStr string JSON input string
-- @return string|nil Encoded form data, nil on failure
-- @return string|nil Error message if conversion fails
local function jsonToFormData(jsonStr)
log("Processing JSON input", "DEBUG")
-- Validate JSON structure
if not jsonStr:match("^%s*{") or not jsonStr:match("}%s*$") then
return nil, "Invalid JSON format"
end
-- Safely parse JSON
local ok, parsedJson = pcall(cjson.decode, jsonStr)
if not ok then
return nil, "Failed to parse JSON: " .. tostring(parsedJson)
end
-- Validate required fields
local requiredFields = {"client_id", "client_secret", "grant_type"}
for _, field in ipairs(requiredFields) do
if not parsedJson[field] then
return nil, string.format("Missing %s field", field)
end
end
-- Mask sensitive information in logs
log(string.format("Parsed values - client_id: %s, grant_type: %s",
parsedJson.client_id:sub(1, 5) .. "*****" .. parsedJson.client_id:sub(-5),
parsedJson.grant_type), "DEBUG")
-- Construct form-encoded data
local formData = string.format(
"client_id=%s&client_secret=%s&grant_type=%s",
urlEncode(parsedJson.client_id),
urlEncode(parsedJson.client_secret),
urlEncode(parsedJson.grant_type)
)
log("Form data generated successfully", "DEBUG")
return formData
end
--- Forward Request to Backend
-- Manages request forwarding with comprehensive error handling and timeout management
-- @param url string Target backend URL
-- @param headers table Request headers
-- @param body string Request body
-- @return table|nil Response object or nil on failure
-- @return string|nil Error message if request fails
local function forwardRequest(url, headers, body)
log("Forwarding request to: " .. url, "DEBUG")
local responseBody = {}
local responseHeaders = {}
-- Configure request with enhanced timeout handling
local request = {
url = url,
method = "POST",
headers = headers,
source = ltn12.source.string(body),
sink = ltn12.sink.table(responseBody),
redirect = true,
timeout = Config.timeouts.client
}
-- Set global timeout configurations
socket.TIMEOUT = Config.timeouts.connect
http.TIMEOUT = Config.timeouts.client
log("Sending request...", "DEBUG")
local startTime = socket.gettime()
local responseStatus, statusCode, responseHeaders = http.request(request)
local endTime = socket.gettime()
log(string.format("Request completed in %.2f seconds - Status: %s",
endTime - startTime,
statusCode or "N/A"), "DEBUG")
if not responseStatus then
log("HTTP Request failed: " .. tostring(statusCode), "ERROR")
return nil, "Connection error: " .. tostring(statusCode)
end
local responseBodyStr = table.concat(responseBody)
-- Optional field removal for security/privacy
if Config.modes.remove_fields then
local success, responseBodyTable = pcall(cjson.decode, responseBodyStr)
if success and type(responseBodyTable) == "table" then
-- Remove specified fields
for _, field in ipairs(Config.remove_fields_list) do
if responseBodyTable[field] ~= nil then
log(string.format("Removing field: %s", field), "DEBUG")
responseBodyTable[field] = nil
end
end
responseBodyStr = cjson.encode(responseBodyTable)
end
end
return {
status = statusCode,
headers = responseHeaders,
body = responseBodyStr
}
end
--- Error Handling Utility
-- Centralized error response generation
-- @param applet HAProxy applet instance
-- @param statusCode number HTTP status code
-- @param errorMessage string Error description
function handleError(applet, statusCode, errorMessage)
log(errorMessage, "ERROR")
applet:set_status(statusCode)
applet:add_header("content-type", "application/json")
applet:start_response()
applet:send(createErrorResponse(errorMessage))
end
--- Response Sending Utility
-- Centralized response transmission
-- @param applet HAProxy applet instance
-- @param response table Response object
function sendResponse(applet, response)
applet:set_status(response.status)
for name, value in pairs(response.headers or {}) do
applet:add_header(name, value)
end
applet:start_response()
if response.body then
applet:send(response.body)
end
log("=== Request completed ===", "INFO")
end
--- Main HAProxy Service Handler
-- Processes incoming requests, converts JSON to form data, and forwards to backend
core.register_service("json_to_form_data", "http", function(applet)
log("=== New request received ===", "INFO")
log("Path: " .. applet.path, "DEBUG")
-- Read request body
local body = applet:receive()
if not body then
return handleError(applet, 400, "Failed to read request body")
end
-- Validate content type
local contentType = applet.headers["content-type"] and applet.headers["content-type"][0]
if not contentType or not contentType:lower():match("application/json") then
return handleError(applet, 415, "Expected application/json content type")
end
-- Convert JSON to form data
local formData, errorMessage = jsonToFormData(body)
if not formData then
return handleError(applet, 400, errorMessage)
end
-- Prepare request headers
local headersOut = {
["content-type"] = "application/x-www-form-urlencoded",
["content-length"] = tostring(#formData)
}
-- Preserve original non-content/length headers
for name, values in pairs(applet.headers) do
local lowerName = name:lower()
if lowerName ~= "content-type" and lowerName ~= "content-length" then
headersOut[name] = values[0]
end
end
-- Backend URL construction
local backendUrl = string.format("http://%s:%s%s",
Config.backend.host,
Config.backend.port,
applet.path
)
-- Forward request to backend
local response, errorMsg = forwardRequest(backendUrl, headersOut, formData)
if response then
log("Successfully forwarding response to client", "DEBUG")
return sendResponse(applet, response)
else
return handleError(applet, 503,
string.format("Service Unavailable: %s", tostring(errorMsg))
)
end
end)
I have gone over your first comment again and referenced the core http client, and decided to convert the code snippet that I pasted above, over to the core httpclient.
LUA Script using core.httpclient: (Added some debugging to check where things are breaking)
--- HAProxy JSON to Form Data Converter
-- @module json_to_form_converter
-- @author Rohan de Jongh
-- @license MIT
-- @description Converts JSON payloads to form-urlencoded data for backend authentication
local cjson = require("cjson")
function dump(o)
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
--- Configuration and Environment Management
local Config = {
--- Backend Server Configuration
backend = {
host = os.getenv("LUA_JSON_TO_FORM_BACKEND_HOST") or "172.31.3.171",
port = os.getenv("LUA_JSON_TO_FORM_BACKEND_PORT") or "8082"
},
--- Operational Modes
modes = {
debug = os.getenv("LUA_JSON_TO_FORM_DEBUG_MODE") == "true" or false,
remove_fields = os.getenv("LUA_JSON_TO_FORM_REMOVE_FIELDS") == "true" or false
},
--- Fields to Remove from Response
remove_fields_list = (function()
local fields_to_remove = os.getenv("LUA_JSON_TO_FORM_REMOVE_FIELDS_LIST")
if not fields_to_remove then
return {} -- Empty Default list
end
local fields = {}
for field in string.gmatch(fields_to_remove, "[^,]+") do
local trimmed_field = field:gsub("^%s+", ""):gsub("%s+$", "")
if trimmed_field ~= "" then
fields[#fields + 1] = trimmed_field
end
end
return fields
end)(),
--- Performance and Security Timeouts
timeouts = {
client = tonumber(os.getenv("LUA_JSON_TO_FORM_TIMEOUT_CLIENT")) or 30,
connect = tonumber(os.getenv("LUA_JSON_TO_FORM_TIMEOUT_CONNECT")) or 5
}
}
--- Logging Utility
local function log(message, level)
if Config.modes.debug then
level = level or "INFO"
print(string.format("[%s] %s - %s",
os.date("%Y-%m-%d %H:%M:%S"),
level,
message
))
end
end
--- Create a Standardized Error Response
local function createErrorResponse(message)
log("Error: " .. message, "ERROR")
return string.format(
'{"error": "%s"}',
message:gsub('"', '\\"')
)
end
--- URL Encoding Utility
local function urlEncode(str)
if not str then return "" end
local preserve = { ['_'] = true, ['-'] = true, ['.'] = true }
return str:gsub("[^%w]", function(c)
return preserve[c] and c or string.format("%%%02X", string.byte(c))
end)
end
--- JSON to Form Data Converter
local function jsonToFormData(jsonStr)
log("Processing JSON input", "DEBUG")
-- Validate JSON structure
if not jsonStr:match("^%s*{") or not jsonStr:match("}%s*$") then
return nil, "Invalid JSON format"
end
-- Safely parse JSON
local ok, parsedJson = pcall(cjson.decode, jsonStr)
if not ok then
return nil, "Failed to parse JSON: " .. tostring(parsedJson)
end
-- Validate required fields
local requiredFields = {"client_id", "client_secret", "grant_type"}
for _, field in ipairs(requiredFields) do
if not parsedJson[field] then
return nil, string.format("Missing %s field", field)
end
end
-- Mask sensitive information in logs
log(string.format("Parsed values - client_id: %s, grant_type: %s",
parsedJson.client_id:sub(1, 5) .. "*****" .. parsedJson.client_id:sub(-5),
parsedJson.grant_type), "DEBUG")
-- Construct form-encoded data
local formData = string.format(
"client_id=%s&client_secret=%s&grant_type=%s",
urlEncode(parsedJson.client_id),
urlEncode(parsedJson.client_secret),
urlEncode(parsedJson.grant_type)
)
log("Form data generated successfully", "DEBUG")
return formData
end
--- Forward Request to Backend with core.httpclient()
local function forwardRequest(applet, url, headers, body)
log("Forwarding request to: " .. url, "DEBUG")
-- Create HTTP client
local httpclient = core.httpclient()
-- Prepare the request
local req = {
url = url,
headers = headers,
body = body,
timeout = Config.timeouts.client,
-- Explicitly set the destination
dst = "127.0.0.1:8088"
}
-- Send the request and wait for response
local success, response = pcall(function()
return httpclient:post(req)
end)
log("HTTP Client Request Details:")
log("URL: " .. tostring(url))
log("Headers: " .. dump(headers))
log("Body: " .. tostring(body))
log("Success: " .. tostring(success))
log("Response: " .. dump(response))
-- Check for request success
if not success then
log("HTTP Request failed: " .. tostring(response), "ERROR")
return nil, "Connection error: " .. tostring(response)
end
-- Normalize headers (extract first value)
local normalizedHeaders = {}
for k, v in pairs(response.headers or {}) do
normalizedHeaders[k] = v[0]
end
-- Optional field removal for security/privacy
local responseBody = response.body
if Config.modes.remove_fields then
local ok, responseBodyTable = pcall(cjson.decode, responseBody)
if ok and type(responseBodyTable) == "table" then
-- Remove specified fields
for _, field in ipairs(Config.remove_fields_list) do
if responseBodyTable[field] ~= nil then
log(string.format("Removing field: %s", field), "DEBUG")
responseBodyTable[field] = nil
end
end
responseBody = cjson.encode(responseBodyTable)
end
end
log("Request completed successfully", "DEBUG")
return {
status = response.status,
headers = normalizedHeaders,
body = responseBody
}
end
--- Error Handling Utility
local function handleError(applet, statusCode, errorMessage)
log(errorMessage, "ERROR")
applet:set_status(statusCode)
applet:add_header("content-type", "application/json")
applet:start_response()
applet:send(createErrorResponse(errorMessage))
end
--- Response Sending Utility
local function sendResponse(applet, response)
log("Sending Response:", "DEBUG")
log("Status: " .. tostring(response.status), "DEBUG")
applet:set_status(response.status)
-- Safely add headers
if response.headers then
for name, value in pairs(response.headers) do
log(string.format("Adding header: %s = %s", name, tostring(value)), "DEBUG")
applet:add_header(name, tostring(value))
end
end
applet:start_response()
if response.body then
applet:send(response.body)
end
log("=== Request completed ===", "INFO")
end
--- Main HAProxy Service Handler
core.register_service("json_to_form_data", "http", function(applet)
log("=== New request received ===", "INFO")
log("Path: " .. applet.path, "DEBUG")
-- Read request body
local body = applet:receive()
if not body then
return handleError(applet, 400, "Failed to read request body")
end
-- Validate content type
local contentType = applet.headers["content-type"] and applet.headers["content-type"][0]
if not contentType or not contentType:lower():match("application/json") then
return handleError(applet, 415, "Expected application/json content type")
end
-- Convert JSON to form data
local formData, errorMessage = jsonToFormData(body)
if not formData then
return handleError(applet, 400, errorMessage)
end
-- Prepare request headers
local headersOut = {
["content-type"] = "application/x-www-form-urlencoded",
["content-length"] = tostring(#formData)
}
-- Preserve original non-content/length headers
for name, values in pairs(applet.headers) do
local lowerName = name:lower()
if lowerName ~= "content-type" and lowerName ~= "content-length" then
headersOut[name] = values[0]
end
end
-- Backend URL construction
local backendUrl = string.format("http://%s:%s%s",
Config.backend.host,
Config.backend.port,
applet.path
)
-- Forward request to backend
local response, errorMsg = forwardRequest(applet, backendUrl, headersOut, formData)
if response then
log("Successfully forwarding response to client", "DEBUG")
return sendResponse(applet, response)
else
return handleError(applet, 503,
string.format("Service Unavailable: %s", tostring(errorMsg))
)
end
end)
With the updated code snippet, I get the following errors now.
Without dst field being part of my request and my URL value = “http://localhost:8088/realms/Sandbox/protocol/openid-connect/token”, I get the following logs. It is interesting to note that a single API call is being performed here, and my 8088 backend is never called. It is like the HTTP client doesn’t know where exactly to go.
haproxy | [2024-12-06 14:20:38] INFO - === New request received ===
haproxy | [2024-12-06 14:20:38] DEBUG - Path: /realms/Sandbox/protocol/openid-connect/token
haproxy | [2024-12-06 14:20:38] DEBUG - Processing JSON input
haproxy | [2024-12-06 14:20:38] DEBUG - Parsed values - client_id: test, grant_type: client_credentials
haproxy | [2024-12-06 14:20:38] DEBUG - Form data generated successfully
haproxy | [2024-12-06 14:20:38] DEBUG - Forwarding request to: http://localhost:8088/realms/Sandbox/protocol/openid-connect/token
haproxy | <6>2024-12-06T14:20:41.116428+02:00 -:- [06/Dec/2024:14:20:38.105] <HTTPCLIENT> -/- 5/0/-1/-1/3010 503 217 - - SC-- 1/0/0/0/3 0/0 {::1} "POST http://localhost:8088/realms/Sandbox/protocol/openid-connect/token HTTP/1.1"
haproxy | [2024-12-06 14:20:41] INFO - HTTP Client Request Details:
haproxy | [2024-12-06 14:20:41] INFO - URL: http://localhost:8088/realms/Sandbox/protocol/openid-connect/token
haproxy | [2024-12-06 14:20:41] INFO - Headers: { ["host"] = localhost:8087,["content-length"] = 119,["cookie"] = _auth_verification=%7B%22nonce%22%3A%22B-hRR0m8kJnj-9ea2wywgNfUBahZxilvz7KfTF6ajZc%22%2C%22state%22%3A%22eyJyZXR1cm5UbyI6Ii9hcGkvdjEvaGVhbHRoQ2hlY2sifQ%22%7D.ACJ66m4TyjOjMQaBg0Qyvf1QKyoNxnQEqhvJotdgcaE; auth_verification=%7B%22nonce%22%3A%22B-hRR0m8kJnj-9ea2wywgNfUBahZxilvz7KfTF6ajZc%22%2C%22state%22%3A%22eyJyZXR1cm5UbyI6Ii9hcGkvdjEvaGVhbHRoQ2hlY2sifQ%22%7D.ShqCbQpYXr_UtEiQKHvcj-FXCUDxNH9ccM9_fqLEgww,["user-agent"] = insomnia/8.6.1,["accept"] = */*,["content-type"] = application/x-www-form-urlencoded,}
haproxy | [2024-12-06 14:20:41] INFO - Body: client_id=test&client_secret=test&grant_type=client_credentials
haproxy | [2024-12-06 14:20:41] INFO - Success: true
haproxy | [2024-12-06 14:20:41] INFO - Response: { ["status"] = 503,["body"] = <html><body><h1>503 Service Unavailable</h1>
haproxy | No server is available to handle this request.
haproxy | </body></html>
haproxy | ,["reason"] = Service Unavailable,["headers"] = { ["content-length"] = { [0] = 107,} ,["content-type"] = { [0] = text/html,} ,["cache-control"] = { [0] = no-cache,} ,} ,}
haproxy | [2024-12-06 14:20:41] DEBUG - Request completed successfully
haproxy | [2024-12-06 14:20:41] DEBUG - Successfully forwarding response to client
haproxy | [2024-12-06 14:20:41] DEBUG - Sending Response:
haproxy | [2024-12-06 14:20:41] DEBUG - Status: 503
haproxy | [2024-12-06 14:20:41] DEBUG - Adding header: content-type = text/html
haproxy | [2024-12-06 14:20:41] DEBUG - Adding header: content-length = 107
haproxy | [2024-12-06 14:20:41] DEBUG - Adding header: cache-control = no-cache
haproxy | [2024-12-06 14:20:41] INFO - === Request completed ===
haproxy | <3>2024-12-06T14:20:41.116999+02:00 172.18.0.1:62582 - http-keycloak-frontend-8087 keycloak-json-converter/<lua.json_to_form_data> 0/0/0/3010/3010 503 217 - - LR-- 1/1/0/0/0 0/0 "POST /realms/Sandbox/protocol/openid-connect/token HTTP/1.1" 854e86c3-5bab-4e00-95d9-568622984e64 -
Adding the dst = "127.0.0.1:8088" field as part of my request and my URL value = "http://localhost:8088/realms/Sandbox/protocol/openid-connect/token", I get the following logs. It is interesting that it is performing numerous API calls this time towards the backend, but it keeps stating the backend has no servers available, even though the backend is available.
haproxy | [2024-12-06 14:25:37] INFO - === New request received ===
haproxy | [2024-12-06 14:25:37] DEBUG - Path: /realms/Sandbox/protocol/openid-connect/token
haproxy | [2024-12-06 14:25:37] DEBUG - Processing JSON input
haproxy | [2024-12-06 14:25:37] DEBUG - Parsed values - client_id: test, grant_type: client_credentials
haproxy | [2024-12-06 14:25:37] DEBUG - Form data generated successfully
haproxy | [2024-12-06 14:25:37] DEBUG - Forwarding request to: http://localhost:8088/realms/Sandbox/protocol/openid-connect/token
haproxy | <3>2024-12-06T14:25:37.293074+02:00 127.0.0.1:42136 - http-keycloak-frontend-8088 https-keycloak-8443/keycloak-frontend-8443-01 0/0/-1/-1/67 -1 0 - - CC-- 3/2/0/0/0 0/0 "POST /realms/Sandbox/protocol/openid-connect/token HTTP/1.1" 55ddb940-f679-4ec2-96ff-147c9ff65057 -
haproxy | <3>2024-12-06T14:25:37.324975+02:00 127.0.0.1:42146 - http-keycloak-frontend-8088 https-keycloak-8443/keycloak-frontend-8443-01 0/0/-1/-1/32 -1 0 - - CC-- 2/1/0/0/0 0/0 "POST /realms/Sandbox/protocol/openid-connect/token HTTP/1.1" 9e2c69f5-03a3-4596-af67-eee3856d16e3 -
haproxy | <3>2024-12-06T14:25:37.357880+02:00 127.0.0.1:42156 - http-keycloak-frontend-8088 https-keycloak-8443/keycloak-frontend-8443-01 0/0/-1/-1/32 -1 0 - - CC-- 2/1/0/0/0 0/0 "POST /realms/Sandbox/protocol/openid-connect/token HTTP/1.1" 3614f8f0-2ac2-4054-bdb1-96d869dc85d1 -
haproxy | <6>2024-12-06T14:25:37.389796+02:00 -:- [06/Dec/2024:14:25:37.222] <HTTPCLIENT> -/- 2/0/136/-1/167 504 198 - - sH-- 2/0/0/0/3 0/0 {} "POST http://localhost:8088/realms/Sandbox/protocol/openid-connect/token HTTP/1.1"
haproxy | <3>2024-12-06T14:25:37.389989+02:00 127.0.0.1:42158 - http-keycloak-frontend-8088 https-keycloak-8443/keycloak-frontend-8443-01 0/0/-1/-1/31 -1 0 - - CC-- 2/1/0/0/0 0/0 "POST /realms/Sandbox/protocol/openid-connect/token HTTP/1.1" e4b8f557-12fd-4f45-b2b0-d13dd7887309 -
haproxy | [2024-12-06 14:25:37] INFO - HTTP Client Request Details:
haproxy | [2024-12-06 14:25:37] INFO - URL: http://localhost:8088/realms/Sandbox/protocol/openid-connect/token
haproxy | [2024-12-06 14:25:37] INFO - Headers: { ["user-agent"] = insomnia/8.6.1,["content-type"] = application/x-www-form-urlencoded,["content-length"] = 119,["cookie"] = _auth_verification=%7B%22nonce%22%3A%22B-hRR0m8kJnj-9ea2wywgNfUBahZxilvz7KfTF6ajZc%22%2C%22state%22%3A%22eyJyZXR1cm5UbyI6Ii9hcGkvdjEvaGVhbHRoQ2hlY2sifQ%22%7D.ACJ66m4TyjOjMQaBg0Qyvf1QKyoNxnQEqhvJotdgcaE; auth_verification=%7B%22nonce%22%3A%22B-hRR0m8kJnj-9ea2wywgNfUBahZxilvz7KfTF6ajZc%22%2C%22state%22%3A%22eyJyZXR1cm5UbyI6Ii9hcGkvdjEvaGVhbHRoQ2hlY2sifQ%22%7D.ShqCbQpYXr_UtEiQKHvcj-FXCUDxNH9ccM9_fqLEgww,["accept"] = */*,["host"] = localhost:8087,}
haproxy | [2024-12-06 14:25:37] INFO - Body: client_id=test&client_secret=test&grant_type=client_credentials
haproxy | [2024-12-06 14:25:37] INFO - Success: true
haproxy | [2024-12-06 14:25:37] INFO - Response: { ["status"] = 504,["reason"] = Gateway Time-out,["body"] = <html><body><text>504 Gateway Time-out</text>
haproxy | The server didn't respond in time.
haproxy | </body></html>
haproxy | ,["headers"] = { ["content-length"] = { [0] = 92,} ,["cache-control"] = { [0] = no-cache,} ,["content-type"] = { [0] = text/html,} ,} ,}
haproxy | [2024-12-06 14:25:37] DEBUG - Request completed successfully
haproxy | [2024-12-06 14:25:37] DEBUG - Successfully forwarding response to client
haproxy | [2024-12-06 14:25:37] DEBUG - Sending Response:
haproxy | [2024-12-06 14:25:37] DEBUG - Status: 504
haproxy | [2024-12-06 14:25:37] DEBUG - Adding header: content-length = 92
haproxy | [2024-12-06 14:25:37] DEBUG - Adding header: cache-control = no-cache
haproxy | [2024-12-06 14:25:37] DEBUG - Adding header: content-type = text/html
haproxy | [2024-12-06 14:25:37] INFO - === Request completed ===
I have also tested converting my backend server over to webhook.site to double check if Keycloak is not rejecting something, but I also don’t receive anything on webhook.site, which tells me I might be missing a small details now between the LUA service and calling my next frontend / backend section and actually being able to go out to the specific connection on the backend.
Logs to webhook site:
haproxy | [2024-12-06 14:30:32] INFO - === New request received ===
haproxy | [2024-12-06 14:30:32] DEBUG - Path: /9b9713a5-aa83-46ed-b9b2-60d71a1b9270
haproxy | [2024-12-06 14:30:32] DEBUG - Processing JSON input
haproxy | [2024-12-06 14:30:32] DEBUG - Parsed values - client_id: test, grant_type: client_credentials
haproxy | [2024-12-06 14:30:32] DEBUG - Form data generated successfully
haproxy | [2024-12-06 14:30:32] DEBUG - Forwarding request to: http://localhost:8088/9b9713a5-aa83-46ed-b9b2-60d71a1b9270
haproxy | <3>2024-12-06T14:30:32.088434+02:00 127.0.0.1:54154 - http-keycloak-frontend-8088 https-keycloak-8443/keycloak-frontend-8443-01 0/0/-1/-1/33 -1 0 - - CC-- 3/2/0/0/0 0/0 "POST /9b9713a5-aa83-46ed-b9b2-60d71a1b9270 HTTP/1.1" f036121e-9cf7-4ffc-87fc-b584be500d37 -
haproxy | <3>2024-12-06T14:30:32.121015+02:00 127.0.0.1:54156 - http-keycloak-frontend-8088 https-keycloak-8443/keycloak-frontend-8443-01 0/0/-1/-1/32 -1 0 - - CC-- 2/1/0/0/0 0/0 "POST /9b9713a5-aa83-46ed-b9b2-60d71a1b9270 HTTP/1.1" 35896caf-37cf-4038-a3f7-d1cf762e05d8 -
haproxy | <3>2024-12-06T14:30:32.153954+02:00 127.0.0.1:54170 - http-keycloak-frontend-8088 https-keycloak-8443/keycloak-frontend-8443-01 0/0/-1/-1/32 -1 0 - - CC-- 2/1/0/0/0 0/0 "POST /9b9713a5-aa83-46ed-b9b2-60d71a1b9270 HTTP/1.1" dba20f93-2dec-41ee-a878-e3f13dd69cbf -
haproxy | <6>2024-12-06T14:30:32.186732+02:00 -:- [06/Dec/2024:14:30:32.054] <HTTPCLIENT> -/- 2/0/100/-1/132 504 198 - - sH-- 2/0/0/0/3 0/0 {} "POST http://localhost:8088/9b9713a5-aa83-46ed-b9b2-60d71a1b9270 HTTP/1.1"
haproxy | <3>2024-12-06T14:30:32.186882+02:00 127.0.0.1:54180 - http-keycloak-frontend-8088 https-keycloak-8443/keycloak-frontend-8443-01 0/0/-1/-1/32 -1 0 - - CC-- 2/1/0/0/0 0/0 "POST /9b9713a5-aa83-46ed-b9b2-60d71a1b9270 HTTP/1.1" d88ca830-fa0a-49dd-8d76-71136c3a1e7d -
haproxy | [2024-12-06 14:30:32] INFO - HTTP Client Request Details:
haproxy | [2024-12-06 14:30:32] INFO - URL: http://localhost:8088/9b9713a5-aa83-46ed-b9b2-60d71a1b9270
haproxy | [2024-12-06 14:30:32] INFO - Headers: { ["content-type"] = application/x-www-form-urlencoded,["user-agent"] = insomnia/8.6.1,["content-length"] = 119,["accept"] = */*,["host"] = localhost:8087,["cookie"] = _auth_verification=%7B%22nonce%22%3A%22B-hRR0m8kJnj-9ea2wywgNfUBahZxilvz7KfTF6ajZc%22%2C%22state%22%3A%22eyJyZXR1cm5UbyI6Ii9hcGkvdjEvaGVhbHRoQ2hlY2sifQ%22%7D.ACJ66m4TyjOjMQaBg0Qyvf1QKyoNxnQEqhvJotdgcaE; auth_verification=%7B%22nonce%22%3A%22B-hRR0m8kJnj-9ea2wywgNfUBahZxilvz7KfTF6ajZc%22%2C%22state%22%3A%22eyJyZXR1cm5UbyI6Ii9hcGkvdjEvaGVhbHRoQ2hlY2sifQ%22%7D.ShqCbQpYXr_UtEiQKHvcj-FXCUDxNH9ccM9_fqLEgww,}
haproxy | [2024-12-06 14:30:32] INFO - Body: client_id=test&client_secret=test&grant_type=client_credentials
haproxy | [2024-12-06 14:30:32] INFO - Success: true
haproxy | [2024-12-06 14:30:32] INFO - Response: { ["status"] = 504,["headers"] = { ["content-length"] = { [0] = 92,} ,["content-type"] = { [0] = text/html,} ,["cache-control"] = { [0] = no-cache,} ,} ,["reason"] = Gateway Time-out,["body"] = <html><body><text>504 Gateway Time-out</text>
haproxy | The server didn't respond in time.
haproxy | </body></html>
haproxy | ,}
haproxy | [2024-12-06 14:30:32] DEBUG - Request completed successfully
haproxy | [2024-12-06 14:30:32] DEBUG - Successfully forwarding response to client
haproxy | [2024-12-06 14:30:32] DEBUG - Sending Response:
haproxy | [2024-12-06 14:30:32] DEBUG - Status: 504
haproxy | [2024-12-06 14:30:32] DEBUG - Adding header: content-type = text/html
haproxy | [2024-12-06 14:30:32] DEBUG - Adding header: content-length = 92
haproxy | [2024-12-06 14:30:32] DEBUG - Adding header: cache-control = no-cache
haproxy | [2024-12-06 14:30:32] INFO - === Request completed ===
haproxy | <3>2024-12-06T14:30:32.187730+02:00 172.18.0.1:61406 - http-keycloak-frontend-8087 keycloak-json-converter/<lua.json_to_form_data> 0/0/0/132/132 504 198 - - LR-- 1/1/0/0/0 0/0 "POST /9b9713a5-aa83-46ed-b9b2-60d71a1b9270 HTTP/1.1" 31d9bc5e-3bd0-440d-993e-702de5f1a7d3 -