<commit_before>local core = require "sys.core"
local type = type
local concat = table.concat
local assert = assert

local socket_pool = {}
local M = {}
local EVENT = {}

local ctx
local tls
local client_ctx

local function new_socket(fd, ctx)
	local s = {
		nil,
		fd = fd,
		delim = false,
		co = false,
		ssl = tls.open(ctx, fd),
		closing = false,
	}
	socket_pool[fd] = s
	return s
end

local function del_socket(s)
	tls.close(s.ssl)
	socket_pool[s.fd] = nil
end

local function wakeup(s, dat)
	local co = s.co
	s.co = false
	core.wakeup(co, dat)
end

local function suspend(s)
	assert(not s.co)
	local co = core.running()
	s.co = co
	return core.wait(co)
end

local function handshake(s)
	local ok = tls.handshake(s.ssl)
	if ok then
		return ok
	end
	s.delim = "~"
	return suspend(s)
end

function EVENT.accept(fd, _, portid, addr)
	local lc = socket_pool[portid];
	local s = new_socket(fd, lc.ctx)
	local ok = handshake(s)
	if not ok then
		return
	end
	local ok, err = core.pcall(lc.disp, fd, addr)
	if not ok then
		core.log(err)
		M.close(fd)
	end
end

function EVENT.close(fd, _, errno)
	local s = socket_pool[fd]
	if s == nil then
		return
	end
	s.closing = true
	if s.co then
		wakeup(s, false)
		del_socket(s)
	end
end

function EVENT.data(fd, message)
	local s = socket_pool[fd]
	if not s then
		return
	end
	local delim = s.delim
	tls.message(s.ssl, message)
	if not delim then	--non suspend read
		return
	end
	if type(delim) == "number" then
		local dat = tls.read(s.ssl, delim)
		if dat then
			local n = delim - #dat
			if n == 0 then
				s.delim = false
			else
				s.delim = n
			end
			wakeup(s, dat)
		end
	elseif delim == "\n" then
		local dat, ok = tls.readline(s.ssl)
		if dat ~= "" then
			if ok then
				s.delim = false
			end
			wakeup(s, dat)
		end
	elseif delim == "~" then
		local ok = tls.handshake(s.ssl)
		if ok then
			s.delim = false
			wakeup(s, true)
		end
	end
end

local function socket_dispatch(type, fd, message, ...)
	EVENT[type](fd, message, ...)
end


local function connect_normal(ip, bind)
	local fd = core.connect(ip, socket_dispatch, bind)
	if not fd then
		return nil
	end
	local s = new_socket(fd, client_ctx)
	local ok = handshake(s)
	if ok then
		return fd
	end
	M.close(fd)
	return nil
end

function M.connect(ip, bind)
	tls = require "sys.tls.tls"
	ctx = require "sys.tls.ctx"
	client_ctx = ctx.client()
	M.connect = connect_normal
	return connect_normal(ip, bind)
end

function M.listen(conf)
	assert(conf.port)
	assert(conf.disp)
	local portid = core.listen(conf.port, socket_dispatch, conf.backlog)
	if not portid then
		return nil
	end
	tls = require "sys.tls.tls"
	ctx = ctx or require "sys.tls.ctx"
	socket_pool[portid] = {
		fd = portid,
		disp = conf.disp,
		co = false,
		ctx = ctx.server(conf.cert, conf.key, conf.ciphers),
	}
	return portid
end

function M.close(fd)
	local s = socket_pool[fd]
	if s == nil then
		return false
	end
	if s.co then<commit_msg>bugfix tls gc of listen socket<commit_after>local core = require "sys.core"
local type = type
local concat = table.concat
local assert = assert

local socket_pool = {}
local M = {}
local EVENT = {}

local ctx
local tls
local client_ctx

local function new_socket(fd, ctx)
	local s = {
		nil,
		fd = fd,
		delim = false,
		co = false,
		ssl = tls.open(ctx, fd),
		closing = false,
	}
	socket_pool[fd] = s
	return s
end

local function del_socket(s)
	tls.close(s.ssl)
	socket_pool[s.fd] = nil
end

local function wakeup(s, dat)
	local co = s.co
	s.co = false
	core.wakeup(co, dat)
end

local function suspend(s)
	assert(not s.co)
	local co = core.running()
	s.co = co
	return core.wait(co)
end

local function handshake(s)
	local ok = tls.handshake(s.ssl)
	if ok then
		return ok
	end
	s.delim = "~"
	return suspend(s)
end

function EVENT.accept(fd, _, portid, addr)
	local lc = socket_pool[portid];
	local s = new_socket(fd, lc.ctx)
	local ok = handshake(s)
	if not ok then
		return
	end
	local ok, err = core.pcall(lc.disp, fd, addr)
	if not ok then
		core.log(err)
		M.close(fd)
	end
end

function EVENT.close(fd, _, errno)
	local s = socket_pool[fd]
	if s == nil then
		return
	end
	s.closing = true
	if s.co then
		wakeup(s, false)
		del_socket(s)
	end
end

function EVENT.data(fd, message)
	local s = socket_pool[fd]
	if not s then
		return
	end
	local delim = s.delim
	tls.message(s.ssl, message)
	if not delim then	--non suspend read
		return
	end
	if type(delim) == "number" then
		local dat = tls.read(s.ssl, delim)
		if dat then
			local n = delim - #dat
			if n == 0 then
				s.delim = false
			else
				s.delim = n
			end
			wakeup(s, dat)
		end
	elseif delim == "\n" then
		local dat, ok = tls.readline(s.ssl)
		if dat ~= "" then
			if ok then
				s.delim = false
			end
			wakeup(s, dat)
		end
	elseif delim == "~" then
		local ok = tls.handshake(s.ssl)
		if ok then
			s.delim = false
			wakeup(s, true)
		end
	end
end

local function socket_dispatch(type, fd, message, ...)
	EVENT[type](fd, message, ...)
end


local function connect_normal(ip, bind)
	local fd = core.connect(ip, socket_dispatch, bind)
	if not fd then
		return nil
	end
	local s = new_socket(fd, client_ctx)
	local ok = handshake(s)
	if ok then
		return fd
	end
	M.close(fd)
	return nil
end

function M.connect(ip, bind)
	tls = require "sys.tls.tls"
	ctx = require "sys.tls.ctx"
	client_ctx = ctx.client()
	M.connect = connect_normal
	return connect_normal(ip, bind)
end

function M.listen(conf)
	assert(conf.port)
	assert(conf.disp)
	local portid = core.listen(conf.port, socket_dispatch, conf.backlog)
	if not portid then
		return nil
	end
	tls = require "sys.tls.tls"
	ctx = ctx or require "sys.tls.ctx"
	local c = ctx.server(conf.cert, conf.key, conf.ciphers)
	new_socket(portid, c).ctx = c
	return portid
end

function M.close(fd)
	local s = socket_pool[fd]
	if s == nil then
		return false
	end
	if s.co then
{'fraction_non_alphanumeric': 0.11376525176147104, 'fraction_numerical': 0.00034370166695308474, 'mean_word_length': 3.211287988422576, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7417105', 'n_tokens_mistral': 2441, 'n_tokens_neox': 2290, 'n_words': 707}