go-gemini/server.go

362 lines
8.4 KiB
Go
Raw Normal View History

2020-10-24 19:15:32 +00:00
package gemini
2020-09-25 23:06:56 +00:00
import (
"context"
2020-09-25 23:06:56 +00:00
"crypto/tls"
2020-11-05 20:27:12 +00:00
"errors"
2020-09-25 23:06:56 +00:00
"log"
"net"
"sync"
2020-09-25 23:06:56 +00:00
"time"
)
2021-02-14 20:50:41 +00:00
// A Server defines parameters for running a Gemini server. The zero value for
// Server is a valid configuration.
2020-09-25 23:06:56 +00:00
type Server struct {
2021-02-14 20:50:41 +00:00
// Addr optionally specifies the TCP address for the server to listen on,
// in the form "host:port". If empty, ":1965" (port 1965) is used.
// See net.Dial for details of the address format.
2020-09-25 23:06:56 +00:00
Addr string
// The Handler to invoke.
Handler Handler
2021-02-14 20:50:41 +00:00
// ReadTimeout is the maximum duration for reading the entire
// request.
//
// A ReadTimeout of zero means no timeout.
ReadTimeout time.Duration
// WriteTimeout is the maximum duration before timing out
// writes of the response.
2021-02-14 20:50:41 +00:00
//
// A WriteTimeout of zero means no timeout.
WriteTimeout time.Duration
// GetCertificate returns a TLS certificate based on the given
// hostname.
//
// If GetCertificate is nil or returns nil, then no certificate
// will be used and the connection will be aborted.
GetCertificate func(hostname string) (*tls.Certificate, error)
2020-09-25 23:06:56 +00:00
2021-02-14 20:50:41 +00:00
// ErrorLog specifies an optional logger for errors accepting connections,
// unexpected behavior from handlers, and underlying file system errors.
2020-11-03 21:11:31 +00:00
// If nil, logging is done via the log package's standard logger.
ErrorLog *log.Logger
listeners map[*net.Listener]context.CancelFunc
conns map[*net.Conn]context.CancelFunc
doneChan chan struct{}
2021-02-21 16:53:15 +00:00
closed bool
mu sync.Mutex
}
// done returns a channel that's closed when the server has finished closing.
func (srv *Server) done() chan struct{} {
srv.mu.Lock()
defer srv.mu.Unlock()
return srv.doneLocked()
}
func (srv *Server) doneLocked() chan struct{} {
if srv.doneChan == nil {
srv.doneChan = make(chan struct{})
}
return srv.doneChan
}
2021-02-21 16:53:15 +00:00
func (srv *Server) isClosed() bool {
srv.mu.Lock()
defer srv.mu.Unlock()
return srv.closed
}
func (srv *Server) tryClose() bool {
srv.mu.Lock()
defer srv.mu.Unlock()
if srv.closed {
return false
}
srv.closed = true
return true
}
// tryFinishShutdown closes srv.done() if there are no active listeners or requests.
func (srv *Server) tryFinishShutdown() {
srv.mu.Lock()
defer srv.mu.Unlock()
if len(srv.listeners) == 0 && len(srv.conns) == 0 {
done := srv.doneLocked()
select {
case <-done:
default:
close(done)
}
}
}
// Close immediately closes all active net.Listeners and connections.
// For a graceful shutdown, use Shutdown.
func (srv *Server) Close() error {
2021-02-21 16:53:15 +00:00
if !srv.tryClose() {
return ErrServerClosed
}
// Close active listeners and connections.
srv.mu.Lock()
for _, cancel := range srv.listeners {
cancel()
}
for _, cancel := range srv.conns {
cancel()
}
srv.mu.Unlock()
select {
case <-srv.done():
return nil
}
}
// Shutdown gracefully shuts down the server without interrupting any
// active connections. Shutdown works by first closing all open
// listeners and then waiting indefinitely for connections
// to close and then shut down.
// If the provided context expires before the shutdown is complete,
// Shutdown returns the context's error.
//
// When Shutdown is called, Serve and ListenAndServer immediately
// return ErrServerClosed. Make sure the program doesn't exit and
// waits instead for Shutdown to return.
//
// Once Shutdown has been called on a server, it may not be reused;
// future calls to methods such as Serve will return ErrServerClosed.
func (srv *Server) Shutdown(ctx context.Context) error {
2021-02-21 16:53:15 +00:00
if !srv.tryClose() {
return ErrServerClosed
}
// Close active listeners.
srv.mu.Lock()
for _, cancel := range srv.listeners {
cancel()
}
srv.mu.Unlock()
// Wait for active connections to finish.
select {
case <-ctx.Done():
return ctx.Err()
case <-srv.done():
return nil
}
}
2020-09-25 23:06:56 +00:00
// ListenAndServe listens for requests at the server's configured address.
2021-02-14 20:50:41 +00:00
// ListenAndServe listens on the TCP network address srv.Addr and then calls
// Serve to handle requests on incoming connections.
//
// If srv.Addr is blank, ":1965" is used.
//
// ListenAndServe always returns a non-nil error. After Shutdown or Close, the
// returned error is ErrServerClosed.
func (srv *Server) ListenAndServe(ctx context.Context) error {
2021-02-21 16:53:15 +00:00
if srv.isClosed() {
return ErrServerClosed
}
2021-02-14 20:50:41 +00:00
addr := srv.Addr
2020-09-25 23:06:56 +00:00
if addr == "" {
addr = ":1965"
}
l, err := net.Listen("tcp", addr)
2020-09-25 23:06:56 +00:00
if err != nil {
return err
}
l = tls.NewListener(l, &tls.Config{
2020-10-28 18:59:45 +00:00
ClientAuth: tls.RequestClientCert,
MinVersion: tls.VersionTLS12,
2021-02-14 20:50:41 +00:00
GetCertificate: srv.getCertificate,
})
return srv.Serve(ctx, l)
}
func (srv *Server) getCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) {
if srv.GetCertificate == nil {
return nil, errors.New("gemini: GetCertificate is nil")
}
return srv.GetCertificate(h.ServerName)
2020-09-25 23:06:56 +00:00
}
2021-02-21 16:53:15 +00:00
func (srv *Server) trackListener(l *net.Listener, cancel context.CancelFunc) bool {
srv.mu.Lock()
defer srv.mu.Unlock()
2021-02-21 16:53:15 +00:00
if srv.closed {
return false
}
if srv.listeners == nil {
srv.listeners = make(map[*net.Listener]context.CancelFunc)
}
srv.listeners[l] = cancel
2021-02-21 16:53:15 +00:00
return true
}
func (srv *Server) deleteListener(l *net.Listener) {
srv.mu.Lock()
defer srv.mu.Unlock()
delete(srv.listeners, l)
}
2021-02-14 20:50:41 +00:00
// Serve accepts incoming connections on the Listener l, creating a new
// service goroutine for each. The service goroutines read requests and
// then calls the appropriate Handler to reply to them.
//
// Serve always returns a non-nil error and closes l. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) Serve(ctx context.Context, l net.Listener) error {
defer l.Close()
lnctx, cancel := context.WithCancel(ctx)
defer cancel()
2021-02-21 16:53:15 +00:00
if !srv.trackListener(&l, cancel) {
return ErrServerClosed
}
defer srv.tryFinishShutdown()
defer srv.deleteListener(&l)
errch := make(chan error, 1)
go func() {
errch <- srv.serve(ctx, l)
}()
select {
case <-lnctx.Done():
2021-02-21 16:53:15 +00:00
if srv.isClosed() {
2021-02-21 05:51:02 +00:00
return ErrServerClosed
}
return lnctx.Err()
case err := <-errch:
return err
}
}
func (srv *Server) serve(ctx context.Context, l net.Listener) error {
2021-02-21 16:53:15 +00:00
var tempDelay time.Duration // how long to sleep on accept failure
2020-09-25 23:06:56 +00:00
for {
rw, err := l.Accept()
if err != nil {
// If this is a temporary error, sleep
if ne, ok := err.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
2021-02-14 20:50:41 +00:00
srv.logf("gemini: Accept error: %v; retrying in %v", err, tempDelay)
2020-09-25 23:06:56 +00:00
time.Sleep(tempDelay)
continue
}
return err
}
tempDelay = 0
2021-02-21 16:53:15 +00:00
go srv.ServeConn(ctx, rw)
2020-09-25 23:06:56 +00:00
}
}
2021-02-21 16:53:15 +00:00
func (srv *Server) trackConn(conn *net.Conn, cancel context.CancelFunc) bool {
srv.mu.Lock()
defer srv.mu.Unlock()
2021-02-21 16:53:15 +00:00
if srv.closed {
return false
}
if srv.conns == nil {
srv.conns = make(map[*net.Conn]context.CancelFunc)
}
srv.conns[conn] = cancel
2021-02-21 16:53:15 +00:00
return true
}
func (srv *Server) deleteConn(conn *net.Conn) {
srv.mu.Lock()
defer srv.mu.Unlock()
delete(srv.conns, conn)
}
2021-02-21 16:53:15 +00:00
// ServeConn serves a Gemini response over the provided connection.
// It closes the connection when the response has been completed.
2021-02-21 16:53:15 +00:00
func (srv *Server) ServeConn(ctx context.Context, conn net.Conn) error {
defer conn.Close()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
2021-02-21 16:53:15 +00:00
if !srv.trackConn(&conn, cancel) {
return ErrServerClosed
}
defer srv.tryFinishShutdown()
defer srv.deleteConn(&conn)
2021-02-14 20:50:41 +00:00
if d := srv.ReadTimeout; d != 0 {
conn.SetReadDeadline(time.Now().Add(d))
}
2021-02-14 20:50:41 +00:00
if d := srv.WriteTimeout; d != 0 {
conn.SetWriteDeadline(time.Now().Add(d))
}
2021-02-21 16:53:15 +00:00
errch := make(chan error, 1)
go func() {
2021-02-21 16:53:15 +00:00
errch <- srv.serveConn(ctx, conn)
}()
select {
case <-ctx.Done():
2021-02-21 16:53:15 +00:00
return ctx.Err()
case err := <-errch:
return err
}
}
2021-02-21 16:53:15 +00:00
func (srv *Server) serveConn(ctx context.Context, conn net.Conn) error {
w := newResponseWriter(conn)
req, err := ReadRequest(conn)
2020-10-21 20:28:50 +00:00
if err != nil {
2021-02-17 18:36:16 +00:00
w.WriteHeader(StatusBadRequest, "Bad request")
2021-02-21 16:53:15 +00:00
return w.Flush()
}
// Store the TLS connection state
if tlsConn, ok := conn.(*tls.Conn); ok {
state := tlsConn.ConnectionState()
req.TLS = &state
2021-02-20 23:12:51 +00:00
req.Host = state.ServerName
}
// Store remote address
req.RemoteAddr = conn.RemoteAddr()
h := srv.Handler
2021-02-15 06:15:23 +00:00
if h == nil {
2021-02-17 18:36:16 +00:00
w.WriteHeader(StatusNotFound, "Not found")
2021-02-21 16:53:15 +00:00
return w.Flush()
2020-10-21 20:28:50 +00:00
}
h.ServeGemini(ctx, w, req)
2021-02-21 16:53:15 +00:00
return w.Flush()
2021-02-20 23:52:33 +00:00
}
2021-02-14 20:50:41 +00:00
func (srv *Server) logf(format string, args ...interface{}) {
if srv.ErrorLog != nil {
srv.ErrorLog.Printf(format, args...)
2020-11-03 21:11:31 +00:00
} else {
log.Printf(format, args...)
}
}