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

lunarmodules / copas / 30296820373

27 Jul 2026 07:06PM UTC coverage: 85.087%. Remained the same
30296820373

push

github

web-flow
Merge f0198211c into c0eb97fbe

55 of 74 new or added lines in 1 file covered. (74.32%)

161 existing lines in 1 file now uncovered.

1472 of 1730 relevant lines covered (85.09%)

55339.13 hits per line

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

80.0
/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
200✔
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
200✔
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
200✔
26
  if pcall(require, "socket") then
201✔
27
    -- found LuaSocket
28
    socket = require "socket"
194✔
29
  end
30

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

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

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

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

52

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

59

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

65

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

72

73
if socket then
200✔
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 = {
194✔
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)
194✔
93
    return function (...)
94
            return statusHandler(pcall(func, ...))
126✔
95
          end
96
  end
97

98
  function socket.newtry(finalizer)
194✔
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()
225✔
110
end
111

112

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

121
  copas = setmetatable({},{
400✔
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
  })
200✔
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"
200✔
138
copas._DESCRIPTION = "Coroutine Oriented Portable Asynchronous Services"
200✔
139
copas._VERSION     = "Copas 4.11.0"
200✔
140

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

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

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

150
-------------------------------------------------------------------------------
151
-- Object names, to track names of thread/coroutines and sockets
152
-------------------------------------------------------------------------------
153
local object_names = setmetatable({}, {
400✔
154
  __mode = "k",
168✔
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 = {}
600✔
173

174
  do  -- set implementation
175
    local reverse = {}
600✔
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)
600✔
180
      if not reverse[skt] then
2,514✔
181
        self[#self + 1] = skt
1,311✔
182
        reverse[skt] = #self
1,311✔
183
        return skt
1,311✔
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)
600✔
190
      local index = reverse[skt]
1,863✔
191
      if index then
1,863✔
192
        reverse[skt] = nil
1,305✔
193
        local top = self[#self]
1,305✔
194
        self[#self] = nil
1,305✔
195
        if top ~= skt then
1,305✔
196
          reverse[top] = index
172✔
197
          self[index] = top
172✔
198
        end
199
        return skt
1,305✔
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
600✔
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)
600✔
214
      if waiters[skt] then
1,215✔
215
        return nil, "Operation already in progress"
12✔
216
      end
217
      waiters[skt] = co
1,203✔
218
      return true
1,203✔
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)
600✔
224
      local co = waiters[skt]
1,203✔
225
      waiters[skt] = nil
1,203✔
226
      return co
1,203✔
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)
600✔
NEW
239
      waiters[skt] = nil
×
NEW
240
      self:remove(skt)
×
241
    end
242

243
  end
244

245
  return set
600✔
246
end
247

248

249

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

254
  function _resumable:push(co)
200✔
255
    resumelist[#resumelist + 1] = co
221,212✔
256
  end
257

258
  function _resumable:clear_resumelist()
200✔
259
    local lst = resumelist
211,760✔
260
    resumelist = {}
211,760✔
261
    return lst
211,760✔
262
  end
263

264
  function _resumable:done()
200✔
265
    return resumelist[1] == nil
216,145✔
266
  end
267

268
  function _resumable:count()
200✔
UNCOV
269
    return #resumelist + #_resumable
×
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
200✔
279

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

283

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

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

300
  -- find the thread that should wake up to the time, if any
301
  function _sleeping:pop(time)
200✔
302
    if time < (heap:peekValue() or math.huge) then
280,602✔
303
      return
211,760✔
304
    end
305
    return heap:pop()
8,003✔
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
200✔
311
    local t = heap:peekValue()
6,924✔
312
    if t then
6,924✔
313
      -- never report less than 0, because select() might block
314
      return math.max(t - gettime(), 0)
6,924✔
315
    end
316
  end
317

318
  function _sleeping:wakeup(co)
200✔
319
    if lethargy[co] then
3,384✔
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
77✔
325
      _resumable:push(co)
12✔
326
      return true
12✔
327
    end
328
    return nil, "not sleeping"
54✔
329
  end
330

331
  -- non-destructive check; unlike wakeup/cancel it doesn't remove 'co'
332
  function _sleeping:issleeping(co)
200✔
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)
200✔
340
    lethargy[co] = nil
66✔
341
    heap:remove(co)
66✔
342
  end
343

344
  function _sleeping:cancelall()
200✔
UNCOV
345
    while heap:size() > 0 do heap:pop() end
×
UNCOV
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)
200✔
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,157✔
357
           and not (tos > 0 and next(lethargy))
3,566✔
358
  end
359

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

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

368
end   -- _sleeping
369

370

371

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

376
local _servers = newsocketset() -- servers being handled
200✔
377
local _threads = setmetatable({}, {__mode = "k"})  -- registered threads added with addthread()
200✔
378
local _canceled = setmetatable({}, {__mode = "k"}) -- threads that are canceled and pending removal
200✔
379
local _autoclose = setmetatable({}, {__mode = "kv"}) -- sockets (value) to close when a thread (key) exits
200✔
380
local _autoclose_r = setmetatable({}, {__mode = "kv"}) -- reverse: sockets (key) to close when a thread (value) exits
200✔
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 = {}
200✔
387
local _writing_log = {}
200✔
388

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

391
local _reading = newsocketset() -- sockets currently being read
200✔
392
local _writing = newsocketset() -- sockets currently being written
200✔
393
local _isSocketTimeout = { -- set of errors indicating a socket-timeout
200✔
394
  ["timeout"] = true,      -- default LuaSocket timeout
168✔
395
  ["wantread"] = true,     -- LuaSec specific timeout
168✔
396
  ["wantwrite"] = true,    -- LuaSec specific timeout
168✔
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 = {
200✔
407
    __mode = "k",
168✔
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)
200✔
416
  user_timeouts_send = setmetatable({}, timeout_mt)
200✔
417
  user_timeouts_receive = setmetatable({}, timeout_mt)
200✔
418
end
419

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

422

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

426
  local socket_register = setmetatable({}, { __mode = "k" })    -- socket by coroutine
200✔
427
  local operation_register = setmetatable({}, { __mode = "k" }) -- operation "read"/"write" by coroutine
200✔
428
  local timeout_flags = setmetatable({}, { __mode = "k" })      -- true if timedout, by coroutine
200✔
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
UNCOV
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)
168✔
463
    local co = coroutine_running()
3,938,926✔
464
    socket_register[co] = skt
3,938,926✔
465
    operation_register[co] = queue
3,938,926✔
466
    timeout_flags[co] = nil
3,938,926✔
467
    if skt then
3,938,926✔
468
      local to = (use_connect_to and user_timeouts_connect[skt]) or
1,969,474✔
469
                 (queue == "read" and user_timeouts_receive[skt]) or
1,969,148✔
470
                 user_timeouts_send[skt]
15,219✔
471
      copas.timeout(to, socket_callback)
2,531,115✔
472
    else
473
      copas.timeout(0)
1,969,463✔
474
    end
475
    return true
3,938,926✔
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)
168✔
485
    operation_register[coroutine_running()] = queue
987✔
486
    return true
987✔
487
  end
488

489

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

495

496
  -- Returns the proper timeout error
497
  function sto_error(err)
168✔
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
200✔
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,215✔
522
  if not claimed then
1,215✔
523
    return nil, err
12✔
524
  end
525
  queue:insert(skt)
1,203✔
526
  coroutine_yield(skt, queue)
1,203✔
527
  return true
1,203✔
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
200✔
534
  local lookup = {
200✔
535
    tcp = "tcp",
168✔
536
    SSL = "ssl",
168✔
537
  }
538

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

544

545
function copas.close(skt, ...)
200✔
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)
200✔
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)
200✔
564

565
  if connect ~= nil and type(connect) ~= "number" then
456✔
UNCOV
566
    return nil, "connect timeout must be 'nil' or a number"
×
567
  end
568
  if connect then
456✔
569
    if connect < 0 then
456✔
UNCOV
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✔
UNCOV
577
    return nil, "send timeout must be 'nil' or a number"
×
578
  end
579
  if send then
456✔
580
    if send < 0 then
456✔
UNCOV
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✔
UNCOV
588
    return nil, "read timeout must be 'nil' or a number"
×
589
  end
590
  if read then
456✔
591
    if read < 0 then
456✔
UNCOV
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)
200✔
612
  local s, err
613
  pattern = pattern or "*l"
1,953,890✔
614
  local current_log = _reading_log
1,953,890✔
615
  sto_timeout(client, "read")
1,953,890✔
616

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

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

625
    if s then
1,954,493✔
626
      current_log[client] = nil
1,953,770✔
627
      sto_timeout()
1,953,770✔
628
      return s, err, part
1,953,770✔
629

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

635
    elseif sto_timed_out() then
796✔
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
609✔
NEW
643
      queue = _writing
×
NEW
644
      direction = "write"
×
UNCOV
645
      current_log = _writing_log
×
646
    else
647
      queue = _reading
609✔
648
      direction = "read"
609✔
649
      current_log = _reading_log
609✔
650
    end
651

652
    current_log[client] = gettime()
609✔
653
    sto_change_queue(direction)
609✔
654
    local ok, werr = wait_on(queue, client)
609✔
655
    if not ok then
609✔
656
      current_log[client] = nil
6✔
657
      sto_timeout()
6✔
658
      return nil, werr, part
6✔
659
    end
660
  until false
603✔
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)
200✔
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()
5✔
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✔
UNCOV
684
      _reading_log[client] = nil
×
UNCOV
685
      sto_timeout()
×
UNCOV
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✔
NEW
697
      _reading_log[client] = nil
×
NEW
698
      sto_timeout()
×
NEW
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)
200✔
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()
2✔
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✔
UNCOV
727
      current_log[client] = nil
×
UNCOV
728
      sto_timeout()
×
UNCOV
729
      return s, err, part
×
730

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

737
    local queue, direction
738
    if err == "wantwrite" then
6✔
NEW
739
      queue = _writing
×
NEW
740
      direction = "write"
×
UNCOV
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✔
NEW
752
      current_log[client] = nil
×
NEW
753
      sto_timeout()
×
NEW
754
      return nil, werr, part
×
755
    end
756
  until false
6✔
757
end
758
copas.receivePartial = copas.receivepartial  -- compat: receivePartial is deprecated
200✔
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)
200✔
764
  local s, err
765
  from = from or 1
15,219✔
766
  local lastIndex = from - 1
15,219✔
767
  local current_log = _writing_log
15,219✔
768
  sto_timeout(client, "write")
15,219✔
769

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

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

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

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

788
    elseif sto_timed_out() then
216✔
UNCOV
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
186✔
NEW
796
      queue = _reading
×
NEW
797
      direction = "read"
×
UNCOV
798
      current_log = _reading_log
×
799
    else
800
      queue = _writing
186✔
801
      direction = "write"
186✔
802
      current_log = _writing_log
186✔
803
    end
804

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

816
function copas.sendto(client, data, ip, port)
200✔
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)
200✔
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✔
UNCOV
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✔
NEW
855
      _writing_log[skt] = nil
×
NEW
856
      sto_timeout()
×
NEW
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
224✔
869
  if not wrap_params then
108✔
UNCOV
870
    error("cannot wrap socket into a secure socket (using 'ssl.wrap()') without parameters/context")
×
871
  end
872

873
  ssl = ssl or require("ssl")
108✔
874
  local nskt = assert(ssl.wrap(skt, wrap_params)) -- assert, because we do not want to silently ignore this one!!
126✔
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)
354✔
908
  local r = setmetatable({}, {
708✔
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
354✔
916
    r.wrap = false
240✔
917
    r.sni = false
240✔
918

919
  elseif t == "table" then
114✔
920
    if sslt.mode or sslt.protocol then
114✔
921
      -- has the mandatory fields for the ssl-params table for handshake
922
      -- backward compatibility
923
      r.wrap = sslt
24✔
924
      r.sni = false
24✔
925
    else
926
      -- has the target definition, copy our known keys
927
      r.wrap = sslt.wrap or false -- 'or false' because we do not want nils
90✔
928
      r.sni = sslt.sni or false -- 'or false' because we do not want nils
90✔
929
    end
930

UNCOV
931
  elseif t == "userdata" then
×
932
    -- it's an ssl-context object for the handshake
933
    -- backward compatibility
UNCOV
934
    r.wrap = sslt
×
UNCOV
935
    r.sni = false
×
936

937
  else
UNCOV
938
    error("ssl parameters; did not expect type "..tostring(sslt))
×
939
  end
940

941
  return r
354✔
942
end
943

944

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

960
  local nskt = ssl_wrap(skt, wrap_params)
108✔
961

962
  sto_timeout(nskt, "write", true)
108✔
963
  local queue
964

965
  repeat
966
    local success, err = nskt:dohandshake()
294✔
967

968
    if success then
294✔
969
      sto_timeout()
96✔
970
      return nskt
96✔
971

972
    elseif not _isSocketTimeout[err] then
198✔
973
      sto_timeout()
12✔
974
      error("TLS/SSL handshake failed: " .. tostring(err))
12✔
975

976
    elseif sto_timed_out() then
217✔
UNCOV
977
      sto_timeout()
×
UNCOV
978
      return nil, sto_error(err)
×
979

980
    elseif err == "wantwrite" then
186✔
UNCOV
981
      sto_change_queue("write")
×
UNCOV
982
      queue = _writing
×
983

984
    elseif err == "wantread" then
186✔
985
      sto_change_queue("read")
186✔
986
      queue = _reading
186✔
987

988
    else
UNCOV
989
      error("TLS/SSL handshake failed: " .. tostring(err))
×
990
    end
991

992
    local ok, werr = wait_on(queue, nskt)
186✔
993
    if not ok then
186✔
NEW
994
      sto_timeout()
×
NEW
995
      error("TLS/SSL handshake failed: " .. tostring(werr))
×
996
    end
997
  until false
186✔
998
end
999

1000
-- flushes a client write buffer (deprecated)
1001
function copas.flush()
200✔
1002
end
1003

1004
-- wraps a TCP socket to use Copas methods (send, receive, flush and settimeout)
1005
local _skt_mt_tcp = {
200✔
1006
      __tostring = function(self)
1007
        return tostring(self.socket).." (copas wrapped)"
18✔
1008
      end,
1009

1010
      __index = {
200✔
1011
        send = function (self, data, from, to)
1012
          return copas.send (self.socket, data, from, to)
15,213✔
1013
        end,
1014

1015
        receive = function (self, pattern, prefix)
1016
          if user_timeouts_receive[self.socket] == 0 then
1,953,867✔
1017
            return copas.receivepartial(self.socket, pattern, prefix)
12✔
1018
          end
1019
          return copas.receive(self.socket, pattern, prefix)
1,953,854✔
1020
        end,
1021

1022
        receivepartial = function (self, pattern, prefix)
UNCOV
1023
          return copas.receivepartial(self.socket, pattern, prefix)
×
1024
        end,
1025

1026
        flush = function (self)
UNCOV
1027
          return copas.flush(self.socket)
×
1028
        end,
1029

1030
        settimeout = function (self, time)
1031
          return copas.settimeout(self.socket, time)
204✔
1032
        end,
1033

1034
        settimeouts = function (self, connect, send, receive)
UNCOV
1035
          return copas.settimeouts(self.socket, connect, send, receive)
×
1036
        end,
1037

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

1049
        close = function(self, ...)
1050
          return copas.close(self.socket, ...)
222✔
1051
        end,
1052

1053
        -- TODO: socket.bind is a shortcut, and must be provided with an alternative
1054
        bind = function(self, ...) return self.socket:bind(...) end,
200✔
1055

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

1066
        getstats = function(self, ...) return self.socket:getstats(...) end,
200✔
1067

1068
        setstats = function(self, ...) return self.socket:setstats(...) end,
200✔
1069

1070
        listen = function(self, ...) return self.socket:listen(...) end,
200✔
1071

1072
        accept = function(self, ...) return self.socket:accept(...) end,
200✔
1073

1074
        setoption = function(self, ...)
UNCOV
1075
          local ok, res, err = pcall(self.socket.setoption, self.socket, ...)
×
UNCOV
1076
          if ok then
×
UNCOV
1077
            return res, err
×
1078
          else
UNCOV
1079
            return nil, "not implemented by LuaSec"
×
1080
          end
1081
        end,
1082

1083
        getoption = function(self, ...)
UNCOV
1084
          local ok, val, err = pcall(self.socket.getoption, self.socket, ...)
×
UNCOV
1085
          if ok then
×
UNCOV
1086
            return val, err
×
1087
          else
UNCOV
1088
            return nil, "not implemented by LuaSec"
×
1089
          end
1090
        end,
1091

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

1102
        shutdown = function(self, ...) return self.socket:shutdown(...) end,
200✔
1103

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

1114
        dohandshake = function(self, wrap_params)
1115
          local nskt, err = copas.dohandshake(self.socket, wrap_params or self.ssl_params.wrap)
108✔
1116
          if not nskt then return nskt, err end
96✔
1117
          self.socket = nskt  -- replace internal socket with the newly wrapped ssl one
96✔
1118
          return self
96✔
1119
        end,
1120

1121
        getalpn = function(self, ...)
UNCOV
1122
          local ok, proto, err = pcall(self.socket.getalpn, self.socket, ...)
×
UNCOV
1123
          if ok then
×
UNCOV
1124
            return proto, err
×
1125
          else
UNCOV
1126
            return nil, "not a tls socket"
×
1127
          end
1128
        end,
1129

1130
        getsniname = function(self, ...)
UNCOV
1131
          local ok, name, err = pcall(self.socket.getsniname, self.socket, ...)
×
UNCOV
1132
          if ok then
×
1133
            return name, err
×
1134
          else
1135
            return nil, "not a tls socket"
×
1136
          end
1137
        end,
1138
      }
200✔
1139
}
1140

1141
-- wraps a UDP socket, copy of TCP one adapted for UDP.
1142
local _skt_mt_udp = {__index = { }}
200✔
1143
for k,v in pairs(_skt_mt_tcp) do _skt_mt_udp[k] = _skt_mt_udp[k] or v end
600✔
1144
for k,v in pairs(_skt_mt_tcp.__index) do _skt_mt_udp.__index[k] = v end
4,600✔
1145

1146
_skt_mt_udp.__index.send        = function(self, ...) return self.socket:send(...) end
206✔
1147

1148
_skt_mt_udp.__index.sendto      = function(self, ...) return self.socket:sendto(...) end
224✔
1149

1150

1151
_skt_mt_udp.__index.receive =     function (self, size)
200✔
1152
                                    return copas.receive (self.socket, (size or UDP_DATAGRAM_MAX))
18✔
1153
                                  end
1154

1155
_skt_mt_udp.__index.receivefrom = function (self, size)
200✔
1156
                                    return copas.receivefrom (self.socket, (size or UDP_DATAGRAM_MAX))
24✔
1157
                                  end
1158

1159
                                  -- TODO: is this DNS related? hence blocking?
1160
_skt_mt_udp.__index.setpeername = function(self, ...) return self.socket:setpeername(...) end
206✔
1161

1162
_skt_mt_udp.__index.setsockname = function(self, ...) return self.socket:setsockname(...) end
200✔
1163

1164
                                    -- do not close client, as it is also the server for udp.
1165
_skt_mt_udp.__index.close       = function(self, ...) return true end
212✔
1166

1167
_skt_mt_udp.__index.settimeouts = function (self, connect, send, receive)
200✔
UNCOV
1168
                                    return copas.settimeouts(self.socket, connect, send, receive)
×
1169
                                  end
1170

1171

1172

1173
---
1174
-- Wraps a LuaSocket socket object in an async Copas based socket object.
1175
-- @param skt The socket to wrap
1176
-- @sslt (optional) Table with ssl parameters, use an empty table to use ssl with defaults
1177
-- @return wrapped socket object
1178
function copas.wrap (skt, sslt)
200✔
1179
  if (getmetatable(skt) == _skt_mt_tcp) or (getmetatable(skt) == _skt_mt_udp) then
390✔
UNCOV
1180
    return skt -- already wrapped
×
1181
  end
1182

1183
  skt:settimeout(0)
392✔
1184

1185
  if isTCP(skt) then
455✔
1186
    return setmetatable ({socket = skt, ssl_params = normalize_sslt(sslt)}, _skt_mt_tcp)
413✔
1187
  else
1188
    return setmetatable ({socket = skt}, _skt_mt_udp)
36✔
1189
  end
1190
end
1191

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

1205

1206
--------------------------------------------------
1207
-- Error handling
1208
--------------------------------------------------
1209

1210
local _errhandlers = setmetatable({}, { __mode = "k" })   -- error handler per coroutine
200✔
1211

1212

1213
function copas.gettraceback(msg, co, skt)
200✔
1214
  local co_str = co == nil and "nil" or copas.getthreadname(co)
38✔
1215
  local skt_str = skt == nil and "nil" or copas.getsocketname(skt)
38✔
1216
  local msg_str = msg == nil and "" or tostring(msg)
38✔
1217
  if msg_str == "" then
38✔
UNCOV
1218
    msg_str = ("(coroutine: %s, socket: %s)"):format(msg_str, co_str, skt_str)
×
1219
  else
1220
    msg_str = ("%s (coroutine: %s, socket: %s)"):format(msg_str, co_str, skt_str)
38✔
1221
  end
1222

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

1232

1233
local function _deferror(msg, co, skt)
1234
  print(copas.gettraceback(msg, co, skt))
29✔
1235
end
1236

1237

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

1249

1250
function copas.geterrorhandler(co)
200✔
1251
  co = co or coroutine_running()
12✔
1252
  return _errhandlers[co] or _deferror
12✔
1253
end
1254

1255

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

1263
-------------------------------------------------------------------------------
1264
-- Thread handling
1265
-------------------------------------------------------------------------------
1266

1267
local function _doTick (co, skt, ...)
1268
  if not co then return end
228,099✔
1269

1270
  -- if a coroutine was canceled/removed, don't resume it
1271
  if _canceled[co] then
228,099✔
1272
    _canceled[co] = nil -- also clean up the registry
18✔
1273
    _threads[co] = nil
18✔
1274
    return
18✔
1275
  end
1276

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

1287
  if new_q == _reading or new_q == _writing then
228,069✔
1288
    -- we're yielding to wait on a socket; the claim was already taken by
1289
    -- the coroutine itself before it yielded (see wait_on below), so by
1290
    -- construction this can't fail here
1291
    new_q:insert (res)
1,203✔
1292
    return
1,203✔
1293
  elseif new_q == _sleeping then
226,866✔
1294
    -- we're yielding to sleep
1295
    new_q:insert (res)
221,398✔
1296
    new_q:push (res, co)
221,398✔
1297
    return
221,398✔
1298
  end
1299

1300
  -- coroutine is terminating
1301

1302
  if ok and coroutine_status(co) ~= "dead" then
5,468✔
1303
    -- it called coroutine.yield from a non-Copas function which is unexpected
1304
    ok = false
6✔
1305
    res = "coroutine.yield was called without a resume first, user-code cannot yield to Copas"
6✔
1306
  end
1307

1308
  if not ok then
5,468✔
1309
    local k, e = pcall(_errhandlers[co] or _deferror, res, co, skt)
46✔
1310
    if not k then
46✔
UNCOV
1311
      print("Failed executing error handler: " .. tostring(e))
×
1312
    end
1313
  end
1314

1315
  local skt_to_close = _autoclose[co]
5,468✔
1316
  if skt_to_close then
5,468✔
1317
    skt_to_close:close()
132✔
1318
    _autoclose[co] = nil
132✔
1319
    _autoclose_r[skt_to_close] = nil
132✔
1320
  end
1321

1322
  _errhandlers[co] = nil
5,468✔
1323
end
1324

1325

1326
local _accept do
200✔
1327
  local client_counters = setmetatable({}, { __mode = "k" })
200✔
1328

1329
  -- accepts a connection on socket input
1330
  function _accept(server_skt, handler)
168✔
1331
    local client_skt = server_skt:accept()
138✔
1332
    if client_skt then
138✔
1333
      local count = (client_counters[server_skt] or 0) + 1
138✔
1334
      client_counters[server_skt] = count
138✔
1335
      object_names[client_skt] = object_names[server_skt] .. ":client_" .. count
154✔
1336

1337
      client_skt:settimeout(0)
138✔
1338
      copas.settimeouts(client_skt, user_timeouts_connect[server_skt],  -- copy server socket timeout settings
276✔
1339
        user_timeouts_send[server_skt], user_timeouts_receive[server_skt])
154✔
1340

1341
      local co = coroutine_create(handler)
138✔
1342
      object_names[co] = object_names[server_skt] .. ":handler_" .. count
138✔
1343

1344
      if copas.autoclose then
138✔
1345
        _autoclose[co] = client_skt
138✔
1346
        _autoclose_r[client_skt] = co
138✔
1347
      end
1348

1349
      _doTick(co, client_skt)
138✔
1350
    end
1351
  end
1352
end
1353

1354
-------------------------------------------------------------------------------
1355
-- Adds a server/handler pair to Copas dispatcher
1356
-------------------------------------------------------------------------------
1357

1358
do
1359
  local function addTCPserver(server, handler, timeout, name)
1360
    server:settimeout(0)
108✔
1361
    if name then
108✔
UNCOV
1362
      object_names[server] = name
×
1363
    end
1364
    _servers[server] = handler
108✔
1365
    _reading:insert(server)
108✔
1366
    if timeout then
108✔
1367
      copas.settimeout(server, timeout)
18✔
1368
    end
1369
  end
1370

1371
  local function addUDPserver(server, handler, timeout, name)
UNCOV
1372
    server:settimeout(0)
×
UNCOV
1373
    local co = coroutine_create(handler)
×
UNCOV
1374
    if name then
×
UNCOV
1375
      object_names[server] = name
×
1376
    end
UNCOV
1377
    object_names[co] = object_names[server]..":handler"
×
UNCOV
1378
    _reading:insert(server)
×
UNCOV
1379
    if timeout then
×
UNCOV
1380
      copas.settimeout(server, timeout)
×
1381
    end
UNCOV
1382
    _doTick(co, server)
×
1383
  end
1384

1385

1386
  function copas.addserver(server, handler, timeout, name)
200✔
1387
    if isTCP(server) then
126✔
1388
      addTCPserver(server, handler, timeout, name)
126✔
1389
    else
UNCOV
1390
      addUDPserver(server, handler, timeout, name)
×
1391
    end
1392
  end
1393
end
1394

1395

1396
function copas.removeserver(server, keep_open)
200✔
1397
  local skt = server
102✔
1398
  local mt = getmetatable(server)
102✔
1399
  if mt == _skt_mt_tcp or mt == _skt_mt_udp then
102✔
UNCOV
1400
    skt = server.socket
×
1401
  end
1402

1403
  _servers:remove(skt)
102✔
1404
  _reading:remove(skt)
102✔
1405

1406
  if keep_open then
102✔
1407
    return true
18✔
1408
  end
1409
  return server:close()
84✔
1410
end
1411

1412

1413

1414
-------------------------------------------------------------------------------
1415
-- Adds an new coroutine thread to Copas dispatcher
1416
-------------------------------------------------------------------------------
1417
function copas.addnamedthread(name, handler, ...)
200✔
1418
  if type(name) == "function" and type(handler) == "string" then
5,638✔
1419
    -- old call, flip args for compatibility
UNCOV
1420
    name, handler = handler, name
×
1421
  end
1422

1423
  -- create a coroutine that skips the first argument, which is always the socket
1424
  -- passed by the scheduler, but `nil` in case of a task/thread
1425
  local thread = coroutine_create(function(_, ...)
11,276✔
1426
    copas.pause()
5,638✔
1427
    return handler(...)
5,614✔
1428
  end)
1429
  if name then
5,638✔
1430
    object_names[thread] = name
532✔
1431
  end
1432

1433
  _threads[thread] = true -- register this thread so it can be removed
5,638✔
1434
  _doTick (thread, nil, ...)
5,638✔
1435
  return thread
5,638✔
1436
end
1437

1438

1439
function copas.addthread(handler, ...)
200✔
1440
  return copas.addnamedthread(nil, handler, ...)
5,040✔
1441
end
1442

1443

1444
function copas.removethread(thread)
200✔
1445
  -- if the specified coroutine is registered, add it to the canceled table so
1446
  -- that next time it tries to resume it exits.
1447
  _canceled[thread] = _threads[thread or 0]
66✔
1448
  _sleeping:cancel(thread)
66✔
1449
end
1450

1451

1452

1453
-------------------------------------------------------------------------------
1454
-- Sleep/pause management functions
1455
-------------------------------------------------------------------------------
1456

1457
-- yields the current coroutine and wakes it after 'sleeptime' seconds.
1458
-- If sleeptime < 0 then it sleeps until explicitly woken up using 'wakeup'
1459
-- TODO: deprecated, remove in next major
1460
function copas.sleep(sleeptime)
200✔
1461
  coroutine_yield((sleeptime or 0), _sleeping)
×
1462
end
1463

1464

1465
-- yields the current coroutine and wakes it after 'sleeptime' seconds.
1466
-- if sleeptime < 0 then it sleeps 0 seconds.
1467
function copas.pause(sleeptime)
200✔
1468
  local s = gettime()
218,020✔
1469
  if sleeptime and sleeptime > 0 then
218,020✔
1470
    coroutine_yield(sleeptime, _sleeping)
9,557✔
1471
  else
1472
    coroutine_yield(0, _sleeping)
209,795✔
1473
  end
1474
  return gettime() - s
217,784✔
1475
end
1476

1477

1478
-- yields the current coroutine until explicitly woken up using 'wakeup'
1479
function copas.pauseforever()
200✔
1480
  local s = gettime()
3,378✔
1481
  coroutine_yield(-1, _sleeping)
3,378✔
1482
  return gettime() - s
3,318✔
1483
end
1484

1485

1486
-- Wakes up a sleeping coroutine 'co'.
1487
-- @return true on success, or nil+"not sleeping" if 'co' wasn't sleeping
1488
-- (eg. it was already woken up, finished, or canceled through `copas.removethread`).
1489
function copas.wakeup(co)
200✔
1490
  return _sleeping:wakeup(co)
3,384✔
1491
end
1492

1493

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

1502

1503

1504
-------------------------------------------------------------------------------
1505
-- Timeout management
1506
-------------------------------------------------------------------------------
1507

1508
do
1509
  local timeout_register = setmetatable({}, { __mode = "k" })
200✔
1510
  local timerwheel = require("timerwheel").new({
401✔
1511
      now = gettime,
200✔
1512
      precision = TIMEOUT_PRECISION,
200✔
1513
      ringsize = math.floor(60*60*24/TIMEOUT_PRECISION),  -- ring size 1 day
200✔
1514
      err_handler = function(err)
1515
        return _deferror(err, core_timer_thread)
16✔
1516
      end,
1517
    })
1518

1519
  core_timer_thread = copas.addnamedthread("copas_core_timer", function()
400✔
1520
    while true do
1521
      copas.pause(TIMEOUT_PRECISION)
7,096✔
1522
      timerwheel:step()
8,049✔
1523
    end
1524
  end)
1525

1526
  -- get the number of timeouts running
1527
  function copas.gettimeouts()
200✔
1528
    return timerwheel:count()
3,566✔
1529
  end
1530

1531
  --- Sets the timeout for the current coroutine.
1532
  -- @param delay delay (seconds), use 0 (or math.huge) to cancel the timerout
1533
  -- @param callback function with signature: `function(coroutine)` where coroutine is the routine that timed-out
1534
  -- @return true
1535
  function copas.timeout(delay, callback)
200✔
1536
    local co = coroutine_running()
3,944,066✔
1537
    local existing_timer = timeout_register[co]
3,944,066✔
1538

1539
    if existing_timer then
3,944,066✔
1540
      timerwheel:cancel(existing_timer)
4,506✔
1541
    end
1542

1543
    if delay > 0 and delay ~= math.huge then
3,944,066✔
1544
      timeout_register[co] = timerwheel:set(delay, callback, co)
7,107✔
1545
    elseif delay == 0 or delay == math.huge then
3,937,972✔
1546
      timeout_register[co] = nil
3,937,972✔
1547
    else
UNCOV
1548
      error("timout value must be greater than or equal to 0, got: "..tostring(delay))
×
1549
    end
1550

1551
    return true
3,944,066✔
1552
  end
1553

1554
end
1555

1556

1557
-------------------------------------------------------------------------------
1558
-- main tasks: manage readable and writable socket sets
1559
-------------------------------------------------------------------------------
1560
-- a task is an object with a required method `step()` that deals with a
1561
-- single step for that task.
1562

1563
local _tasks = {} do
200✔
1564
  function _tasks:add(tsk)
200✔
1565
    _tasks[#_tasks + 1] = tsk
800✔
1566
  end
1567
end
1568

1569

1570
-- a task to check ready to read events
1571
local _readable_task = {} do
200✔
1572

1573
  _readable_task._events = {}
200✔
1574

1575
  local function tick(skt)
1576
    local handler = _servers[skt]
885✔
1577
    if handler then
885✔
1578
      _accept(skt, handler)
161✔
1579
    else
1580
      _reading:remove(skt)
747✔
1581
      _doTick(_reading:release(skt), skt)
880✔
1582
    end
1583
  end
1584

1585
  function _readable_task:step()
200✔
1586
    for _, skt in ipairs(self._events) do
212,649✔
1587
      tick(skt)
885✔
1588
    end
1589
  end
1590

1591
  _tasks:add(_readable_task)
232✔
1592
end
1593

1594

1595
-- a task to check ready to write events
1596
local _writable_task = {} do
200✔
1597

1598
  _writable_task._events = {}
200✔
1599

1600
  local function tick(skt)
1601
    _writing:remove(skt)
372✔
1602
    _doTick(_writing:release(skt), skt)
433✔
1603
  end
1604

1605
  function _writable_task:step()
200✔
1606
    for _, skt in ipairs(self._events) do
212,132✔
1607
      tick(skt)
372✔
1608
    end
1609
  end
1610

1611
  _tasks:add(_writable_task)
232✔
1612
end
1613

1614

1615

1616
-- sleeping threads task
1617
local _sleeping_task = {} do
200✔
1618

1619
  function _sleeping_task:step()
200✔
1620
    local now = gettime()
211,760✔
1621

1622
    local co = _sleeping:pop(now)
211,760✔
1623
    while co do
219,763✔
1624
      -- we're pushing them to _resumable, since that list will be replaced before
1625
      -- executing. This prevents tasks running twice in a row with pause(0) for example.
1626
      -- So here we won't execute, but at _resumable step which is next
1627
      _resumable:push(co)
8,003✔
1628
      co = _sleeping:pop(now)
9,334✔
1629
    end
1630
  end
1631

1632
  _tasks:add(_sleeping_task)
200✔
1633
end
1634

1635

1636

1637
-- resumable threads task
1638
local _resumable_task = {} do
200✔
1639

1640
  function _resumable_task:step()
200✔
1641
    -- replace the resume list before iterating, so items placed in there
1642
    -- will indeed end up in the next copas step, not in this one, and not
1643
    -- create a loop
1644
    local resumelist = _resumable:clear_resumelist()
211,760✔
1645

1646
    for _, co in ipairs(resumelist) do
432,956✔
1647
      _doTick(co)
221,204✔
1648
    end
1649
  end
1650

1651
  _tasks:add(_resumable_task)
200✔
1652
end
1653

1654

1655
-------------------------------------------------------------------------------
1656
-- Checks for reads and writes on sockets
1657
-------------------------------------------------------------------------------
1658
local _select_plain do
200✔
1659

1660
  local last_cleansing = 0
200✔
1661
  local duration = function(t2, t1) return t2-t1 end
211,874✔
1662

1663
  if not socket then
200✔
1664
    -- socket module unavailable, switch to luasystem sleep
1665
    _select_plain = block_sleep
6✔
1666
  else
1667
    -- use socket.select to handle socket-io
1668
    _select_plain = function(timeout)
1669
      local err
1670
      local now = gettime()
211,674✔
1671

1672
      -- remove any closed sockets to prevent select from hanging on them
1673
      if _closed[1] then
211,674✔
1674
        for i, skt in ipairs(_closed) do
452✔
1675
          _closed[i] = { _reading:remove(skt), _writing:remove(skt) }
304✔
1676
        end
1677
      end
1678

1679
      _readable_task._events, _writable_task._events, err = socket.select(_reading, _writing, timeout)
211,674✔
1680
      local r_events, w_events = _readable_task._events, _writable_task._events
211,674✔
1681

1682
      -- inject closed sockets in readable/writeable task so they can error out properly
1683
      if _closed[1] then
211,674✔
1684
        for i, skts in ipairs(_closed) do
452✔
1685
          _closed[i] = nil
228✔
1686
          r_events[#r_events+1] = skts[1]
228✔
1687
          w_events[#w_events+1] = skts[2]
228✔
1688
        end
1689
      end
1690

1691
      if duration(now, last_cleansing) > WATCH_DOG_TIMEOUT then
271,167✔
1692
        last_cleansing = now
188✔
1693

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

1707
        -- Do the same for writing
1708
        for skt,time in pairs(_writing_log) do
188✔
UNCOV
1709
          if not w_events[skt] and duration(now, time) > WATCH_DOG_TIMEOUT then
×
UNCOV
1710
            _writing_log[skt] = nil
×
UNCOV
1711
            w_events[#w_events + 1] = skt
×
UNCOV
1712
            w_events[skt] = #w_events
×
1713
          end
1714
        end
1715
      end
1716

1717
      if err == "timeout" and #r_events + #w_events > 0 then
211,674✔
1718
        return nil
6✔
1719
      else
1720
        return err
211,668✔
1721
      end
1722
    end
1723
  end
1724
end
1725

1726

1727

1728
-------------------------------------------------------------------------------
1729
-- Dispatcher loop step.
1730
-- Listen to client requests and handles them
1731
-- Returns false if no socket-data was handled, or true if there was data
1732
-- handled (or nil + error message)
1733
-------------------------------------------------------------------------------
1734

1735
local copas_stats
1736
local min_ever, max_ever
1737

1738
local _select = _select_plain
200✔
1739

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

UNCOV
1757
  local err = _select_plain(timeout)
×
1758

UNCOV
1759
  local now = gettime()
×
UNCOV
1760
  copas_stats.time_start = copas_stats.time_start or now
×
UNCOV
1761
  copas_stats.step_start = now
×
1762

UNCOV
1763
  return err
×
1764
end
1765

1766

1767
function copas.step(timeout)
200✔
1768
  -- Need to wake up the select call in time for the next sleeping event
1769
  if not _resumable:done() then
271,272✔
1770
    timeout = 0
204,840✔
1771
  else
1772
    timeout = math.min(_sleeping:getnext(), timeout or math.huge)
8,080✔
1773
  end
1774

1775
  local err = _select(timeout)
211,764✔
1776

1777
  for _, tsk in ipairs(_tasks) do
1,058,800✔
1778
    tsk:step()
847,048✔
1779
  end
1780

1781
  if err then
211,752✔
1782
    if err == "timeout" then
210,657✔
1783
      if timeout + 0.01 > TIMEOUT_PRECISION and math.random(100) > 90 then
210,567✔
1784
        -- we were idle, so occasionally do a GC sweep to ensure lingering
1785
        -- sockets are closed, and we don't accidentally block the loop from
1786
        -- exiting
1787
        collectgarbage()
488✔
1788
      end
1789
      return false
210,567✔
1790
    end
1791
    return nil, err
90✔
1792
  end
1793

1794
  return true
1,095✔
1795
end
1796

1797

1798
-------------------------------------------------------------------------------
1799
-- Check whether there is something to do.
1800
-- returns false if there are no sockets for read/write nor tasks scheduled
1801
-- (which means Copas is in an empty spin)
1802
-------------------------------------------------------------------------------
1803
function copas.finished()
200✔
1804
  return #_reading == 0 and #_writing == 0 and _resumable:done() and _sleeping:done(copas.gettimeouts())
213,658✔
1805
end
1806

1807

1808
local resetexit do
200✔
1809
  local exit_semaphore, exiting
1810

1811
  function resetexit()
168✔
1812
    exit_semaphore = copas.semaphore.new(1, 0, math.huge)
471✔
1813
    exiting = false
380✔
1814
  end
1815

1816
  -- Signals tasks to exit. But only if they check for it. By calling `copas.exiting`
1817
  -- they can check if they should exit. Or by calling `copas.waitforexit` they can
1818
  -- wait until the exit signal is given.
1819
  function copas.exit()
200✔
1820
    if exiting then return end
368✔
1821
    exiting = true
368✔
1822
    exit_semaphore:destroy()
368✔
1823
  end
1824

1825
  -- returns whether Copas is in the process of exiting. Exit can be started by
1826
  -- calling `copas.exit()`.
1827
  function copas.exiting()
232✔
1828
    return exiting
730✔
1829
  end
1830

1831
  -- Pauses the current coroutine until Copas is exiting. To be used as an exit
1832
  -- signal for tasks that need to clean up before exiting.
1833
  function copas.waitforexit()
232✔
1834
    exit_semaphore:take(1)
12✔
1835
  end
1836
end
1837

1838

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

1848
  -- 2. drain sleeping heap
UNCOV
1849
  _sleeping:cancelall()
×
1850

1851
  -- 3. close and drain reading sockets
UNCOV
1852
  while _reading[1] do
×
UNCOV
1853
    copas.close(_reading[1])
×
UNCOV
1854
    _reading:remove(_reading[1])
×
1855
  end
1856

1857
  -- 4. close and drain writing sockets
UNCOV
1858
  while _writing[1] do
×
UNCOV
1859
    copas.close(_writing[1])
×
UNCOV
1860
    _writing:remove(_writing[1])
×
1861
  end
1862

1863
  -- 5. remove all servers
UNCOV
1864
  while _servers[1] do
×
UNCOV
1865
    copas.removeserver(_servers[1])
×
1866
  end
1867

1868
  -- 6. clear non-weak ancillary tables
UNCOV
1869
  _closed = {}
×
UNCOV
1870
  _reading_log = {}
×
UNCOV
1871
  _writing_log = {}
×
1872

1873
  -- 7. signal exit
UNCOV
1874
  copas.exit()
×
1875
end
1876

1877

1878
local _getstats do
200✔
1879
  local _getstats_instrumented, _getstats_plain
1880

1881

1882
  function _getstats_plain(enable)
168✔
1883
    -- this function gets hit if turned off, so turn on if true
UNCOV
1884
    if enable == true then
×
UNCOV
1885
      _select = _select_instrumented
×
UNCOV
1886
      _getstats = _getstats_instrumented
×
1887
      -- reset stats
UNCOV
1888
      min_ever = nil
×
UNCOV
1889
      max_ever = nil
×
UNCOV
1890
      copas_stats = nil
×
1891
    end
UNCOV
1892
    return {}
×
1893
  end
1894

1895

1896
  -- convert from seconds to millisecs, with microsec precision
1897
  local function useconds(t)
UNCOV
1898
    return math.floor((t * 1000000) + 0.5) / 1000
×
1899
  end
1900
  -- convert from seconds to seconds, with millisec precision
1901
  local function mseconds(t)
UNCOV
1902
    return math.floor((t * 1000) + 0.5) / 1000
×
1903
  end
1904

1905

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

UNCOV
1928
    stats.duration_avg = useconds(stats.duration_avg)
×
UNCOV
1929
    stats.duration_max = useconds(stats.duration_max)
×
1930
    stats.duration_max_ever = useconds(stats.duration_max_ever)
×
UNCOV
1931
    stats.duration_min = useconds(stats.duration_min)
×
UNCOV
1932
    stats.duration_min_ever = useconds(stats.duration_min_ever)
×
1933
    stats.duration_tot = useconds(stats.duration_tot)
×
1934
    stats.time_avg = useconds(stats.time_avg)
×
1935
    stats.time_start = mseconds(stats.time_start)
×
UNCOV
1936
    stats.time_end = mseconds(stats.time_end)
×
UNCOV
1937
    stats.time_tot = mseconds(stats.time_tot)
×
UNCOV
1938
    return stats
×
1939
  end
1940

1941
  _getstats = _getstats_plain
200✔
1942
end
1943

1944

1945
function copas.status(enable_stats)
200✔
1946
  local res = _getstats(enable_stats)
×
UNCOV
1947
  res.running = not not copas.running
×
UNCOV
1948
  res.timeout = copas.gettimeouts()
×
UNCOV
1949
  res.timer, res.inactive = _sleeping:status()
×
1950
  res.read = #_reading
×
1951
  res.write = #_writing
×
1952
  res.active = _resumable:count()
×
UNCOV
1953
  return res
×
1954
end
1955

1956

1957
-------------------------------------------------------------------------------
1958
-- Dispatcher endless loop.
1959
-- Listen to client requests and handles them forever
1960
-------------------------------------------------------------------------------
1961
function copas.loop(initializer, timeout)
232✔
1962
  if type(initializer) == "function" then
380✔
1963
    copas.addnamedthread("copas_initializer", initializer)
204✔
1964
  else
1965
    timeout = initializer or timeout
204✔
1966
  end
1967

1968
  resetexit()
380✔
1969
  copas.running = true
380✔
1970
  while true do
1971
    copas.step(timeout)
211,764✔
1972
    if copas.finished() then
271,258✔
1973
      if copas.exiting() then
849✔
1974
        break
182✔
1975
      end
1976
      copas.exit()
362✔
1977
    end
1978
  end
1979
  copas.running = false
368✔
1980
end
1981

1982

1983
-------------------------------------------------------------------------------
1984
-- Naming sockets and coroutines.
1985
-------------------------------------------------------------------------------
1986
do
1987
  local function realsocket(skt)
1988
    local mt = getmetatable(skt)
90✔
1989
    if mt == _skt_mt_tcp or mt == _skt_mt_udp then
90✔
1990
      return skt.socket
90✔
1991
    else
1992
      return skt
×
1993
    end
1994
  end
1995

1996

1997
  function copas.setsocketname(name, skt)
232✔
1998
    assert(type(name) == "string", "expected arg #1 to be a string")
90✔
1999
    skt = assert(realsocket(skt), "expected arg #2 to be a socket")
105✔
2000
    object_names[skt] = name
90✔
2001
  end
2002

2003

2004
  function copas.getsocketname(skt)
232✔
2005
    skt = assert(realsocket(skt), "expected arg #1 to be a socket")
×
2006
    return object_names[skt]
×
2007
  end
2008
end
2009

2010

2011
function copas.setthreadname(name, coro)
232✔
2012
  assert(type(name) == "string", "expected arg #1 to be a string")
60✔
2013
  coro = coro or coroutine_running()
60✔
2014
  assert(type(coro) == "thread", "expected arg #2 to be a coroutine or nil")
60✔
2015
  object_names[coro] = name
60✔
2016
end
2017

2018

2019
function copas.getthreadname(coro)
232✔
2020
  coro = coro or coroutine_running()
38✔
2021
  assert(type(coro) == "thread", "expected arg #1 to be a coroutine or nil")
38✔
2022
  return object_names[coro]
40✔
2023
end
2024

2025
-------------------------------------------------------------------------------
2026
-- Debug functionality.
2027
-------------------------------------------------------------------------------
2028
do
2029
  copas.debug = {}
200✔
2030

2031
  local log_core    -- if truthy, the core-timer will also be logged
2032
  local debug_log   -- function used as logger
2033

2034

2035
  local debug_yield = function(skt, queue)
2036
    local name = object_names[coroutine_running()]
4,499✔
2037

2038
    if log_core or name ~= "copas_core_timer" then
4,499✔
2039
      if queue == _sleeping then
4,479✔
2040
        debug_log("yielding '", name, "' to SLEEP for ", skt," seconds")
4,408✔
2041

2042
      elseif queue == _writing then
71✔
2043
        debug_log("yielding '", name, "' to WRITE on '", object_names[skt], "'")
14✔
2044

2045
      elseif queue == _reading then
59✔
2046
        debug_log("yielding '", name, "' to READ on '", object_names[skt], "'")
61✔
2047

2048
      else
UNCOV
2049
        debug_log("thread '", name, "' yielding to unexpected queue; ", tostring(queue), " (", type(queue), ")", debug.traceback())
×
2050
      end
2051
    end
2052

2053
    return coroutine.yield(skt, queue)
4,499✔
2054
  end
2055

2056

2057
  local debug_resume = function(coro, skt, ...)
2058
    local name = object_names[coro]
4,511✔
2059

2060
    if skt then
4,511✔
2061
      debug_log("resuming '", name, "' for socket '", object_names[skt], "'")
71✔
2062
    else
2063
      if log_core or name ~= "copas_core_timer" then
4,440✔
2064
        debug_log("resuming '", name, "'")
4,420✔
2065
      end
2066
    end
2067
    return coroutine.resume(coro, skt, ...)
4,511✔
2068
  end
2069

2070

2071
  local debug_create = function(f)
2072
    local f_wrapped = function(...)
2073
      local results = pack(f(...))
14✔
2074
      debug_log("exiting '", object_names[coroutine_running()], "'")
12✔
2075
      return unpack(results)
12✔
2076
    end
2077

2078
    return coroutine.create(f_wrapped)
12✔
2079
  end
2080

2081

2082
  debug_log = fnil
200✔
2083

2084

2085
  -- enables debug output for all coroutine operations.
2086
  function copas.debug.start(logger, core)
400✔
2087
    log_core = core
6✔
2088
    debug_log = logger or print
6✔
2089
    coroutine_yield = debug_yield
6✔
2090
    coroutine_resume = debug_resume
6✔
2091
    coroutine_create = debug_create
6✔
2092
  end
2093

2094

2095
  -- disables debug output for coroutine operations.
2096
  function copas.debug.stop()
400✔
UNCOV
2097
    debug_log = fnil
×
UNCOV
2098
    coroutine_yield = coroutine.yield
×
UNCOV
2099
    coroutine_resume = coroutine.resume
×
UNCOV
2100
    coroutine_create = coroutine.create
×
2101
  end
2102

2103
  do
2104
    local call_id = 0
200✔
2105

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

2202
    local debug_mt = {
200✔
2203
      __index = function(self, key)
2204
        local value = self.__original_socket[key]
166✔
2205
        if type(value) ~= "function" then
166✔
UNCOV
2206
          return value
×
2207
        end
2208
        return function(self2, ...)
2209
            local my_id = call_id + 1
166✔
2210
            call_id = my_id
166✔
2211
            local results
2212

2213
            if self2 ~= self then
166✔
2214
              -- there is no self
UNCOV
2215
              print_call(tostring(key).."_in", my_id .. "-calling '"..tostring(key) .. "' with; ", self, ...)
×
UNCOV
2216
              results = pack(value(self, ...))
×
2217
            else
2218
              print_call(tostring(key).."_in", my_id .. "-calling '" .. tostring(key) .. "' with; ", self.__original_socket, ...)
166✔
2219
              results = pack(value(self.__original_socket, ...))
201✔
2220
            end
2221
            print_call(tostring(key).."_out", my_id .. "-results '"..tostring(key) .. "' returned; ", unpack(results))
201✔
2222
            return unpack(results)
166✔
2223
          end
2224
      end,
2225
      __tostring = function(self)
2226
        return tostring(self.__original_socket)
48✔
2227
      end
2228
    }
2229

2230

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

2242
      local proxy = setmetatable({
24✔
2243
        __original_socket = original_skt
12✔
2244
      }, debug_mt)
12✔
2245

2246
      return proxy
12✔
2247
    end
2248
  end
2249
end
2250

2251

2252
return copas
200✔
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