Not to my knowledge, other than constructing the request by hand or using the luncheon.Request:serialize
method which provide the whole request as a lua string.
TBH I am still very confused about how any http server would be unable to handle the first request line being sent alone as this is absolutely a valid use in the TCP and HTTP specifications. Without any more information about how this server is implemented it will be difficult to know what exactly is the correct way to perform these sends, do we need to send the first line and all the headers or is it enough to send the first line and one header?
Looking at lua-http, it looks like they also send the headers after the first line (though that code base isn’t the easiest to follow so maybe not?). Short of getting more information about the code running on the device it would be good to do a little more investigation into what does and does not work on these devices.
For example trying to use something like the following script
local luncheon = require "luncheon"
local socket = require "socket"
local function send_assert(socket, chunk)
print("sending chunk", #chunk)
local ct = assert(socket:send(chunk or ""))
assert(ct == #chunk, "only sent " .. tostring(ct) .. "bytes expected " .. tostring(#chunk))
end
local function send_one_line_at_a_time(socket, request)
for line in request:iter() do
send_assert(socket, line)
end
end
local function send_in_two_chunks(socket, request)
local source = request:iter()
local chunk = {}
local line = source()
repeat
table.insert(chunk, line)
line = source()
until line ~= ""
send_assert(socket, table.concat(chunk, ""))
chunk = {}
line = source()
while line ~= nil do
table.insert(chunk, line)
line = source()
end
send_assert(socket, table.concat(chunk, ""))
end
local function send_in_one_chunk(socket, request)
send_assert(socket, request:serialize())
end
local variatons = {
send_in_one_chunk,
send_in_two_chunks,
send_one_line_at_a_time,
}
local function send_request(ip, port, send_variation, method, url, headers, content)
print("send_request", ip, port, method, url)
local sock = socket.tcp()
assert(sock:connect(ip, port))
local request = assert(luncheon.Request.new(method or "GET", url or "/"))
for k,v in pairs(headers or {}) do
request:add_header(k, v)
end
request:append_body(content or "")
variatons[send_variation](sock, request)
return assert(luncheon.Response.tcp_source(sock))
end
local function print_fancy(...)
local dash = "-"
print(dash:rep(10))
print(...)
print(dash:rep(10))
end
-- Update these values as needed
local ip_addr = "127.0.0.1" -- update this value
local port = 80 -- update this value if needed
local endpoint = "/"
local content = ""
local headers = {
host = ip_addr,
["content-length"] = tostring(#content),
}
local method = "GET"
-- Run the 3 tests
for i=1, #variatons do
print_fancy("requesting", i)
local resp = send_request(ip_addr, port, i, method, endpoint, headers, content)
print_fancy("recieved: ", i)
print(resp.status, resp.status_msg)
print(resp:get_headers():serialize())
print(resp:get_body())
print_fancy()
end
Which all 3 variations return 200 when pointed to an Nginx server, updating the IP address to see if any variations fail might be helpful.
note that updating the values between -- Update these values as needed
and -- Run the 3 tests
would be needed.