socket.lua 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. -----------------------------------------------------------------------------
  2. -- LuaSocket helper module
  3. -- Author: Diego Nehab
  4. -----------------------------------------------------------------------------
  5. -----------------------------------------------------------------------------
  6. -- Declare module and import dependencies
  7. -----------------------------------------------------------------------------
  8. local base = _G
  9. local string = require("string")
  10. local math = require("math")
  11. local socket = require("socket.core")
  12. local _M = socket
  13. -----------------------------------------------------------------------------
  14. -- Exported auxiliar functions
  15. -----------------------------------------------------------------------------
  16. function _M.connect4(address, port, laddress, lport)
  17. return socket.connect(address, port, laddress, lport, "inet")
  18. end
  19. function _M.connect6(address, port, laddress, lport)
  20. return socket.connect(address, port, laddress, lport, "inet6")
  21. end
  22. function _M.bind(host, port, backlog)
  23. if host == "*" then host = "0.0.0.0" end
  24. local addrinfo, err = socket.dns.getaddrinfo(host);
  25. if not addrinfo then return nil, err end
  26. local sock, res
  27. err = "no info on address"
  28. for i, alt in base.ipairs(addrinfo) do
  29. if alt.family == "inet" then
  30. sock, err = socket.tcp4()
  31. else
  32. sock, err = socket.tcp6()
  33. end
  34. if not sock then return nil, err end
  35. sock:setoption("reuseaddr", true)
  36. res, err = sock:bind(alt.addr, port)
  37. if not res then
  38. sock:close()
  39. else
  40. res, err = sock:listen(backlog)
  41. if not res then
  42. sock:close()
  43. else
  44. return sock
  45. end
  46. end
  47. end
  48. return nil, err
  49. end
  50. _M.try = _M.newtry()
  51. function _M.choose(table)
  52. return function(name, opt1, opt2)
  53. if base.type(name) ~= "string" then
  54. name, opt1, opt2 = "default", name, opt1
  55. end
  56. local f = table[name or "nil"]
  57. if not f then base.error("unknown key (".. base.tostring(name) ..")", 3)
  58. else return f(opt1, opt2) end
  59. end
  60. end
  61. -----------------------------------------------------------------------------
  62. -- Socket sources and sinks, conforming to LTN12
  63. -----------------------------------------------------------------------------
  64. -- create namespaces inside LuaSocket namespace
  65. local sourcet, sinkt = {}, {}
  66. _M.sourcet = sourcet
  67. _M.sinkt = sinkt
  68. _M.BLOCKSIZE = 2048
  69. sinkt["close-when-done"] = function(sock)
  70. return base.setmetatable({
  71. getfd = function() return sock:getfd() end,
  72. dirty = function() return sock:dirty() end
  73. }, {
  74. __call = function(self, chunk, err)
  75. if not chunk then
  76. sock:close()
  77. return 1
  78. else return sock:send(chunk) end
  79. end
  80. })
  81. end
  82. sinkt["keep-open"] = function(sock)
  83. return base.setmetatable({
  84. getfd = function() return sock:getfd() end,
  85. dirty = function() return sock:dirty() end
  86. }, {
  87. __call = function(self, chunk, err)
  88. if chunk then return sock:send(chunk)
  89. else return 1 end
  90. end
  91. })
  92. end
  93. sinkt["default"] = sinkt["keep-open"]
  94. _M.sink = _M.choose(sinkt)
  95. sourcet["by-length"] = function(sock, length)
  96. return base.setmetatable({
  97. getfd = function() return sock:getfd() end,
  98. dirty = function() return sock:dirty() end
  99. }, {
  100. __call = function()
  101. if length <= 0 then return nil end
  102. local size = math.min(socket.BLOCKSIZE, length)
  103. local chunk, err = sock:receive(size)
  104. if err then return nil, err end
  105. length = length - string.len(chunk)
  106. return chunk
  107. end
  108. })
  109. end
  110. sourcet["until-closed"] = function(sock)
  111. local done
  112. return base.setmetatable({
  113. getfd = function() return sock:getfd() end,
  114. dirty = function() return sock:dirty() end
  115. }, {
  116. __call = function()
  117. if done then return nil end
  118. local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
  119. if not err then return chunk
  120. elseif err == "closed" then
  121. sock:close()
  122. done = 1
  123. return partial
  124. else return nil, err end
  125. end
  126. })
  127. end
  128. sourcet["default"] = sourcet["until-closed"]
  129. _M.source = _M.choose(sourcet)
  130. return _M