diff options
author | tjpcc <tjp@ctrl-c.club> | 2023-01-09 16:40:24 -0700 |
---|---|---|
committer | tjpcc <tjp@ctrl-c.club> | 2023-01-09 16:40:24 -0700 |
commit | ff05d62013906f3086b452bfeda3e0d5b9b7a541 (patch) | |
tree | 3be29de0b1bc7c273041c6d89b71ca447c940556 /gemini/serve.go |
Initial commit.
some basics:
- minimal README
- some TODOs
- server and request handler framework
- contribs: file serving, request logging
- server examples
- CI setup
Diffstat (limited to 'gemini/serve.go')
-rw-r--r-- | gemini/serve.go | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/gemini/serve.go b/gemini/serve.go new file mode 100644 index 0000000..d439472 --- /dev/null +++ b/gemini/serve.go @@ -0,0 +1,89 @@ +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 + } +} |