• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lunarmodules / copas / 30401607642

28 Jul 2026 09:40PM UTC coverage: 85.624% (-0.1%) from 85.764%
30401607642

push

github

Tieske
fix(http): do not send literal IP addresses as the TLS SNI name

RFC 6066 forbids IP literals in the SNI extension, but the SNI
fallback used u.host unconditionally, so connecting (directly or via
redirect) to a bare IPv4/IPv6 address sent that address as the SNI
name instead of a real hostname.

Added ishostliteral() to detect IPv4/IPv6 literals and skip the
fallback for them. When no usable name is available, ssl_params.sni
is collapsed to false so copas.lua's connect() omits the SNI
extension entirely rather than send a bogus name.

5 of 8 new or added lines in 1 file covered. (62.5%)

24 existing lines in 6 files now uncovered.

1495 of 1746 relevant lines covered (85.62%)

55712.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/src/copas/future.lua
1
local copas = require("copas")
6✔
2
local semaphore = require("copas.semaphore")
6✔
3

4
local pcall = pcall
6✔
5
if _VERSION == "Lua 5.1" and not jit then  -- obsolete: only for Lua 5.1 compatibility
6✔
UNCOV
6
  pcall = require("coxpcall").pcall
1✔
7
end
8

9

10
-- nil-safe versions for pack/unpack
11
local _unpack = unpack or table.unpack
6✔
12
local unpack = function(t, i, j) return _unpack(t, i or 1, j or t.n or #t) end
108✔
13
local pack = function(...) return { n = select("#", ...), ...} end
78✔
14

15

16

17
-- Module table
18

19
local M = {}
6✔
20

21
M.SUCCESS = true
6✔
22
M.PENDING = false
6✔
23
M.ERROR   = "error"
6✔
24

25
setmetatable(M, {
12✔
26
  __index = function(_, k)
27
    error("unknown field 'future." .. tostring(k) .. "'", 2)
6✔
28
  end,
29
})
30

31

32

33
-- Future class
34

35
local future = {}
6✔
36
future.__index = future
6✔
37

38
-- calling on the future executes the `get` method
39
future.__call = function(self, ...) return self:get(...) end
12✔
40

41

42
local function new_future()
43
  local self = setmetatable({
144✔
44
    results = nil, -- results will be stored here in a 'packed' table (pcall-style: true/false prefix)
60✔
45
    sema = semaphore.new(math.huge, 0, math.huge),
84✔
46
    coro = nil -- the coroutine that will execute the task
60✔
47
  }, future)
72✔
48

49
  return self
72✔
50
end
51

52

53
-- Waits for the task to complete.
54
-- Returns like pcall: true + results on success, false + errmsg on error.
55
function future:get()
6✔
56
  if not self.results then
96✔
57
    self.sema:take(1, math.huge) -- wait until the result is ready
84✔
58
  end
59
  return unpack(self.results)
96✔
60
end
61

62

63
-- Non-blocking check on the future status.
64
-- Returns:
65
--   M.PENDING (false)             -- task not yet complete
66
--   M.SUCCESS (true), results...  -- task completed successfully
67
--   M.ERROR ("error"), errmsg     -- task failed with an error
68
function future:try()
6✔
69
  if not self.results then
24✔
70
    return M.PENDING
6✔
71
  end
72
  if self.results[1] then
18✔
73
    return M.SUCCESS, unpack(self.results, 2)
7✔
74
  else
75
    return M.ERROR, self.results[2]
12✔
76
  end
77
end
78

79

80
-- Cancels the task if it has not yet completed.
81
-- Returns true if cancelled, false if already done.
82
function future:cancel()
6✔
83
  if self.results then
18✔
84
    return false  -- already done (or already cancelled)
6✔
85
  end
86
  self.results = pack(false, "cancelled")
14✔
87
  self.sema:release_all()
12✔
88
  copas.removethread(self.coro)
12✔
89
  return true
12✔
90
end
91

92

93

94
-- Module implementation
95

96
-- Mimics copas.addnamedthread but returns a future instead of the coroutine.
97
function M.addnamedthread(name, func, ...)
6✔
98
  local f = new_future()
72✔
99

100
  f.coro = copas.addnamedthread(name, function(...)
144✔
101
    local results
102
    local ok, err = pcall(function(...) results = pack(true, func(...)) end, ...)
136✔
103
    if not ok then
60✔
104
      results = pack(false, err)
14✔
105
    end
106
    if not f.results then  -- don't overwrite a cancel
60✔
107
      f.results = results
60✔
108
      f.sema:release_all()
60✔
109
    end
110
  end, ...)
204✔
111

112
  return f
72✔
113
end
114

115

116
-- Mimica copas.addthread but returns a future instead of the coroutine.
117
function M.addthread(func, ...)
6✔
118
  return M.addnamedthread(nil, func, ...)
66✔
119
end
120

121

122
return M
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc