package gemini import ( "context" "crypto/tls" "io" "net" "sync" ) type Server struct { ctx context.Context cancel context.CancelFunc wg *sync.WaitGroup listener net.Listener handler Handler } func NewServer(ctx context.Context, tlsConfig *tls.Config, listener net.Listener, handler Handler) *Server { ctx, cancel := context.WithCancel(ctx) s := &Server{ ctx: ctx, cancel: cancel, wg: &sync.WaitGroup{}, listener: tls.NewListener(listener, tlsConfig), handler: handler, } go s.propagateCancel() return s } func (s *Server) Close() { s.cancel() s.wg.Wait() } func (s *Server) Serve() { s.wg.Add(1) defer s.wg.Done() for { conn, err := s.listener.Accept() if err != nil { return } s.wg.Add(1) go s.handleConn(conn) } } func (s *Server) handleConn(conn net.Conn) { defer s.wg.Done() defer conn.Close() req, err := ParseRequest(conn) if tlsconn, ok := conn.(*tls.Conn); req != nil && ok { state := tlsconn.ConnectionState() req.TLSState = &state } var resp *Response if err == nil { resp = s.handler(s.ctx, req) } else { resp = BadRequest(err.Error()) } defer resp.Close() _, _ = io.Copy(conn, resp) } func (s *Server) propagateCancel() { go func() { <-s.ctx.Done() _ = s.listener.Close() }() } func (s *Server) closed() bool { select { case <-s.ctx.Done(): return true default: return false } }