Migrating from luasocket to lua-http

I saw https://github.com/brunoos/luasec/issues/72#issuecomment-205328635 and couldn’t resist writing the linked code to use lua-http instead.

As the code was originally using luasocket’s http interface, it was straightforward to convert it to lua-http’s http.compat.socket module. This compatability interface provides the same API as luasocket’s socket.http and luasec’s ssl.https modules.


local http = require "http.compat.socket" -- require "socket.http"
local https = http -- require "ssl.https"
local ltn12 = require "ltn12"
local string_sub = string.sub
local table_concat = table.concat
local function make_request(url, timeout)
    http.TIMEOUT = timeout
    http.USERAGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36"
    local b, c, h, s
    local tbody = {}
    local https_opt = {
        url = url,
        protocol = "tlsv1",
        verify = "none",
        headers = {
            ["Accept"] = "*/*",
            ["Accept-Language"] = "sk;q=0.8,en-US,en;q=0.6,cs;q=0.4",
            ["Accept-Charset"] = "UTF-8;q=0.8,*;q=0.7",
        },
        sink = ltn12.sink.table(tbody),
        redirect = false,
    }
    local http_opt = {
        url = url,
        headers = {
            ["Accept"] = "*/*",
            ["Accept-Language"] = "sk;q=0.8,en-US,en;q=0.6,cs;q=0.4",
            ["Accept-Charset"] = "UTF-8;q=0.8,*;q=0.7",
        },
        sink = ltn12.sink.table(tbody),
        redirect = false,
    }
    if string_sub(url, 1, 5) ~= "https" then
        _, c, h, s = http.request(http_opt)
    else
        _, c, h, s = https.request(https_opt)
    end
    -- make headers keys lowercase
    if h ~= nil then
        local h_tmp = {}
        for k, v in pairs(h) do h_tmp[k:lower()] = v end
        h = h_tmp
    end
    -- concat body parts
    b = table_concat(tbody)
    return {body = b, code = c, headers = h, status = s}
end

However, this function can be rewritten much more nicely using the http.request module.


local http_req = require "http.request"
local h1_reason_phrases = require "http.h1_reason_phrases"
local function make_request(url, timeout)
    local r = http_req.new_from_uri(url)
    r.headers:upsert("useragent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36")
    r.headers:upsert("accept", "*/*")
    r.headers:upsert("accept-language", "sk;q=0.8,en-US,en;q=0.6,cs;q=0.4")
    r.headers:upsert("accept-charset", "UTF-8;q=0.8,*;q=0.7")
    r.follow_redirects = false
    local headers, stream = r:go(timeout)
    if headers == nil then
        return nil, stream
    end
    local b, err = stream:get_body_as_string(timeout) -- XXX: use a deadline instead of a timeout?
    stream:shutdown() -- shutdown ASAP to free resources
    if b == nil then
        return nil, err
    end
    local c = headers:get(":status")
    local s = h1_reason_phrases[c] -- look up reason phrase for code
    c = tonumber(c, 10) or c -- the code might not be numeric
    -- convert from headers object to unordered table of key/value pairs
    local h = {}
    for name in headers:each() do
        if name ~= ":status" and h[name] == nil then
            h[name] = headers:get_comma_separated(name)
        end
    end
    return {body = b, code = c, headers = h, status = s}
end

If the user has the flexibility to change the API of their make_request function, they may wish to use the lua-http http.headers object directly instead of transforming it to code + table of header name => value pairs.