For HTTP requests Moai Cloud exposes curl and not much else. I've posted some HTTP boilerplate below the fold. This was hastily adapted from my own service production code. I'm posting it now for use in my next blog post. I'll be formally releasing a production-hardened version of this code (along with my web service utilities) in the near future so stay tuned.

 

local _private = {}
http = http or {}

ANY     = 'ANY'
DELETE  = 'DELETE'
GET     = 'GET'
POST    = 'POST'
PUT     = 'PUT'

--============================================================--
-- _private
--============================================================--

----------------------------------------------------------------
function _private.http ( verb, url, query, body, timeout )

    local url = url .. http.buildQueryString ( query )

    local c = assert ( curl.new (), "Could not initialize Curl." )
    local file = ''
    
    c:setopt ( curl.OPT_WRITEFUNCTION, function ( stream, buffer )
        file = file .. buffer
        return string.len ( buffer )
    end );
    
    c:setopt ( curl.OPT_CUSTOMREQUEST, verb )
    c:setopt ( curl.OPT_BUFFERSIZE, 2^13 )
    c:setopt ( curl.OPT_HTTPHEADER, 'Connection: Keep-Alive', 'Accept-Language: en-us' )
    c:setopt ( curl.OPT_URL, url )
    c:setopt ( curl.OPT_CONNECTTIMEOUT, timeout or 15 )
    
    if body then
        local ok, jsonString = pcall ( json.encode, body )
        if ok then
            c:setopt ( curl.OPT_POSTFIELDS, jsonString )
            c:setopt ( curl.OPT_POSTFIELDSIZE, #jsonString )
        end
    end
    
    local ok, err = c:perform ()
    if ok then return file end
    return nil, err
end

--============================================================--
-- http
--============================================================--

local DEFAULT_TIMEOUT = 30

----------------------------------------------------------------
function http.buildQueryString ( t )

    if not t then return '' end
    
    local sorted = {}
    
    for k, v in pairs ( t ) do
        local encoded = urlEncode ( k ) .. '=' .. urlEncode ( v )
        table.insert ( sorted, encoded )
    end
    
    table.sort ( sorted )
    local str = table.concat ( sorted, '&' )
    
    if #str > 0 then
        str = '?' .. str
    end
    
    return str
end

----------------------------------------------------------------
function http.delete ( url, query, timeout )
    return _private.http ( DELETE, url, query, nil, timeout )
end

----------------------------------------------------------------
function http.get ( url, query, timeout )
    return _private.http ( GET, url, query, nil, timeout )
end

----------------------------------------------------------------
function http.post ( url, query, body, timeout )
    return _private.http ( POST, url, query, body, timeout )
end

----------------------------------------------------------------
function http.put ( url, query, body, timeout )
    return _private.http ( PUT, url, query, body, timeout )
end

----------------------------------------------------------------
function http.urlDecode ( data )

    return data:gsub ( '%+', ' ' ):gsub ( '%%(%x%x)', function ( s )
        return string.char ( tonumber ( s, 16 ))
    end )
end

----------------------------------------------------------------
function http.urlEncode ( str )

    if str then
             
        -- convert line endings
        str = string.gsub ( str, "\n", "\r\n" )
        
        -- escape all characters but alphanumeric, '.' and '-'
        str = string.gsub ( str, "([^%w%.%- ])",
            function ( c ) 
                return "%" .. string.format ( "%02X", string.byte ( c ))
            end
        )
        
        -- convert spaces to "+" symbols
        str = string.gsub ( str, " ", "+" )
    end
     
    return str
end

----------------------------------------------------------------
function http.urlParse ( data, sep )

    local result = {}
    sep = sep or '&'
    data = data .. sep

    for piece in data:gmatch("(.-)" .. sep) do
        local k,v = piece:match("%s*(.-)%s*=(.*)")
        if k then
            result[http.urlDecode(k)] = http.urlDecode(v)
        else
            result[#result + 1] = http.urlDecode(piece)
        end
    end

    return result
end