Skip to content

Instantly share code, notes, and snippets.

@leafo
Last active May 1, 2019 13:28
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leafo/6788652 to your computer and use it in GitHub Desktop.
Save leafo/6788652 to your computer and use it in GitHub Desktop.
Making nonblocking http requests with lua-ev and LuaSocket
bit = require "bit"
ev = require "ev"
ltn12 = require "ltn12"
socket = require "socket"
-- make protect and newtry coroutine friendly
socket.protect = (fn) -> fn
socket.newtry = (finalizer) ->
(...) ->
unless ...
finalizer select 2, ...
...
http = require "socket.http"
-- creates a wrapped socket that yields on timeout
make_non_blocking = do
local nonblocking_mt
nonblocking_mt = {
connect: (...) =>
status, err = @sock\connect ...
if err == "timeout"
coroutine.yield "both"
1
else
status, err
send: (...) =>
while true
byte, err, partial = @sock\send ...
if err == "timeout"
if partial and partial > 0
return partial
else
coroutine.yield "write"
else
return byte, err, partial
receive: (...) =>
while true
msg, err, partial = @sock\receive ...
if err == "timeout"
if partial and #partial > 0
return partial
else
coroutine.yield "read"
else
return msg, err, partial
settimeout: =>
true -- can't change the timeout
__index: (name) =>
-- try mt
if val = nonblocking_mt[name]
return val
-- cache bound method
@[name] = (...) =>
@sock[name] @sock, ...
@[name]
}
->
sock = socket.tcp!
sock\settimeout 0
setmetatable { :sock }, nonblocking_mt
-- this function does the request by creating a non-blocking socket in a
-- coroutine and resumes it when the file descriptor is ready to be read/written
request = (url, finished_fn, loop=ev.Loop.default) ->
local push_callback
sock = make_non_blocking!
thread = coroutine.create ->
t = {}
ok, status, headers = http.request {
url: url
sink: ltn12.sink.table t
create: -> sock
}
finished_fn status, table.concat(t), headers
"done"
ev_callback = (loop, io, io_status) ->
status, msg = coroutine.resume thread
unless status
io\stop loop if io
print "Error", msg
return
push_callback msg
local ev_io, last_bitmask
push_callback = (needed) ->
bitmask = switch needed
when "both"
bit.bor ev.READ, ev.WRITE
when "read"
ev.READ
when "write"
ev.WRITE
when "done"
if ev_io
ev_io\stop loop
return
else
error "unknown message #{needed}"
return if last_bitmask == bitmask
last_bitmask = bitmask
if ev_io
ev_io\stop loop
ev_io = ev.IO.new ev_callback, sock\getfd!, bitmask
ev_io\start loop
ev_callback! -- initiate request
-- and here we try it out
request "http://leafo.net", (status, body, headers) ->
print "ok", status, #body
loop = ev.Loop.default
loop\loop!
@Bobbyjoness
Copy link

hey leafo for usage with LOVE would you recommend using coroutines or threads for http?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment