--- Module `pegasus.plugins.tls`
--
-- A plugin that enables TLS (https) for connections using LuaSec.
-- Should be the first plugin, since it wraps the client socket and performs
-- the TLS handshake before any other plugin or handler accesses the socket.
--
-- @module pegasus.plugins.tls
--
-- This plugin should not be used with Copas. Since Copas has native TLS support
-- and can handle simultaneous `http` and `https` connections. See the Copas example
-- to learn how to set that up.
local ssl = require("ssl")


local TLS = {}
TLS.__index = TLS


--- Creates a new plugin instance.
-- IMPORTANT: this must be the first plugin to execute before the client-socket is accessed!
-- @tparam sslparams table the data-structure that contains the properties for the luasec functions.
-- The structure is set up to mimic the LuaSec functions for the handshake.
-- @return the new plugin
-- @usage
-- local sslparams = {
--   wrap = table | context,    -- parameter to LuaSec 'wrap()'
--   sni = {                    -- parameters to LuaSec 'sni()'
--     names = string | table   --   1st parameter
--     strict = bool            --   2nd parameter
--   }
-- }
-- local tls_plugin = require("pegasus.plugins.tls"):new(sslparams)
---@param sslparams table|nil
---@return TLS
function TLS:new(sslparams)
  sslparams = sslparams or {}
  assert(sslparams.wrap, "'sslparam.wrap' is a required option")

  return setmetatable({
    sslparams = sslparams
  }, TLS)
end

--- Wrap an accepted client socket and perform the TLS handshake.
-- Optionally sets SNI if provided in `sslparams`.
-- @tparam table client accepted client socket
-- @tparam table handler the Pegasus handler (for logging)
-- @treturn table|false wrapped client or false on failure
---@param client table
---@param handler table
---@return table|false
function TLS:newConnection(client, handler)
  local params = self.sslparams

  -- wrap the client socket and replace it
  client = assert(ssl.wrap(client, params.wrap))

  if params.sni then
    assert(client:sni(params.sni.names, params.sni.strict))
  end

  local ok, err = client:dohandshake()
  if not ok then
    handler.log:error("tls handshake failed: %s", err)
    return false
  end

  return client
end


return TLS
