• 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

81.26
/src/copas.lua
1
-------------------------------------------------------------------------------
2
-- Copas - Coroutine Oriented Portable Asynchronous Services
3
--
4
-- A dispatcher based on coroutines that can be used by TCP/IP servers.
5
-- Uses LuaSocket as the interface with the TCP/IP stack.
6
--
7
-- Authors: Andre Carregal, Javier Guerra, and Fabio Mascarenhas
8
-- Contributors: Diego Nehab, Mike Pall, David Burgess, Leonardo Godinho,
9
--               Thomas Harning Jr., and Gary NG
10
--
11
-- Copyright 2005-2013 - Kepler Project (www.keplerproject.org), 2015-2026 Thijs Schreijer
12
--
13
-- $Id: copas.lua,v 1.37 2009/04/07 22:09:52 carregal Exp $
14
-------------------------------------------------------------------------------
15

16
if package.loaded["socket.http"] and (_VERSION=="Lua 5.1") then     -- obsolete: only for Lua 5.1 compatibility
206✔
17
  error("you must require copas before require'ing socket.http")
×
18
end
19
if package.loaded["copas.http"] and (_VERSION=="Lua 5.1") then     -- obsolete: only for Lua 5.1 compatibility
206✔
20
  error("you must require copas before require'ing copas.http")
×
21
end
22

23
-- load either LuaSocket, or LuaSystem
24
-- note: with luasocket we don't use 'sleep' but 'select' with no sockets
25
local socket, system do
206✔
26
  if pcall(require, "socket") then
207✔
27
    -- found LuaSocket
28
    socket = require "socket"
200✔
29
  end
30

31
  -- try LuaSystem as fallback
32
  if pcall(require, "system") then
207✔
33
    system = require "system"
207✔
34
  end
35

36
  if not (socket or system) then
206✔
37
    error("Neither LuaSocket nor LuaSystem found, Copas requires at least one of them")
×
38
  end
39
end
40

41
local binaryheap = require "binaryheap"
206✔
42
local gettime = (socket or system).gettime
206✔
43
local block_sleep = (socket or system).sleep
206✔
44
local ssl -- only loaded upon demand
45

46
local core_timer_thread
47
local WATCH_DOG_TIMEOUT = 120
206✔
48
local UDP_DATAGRAM_MAX = (socket or {})._DATAGRAMSIZE or 8192
206✔
49
local TIMEOUT_PRECISION = 0.1  -- 100ms
206✔
50
local fnil = function() end
225,835✔
51

52

53
local coroutine_create = coroutine.create
206✔
54
local coroutine_running = coroutine.running
206✔
55
local coroutine_yield = coroutine.yield
206✔
56
local coroutine_resume = coroutine.resume
206✔
57
local coroutine_status = coroutine.status
206✔
58

59

60
-- nil-safe versions for pack/unpack
61
local _unpack = unpack or table.unpack
206✔
62
local unpack = function(t, i, j) return _unpack(t, i or 1, j or t.n or #t) end
856✔
63
local pack = function(...) return { n = select("#", ...), ...} end
1,175✔
64

65

66
local pcall = pcall
206✔
67
if _VERSION=="Lua 5.1" and not jit then     -- obsolete: only for Lua 5.1 compatibility
206✔
UNCOV
68
  pcall = require("coxpcall").pcall
33✔
UNCOV
69
  coroutine_running = require("coxpcall").running
33✔
70
end
71

72

73
if socket then
206✔
74
  -- Redefines LuaSocket functions with coroutine safe versions (pure Lua)
75
  -- (this allows the use of socket.http from within copas)
76
  local err_mt = {
200✔
77
    __tostring = function (self)
78
      return "Copas 'try' error intermediate table: '"..tostring(self[1].."'")
×
79
    end,
80
  }
81

82
  local function statusHandler(status, ...)
83
    if status then return ... end
108✔
84
    local err = (...)
54✔
85
    if type(err) == "table" and getmetatable(err) == err_mt then
54✔
86
      return nil, err[1]
54✔
87
    else
88
      error(err)
×
89
    end
90
  end
91

92
  function socket.protect(func)
200✔
93
    return function (...)
94
            return statusHandler(pcall(func, ...))
126✔
95
          end
96
  end
97

98
  function socket.newtry(finalizer)
200✔
99
    return function (...)
100
            local status = (...)
1,710✔
101
            if not status then
1,710✔
102
              pcall(finalizer or fnil, select(2, ...))
54✔
103
              error(setmetatable({ (select(2, ...)) }, err_mt), 0)
54✔
104
            end
105
            return ...
1,656✔
106
          end
107
  end
108

109
  socket.try = socket.newtry()
232✔
110
end
111

112

113
-- Setup the Copas meta table to auto-load submodules and define a default method
114
local copas do
206✔
115
  local submodules = { "ftp", "future", "http", "lock", "queue", "semaphore", "smtp", "timer" }
206✔
116
  for i, key in ipairs(submodules) do
1,854✔
117
    submodules[key] = true
1,648✔
118
    submodules[i] = nil
1,648✔
119
  end
120

121
  copas = setmetatable({},{
412✔
122
    __index = function(self, key)
123
      if submodules[key] then
266✔
124
        self[key] = require("copas."..key)
268✔
125
        submodules[key] = nil
266✔
126
        return rawget(self, key)
266✔
127
      end
128
    end,
129
    __call = function(self, ...)
130
      return self.loop(...)
6✔
131
    end,
132
  })
206✔
133
end
134

135

136
-- Meta information is public even if beginning with an "_"
137
copas._COPYRIGHT   = "Copyright (C) 2005-2013 Kepler Project, 2015-2026 Thijs Schreijer"
206✔
138
copas._DESCRIPTION = "Coroutine Oriented Portable Asynchronous Services"
206✔
139
copas._VERSION     = "Copas 4.11.0"
206✔
140

141
-- Close the socket associated with the current connection after the handler finishes
142
copas.autoclose = true
206✔
143

144
-- indicator for the loop running
145
copas.running = false
206✔
146

147
-- gettime method from either LuaSocket or LuaSystem: time in (fractional) seconds, since epoch.
148
copas.gettime = gettime
206✔
149

150
-------------------------------------------------------------------------------
151
-- Object names, to track names of thread/coroutines and sockets
152
-------------------------------------------------------------------------------
153
local object_names = setmetatable({}, {
412✔
154
  __mode = "k",
173✔
155
  __index = function(self, key)
156
    local name = tostring(key)
204✔
157
    if key ~= nil then
204✔
158
      rawset(self, key, name)
204✔
159
    end
160
    return name
204✔
161
  end
162
})
163

164
-------------------------------------------------------------------------------
165
-- Simple set implementation
166
-- tracks at the waiting coroutine per socket in the set.
167
-- Sets exist for reading and writing. So each socket can have a reader and writer
168
-- simultaneously, but only one reader and one writer at a time.
169
-------------------------------------------------------------------------------
170

171
local function newsocketset()
172
  local set = {}
618✔
173

174
  do  -- set implementation
175
    local reverse = {}
618✔
176

177
    -- Adds a socket to the set, does nothing if it exists
178
    -- @return skt if added, or nil if it existed
179
    function set:insert(skt)
618✔
180
      if not reverse[skt] then
2,588✔
181
        self[#self + 1] = skt
1,348✔
182
        reverse[skt] = #self
1,348✔
183
        return skt
1,348✔
184
      end
185
    end
186

187
    -- Removes socket from the set, does nothing if not found
188
    -- @return skt if removed, or nil if it wasn't in the set
189
    function set:remove(skt)
618✔
190
      local index = reverse[skt]
1,900✔
191
      if index then
1,900✔
192
        reverse[skt] = nil
1,342✔
193
        local top = self[#self]
1,342✔
194
        self[#self] = nil
1,342✔
195
        if top ~= skt then
1,342✔
196
          reverse[top] = index
174✔
197
          self[index] = top
174✔
198
        end
199
        return skt
1,342✔
200
      end
201
    end
202

203
  end
204

205
  do  -- single-waiter implementation
206
    -- the set instance (read or write) determines what operation the coroutine is waiting for
207
    local waiters = setmetatable({}, { __mode = "k" }) -- coroutine by socket
618✔
208

209
    -- Registers the coroutine as the socket's waiter, to be resumed
210
    -- once the socket becomes ready.
211
    -- @return true on success, or nil + error message if another coroutine
212
    -- is already waiting on this socket read/write.
213
    function set:claim(skt, co)
618✔
214
      if waiters[skt] then
1,252✔
215
        return nil, "Operation already in progress"
12✔
216
      end
217
      waiters[skt] = co
1,240✔
218
      return true
1,240✔
219
    end
220

221
    -- Clears and returns the coroutine waiting on the socket read/write operation, or nil if
222
    -- none is waiting.
223
    function set:release(skt)
618✔
224
      local co = waiters[skt]
1,240✔
225
      waiters[skt] = nil
1,240✔
226
      return co
1,240✔
227
    end
228

229
    -- Drops the socket from the set and discards its waiting coroutine (if
230
    -- any), without resuming it. Only call this when the socket is being
231
    -- discarded outright and no waiter is expected to be woken through the
232
    -- normal readiness path (e.g. the socket object itself is being thrown
233
    -- away). Do not use this as a general substitute for `remove`: if a
234
    -- waiter still needs to observe the outcome (for example a coroutine
235
    -- waiting on a socket that is being closed, which is resumed via the
236
    -- normal tick()/release() path with a "closed" result), purging here
237
    -- would silently drop it instead.
238
    function set:purge(skt)
618✔
239
      waiters[skt] = nil
×
240
      self:remove(skt)
×
241
    end
242

243
  end
244

245
  return set
618✔
246
end
247

248

249

250
-- Threads immediately resumable
251
local _resumable = {} do
206✔
252
  local resumelist = {}
206✔
253

254
  function _resumable:push(co)
206✔
255
    resumelist[#resumelist + 1] = co
225,412✔
256
  end
257

258
  function _resumable:clear_resumelist()
206✔
259
    local lst = resumelist
215,933✔
260
    resumelist = {}
215,933✔
261
    return lst
215,933✔
262
  end
263

264
  function _resumable:done()
206✔
265
    return resumelist[1] == nil
220,411✔
266
  end
267

268
  function _resumable:count()
206✔
269
    return #resumelist + #_resumable
12✔
270
  end
271

272
end
273

274

275

276
-- Similar to the socket set above, but tailored for the use of
277
-- sleeping threads
278
local _sleeping = {} do
206✔
279

280
  local heap = binaryheap.minUnique()
206✔
281
  local lethargy = setmetatable({}, { __mode = "k" }) -- list of coroutines sleeping without a wakeup time
206✔
282

283

284
  -- Required base implementation
285
  -----------------------------------------
286
  _sleeping.insert = fnil
206✔
287
  _sleeping.remove = fnil
206✔
288

289
  -- push a new timer on the heap
290
  function _sleeping:push(sleeptime, co)
206✔
291
    if sleeptime < 0 then
225,599✔
292
      lethargy[co] = true
3,378✔
293
    elseif sleeptime == 0 then
222,221✔
294
      _resumable:push(co)
274,102✔
295
    else
296
      heap:insert(gettime() + sleeptime, co)
8,219✔
297
    end
298
  end
299

300
  -- find the thread that should wake up to the time, if any
301
  function _sleeping:pop(time)
206✔
302
    if time < (heap:peekValue() or math.huge) then
285,690✔
303
      return
215,933✔
304
    end
305
    return heap:pop()
7,990✔
306
  end
307

308
  -- additional methods for time management
309
  -----------------------------------------
310
  function _sleeping:getnext()  -- returns delay until next sleep expires, or nil if there is none
206✔
311
    local t = heap:peekValue()
6,900✔
312
    if t then
6,900✔
313
      -- never report less than 0, because select() might block
314
      return math.max(t - gettime(), 0)
6,900✔
315
    end
316
  end
317

318
  function _sleeping:wakeup(co)
206✔
319
    if lethargy[co] then
3,426✔
320
      lethargy[co] = nil
3,318✔
321
      _resumable:push(co)
3,318✔
322
      return true
3,318✔
323
    end
324
    if heap:remove(co) then
126✔
325
      _resumable:push(co)
18✔
326
      return true
18✔
327
    end
328
    return nil, "not sleeping"
90✔
329
  end
330

331
  -- non-destructive check; unlike wakeup/cancel it doesn't remove 'co'
332
  function _sleeping:issleeping(co)
206✔
333
    if lethargy[co] then
606✔
334
      return true
564✔
335
    end
336
    return heap:valueByPayload(co) ~= nil
49✔
337
  end
338

339
  function _sleeping:cancel(co)
206✔
340
    lethargy[co] = nil
66✔
341
    heap:remove(co)
66✔
342
  end
343

344
  function _sleeping:cancelall()
206✔
345
    while heap:size() > 0 do heap:pop() end
×
346
    heap:insert(gettime() + TIMEOUT_PRECISION, core_timer_thread)
×
347
    -- lethargy is weak; copas's idle GC sweeps will clean it within a few steps
348
  end
349

350
  -- @param tos number of timeouts running
351
  function _sleeping:done(tos)
206✔
352
    -- return true if we have nothing more to do
353
    -- the timeout task doesn't qualify as work (fallbacks only),
354
    -- the lethargy also doesn't qualify as work ('dead' tasks),
355
    -- but the combination of a timeout + a lethargy can be work
356
    return heap:size() == 1       -- 1 means only the timeout-timer task is running
4,171✔
357
           and not (tos > 0 and next(lethargy))
3,576✔
358
  end
359

360
  -- gets number of threads in binaryheap and lethargy
361
  function _sleeping:status()
206✔
362
    local c = 0
12✔
363
    for _ in pairs(lethargy) do c = c + 1 end
12✔
364

365
    return heap:size(), c
14✔
366
  end
367

368
end   -- _sleeping
369

370

371

372
-------------------------------------------------------------------------------
373
-- Tracking coroutines and sockets
374
-------------------------------------------------------------------------------
375

376
local _servers = newsocketset() -- servers being handled
206✔
377
local _threads = setmetatable({}, {__mode = "k"})  -- registered threads added with addthread()
206✔
378
local _canceled = setmetatable({}, {__mode = "k"}) -- threads that are canceled and pending removal
206✔
379
local _autoclose = setmetatable({}, {__mode = "kv"}) -- sockets (value) to close when a thread (key) exits
206✔
380
local _autoclose_r = setmetatable({}, {__mode = "kv"}) -- reverse: sockets (key) to close when a thread (value) exits
206✔
381

382

383
-- for each socket we log the last read and last write times to enable the
384
-- watchdog to follow up if it takes too long.
385
-- tables contain the time, indexed by the socket
386
local _reading_log = {}
206✔
387
local _writing_log = {}
206✔
388

389
local _closed = {} -- track sockets that have been closed (list/array)
206✔
390

391
local _reading = newsocketset() -- sockets currently being read
206✔
392
local _writing = newsocketset() -- sockets currently being written
206✔
393
local _isSocketTimeout = { -- set of errors indicating a socket-timeout
206✔
394
  ["timeout"] = true,      -- default LuaSocket timeout
173✔
395
  ["wantread"] = true,     -- LuaSec specific timeout
173✔
396
  ["wantwrite"] = true,    -- LuaSec specific timeout
173✔
397
}
398

399
-------------------------------------------------------------------------------
400
-- Coroutine based socket timeouts.
401
-------------------------------------------------------------------------------
402
local user_timeouts_connect
403
local user_timeouts_send
404
local user_timeouts_receive
405
do
406
  local timeout_mt = {
206✔
407
    __mode = "k",
173✔
408
    __index = function(self, skt)
409
      -- if there is no timeout found, we insert one automatically, to block forever
410
      self[skt] = math.huge
474✔
411
      return self[skt]
474✔
412
    end,
413
  }
414

415
  user_timeouts_connect = setmetatable({}, timeout_mt)
206✔
416
  user_timeouts_send = setmetatable({}, timeout_mt)
206✔
417
  user_timeouts_receive = setmetatable({}, timeout_mt)
206✔
418
end
419

420
local useSocketTimeoutErrors = setmetatable({},{ __mode = "k" })
206✔
421

422

423
-- sto = socket-time-out
424
local sto_timeout, sto_timed_out, sto_change_queue, sto_error do
206✔
425

426
  local socket_register = setmetatable({}, { __mode = "k" })    -- socket by coroutine
206✔
427
  local operation_register = setmetatable({}, { __mode = "k" }) -- operation "read"/"write" by coroutine
206✔
428
  local timeout_flags = setmetatable({}, { __mode = "k" })      -- true if timedout, by coroutine
206✔
429

430

431
  -- The callback called when a socket timeout occurs.
432
  local function socket_callback(co)
433
    local skt = socket_register[co]
84✔
434
    local queue = operation_register[co]
84✔
435

436
    -- flag the timeout and resume the coroutine
437
    timeout_flags[co] = true
84✔
438
    _resumable:push(co)
84✔
439

440
    -- release our claim on the socket and stop watching it; the timer, not
441
    -- the readiness path, is resuming `co`, so nothing else will do this
442
    if queue == "read" then
84✔
443
      _reading:release(skt)
72✔
444
      _reading:remove(skt)
84✔
445
    elseif queue == "write" then
12✔
446
      _writing:release(skt)
12✔
447
      _writing:remove(skt)
14✔
448
    else
449
      error("bad queue name; expected 'read'/'write', got: "..tostring(queue))
×
450
    end
451
  end
452

453

454
  -- Sets a socket timeout.
455
  -- Calling it as `sto_timeout()` will cancel the timeout.
456
  -- @param skt (socket) the socket on which to operate, use 'nil' to cancel the current timeout
457
  -- @param queue (string) the queue the socket is currently in: "read" or "write"
458
  -- @param use_connect_to (bool) if truthy, use the connect timeout instead of the
459
  --   read/write timeout implied by queue. Needed because connect also uses the "write"
460
  --   queue, so the queue value alone cannot distinguish connect from send operations.
461
  -- @return true
462
  function sto_timeout(skt, queue, use_connect_to)
173✔
463
    local co = coroutine_running()
3,999,944✔
464
    socket_register[co] = skt
3,999,944✔
465
    operation_register[co] = queue
3,999,944✔
466
    timeout_flags[co] = nil
3,999,944✔
467
    if skt then
3,999,944✔
468
      local to = (use_connect_to and user_timeouts_connect[skt]) or
1,999,983✔
469
                 (queue == "read" and user_timeouts_receive[skt]) or
1,999,657✔
470
                 user_timeouts_send[skt]
15,228✔
471
      copas.timeout(to, socket_callback)
2,571,466✔
472
    else
473
      copas.timeout(0)
1,999,972✔
474
    end
475
    return true
3,999,944✔
476
  end
477

478

479
  -- Changes the timeout to a different queue (read/write).
480
  -- Only usefull with ssl-handshakes and "wantread", "wantwrite" errors, when
481
  -- the queue has to be changed, so the timeout handler knows where to find the socket.
482
  -- @param queue (string) the new queue the socket is in, must be either "read" or "write"
483
  -- @return true
484
  function sto_change_queue(queue)
173✔
485
    operation_register[coroutine_running()] = queue
1,024✔
486
    return true
1,024✔
487
  end
488

489

490
  -- Responds with `true` if the operation timed-out.
491
  function sto_timed_out()
173✔
492
    return timeout_flags[coroutine_running()]
1,336✔
493
  end
494

495

496
  -- Returns the proper timeout error
497
  function sto_error(err)
173✔
498
    return useSocketTimeoutErrors[coroutine_running()] and err or "timeout"
84✔
499
  end
500

501

502
  -- only in case of testing export some internals
503
  if _G._TEST then
206✔
504
    copas._socket_register = socket_register
6✔
505
    copas._operation_register = operation_register
6✔
506
    copas._timeout_flags = timeout_flags
6✔
507
  end
508
end
509

510

511

512
-------------------------------------------------------------------------------
513
-- Coroutine based socket I/O functions.
514
-------------------------------------------------------------------------------
515

516
-- Claims the socket for the current coroutine and yields to wait for it to
517
-- become ready, returning `true` once resumed.
518
-- @return nil + error if another coroutine is already waiting on this
519
-- socket (a bug in the caller, not a Copas failure).
520
local function wait_on(queue, skt)
521
  local claimed, err = queue:claim(skt, coroutine_running())
1,252✔
522
  if not claimed then
1,252✔
523
    return nil, err
12✔
524
  end
525
  queue:insert(skt)
1,240✔
526
  coroutine_yield(skt, queue)
1,240✔
527
  return true
1,240✔
528
end
529

530

531
-- Returns "tcp"" for plain TCP and "ssl" for ssl-wrapped sockets, so truthy
532
-- for tcp based, and falsy for udp based.
533
local isTCP do
206✔
534
  local lookup = {
206✔
535
    tcp = "tcp",
173✔
536
    SSL = "ssl",
173✔
537
  }
538

539
  function isTCP(socket)
173✔
540
    return lookup[tostring(socket):sub(1,3)]
854✔
541
  end
542
end
543

544

545
function copas.close(skt, ...)
206✔
546
  _closed[#_closed+1] = skt
228✔
547
  return skt:close(...)
228✔
548
end
549

550

551
-- nil or negative is indefinitly
552
function copas.settimeout(skt, timeout)
206✔
553
  timeout = timeout or -1
228✔
554
  if type(timeout) ~= "number" then
228✔
555
    return nil, "timeout must be 'nil' or a number"
18✔
556
  end
557

558
  return copas.settimeouts(skt, timeout, timeout, timeout)
210✔
559
end
560

561

562
-- negative is indefinitly, nil means do not change
563
function copas.settimeouts(skt, connect, send, read)
206✔
564

565
  if connect ~= nil and type(connect) ~= "number" then
456✔
566
    return nil, "connect timeout must be 'nil' or a number"
×
567
  end
568
  if connect then
456✔
569
    if connect < 0 then
456✔
570
      connect = nil
×
571
    end
572
    user_timeouts_connect[skt] = connect
456✔
573
  end
574

575

576
  if send ~= nil and type(send) ~= "number" then
456✔
577
    return nil, "send timeout must be 'nil' or a number"
×
578
  end
579
  if send then
456✔
580
    if send < 0 then
456✔
581
      send = nil
×
582
    end
583
    user_timeouts_send[skt] = send
456✔
584
  end
585

586

587
  if read ~= nil and type(read) ~= "number" then
456✔
588
    return nil, "read timeout must be 'nil' or a number"
×
589
  end
590
  if read then
456✔
591
    if read < 0 then
456✔
592
      read = nil
×
593
    end
594
    user_timeouts_receive[skt] = read
456✔
595
  end
596

597

598
  return true
456✔
599
end
600

601
-- reads a pattern from a client and yields to the reading set on timeouts
602
-- UDP: a UDP socket expects a second argument to be a number, so it MUST
603
-- be provided as the 'pattern' below defaults to a string. Will throw a
604
-- 'bad argument' error if omitted.
605
-- SECURITY: the default pattern "*l" has no maximum length, matching LuaSocket
606
-- and Lua file-io semantics. It buffers until a newline/EOF/error, and the
607
-- per-operation timeout resets on every partial receive, so it does not bound
608
-- the accumulated size either. Do not use the default line-read directly on
609
-- untrusted/remote input without an application-enforced size limit; use a
610
-- numeric (sized) pattern or receivepartial with your own cumulative cap instead.
611
function copas.receive(client, pattern, part)
206✔
612
  local s, err
613
  pattern = pattern or "*l"
1,984,390✔
614
  local current_log = _reading_log
1,984,390✔
615
  sto_timeout(client, "read")
1,984,390✔
616

617
  repeat
618
    s, err, part = client:receive(pattern, part)
1,985,027✔
619

620
    -- guarantees that high throughput doesn't take other threads to starvation
621
    if (math.random(100) > 90) then
1,985,027✔
622
      copas.pause()
198,910✔
623
    end
624

625
    if s then
1,985,027✔
626
      current_log[client] = nil
1,984,270✔
627
      sto_timeout()
1,984,270✔
628
      return s, err, part
1,984,270✔
629

630
    elseif not _isSocketTimeout[err] then
757✔
631
      current_log[client] = nil
48✔
632
      sto_timeout()
48✔
633
      return s, err, part
48✔
634

635
    elseif sto_timed_out() then
831✔
636
      current_log[client] = nil
66✔
637
      sto_timeout()
66✔
638
      return nil, sto_error(err), part
77✔
639
    end
640

641
    local queue, direction
642
    if err == "wantwrite" then -- wantwrite may be returned during SSL renegotiations
643✔
643
      queue = _writing
×
644
      direction = "write"
×
645
      current_log = _writing_log
×
646
    else
647
      queue = _reading
643✔
648
      direction = "read"
643✔
649
      current_log = _reading_log
643✔
650
    end
651

652
    current_log[client] = gettime()
643✔
653
    sto_change_queue(direction)
643✔
654
    local ok, werr = wait_on(queue, client)
643✔
655
    if not ok then
643✔
656
      current_log[client] = nil
6✔
657
      sto_timeout()
6✔
658
      return nil, werr, part
6✔
659
    end
660
  until false
637✔
661
end
662

663
-- receives data from a client over UDP. Not available for TCP.
664
-- (this is a copy of receive() method, adapted for receivefrom() use)
665
function copas.receivefrom(client, size)
206✔
666
  local s, err, port
667
  size = size or UDP_DATAGRAM_MAX
24✔
668
  sto_timeout(client, "read")
24✔
669

670
  repeat
671
    s, err, port = client:receivefrom(size) -- upon success err holds ip address
48✔
672

673
    -- garantees that high throughput doesn't take other threads to starvation
674
    if (math.random(100) > 90) then
48✔
675
      copas.pause()
3✔
676
    end
677

678
    if s then
48✔
679
      _reading_log[client] = nil
18✔
680
      sto_timeout()
18✔
681
      return s, err, port
18✔
682

683
    elseif err ~= "timeout" then
30✔
684
      _reading_log[client] = nil
×
685
      sto_timeout()
×
686
      return s, err, port
×
687

688
    elseif sto_timed_out() then
35✔
689
      _reading_log[client] = nil
6✔
690
      sto_timeout()
6✔
691
      return nil, sto_error(err), port
7✔
692
    end
693

694
    _reading_log[client] = gettime()
24✔
695
    local ok, werr = wait_on(_reading, client)
24✔
696
    if not ok then
24✔
697
      _reading_log[client] = nil
×
698
      sto_timeout()
×
699
      return nil, werr, port
×
700
    end
701
  until false
24✔
702
end
703

704
-- same as above but with special treatment when reading chunks,
705
-- unblocks on any data received.
706
function copas.receivepartial(client, pattern, part)
206✔
707
  local s, err
708
  pattern = pattern or "*l"
12✔
709
  local orig_size = #(part or "")
12✔
710
  local current_log = _reading_log
12✔
711
  sto_timeout(client, "read")
12✔
712

713
  repeat
714
    s, err, part = client:receive(pattern, part)
18✔
715

716
    -- guarantees that high throughput doesn't take other threads to starvation
717
    if (math.random(100) > 90) then
18✔
718
      copas.pause()
×
719
    end
720

721
    if s or (type(part) == "string" and #part > orig_size) then
18✔
722
      current_log[client] = nil
12✔
723
      sto_timeout()
12✔
724
      return s, err, part
12✔
725

726
    elseif not _isSocketTimeout[err] then
6✔
727
      current_log[client] = nil
×
728
      sto_timeout()
×
729
      return s, err, part
×
730

731
    elseif sto_timed_out() then
7✔
732
      current_log[client] = nil
×
733
      sto_timeout()
×
734
      return nil, sto_error(err), part
×
735
    end
736

737
    local queue, direction
738
    if err == "wantwrite" then
6✔
739
      queue = _writing
×
740
      direction = "write"
×
741
      current_log = _writing_log
×
742
    else
743
      queue = _reading
6✔
744
      direction = "read"
6✔
745
      current_log = _reading_log
6✔
746
    end
747

748
    current_log[client] = gettime()
6✔
749
    sto_change_queue(direction)
6✔
750
    local ok, werr = wait_on(queue, client)
6✔
751
    if not ok then
6✔
752
      current_log[client] = nil
×
753
      sto_timeout()
×
754
      return nil, werr, part
×
755
    end
756
  until false
6✔
757
end
758
copas.receivePartial = copas.receivepartial  -- compat: receivePartial is deprecated
206✔
759

760
-- sends data to a client. The operation is buffered and
761
-- yields to the writing set on timeouts
762
-- Note: from and to parameters will be ignored by/for UDP sockets
763
function copas.send(client, data, from, to)
206✔
764
  local s, err
765
  from = from or 1
15,228✔
766
  local lastIndex = from - 1
15,228✔
767
  local current_log = _writing_log
15,228✔
768
  sto_timeout(client, "write")
15,228✔
769

770
  repeat
771
    s, err, lastIndex = client:send(data, lastIndex + 1, to)
15,411✔
772

773
    -- guarantees that high throughput doesn't take other threads to starvation
774
    if (math.random(100) > 90) then
15,411✔
775
      copas.pause()
1,467✔
776
    end
777

778
    if s then
15,411✔
779
      current_log[client] = nil
15,192✔
780
      sto_timeout()
15,192✔
781
      return s, err, lastIndex
15,192✔
782

783
    elseif not _isSocketTimeout[err] then
219✔
784
      current_log[client] = nil
30✔
785
      sto_timeout()
30✔
786
      return s, err, lastIndex
30✔
787

788
    elseif sto_timed_out() then
219✔
789
      current_log[client] = nil
×
790
      sto_timeout()
×
791
      return nil, sto_error(err), lastIndex
×
792
    end
793

794
    local queue, direction
795
    if err == "wantread" then
189✔
796
      queue = _reading
×
797
      direction = "read"
×
798
      current_log = _reading_log
×
799
    else
800
      queue = _writing
189✔
801
      direction = "write"
189✔
802
      current_log = _writing_log
189✔
803
    end
804

805
    current_log[client] = gettime()
189✔
806
    sto_change_queue(direction)
189✔
807
    local ok, werr = wait_on(queue, client)
189✔
808
    if not ok then
189✔
809
      current_log[client] = nil
6✔
810
      sto_timeout()
6✔
811
      return nil, werr, lastIndex
6✔
812
    end
813
  until false
183✔
814
end
815

816
function copas.sendto(client, data, ip, port)
206✔
817
  -- deprecated; for backward compatibility only, since UDP doesn't block on sending
818
  return client:sendto(data, ip, port)
×
819
end
820

821
-- waits until connection is completed
822
function copas.connect(skt, host, port)
206✔
823
  skt:settimeout(0)
212✔
824
  local ret, err, tried_more_than_once
825
  sto_timeout(skt, "write", true)
210✔
826

827
  repeat
828
    ret, err = skt:connect(host, port)
422✔
829

830
    -- non-blocking connect on Windows results in error "Operation already
831
    -- in progress" to indicate that it is completing the request async. So essentially
832
    -- it is the same as "timeout"
833
    if ret or (err ~= "timeout" and err ~= "Operation already in progress") then
414✔
834
      _writing_log[skt] = nil
198✔
835
      sto_timeout()
198✔
836
      -- Once the async connect completes, Windows returns the error "already connected"
837
      -- to indicate it is done, so that error should be ignored. Except when it is the
838
      -- first call to connect, then it was already connected to something else and the
839
      -- error should be returned
840
      if (not ret) and (err == "already connected" and tried_more_than_once) then
198✔
841
        return 1
×
842
      end
843
      return ret, err
198✔
844

845
    elseif sto_timed_out() then
252✔
846
      _writing_log[skt] = nil
12✔
847
      sto_timeout()
12✔
848
      return nil, sto_error(err)
14✔
849
    end
850

851
    tried_more_than_once = tried_more_than_once or true
204✔
852
    _writing_log[skt] = gettime()
204✔
853
    local ok, werr = wait_on(_writing, skt)
204✔
854
    if not ok then
204✔
855
      _writing_log[skt] = nil
×
856
      sto_timeout()
×
857
      return nil, werr
×
858
    end
859
  until false
204✔
860
end
861

862

863
-- Wraps a tcp socket in an ssl socket and configures it. If the socket was
864
-- already wrapped, it does nothing and returns the socket.
865
-- @param wrap_params the parameters for the ssl-context
866
-- @return wrapped socket, or throws an error
867
local function ssl_wrap(skt, wrap_params)
868
  if isTCP(skt) == "ssl" then return skt end -- was already wrapped
238✔
869
  if not wrap_params then
120✔
870
    error("cannot wrap socket into a secure socket (using 'ssl.wrap()') without parameters/context")
×
871
  end
872

873
  ssl = ssl or require("ssl")
120✔
874
  local nskt = assert(ssl.wrap(skt, wrap_params)) -- assert, because we do not want to silently ignore this one!!
138✔
875

876
  nskt:settimeout(0)  -- non-blocking on the ssl-socket
108✔
877
  copas.settimeouts(nskt, user_timeouts_connect[skt],
216✔
878
    user_timeouts_send[skt], user_timeouts_receive[skt]) -- copy copas user-timeout to newly wrapped one
112✔
879

880
  local co = _autoclose_r[skt]
108✔
881
  if co then
108✔
882
    -- socket registered for autoclose, move registration to wrapped one
883
    _autoclose[co] = nskt
24✔
884
    _autoclose_r[skt] = nil
24✔
885
    _autoclose_r[nskt] = co
24✔
886
  end
887

888
  local sock_name = object_names[skt]
108✔
889
  if sock_name ~= tostring(skt) then
108✔
890
    -- socket had a custom name, so copy it over
891
    object_names[nskt] = sock_name
36✔
892
  end
893
  return nskt
108✔
894
end
895

896

897
-- For each luasec method we have a subtable, allows for future extension.
898
-- Required structure:
899
-- {
900
--   wrap = ... -- parameter to 'wrap()'; the ssl parameter table, or the context object
901
--   sni = {                  -- parameters to 'sni()'
902
--     names = string | table -- 1st parameter
903
--     strict = bool          -- 2nd parameter
904
--   }
905
-- }
906
local function normalize_sslt(sslt)
907
  local t = type(sslt)
384✔
908
  local r = setmetatable({}, {
768✔
909
    __index = function(self, key)
910
      -- a bug if this happens, here as a sanity check, just being careful since
911
      -- this is security stuff
912
      error("accessing unknown 'ssl_params' table key: "..tostring(key))
×
913
    end,
914
  })
915
  if t == "nil" then
384✔
916
    r.wrap = false
246✔
917
    r.sni = false
246✔
918

919
  elseif t == "table" then
138✔
920
    if sslt.wrap or sslt.sni then
138✔
921
      -- has the target definition (current format), copy our known keys
922
      r.wrap = sslt.wrap or false -- 'or false' because we do not want nils
96✔
923
      r.sni = sslt.sni or false -- 'or false' because we do not want nils
96✔
924
    else
925
      -- neither of our own keys is truthy, so treat it as a flat luasec
926
      -- context/wrap-params table (backward compatibility), or garbage.
927
      -- There are no default TLS settings, so we cannot detect/reject a bad
928
      -- table here. But we do not need to; LuaSec validates its mandatory
929
      -- fields (mode, protocol, etc.) itself and throws on anything
930
      -- incomplete, so this can never fail open into silent plaintext.
931
      r.wrap = sslt
42✔
932
      r.sni = false
42✔
933
    end
934

935
  elseif t == "userdata" then
×
936
    -- it's an ssl-context object for the handshake
937
    -- backward compatibility
938
    r.wrap = sslt
×
939
    r.sni = false
×
940

941
  else
942
    error("ssl parameters; did not expect type "..tostring(sslt))
×
943
  end
944

945
  return r
384✔
946
end
947

948

949
---
950
-- Peforms an (async) ssl handshake on a connected TCP client socket.
951
-- NOTE: if not ssl-wrapped already, then replace all previous socket references, with the returned new ssl wrapped socket
952
-- Throws error and does not return nil+error, as that might silently fail
953
-- in code like this;
954
--   copas.addserver(s1, function(skt)
955
--       skt = copas.wrap(skt, sparams)
956
--       skt:dohandshake()   --> without explicit error checking, this fails silently and
957
--       skt:send(body)      --> continues unencrypted
958
-- @param skt Regular LuaSocket CLIENT socket object
959
-- @param wrap_params Table with ssl parameters
960
-- @return wrapped ssl socket, or throws an error
961
function copas.dohandshake(skt, wrap_params)
206✔
962
  ssl = ssl or require("ssl")
120✔
963

964
  local nskt = ssl_wrap(skt, wrap_params)
120✔
965

966
  sto_timeout(nskt, "write", true)
108✔
967
  local queue
968

969
  repeat
970
    local success, err = nskt:dohandshake()
294✔
971

972
    if success then
294✔
973
      sto_timeout()
96✔
974
      return nskt
96✔
975

976
    elseif not _isSocketTimeout[err] then
198✔
977
      sto_timeout()
12✔
978
      error("TLS/SSL handshake failed: " .. tostring(err))
12✔
979

980
    elseif sto_timed_out() then
217✔
981
      sto_timeout()
×
982
      return nil, sto_error(err)
×
983

984
    elseif err == "wantwrite" then
186✔
985
      sto_change_queue("write")
×
986
      queue = _writing
×
987

988
    elseif err == "wantread" then
186✔
989
      sto_change_queue("read")
186✔
990
      queue = _reading
186✔
991

992
    else
993
      error("TLS/SSL handshake failed: " .. tostring(err))
×
994
    end
995

996
    local ok, werr = wait_on(queue, nskt)
186✔
997
    if not ok then
186✔
998
      sto_timeout()
×
999
      error("TLS/SSL handshake failed: " .. tostring(werr))
×
1000
    end
1001
  until false
186✔
1002
end
1003

1004
-- flushes a client write buffer (deprecated)
1005
function copas.flush()
206✔
1006
end
1007

1008
-- wraps a TCP socket to use Copas methods (send, receive, flush and settimeout)
1009
local _skt_mt_tcp = {
206✔
1010
      __tostring = function(self)
1011
        return tostring(self.socket).." (copas wrapped)"
18✔
1012
      end,
1013

1014
      __index = {
206✔
1015
        send = function (self, data, from, to)
1016
          return copas.send (self.socket, data, from, to)
15,222✔
1017
        end,
1018

1019
        receive = function (self, pattern, prefix)
1020
          if user_timeouts_receive[self.socket] == 0 then
1,984,367✔
1021
            return copas.receivepartial(self.socket, pattern, prefix)
12✔
1022
          end
1023
          return copas.receive(self.socket, pattern, prefix)
1,984,354✔
1024
        end,
1025

1026
        receivepartial = function (self, pattern, prefix)
1027
          return copas.receivepartial(self.socket, pattern, prefix)
×
1028
        end,
1029

1030
        flush = function (self)
1031
          return copas.flush(self.socket)
×
1032
        end,
1033

1034
        settimeout = function (self, time)
1035
          return copas.settimeout(self.socket, time)
204✔
1036
        end,
1037

1038
        settimeouts = function (self, connect, send, receive)
1039
          return copas.settimeouts(self.socket, connect, send, receive)
×
1040
        end,
1041

1042
        -- TODO: socket.connect is a shortcut, and must be provided with an alternative
1043
        -- if ssl parameters are available, it will also include a handshake
1044
        connect = function(self, ...)
1045
          local res, err = copas.connect(self.socket, ...)
210✔
1046
          if res then
210✔
1047
            if self.ssl_params.sni then self:sni() end
192✔
1048
            if self.ssl_params.wrap then res, err = self:dohandshake() end
205✔
1049
          end
1050
          return res, err
204✔
1051
        end,
1052

1053
        close = function(self, ...)
1054
          return copas.close(self.socket, ...)
222✔
1055
        end,
1056

1057
        -- TODO: socket.bind is a shortcut, and must be provided with an alternative
1058
        bind = function(self, ...) return self.socket:bind(...) end,
206✔
1059

1060
        -- TODO: is this DNS related? hence blocking?
1061
        getsockname = function(self, ...)
1062
          local ok, ip, port, family = pcall(self.socket.getsockname, self.socket, ...)
×
1063
          if ok then
×
1064
            return ip, port, family
×
1065
          else
1066
            return nil, "not implemented by LuaSec"
×
1067
          end
1068
        end,
1069

1070
        getstats = function(self, ...) return self.socket:getstats(...) end,
206✔
1071

1072
        setstats = function(self, ...) return self.socket:setstats(...) end,
206✔
1073

1074
        listen = function(self, ...) return self.socket:listen(...) end,
206✔
1075

1076
        accept = function(self, ...) return self.socket:accept(...) end,
206✔
1077

1078
        setoption = function(self, ...)
1079
          local ok, res, err = pcall(self.socket.setoption, self.socket, ...)
×
1080
          if ok then
×
1081
            return res, err
×
1082
          else
1083
            return nil, "not implemented by LuaSec"
×
1084
          end
1085
        end,
1086

1087
        getoption = function(self, ...)
1088
          local ok, val, err = pcall(self.socket.getoption, self.socket, ...)
×
1089
          if ok then
×
1090
            return val, err
×
1091
          else
1092
            return nil, "not implemented by LuaSec"
×
1093
          end
1094
        end,
1095

1096
        -- TODO: is this DNS related? hence blocking?
1097
        getpeername = function(self, ...)
1098
          local ok, ip, port, family = pcall(self.socket.getpeername, self.socket, ...)
×
1099
          if ok then
×
1100
            return ip, port, family
×
1101
          else
1102
            return nil, "not implemented by LuaSec"
×
1103
          end
1104
        end,
1105

1106
        shutdown = function(self, ...) return self.socket:shutdown(...) end,
206✔
1107

1108
        sni = function(self, names, strict)
1109
          local sslp = self.ssl_params
84✔
1110
          self.socket = ssl_wrap(self.socket, sslp.wrap)
98✔
1111
          if names == nil then
84✔
1112
            names = sslp.sni.names
72✔
1113
            strict = sslp.sni.strict
72✔
1114
          end
1115
          return self.socket:sni(names, strict)
84✔
1116
        end,
1117

1118
        dohandshake = function(self, wrap_params)
1119
          local nskt, err = copas.dohandshake(self.socket, wrap_params or self.ssl_params.wrap)
120✔
1120
          if not nskt then return nskt, err end
96✔
1121
          self.socket = nskt  -- replace internal socket with the newly wrapped ssl one
96✔
1122
          return self
96✔
1123
        end,
1124

1125
        getalpn = function(self, ...)
1126
          local ok, proto, err = pcall(self.socket.getalpn, self.socket, ...)
×
1127
          if ok then
×
1128
            return proto, err
×
1129
          else
1130
            return nil, "not a tls socket"
×
1131
          end
1132
        end,
1133

1134
        getsniname = function(self, ...)
1135
          local ok, name, err = pcall(self.socket.getsniname, self.socket, ...)
×
1136
          if ok then
×
1137
            return name, err
×
1138
          else
1139
            return nil, "not a tls socket"
×
1140
          end
1141
        end,
1142
      }
206✔
1143
}
1144

1145
-- wraps a UDP socket, copy of TCP one adapted for UDP.
1146
local _skt_mt_udp = {__index = { }}
206✔
1147
for k,v in pairs(_skt_mt_tcp) do _skt_mt_udp[k] = _skt_mt_udp[k] or v end
618✔
1148
for k,v in pairs(_skt_mt_tcp.__index) do _skt_mt_udp.__index[k] = v end
4,738✔
1149

1150
_skt_mt_udp.__index.send        = function(self, ...) return self.socket:send(...) end
212✔
1151

1152
_skt_mt_udp.__index.sendto      = function(self, ...) return self.socket:sendto(...) end
230✔
1153

1154

1155
_skt_mt_udp.__index.receive =     function (self, size)
206✔
1156
                                    return copas.receive (self.socket, (size or UDP_DATAGRAM_MAX))
18✔
1157
                                  end
1158

1159
_skt_mt_udp.__index.receivefrom = function (self, size)
206✔
1160
                                    return copas.receivefrom (self.socket, (size or UDP_DATAGRAM_MAX))
24✔
1161
                                  end
1162

1163
                                  -- TODO: is this DNS related? hence blocking?
1164
_skt_mt_udp.__index.setpeername = function(self, ...) return self.socket:setpeername(...) end
212✔
1165

1166
_skt_mt_udp.__index.setsockname = function(self, ...) return self.socket:setsockname(...) end
206✔
1167

1168
                                    -- do not close client, as it is also the server for udp.
1169
_skt_mt_udp.__index.close       = function(self, ...) return true end
218✔
1170

1171
_skt_mt_udp.__index.settimeouts = function (self, connect, send, receive)
206✔
1172
                                    return copas.settimeouts(self.socket, connect, send, receive)
×
1173
                                  end
1174

1175

1176

1177
---
1178
-- Wraps a LuaSocket socket object in an async Copas based socket object.
1179
-- @param skt The socket to wrap
1180
-- @param[opt] sslt Table with ssl parameters (see 'sslparams' in the reference docs).
1181
-- Omit the parameter to disable TLS entirely.
1182
-- @return wrapped socket object
1183
function copas.wrap (skt, sslt)
206✔
1184
  if (getmetatable(skt) == _skt_mt_tcp) or (getmetatable(skt) == _skt_mt_udp) then
420✔
1185
    return skt -- already wrapped
×
1186
  end
1187

1188
  skt:settimeout(0)
422✔
1189

1190
  if isTCP(skt) then
490✔
1191
    return setmetatable ({socket = skt, ssl_params = normalize_sslt(sslt)}, _skt_mt_tcp)
448✔
1192
  else
1193
    return setmetatable ({socket = skt}, _skt_mt_udp)
36✔
1194
  end
1195
end
1196

1197
--- Wraps a handler in a function that deals with wrapping the socket and doing the
1198
-- optional ssl handshake.
1199
function copas.handler(handler, sslparams)
206✔
1200
  -- TODO: pass a timeout value to set, and use during handshake
1201
  return function (skt, ...)
1202
    skt = copas.wrap(skt, sslparams) -- this call will normalize the sslparams table
112✔
1203
    local sslp = skt.ssl_params
96✔
1204
    if sslp.sni then skt:sni(sslp.sni.names, sslp.sni.strict) end
96✔
1205
    if sslp.wrap then skt:dohandshake(sslp.wrap) end
96✔
1206
    return handler(skt, ...)
90✔
1207
  end
1208
end
1209

1210

1211
--------------------------------------------------
1212
-- Error handling
1213
--------------------------------------------------
1214

1215
local _errhandlers = setmetatable({}, { __mode = "k" })   -- error handler per coroutine
206✔
1216

1217

1218
function copas.gettraceback(msg, co, skt)
206✔
1219
  local co_str = co == nil and "nil" or copas.getthreadname(co)
38✔
1220
  local skt_str = skt == nil and "nil" or copas.getsocketname(skt)
38✔
1221
  local msg_str = msg == nil and "" or tostring(msg)
38✔
1222
  if msg_str == "" then
38✔
1223
    msg_str = ("(coroutine: %s, socket: %s)"):format(msg_str, co_str, skt_str)
×
1224
  else
1225
    msg_str = ("%s (coroutine: %s, socket: %s)"):format(msg_str, co_str, skt_str)
38✔
1226
  end
1227

1228
  if type(co) == "thread" then
38✔
1229
    -- regular Copas coroutine
1230
    return debug.traceback(co, msg_str)
38✔
1231
  end
1232
  -- not a coroutine, but the main thread, this happens if a timeout callback
1233
  -- (see `copas.timeout` causes an error (those callbacks run on the main thread).
1234
  return debug.traceback(msg_str, 2)
×
1235
end
1236

1237

1238
local function _deferror(msg, co, skt)
1239
  print(copas.gettraceback(msg, co, skt))
29✔
1240
end
1241

1242

1243
function copas.seterrorhandler(err, default)
206✔
1244
  assert(err == nil or type(err) == "function", "Expected the handler to be a function, or nil")
60✔
1245
  if default then
60✔
1246
    assert(err ~= nil, "Expected the handler to be a function when setting the default")
42✔
1247
    _deferror = err
42✔
1248
  else
1249
    _errhandlers[coroutine_running()] = err
18✔
1250
  end
1251
end
1252
copas.setErrorHandler = copas.seterrorhandler  -- deprecated; old casing
206✔
1253

1254

1255
function copas.geterrorhandler(co)
206✔
1256
  co = co or coroutine_running()
12✔
1257
  return _errhandlers[co] or _deferror
12✔
1258
end
1259

1260

1261
-- if `bool` is truthy, then the original socket errors will be returned in case of timeouts;
1262
-- `timeout, wantread, wantwrite, Operation already in progress`. If falsy, it will always
1263
-- return `timeout`.
1264
function copas.useSocketTimeoutErrors(bool)
206✔
1265
  useSocketTimeoutErrors[coroutine_running()] = not not bool -- force to a boolean
6✔
1266
end
1267

1268
-------------------------------------------------------------------------------
1269
-- Thread handling
1270
-------------------------------------------------------------------------------
1271

1272
local function _doTick (co, skt, ...)
1273
  if not co then return end
232,391✔
1274

1275
  -- if a coroutine was canceled/removed, don't resume it
1276
  if _canceled[co] then
232,391✔
1277
    _canceled[co] = nil -- also clean up the registry
18✔
1278
    _threads[co] = nil
18✔
1279
    return
18✔
1280
  end
1281

1282
  -- res: the socket (being read/write on) or the time to sleep
1283
  -- new_q: either _writing, _reading, or _sleeping
1284
  -- local time_before = gettime()
1285
  local ok, res, new_q = coroutine_resume(co, skt, ...)
232,373✔
1286
  -- local duration = gettime() - time_before
1287
  -- if duration > 1 then
1288
  --   duration = math.floor(duration * 1000)
1289
  --   pcall(_errhandlers[co] or _deferror, "task ran for "..tostring(duration).." milliseconds.", co, skt)
1290
  -- end
1291

1292
  if new_q == _reading or new_q == _writing then
232,361✔
1293
    -- we're yielding to wait on a socket; the claim was already taken by
1294
    -- the coroutine itself before it yielded (see wait_on below), so by
1295
    -- construction this can't fail here
1296
    new_q:insert (res)
1,240✔
1297
    return
1,240✔
1298
  elseif new_q == _sleeping then
231,121✔
1299
    -- we're yielding to sleep
1300
    new_q:insert (res)
225,599✔
1301
    new_q:push (res, co)
225,599✔
1302
    return
225,599✔
1303
  end
1304

1305
  -- coroutine is terminating
1306

1307
  if ok and coroutine_status(co) ~= "dead" then
5,522✔
1308
    -- it called coroutine.yield from a non-Copas function which is unexpected
1309
    ok = false
6✔
1310
    res = "coroutine.yield was called without a resume first, user-code cannot yield to Copas"
6✔
1311
  end
1312

1313
  if not ok then
5,522✔
1314
    local k, e = pcall(_errhandlers[co] or _deferror, res, co, skt)
46✔
1315
    if not k then
46✔
1316
      print("Failed executing error handler: " .. tostring(e))
×
1317
    end
1318
  end
1319

1320
  local skt_to_close = _autoclose[co]
5,522✔
1321
  if skt_to_close then
5,522✔
1322
    skt_to_close:close()
132✔
1323
    _autoclose[co] = nil
132✔
1324
    _autoclose_r[skt_to_close] = nil
132✔
1325
  end
1326

1327
  _errhandlers[co] = nil
5,522✔
1328
end
1329

1330

1331
local _accept do
206✔
1332
  local client_counters = setmetatable({}, { __mode = "k" })
206✔
1333

1334
  -- accepts a connection on socket input
1335
  function _accept(server_skt, handler)
173✔
1336
    local client_skt = server_skt:accept()
138✔
1337
    if client_skt then
138✔
1338
      local count = (client_counters[server_skt] or 0) + 1
138✔
1339
      client_counters[server_skt] = count
138✔
1340
      object_names[client_skt] = object_names[server_skt] .. ":client_" .. count
154✔
1341

1342
      client_skt:settimeout(0)
138✔
1343
      copas.settimeouts(client_skt, user_timeouts_connect[server_skt],  -- copy server socket timeout settings
276✔
1344
        user_timeouts_send[server_skt], user_timeouts_receive[server_skt])
154✔
1345

1346
      local co = coroutine_create(handler)
138✔
1347
      object_names[co] = object_names[server_skt] .. ":handler_" .. count
138✔
1348

1349
      if copas.autoclose then
138✔
1350
        _autoclose[co] = client_skt
138✔
1351
        _autoclose_r[client_skt] = co
138✔
1352
      end
1353

1354
      _doTick(co, client_skt)
138✔
1355
    end
1356
  end
1357
end
1358

1359
-------------------------------------------------------------------------------
1360
-- Adds a server/handler pair to Copas dispatcher
1361
-------------------------------------------------------------------------------
1362

1363
do
1364
  local function addTCPserver(server, handler, timeout, name)
1365
    server:settimeout(0)
108✔
1366
    if name then
108✔
1367
      object_names[server] = name
×
1368
    end
1369
    _servers[server] = handler
108✔
1370
    _reading:insert(server)
108✔
1371
    if timeout then
108✔
1372
      copas.settimeout(server, timeout)
18✔
1373
    end
1374
  end
1375

1376
  local function addUDPserver(server, handler, timeout, name)
1377
    server:settimeout(0)
×
1378
    local co = coroutine_create(handler)
×
1379
    if name then
×
1380
      object_names[server] = name
×
1381
    end
1382
    object_names[co] = object_names[server]..":handler"
×
1383
    _reading:insert(server)
×
1384
    if timeout then
×
1385
      copas.settimeout(server, timeout)
×
1386
    end
1387
    _doTick(co, server)
×
1388
  end
1389

1390

1391
  function copas.addserver(server, handler, timeout, name)
206✔
1392
    if isTCP(server) then
126✔
1393
      addTCPserver(server, handler, timeout, name)
126✔
1394
    else
1395
      addUDPserver(server, handler, timeout, name)
×
1396
    end
1397
  end
1398
end
1399

1400

1401
function copas.removeserver(server, keep_open)
206✔
1402
  local skt = server
102✔
1403
  local mt = getmetatable(server)
102✔
1404
  if mt == _skt_mt_tcp or mt == _skt_mt_udp then
102✔
1405
    skt = server.socket
×
1406
  end
1407

1408
  _servers:remove(skt)
102✔
1409
  _reading:remove(skt)
102✔
1410

1411
  if keep_open then
102✔
1412
    return true
18✔
1413
  end
1414
  return server:close()
84✔
1415
end
1416

1417

1418

1419
-------------------------------------------------------------------------------
1420
-- Adds an new coroutine thread to Copas dispatcher
1421
-------------------------------------------------------------------------------
1422
function copas.addnamedthread(name, handler, ...)
206✔
1423
  if type(name) == "function" and type(handler) == "string" then
5,698✔
1424
    -- old call, flip args for compatibility
1425
    name, handler = handler, name
×
1426
  end
1427

1428
  -- create a coroutine that skips the first argument, which is always the socket
1429
  -- passed by the scheduler, but `nil` in case of a task/thread
1430
  local thread = coroutine_create(function(_, ...)
11,396✔
1431
    copas.pause()
5,698✔
1432
    return handler(...)
5,668✔
1433
  end)
1434
  if name then
5,698✔
1435
    object_names[thread] = name
586✔
1436
  end
1437

1438
  _threads[thread] = true -- register this thread so it can be removed
5,698✔
1439
  _doTick (thread, nil, ...)
5,698✔
1440
  return thread
5,698✔
1441
end
1442

1443

1444
function copas.addthread(handler, ...)
206✔
1445
  return copas.addnamedthread(nil, handler, ...)
5,046✔
1446
end
1447

1448

1449
function copas.removethread(thread)
206✔
1450
  -- if the specified coroutine is registered, add it to the canceled table so
1451
  -- that next time it tries to resume it exits.
1452
  _canceled[thread] = _threads[thread or 0]
66✔
1453
  _sleeping:cancel(thread)
66✔
1454
end
1455

1456

1457

1458
-------------------------------------------------------------------------------
1459
-- Sleep/pause management functions
1460
-------------------------------------------------------------------------------
1461

1462
-- yields the current coroutine and wakes it after 'sleeptime' seconds.
1463
-- If sleeptime < 0 then it sleeps until explicitly woken up using 'wakeup'
1464
-- TODO: deprecated, remove in next major
1465
function copas.sleep(sleeptime)
206✔
1466
  coroutine_yield((sleeptime or 0), _sleeping)
×
1467
end
1468

1469

1470
-- yields the current coroutine and wakes it after 'sleeptime' seconds.
1471
-- if sleeptime < 0 then it sleeps 0 seconds.
1472
function copas.pause(sleeptime)
206✔
1473
  local s = gettime()
222,221✔
1474
  if sleeptime and sleeptime > 0 then
222,221✔
1475
    coroutine_yield(sleeptime, _sleeping)
9,555✔
1476
  else
1477
    coroutine_yield(0, _sleeping)
214,002✔
1478
  end
1479
  return gettime() - s
221,979✔
1480
end
1481

1482

1483
-- yields the current coroutine until explicitly woken up using 'wakeup'
1484
function copas.pauseforever()
206✔
1485
  local s = gettime()
3,378✔
1486
  coroutine_yield(-1, _sleeping)
3,378✔
1487
  return gettime() - s
3,318✔
1488
end
1489

1490

1491
-- Wakes up a sleeping coroutine 'co'.
1492
-- @return true on success, or nil+"not sleeping" if 'co' wasn't sleeping
1493
-- (eg. it was already woken up, finished, or canceled through `copas.removethread`).
1494
function copas.wakeup(co)
206✔
1495
  return _sleeping:wakeup(co)
3,426✔
1496
end
1497

1498

1499
-- Checks whether a coroutine 'co' is currently sleeping (paused or
1500
-- paused-forever), without waking it up. Useful to detect a coroutine
1501
-- that was canceled (eg. through `copas.removethread`) while it was
1502
-- expected to still be waiting.
1503
function copas.issleeping(co)
206✔
1504
  return _sleeping:issleeping(co)
606✔
1505
end
1506

1507

1508

1509
-------------------------------------------------------------------------------
1510
-- Timeout management
1511
-------------------------------------------------------------------------------
1512

1513
do
1514
  local timeout_register = setmetatable({}, { __mode = "k" })
206✔
1515
  local timerwheel = require("timerwheel").new({
413✔
1516
      now = gettime,
206✔
1517
      precision = TIMEOUT_PRECISION,
206✔
1518
      ringsize = math.floor(60*60*24/TIMEOUT_PRECISION),  -- ring size 1 day
206✔
1519
      err_handler = function(err)
1520
        return _deferror(err, core_timer_thread)
16✔
1521
      end,
1522
    })
1523

1524
  core_timer_thread = copas.addnamedthread("copas_core_timer", function()
412✔
1525
    while true do
1526
      copas.pause(TIMEOUT_PRECISION)
7,074✔
1527
      timerwheel:step()
8,028✔
1528
    end
1529
  end)
1530

1531
  -- get the number of timeouts running
1532
  function copas.gettimeouts()
206✔
1533
    return timerwheel:count()
3,588✔
1534
  end
1535

1536
  --- Sets the timeout for the current coroutine.
1537
  -- @param delay delay (seconds), use 0 (or math.huge) to cancel the timerout
1538
  -- @param callback function with signature: `function(coroutine)` where coroutine is the routine that timed-out
1539
  -- @return true
1540
  function copas.timeout(delay, callback)
206✔
1541
    local co = coroutine_running()
4,005,084✔
1542
    local existing_timer = timeout_register[co]
4,005,084✔
1543

1544
    if existing_timer then
4,005,084✔
1545
      timerwheel:cancel(existing_timer)
4,518✔
1546
    end
1547

1548
    if delay > 0 and delay ~= math.huge then
4,005,084✔
1549
      timeout_register[co] = timerwheel:set(delay, callback, co)
7,116✔
1550
    elseif delay == 0 or delay == math.huge then
3,998,978✔
1551
      timeout_register[co] = nil
3,998,978✔
1552
    else
1553
      error("timout value must be greater than or equal to 0, got: "..tostring(delay))
×
1554
    end
1555

1556
    return true
4,005,084✔
1557
  end
1558

1559
end
1560

1561

1562
-------------------------------------------------------------------------------
1563
-- main tasks: manage readable and writable socket sets
1564
-------------------------------------------------------------------------------
1565
-- a task is an object with a required method `step()` that deals with a
1566
-- single step for that task.
1567

1568
local _tasks = {} do
206✔
1569
  function _tasks:add(tsk)
206✔
1570
    _tasks[#_tasks + 1] = tsk
824✔
1571
  end
1572
end
1573

1574

1575
-- a task to check ready to read events
1576
local _readable_task = {} do
206✔
1577

1578
  _readable_task._events = {}
206✔
1579

1580
  local function tick(skt)
1581
    local handler = _servers[skt]
919✔
1582
    if handler then
919✔
1583
      _accept(skt, handler)
161✔
1584
    else
1585
      _reading:remove(skt)
781✔
1586
      _doTick(_reading:release(skt), skt)
915✔
1587
    end
1588
  end
1589

1590
  function _readable_task:step()
206✔
1591
    for _, skt in ipairs(self._events) do
216,857✔
1592
      tick(skt)
919✔
1593
    end
1594
  end
1595

1596
  _tasks:add(_readable_task)
239✔
1597
end
1598

1599

1600
-- a task to check ready to write events
1601
local _writable_task = {} do
206✔
1602

1603
  _writable_task._events = {}
206✔
1604

1605
  local function tick(skt)
1606
    _writing:remove(skt)
375✔
1607
    _doTick(_writing:release(skt), skt)
436✔
1608
  end
1609

1610
  function _writable_task:step()
206✔
1611
    for _, skt in ipairs(self._events) do
216,308✔
1612
      tick(skt)
375✔
1613
    end
1614
  end
1615

1616
  _tasks:add(_writable_task)
239✔
1617
end
1618

1619

1620

1621
-- sleeping threads task
1622
local _sleeping_task = {} do
206✔
1623

1624
  function _sleeping_task:step()
206✔
1625
    local now = gettime()
215,933✔
1626

1627
    local co = _sleeping:pop(now)
215,933✔
1628
    while co do
223,923✔
1629
      -- we're pushing them to _resumable, since that list will be replaced before
1630
      -- executing. This prevents tasks running twice in a row with pause(0) for example.
1631
      -- So here we won't execute, but at _resumable step which is next
1632
      _resumable:push(co)
7,990✔
1633
      co = _sleeping:pop(now)
9,324✔
1634
    end
1635
  end
1636

1637
  _tasks:add(_sleeping_task)
206✔
1638
end
1639

1640

1641

1642
-- resumable threads task
1643
local _resumable_task = {} do
206✔
1644

1645
  function _resumable_task:step()
206✔
1646
    -- replace the resume list before iterating, so items placed in there
1647
    -- will indeed end up in the next copas step, not in this one, and not
1648
    -- create a loop
1649
    local resumelist = _resumable:clear_resumelist()
215,933✔
1650

1651
    for _, co in ipairs(resumelist) do
441,325✔
1652
      _doTick(co)
225,399✔
1653
    end
1654
  end
1655

1656
  _tasks:add(_resumable_task)
206✔
1657
end
1658

1659

1660
-------------------------------------------------------------------------------
1661
-- Checks for reads and writes on sockets
1662
-------------------------------------------------------------------------------
1663
local _select_plain do
206✔
1664

1665
  local last_cleansing = 0
206✔
1666
  local duration = function(t2, t1) return t2-t1 end
216,058✔
1667

1668
  if not socket then
206✔
1669
    -- socket module unavailable, switch to luasystem sleep
1670
    _select_plain = block_sleep
6✔
1671
  else
1672
    -- use socket.select to handle socket-io
1673
    _select_plain = function(timeout)
1674
      local err
1675
      local now = gettime()
215,852✔
1676

1677
      -- remove any closed sockets to prevent select from hanging on them
1678
      if _closed[1] then
215,852✔
1679
        for i, skt in ipairs(_closed) do
453✔
1680
          _closed[i] = { _reading:remove(skt), _writing:remove(skt) }
304✔
1681
        end
1682
      end
1683

1684
      _readable_task._events, _writable_task._events, err = socket.select(_reading, _writing, timeout)
215,852✔
1685
      local r_events, w_events = _readable_task._events, _writable_task._events
215,852✔
1686

1687
      -- inject closed sockets in readable/writeable task so they can error out properly
1688
      if _closed[1] then
215,852✔
1689
        for i, skts in ipairs(_closed) do
453✔
1690
          _closed[i] = nil
228✔
1691
          r_events[#r_events+1] = skts[1]
228✔
1692
          w_events[#w_events+1] = skts[2]
228✔
1693
        end
1694
      end
1695

1696
      if duration(now, last_cleansing) > WATCH_DOG_TIMEOUT then
276,274✔
1697
        last_cleansing = now
188✔
1698

1699
        -- Check all sockets selected for reading, and check how long they have been waiting
1700
        -- for data already, without select returning them as readable
1701
        for skt,time in pairs(_reading_log) do
188✔
1702
          if not r_events[skt] and duration(now, time) > WATCH_DOG_TIMEOUT then
×
1703
            -- This one timedout while waiting to become readable, so move
1704
            -- it in the readable list and try and read anyway, despite not
1705
            -- having been returned by select
1706
            _reading_log[skt] = nil
×
1707
            r_events[#r_events + 1] = skt
×
1708
            r_events[skt] = #r_events
×
1709
          end
1710
        end
1711

1712
        -- Do the same for writing
1713
        for skt,time in pairs(_writing_log) do
188✔
1714
          if not w_events[skt] and duration(now, time) > WATCH_DOG_TIMEOUT then
×
1715
            _writing_log[skt] = nil
×
1716
            w_events[#w_events + 1] = skt
×
1717
            w_events[skt] = #w_events
×
1718
          end
1719
        end
1720
      end
1721

1722
      if err == "timeout" and #r_events + #w_events > 0 then
215,852✔
1723
        return nil
6✔
1724
      else
1725
        return err
215,846✔
1726
      end
1727
    end
1728
  end
1729
end
1730

1731

1732

1733
-------------------------------------------------------------------------------
1734
-- Dispatcher loop step.
1735
-- Listen to client requests and handles them
1736
-- Returns false if no socket-data was handled, or true if there was data
1737
-- handled (or nil + error message)
1738
-------------------------------------------------------------------------------
1739

1740
local copas_stats
1741
local min_ever, max_ever
1742

1743
local _select = _select_plain
206✔
1744

1745
-- instrumented version of _select() to collect stats
1746
local _select_instrumented = function(timeout)
1747
  if copas_stats then
×
1748
    local step_duration = gettime() - copas_stats.step_start
×
1749
    copas_stats.duration_max = math.max(copas_stats.duration_max, step_duration)
×
1750
    copas_stats.duration_min = math.min(copas_stats.duration_min, step_duration)
×
1751
    copas_stats.duration_tot = copas_stats.duration_tot + step_duration
×
1752
    copas_stats.steps = copas_stats.steps + 1
×
1753
  else
1754
    copas_stats = {
×
1755
      duration_max = -1,
1756
      duration_min = 999999,
1757
      duration_tot = 0,
1758
      steps = 0,
1759
    }
1760
  end
1761

1762
  local err = _select_plain(timeout)
×
1763

1764
  local now = gettime()
×
1765
  copas_stats.time_start = copas_stats.time_start or now
×
1766
  copas_stats.step_start = now
×
1767

1768
  return err
×
1769
end
1770

1771

1772
function copas.step(timeout)
206✔
1773
  -- Need to wake up the select call in time for the next sleeping event
1774
  if not _resumable:done() then
276,371✔
1775
    timeout = 0
209,038✔
1776
  else
1777
    timeout = math.min(_sleeping:getnext(), timeout or math.huge)
8,055✔
1778
  end
1779

1780
  local err = _select(timeout)
215,938✔
1781

1782
  for _, tsk in ipairs(_tasks) do
1,079,668✔
1783
    tsk:step()
863,742✔
1784
  end
1785

1786
  if err then
215,926✔
1787
    if err == "timeout" then
214,794✔
1788
      if timeout + 0.01 > TIMEOUT_PRECISION and math.random(100) > 90 then
214,708✔
1789
        -- we were idle, so occasionally do a GC sweep to ensure lingering
1790
        -- sockets are closed, and we don't accidentally block the loop from
1791
        -- exiting
1792
        collectgarbage()
492✔
1793
      end
1794
      return false
214,708✔
1795
    end
1796
    return nil, err
86✔
1797
  end
1798

1799
  return true
1,132✔
1800
end
1801

1802

1803
-------------------------------------------------------------------------------
1804
-- Check whether there is something to do.
1805
-- returns false if there are no sockets for read/write nor tasks scheduled
1806
-- (which means Copas is in an empty spin)
1807
-------------------------------------------------------------------------------
1808
function copas.finished()
206✔
1809
  return #_reading == 0 and #_writing == 0 and _resumable:done() and _sleeping:done(copas.gettimeouts())
217,857✔
1810
end
1811

1812

1813
local resetexit do
206✔
1814
  local exit_semaphore, exiting
1815

1816
  function resetexit()
173✔
1817
    exit_semaphore = copas.semaphore.new(1, 0, math.huge)
478✔
1818
    exiting = false
386✔
1819
  end
1820

1821
  -- Signals tasks to exit. But only if they check for it. By calling `copas.exiting`
1822
  -- they can check if they should exit. Or by calling `copas.waitforexit` they can
1823
  -- wait until the exit signal is given.
1824
  function copas.exit()
206✔
1825
    if exiting then return end
374✔
1826
    exiting = true
374✔
1827
    exit_semaphore:destroy()
374✔
1828
  end
1829

1830
  -- returns whether Copas is in the process of exiting. Exit can be started by
1831
  -- calling `copas.exit()`.
1832
  function copas.exiting()
239✔
1833
    return exiting
742✔
1834
  end
1835

1836
  -- Pauses the current coroutine until Copas is exiting. To be used as an exit
1837
  -- signal for tasks that need to clean up before exiting.
1838
  function copas.waitforexit()
239✔
1839
    exit_semaphore:take(1)
12✔
1840
  end
1841
end
1842

1843

1844
--- Forcibly cancels all pending work and signals exit.
1845
-- Intended for test teardown only. Abandons all registered threads and sockets
1846
-- without giving them a chance to clean up. After this call copas.finished()
1847
-- will return true and the loop will exit. The module is left in a clean state
1848
-- ready for the next copas.loop() call.
1849
function copas.cancelall()
206✔
1850
  -- 1. clear resumable queue
1851
  _resumable:clear_resumelist()
×
1852

1853
  -- 2. drain sleeping heap
1854
  _sleeping:cancelall()
×
1855

1856
  -- 3. close and drain reading sockets
1857
  while _reading[1] do
×
1858
    copas.close(_reading[1])
×
1859
    _reading:remove(_reading[1])
×
1860
  end
1861

1862
  -- 4. close and drain writing sockets
1863
  while _writing[1] do
×
1864
    copas.close(_writing[1])
×
1865
    _writing:remove(_writing[1])
×
1866
  end
1867

1868
  -- 5. remove all servers
1869
  while _servers[1] do
×
1870
    copas.removeserver(_servers[1])
×
1871
  end
1872

1873
  -- 6. clear non-weak ancillary tables
1874
  _closed = {}
×
1875
  _reading_log = {}
×
1876
  _writing_log = {}
×
1877

1878
  -- 7. signal exit
1879
  copas.exit()
×
1880
end
1881

1882

1883
local _getstats do
206✔
1884
  local _getstats_instrumented, _getstats_plain
1885

1886

1887
  function _getstats_plain(enable)
173✔
1888
    -- this function gets hit if turned off, so turn on if true
1889
    if enable == true then
12✔
1890
      _select = _select_instrumented
×
1891
      _getstats = _getstats_instrumented
×
1892
      -- reset stats
1893
      min_ever = nil
×
1894
      max_ever = nil
×
1895
      copas_stats = nil
×
1896
    end
1897
    return {}
12✔
1898
  end
1899

1900

1901
  -- convert from seconds to millisecs, with microsec precision
1902
  local function useconds(t)
1903
    return math.floor((t * 1000000) + 0.5) / 1000
×
1904
  end
1905
  -- convert from seconds to seconds, with millisec precision
1906
  local function mseconds(t)
1907
    return math.floor((t * 1000) + 0.5) / 1000
×
1908
  end
1909

1910

1911
  function _getstats_instrumented(enable)
173✔
1912
    if enable == false then
×
1913
      _select = _select_plain
×
1914
      _getstats = _getstats_plain
×
1915
      -- instrumentation disabled, so switch to the plain implementation
1916
      return _getstats(enable)
×
1917
    end
1918
    if (not copas_stats) or (copas_stats.step == 0) then
×
1919
      return {}
×
1920
    end
1921
    local stats = copas_stats
×
1922
    copas_stats = nil
×
1923
    min_ever = math.min(min_ever or 9999999, stats.duration_min)
×
1924
    max_ever = math.max(max_ever or 0, stats.duration_max)
×
1925
    stats.duration_min_ever = min_ever
×
1926
    stats.duration_max_ever = max_ever
×
1927
    stats.duration_avg = stats.duration_tot / stats.steps
×
1928
    stats.step_start = nil
×
1929
    stats.time_end = gettime()
×
1930
    stats.time_tot = stats.time_end - stats.time_start
×
1931
    stats.time_avg = stats.time_tot / stats.steps
×
1932

1933
    stats.duration_avg = useconds(stats.duration_avg)
×
1934
    stats.duration_max = useconds(stats.duration_max)
×
1935
    stats.duration_max_ever = useconds(stats.duration_max_ever)
×
1936
    stats.duration_min = useconds(stats.duration_min)
×
1937
    stats.duration_min_ever = useconds(stats.duration_min_ever)
×
1938
    stats.duration_tot = useconds(stats.duration_tot)
×
1939
    stats.time_avg = useconds(stats.time_avg)
×
1940
    stats.time_start = mseconds(stats.time_start)
×
1941
    stats.time_end = mseconds(stats.time_end)
×
1942
    stats.time_tot = mseconds(stats.time_tot)
×
1943
    return stats
×
1944
  end
1945

1946
  _getstats = _getstats_plain
206✔
1947
end
1948

1949

1950
function copas.status(enable_stats)
206✔
1951
  local res = _getstats(enable_stats)
12✔
1952
  res.running = not not copas.running
12✔
1953
  res.timeout = copas.gettimeouts()
14✔
1954
  res.timer, res.inactive = _sleeping:status()
14✔
1955
  res.read = #_reading
12✔
1956
  res.write = #_writing
12✔
1957
  res.active = _resumable:count()
14✔
1958
  return res
12✔
1959
end
1960

1961

1962
-------------------------------------------------------------------------------
1963
-- Dispatcher endless loop.
1964
-- Listen to client requests and handles them forever
1965
-------------------------------------------------------------------------------
1966
function copas.loop(initializer, timeout)
239✔
1967
  if type(initializer) == "function" then
386✔
1968
    copas.addnamedthread("copas_initializer", initializer)
211✔
1969
  else
1970
    timeout = initializer or timeout
204✔
1971
  end
1972

1973
  resetexit()
386✔
1974
  copas.running = true
386✔
1975
  while true do
1976
    copas.step(timeout)
215,938✔
1977
    if copas.finished() then
276,357✔
1978
      if copas.exiting() then
863✔
1979
        break
185✔
1980
      end
1981
      copas.exit()
368✔
1982
    end
1983
  end
1984
  copas.running = false
374✔
1985
end
1986

1987

1988
-------------------------------------------------------------------------------
1989
-- Naming sockets and coroutines.
1990
-------------------------------------------------------------------------------
1991
do
1992
  local function realsocket(skt)
1993
    local mt = getmetatable(skt)
90✔
1994
    if mt == _skt_mt_tcp or mt == _skt_mt_udp then
90✔
1995
      return skt.socket
90✔
1996
    else
1997
      return skt
×
1998
    end
1999
  end
2000

2001

2002
  function copas.setsocketname(name, skt)
239✔
2003
    assert(type(name) == "string", "expected arg #1 to be a string")
90✔
2004
    skt = assert(realsocket(skt), "expected arg #2 to be a socket")
105✔
2005
    object_names[skt] = name
90✔
2006
  end
2007

2008

2009
  function copas.getsocketname(skt)
239✔
2010
    skt = assert(realsocket(skt), "expected arg #1 to be a socket")
×
2011
    return object_names[skt]
×
2012
  end
2013
end
2014

2015

2016
function copas.setthreadname(name, coro)
239✔
2017
  assert(type(name) == "string", "expected arg #1 to be a string")
60✔
2018
  coro = coro or coroutine_running()
60✔
2019
  assert(type(coro) == "thread", "expected arg #2 to be a coroutine or nil")
60✔
2020
  object_names[coro] = name
60✔
2021
end
2022

2023

2024
function copas.getthreadname(coro)
239✔
2025
  coro = coro or coroutine_running()
38✔
2026
  assert(type(coro) == "thread", "expected arg #1 to be a coroutine or nil")
38✔
2027
  return object_names[coro]
40✔
2028
end
2029

2030
-------------------------------------------------------------------------------
2031
-- Debug functionality.
2032
-------------------------------------------------------------------------------
2033
do
2034
  copas.debug = {}
206✔
2035

2036
  local log_core    -- if truthy, the core-timer will also be logged
2037
  local debug_log   -- function used as logger
2038

2039

2040
  local debug_yield = function(skt, queue)
2041
    local name = object_names[coroutine_running()]
4,935✔
2042

2043
    if log_core or name ~= "copas_core_timer" then
4,935✔
2044
      if queue == _sleeping then
4,913✔
2045
        debug_log("yielding '", name, "' to SLEEP for ", skt," seconds")
4,828✔
2046

2047
      elseif queue == _writing then
85✔
2048
        debug_log("yielding '", name, "' to WRITE on '", object_names[skt], "'")
14✔
2049

2050
      elseif queue == _reading then
73✔
2051
        debug_log("yielding '", name, "' to READ on '", object_names[skt], "'")
75✔
2052

2053
      else
2054
        debug_log("thread '", name, "' yielding to unexpected queue; ", tostring(queue), " (", type(queue), ")", debug.traceback())
×
2055
      end
2056
    end
2057

2058
    return coroutine.yield(skt, queue)
4,935✔
2059
  end
2060

2061

2062
  local debug_resume = function(coro, skt, ...)
2063
    local name = object_names[coro]
4,947✔
2064

2065
    if skt then
4,947✔
2066
      debug_log("resuming '", name, "' for socket '", object_names[skt], "'")
85✔
2067
    else
2068
      if log_core or name ~= "copas_core_timer" then
4,862✔
2069
        debug_log("resuming '", name, "'")
4,840✔
2070
      end
2071
    end
2072
    return coroutine.resume(coro, skt, ...)
4,947✔
2073
  end
2074

2075

2076
  local debug_create = function(f)
2077
    local f_wrapped = function(...)
2078
      local results = pack(f(...))
14✔
2079
      debug_log("exiting '", object_names[coroutine_running()], "'")
12✔
2080
      return unpack(results)
12✔
2081
    end
2082

2083
    return coroutine.create(f_wrapped)
12✔
2084
  end
2085

2086

2087
  debug_log = fnil
206✔
2088

2089

2090
  -- enables debug output for all coroutine operations.
2091
  function copas.debug.start(logger, core)
412✔
2092
    log_core = core
6✔
2093
    debug_log = logger or print
6✔
2094
    coroutine_yield = debug_yield
6✔
2095
    coroutine_resume = debug_resume
6✔
2096
    coroutine_create = debug_create
6✔
2097
  end
2098

2099

2100
  -- disables debug output for coroutine operations.
2101
  function copas.debug.stop()
412✔
2102
    debug_log = fnil
×
2103
    coroutine_yield = coroutine.yield
×
2104
    coroutine_resume = coroutine.resume
×
2105
    coroutine_create = coroutine.create
×
2106
  end
2107

2108
  do
2109
    local call_id = 0
206✔
2110

2111
    -- Description table of socket functions for debug output.
2112
    -- each socket function name has TWO entries;
2113
    -- 'name_in' and 'name_out', each being an array of names/descriptions of respectively
2114
    -- input parameters and return values.
2115
    -- If either table has a 'callback' key, then that is a function that will be called
2116
    -- with the parameters/return-values for further inspection.
2117
    local args = {
206✔
2118
      settimeout_in = {
206✔
2119
        "socket ",
173✔
2120
        "seconds",
173✔
2121
        "mode   ",
2122
      },
206✔
2123
      settimeout_out = {
206✔
2124
        "success",
173✔
2125
        "error  ",
2126
      },
206✔
2127
      connect_in = {
206✔
2128
        "socket ",
173✔
2129
        "address",
173✔
2130
        "port   ",
2131
      },
206✔
2132
      connect_out = {
206✔
2133
        "success",
173✔
2134
        "error  ",
2135
      },
206✔
2136
      getfd_in = {
206✔
2137
        "socket ",
2138
        -- callback = function(...)
2139
        --   print(debug.traceback("called from:", 4))
2140
        -- end,
2141
      },
206✔
2142
      getfd_out = {
206✔
2143
        "fd",
2144
      },
206✔
2145
      send_in = {
206✔
2146
        "socket   ",
173✔
2147
        "data     ",
173✔
2148
        "idx-start",
173✔
2149
        "idx-end  ",
2150
      },
206✔
2151
      send_out = {
206✔
2152
        "last-idx-send    ",
173✔
2153
        "error            ",
173✔
2154
        "err-last-idx-send",
2155
      },
206✔
2156
      receive_in = {
206✔
2157
        "socket ",
173✔
2158
        "pattern",
173✔
2159
        "prefix ",
2160
      },
206✔
2161
      receive_out = {
206✔
2162
        "received    ",
173✔
2163
        "error       ",
173✔
2164
        "partial data",
2165
      },
206✔
2166
      dirty_in = {
206✔
2167
        "socket",
2168
        -- callback = function(...)
2169
        --   print(debug.traceback("called from:", 4))
2170
        -- end,
2171
      },
206✔
2172
      dirty_out = {
206✔
2173
        "data in read-buffer",
2174
      },
206✔
2175
      close_in = {
206✔
2176
        "socket",
2177
        -- callback = function(...)
2178
        --   print(debug.traceback("called from:", 4))
2179
        -- end,
2180
      },
206✔
2181
      close_out = {
206✔
2182
        "success",
173✔
2183
        "error",
2184
      },
206✔
2185
    }
2186
    local function print_call(func, msg, ...)
2187
      print(msg)
638✔
2188
      local arg = pack(...)
638✔
2189
      local desc = args[func] or {}
638✔
2190
      for i = 1, math.max(arg.n, #desc) do
1,420✔
2191
        local value = arg[i]
782✔
2192
        if type(value) == "string" then
782✔
2193
          local xvalue = value:sub(1,30)
36✔
2194
          if xvalue ~= value then
36✔
2195
            xvalue = xvalue .."(...truncated)"
×
2196
          end
2197
          print("\t"..(desc[i] or i)..": '"..tostring(xvalue).."' ("..type(value).." #"..#value..")")
36✔
2198
        else
2199
          print("\t"..(desc[i] or i)..": '"..tostring(value).."' ("..type(value)..")")
746✔
2200
        end
2201
      end
2202
      if desc.callback then
638✔
2203
        desc.callback(...)
×
2204
      end
2205
    end
2206

2207
    local debug_mt = {
206✔
2208
      __index = function(self, key)
2209
        local value = self.__original_socket[key]
319✔
2210
        if type(value) ~= "function" then
319✔
2211
          return value
×
2212
        end
2213
        return function(self2, ...)
2214
            local my_id = call_id + 1
319✔
2215
            call_id = my_id
319✔
2216
            local results
2217

2218
            if self2 ~= self then
319✔
2219
              -- there is no self
2220
              print_call(tostring(key).."_in", my_id .. "-calling '"..tostring(key) .. "' with; ", self, ...)
×
2221
              results = pack(value(self, ...))
×
2222
            else
2223
              print_call(tostring(key).."_in", my_id .. "-calling '" .. tostring(key) .. "' with; ", self.__original_socket, ...)
319✔
2224
              results = pack(value(self.__original_socket, ...))
366✔
2225
            end
2226
            print_call(tostring(key).."_out", my_id .. "-results '"..tostring(key) .. "' returned; ", unpack(results))
366✔
2227
            return unpack(results)
319✔
2228
          end
2229
      end,
2230
      __tostring = function(self)
2231
        return tostring(self.__original_socket)
48✔
2232
      end
2233
    }
2234

2235

2236
    -- wraps a socket (copas or luasocket) in a debug version printing all calls
2237
    -- and their parameters/return values. Extremely noisy!
2238
    -- returns the wrapped socket.
2239
    -- NOTE: only for plain sockets, will not support TLS
2240
    function copas.debug.socket(original_skt)
412✔
2241
      if (getmetatable(original_skt) == _skt_mt_tcp) or (getmetatable(original_skt) == _skt_mt_udp) then
12✔
2242
        -- already wrapped as Copas socket, so recurse with the original luasocket one
2243
        original_skt.socket = copas.debug.socket(original_skt.socket)
×
2244
        return original_skt
×
2245
      end
2246

2247
      local proxy = setmetatable({
24✔
2248
        __original_socket = original_skt
12✔
2249
      }, debug_mt)
12✔
2250

2251
      return proxy
12✔
2252
    end
2253
  end
2254
end
2255

2256

2257
return copas
206✔
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