Search code examples
lualuasocket

Lua socket asynchronous calls


I am writing a program that uses Lua socket to communicate with a http server. The API that I am using is "socket.http.request", and I have found that it is synchronous. My understanding is that it waits until it gets some response or time outs. Is my understanding correct? If so, I'd prefer to use an asynchronous API.

I also found another API "socket.http.request_cb", which calls a call back function when the request is processed. However, it doesn't seem to work here. (This API is not available on the version that I'm using.) I'm using Lua 5.1 and Lua socket 2.0.2 here. Could anyone let me know which version of Lua or Lua socket has this API?


Solution

  • With connection:settimeout() you can set a time out for a connection. This is used in this example of a parallel downloader for Lua Socket:

    function download (host, file, port)
        port = port or 80
        print (host, file, port)    
        local connectStatus, myConnection = pcall (socket.connect,host,port)
        if (connectStatus) then
            myConnection:settimeout(0.01) -- do not block you can play with this value
            local count = 0 -- counts number of bytes read
            -- May be easier to do this LuaSocket's HTTP functions
            myConnection:send("GET " .. file .. " HTTP/1.0\r\n\r\n")
            local lastStatus = nil
            while true do
                local buffer, status, overflow = receive(myConnection, lastStatus)
                -- If buffer is not null the call was a success (changed in LuaSocket 2.0)
                if (buffer ~= nil) then
                     io.write("+")
                     io.flush()
                     count = count + string.len(buffer)
                else
                    print ("\n\"" .. status .. "\" with " .. string.len(overflow) .. " bytes of " .. file)
                    io.flush()
                    count = count + string.len(overflow)
                end
                if status == "closed" then break end
                    lastStatus=status
                end
            myConnection:close()
            print(file, count)
        else
            print("Connection failed with error : " .. myConnection)
            io.flush()
        end
    end
    
    threads = {} -- list of all live threads
    
    function get (host, file, port)
        -- create coroutine
        local co = coroutine.create(
            function ()
                download(host, file, port)
            end)
        -- insert it in the 
        table.insert(threads, co)
    end
    
    function receive (myConnection, status)
        if status == "timeout" then
            print (myConnection, "Yielding to dispatcher")
            io.flush()
            coroutine.yield(myConnection)
        end
        return myConnection:receive(1024)
    end
    
    function dispatcher ()
        while true do
            local n = table.getn(threads)
            if n == 0 then break end -- no more threads to run
            local connections = {}
            for i=1,n do
                print (threads[i], "Resuming")
                io.flush()
                local status, res = coroutine.resume(threads[i])
                if not res then -- thread finished its task?
                    table.remove(threads, i)
                    break
                else -- timeout
                    table.insert(connections, res)
                end
            end
            if table.getn(connections) == n then
                socket.select(connections)
            end
        end
    end
    
    host = "www.w3.org"
    get(host, "/TR/html401/html40.txt")
    get(host,"/TR/2002/REC-xhtml1-20020801/xhtml1.pdf")
    get(host,"/TR/REC-html32.html")
    get(host,"/TR/2000/REC-DOM-Level-2-Core-20001113/DOM2-Core.txt")
    dispatcher()