2020-10-24 13:15:32 -06:00
|
|
|
package gemini
|
2020-09-25 17:06:56 -06:00
|
|
|
|
|
|
|
import (
|
2021-02-14 21:58:28 -07:00
|
|
|
"context"
|
2020-09-25 17:06:56 -06:00
|
|
|
"crypto/tls"
|
2020-11-05 13:27:12 -07:00
|
|
|
"errors"
|
2020-09-25 17:06:56 -06:00
|
|
|
"log"
|
|
|
|
"net"
|
2021-02-14 21:58:28 -07:00
|
|
|
"sync"
|
2020-09-25 17:06:56 -06:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2021-02-14 13:50:41 -07:00
|
|
|
// A Server defines parameters for running a Gemini server. The zero value for
|
|
|
|
// Server is a valid configuration.
|
2020-09-25 17:06:56 -06:00
|
|
|
type Server struct {
|
2021-02-14 13:50:41 -07: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 17:06:56 -06:00
|
|
|
Addr string
|
|
|
|
|
2021-02-17 18:30:59 -07:00
|
|
|
// The Handler to invoke.
|
|
|
|
Handler Handler
|
|
|
|
|
2021-02-14 13:50:41 -07:00
|
|
|
// ReadTimeout is the maximum duration for reading the entire
|
|
|
|
// request.
|
|
|
|
//
|
|
|
|
// A ReadTimeout of zero means no timeout.
|
2020-10-31 19:07:02 -06:00
|
|
|
ReadTimeout time.Duration
|
|
|
|
|
|
|
|
// WriteTimeout is the maximum duration before timing out
|
|
|
|
// writes of the response.
|
2021-02-14 13:50:41 -07:00
|
|
|
//
|
|
|
|
// A WriteTimeout of zero means no timeout.
|
2020-10-31 19:07:02 -06:00
|
|
|
WriteTimeout time.Duration
|
|
|
|
|
2021-02-19 16:53:06 -07:00
|
|
|
// 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.
|
2021-02-18 20:22:17 -07:00
|
|
|
GetCertificate func(hostname string) (*tls.Certificate, error)
|
2020-09-25 17:06:56 -06:00
|
|
|
|
2021-02-14 13:50:41 -07:00
|
|
|
// ErrorLog specifies an optional logger for errors accepting connections,
|
|
|
|
// unexpected behavior from handlers, and underlying file system errors.
|
2020-11-03 14:11:31 -07:00
|
|
|
// If nil, logging is done via the log package's standard logger.
|
|
|
|
ErrorLog *log.Logger
|
|
|
|
|
2021-02-22 18:06:05 -07:00
|
|
|
listeners map[*net.Listener]struct{}
|
|
|
|
conns map[*net.Conn]struct{}
|
|
|
|
closedChan chan struct{} // closed when the server is closed
|
|
|
|
doneChan chan struct{} // closed when no more connections are open
|
|
|
|
mu sync.Mutex
|
2020-10-11 15:53:22 -06:00
|
|
|
}
|
|
|
|
|
2021-02-21 14:05:08 -07:00
|
|
|
const (
|
|
|
|
serverOk int32 = iota
|
|
|
|
serverShutdown
|
|
|
|
serverClosed
|
|
|
|
)
|
|
|
|
|
2021-02-22 18:06:05 -07:00
|
|
|
// closed returns a channel that's closed when the server is closed.
|
|
|
|
func (srv *Server) closed() chan struct{} {
|
|
|
|
srv.mu.Lock()
|
|
|
|
defer srv.mu.Unlock()
|
|
|
|
return srv.closedLocked()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (srv *Server) closedLocked() chan struct{} {
|
|
|
|
if srv.closedChan == nil {
|
|
|
|
srv.closedChan = make(chan struct{})
|
|
|
|
}
|
|
|
|
return srv.closedChan
|
|
|
|
}
|
|
|
|
|
|
|
|
// done returns a channel that's closed when the server is closed and
|
|
|
|
// all listeners and connections are closed.
|
2021-02-20 22:21:29 -07:00
|
|
|
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-22 18:06:05 -07:00
|
|
|
// tryFinishShutdown closes srv.done() if the server is closed and
|
|
|
|
// there are no active listeners or connections.
|
2021-02-20 22:21:29 -07:00
|
|
|
func (srv *Server) tryFinishShutdown() {
|
|
|
|
srv.mu.Lock()
|
|
|
|
defer srv.mu.Unlock()
|
2021-02-22 18:06:05 -07:00
|
|
|
select {
|
|
|
|
case <-srv.closedLocked():
|
|
|
|
default:
|
|
|
|
return
|
|
|
|
}
|
2021-02-20 22:21:29 -07:00
|
|
|
if len(srv.listeners) == 0 && len(srv.conns) == 0 {
|
2021-02-22 18:06:05 -07:00
|
|
|
ch := srv.doneLocked()
|
2021-02-20 22:21:29 -07:00
|
|
|
select {
|
2021-02-22 18:06:05 -07:00
|
|
|
case <-ch:
|
2021-02-20 22:21:29 -07:00
|
|
|
default:
|
2021-02-22 18:06:05 -07:00
|
|
|
close(ch)
|
2021-02-20 22:21:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close immediately closes all active net.Listeners and connections.
|
|
|
|
// For a graceful shutdown, use Shutdown.
|
|
|
|
func (srv *Server) Close() error {
|
2021-02-22 18:06:05 -07:00
|
|
|
ch := srv.closed()
|
|
|
|
select {
|
|
|
|
case <-ch:
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
close(ch)
|
2021-02-20 22:21:29 -07:00
|
|
|
}
|
|
|
|
|
2021-02-22 18:06:05 -07:00
|
|
|
srv.tryFinishShutdown()
|
|
|
|
|
|
|
|
// Force all active connections to close.
|
2021-02-20 22:21:29 -07:00
|
|
|
srv.mu.Lock()
|
2021-02-22 18:06:05 -07:00
|
|
|
for conn := range srv.conns {
|
|
|
|
(*conn).Close()
|
2021-02-20 22:21:29 -07:00
|
|
|
}
|
|
|
|
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.
|
|
|
|
//
|
2021-02-22 18:06:05 -07:00
|
|
|
// When Shutdown is called, Serve and ListenAndServe immediately
|
2021-02-20 22:21:29 -07:00
|
|
|
// 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-22 18:06:05 -07:00
|
|
|
ch := srv.closed()
|
|
|
|
select {
|
|
|
|
case <-ch:
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
close(ch)
|
2021-02-20 22:21:29 -07:00
|
|
|
}
|
|
|
|
|
2021-02-22 18:06:05 -07:00
|
|
|
srv.tryFinishShutdown()
|
2021-02-20 22:21:29 -07:00
|
|
|
|
|
|
|
// Wait for active connections to finish.
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
|
|
|
case <-srv.done():
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-25 17:06:56 -06:00
|
|
|
// ListenAndServe listens for requests at the server's configured address.
|
2021-02-14 13:50:41 -07: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.
|
2021-02-20 22:21:29 -07:00
|
|
|
func (srv *Server) ListenAndServe(ctx context.Context) error {
|
2021-02-22 18:06:05 -07:00
|
|
|
select {
|
|
|
|
case <-srv.closed():
|
2021-02-15 18:16:32 -07:00
|
|
|
return ErrServerClosed
|
2021-02-22 18:06:05 -07:00
|
|
|
default:
|
2021-02-15 18:16:32 -07:00
|
|
|
}
|
|
|
|
|
2021-02-14 13:50:41 -07:00
|
|
|
addr := srv.Addr
|
2020-09-25 17:06:56 -06:00
|
|
|
if addr == "" {
|
|
|
|
addr = ":1965"
|
|
|
|
}
|
|
|
|
|
2021-02-20 22:21:29 -07:00
|
|
|
l, err := net.Listen("tcp", addr)
|
2020-09-25 17:06:56 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-02-20 22:21:29 -07:00
|
|
|
l = tls.NewListener(l, &tls.Config{
|
2020-10-28 12:59:45 -06:00
|
|
|
ClientAuth: tls.RequestClientCert,
|
|
|
|
MinVersion: tls.VersionTLS12,
|
2021-02-14 13:50:41 -07:00
|
|
|
GetCertificate: srv.getCertificate,
|
2021-02-20 22:21:29 -07:00
|
|
|
})
|
|
|
|
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 17:06:56 -06:00
|
|
|
}
|
|
|
|
|
2021-02-22 18:06:05 -07:00
|
|
|
func (srv *Server) trackListener(l *net.Listener) bool {
|
2021-02-21 14:05:08 -07:00
|
|
|
srv.mu.Lock()
|
|
|
|
defer srv.mu.Unlock()
|
2021-02-22 18:06:05 -07:00
|
|
|
select {
|
|
|
|
case <-srv.closedLocked():
|
|
|
|
return false
|
|
|
|
default:
|
|
|
|
}
|
2021-02-14 21:58:28 -07:00
|
|
|
if srv.listeners == nil {
|
2021-02-22 18:06:05 -07:00
|
|
|
srv.listeners = make(map[*net.Listener]struct{})
|
2021-02-14 21:58:28 -07:00
|
|
|
}
|
2021-02-22 18:06:05 -07:00
|
|
|
srv.listeners[l] = struct{}{}
|
2021-02-21 09:53:15 -07:00
|
|
|
return true
|
2021-02-14 21:58:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (srv *Server) deleteListener(l *net.Listener) {
|
|
|
|
srv.mu.Lock()
|
|
|
|
defer srv.mu.Unlock()
|
|
|
|
delete(srv.listeners, l)
|
|
|
|
}
|
|
|
|
|
2021-02-14 13:50:41 -07: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.
|
2021-02-20 22:21:29 -07:00
|
|
|
func (srv *Server) Serve(ctx context.Context, l net.Listener) error {
|
2021-02-14 21:58:28 -07:00
|
|
|
defer l.Close()
|
|
|
|
|
2021-02-22 18:06:05 -07:00
|
|
|
if !srv.trackListener(&l) {
|
2021-02-21 09:53:15 -07:00
|
|
|
return ErrServerClosed
|
|
|
|
}
|
2021-02-20 22:21:29 -07:00
|
|
|
defer srv.tryFinishShutdown()
|
2021-02-15 18:18:08 -07:00
|
|
|
defer srv.deleteListener(&l)
|
|
|
|
|
2021-02-20 22:21:29 -07:00
|
|
|
errch := make(chan error, 1)
|
|
|
|
go func() {
|
|
|
|
errch <- srv.serve(ctx, l)
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
2021-02-22 18:06:05 -07:00
|
|
|
case <-ctx.Done():
|
|
|
|
return ctx.Err()
|
2021-02-20 22:21:29 -07:00
|
|
|
case err := <-errch:
|
|
|
|
return err
|
2021-02-22 18:06:05 -07:00
|
|
|
case <-srv.closed():
|
|
|
|
return ErrServerClosed
|
2021-02-14 21:58:28 -07:00
|
|
|
}
|
2021-02-20 22:21:29 -07:00
|
|
|
}
|
2021-02-14 21:58:28 -07:00
|
|
|
|
2021-02-20 22:21:29 -07:00
|
|
|
func (srv *Server) serve(ctx context.Context, l net.Listener) error {
|
2021-02-21 09:53:15 -07:00
|
|
|
var tempDelay time.Duration // how long to sleep on accept failure
|
2020-09-25 17:06:56 -06: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 13:50:41 -07:00
|
|
|
srv.logf("gemini: Accept error: %v; retrying in %v", err, tempDelay)
|
2020-09-25 17:06:56 -06:00
|
|
|
time.Sleep(tempDelay)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
tempDelay = 0
|
2021-02-21 09:53:15 -07:00
|
|
|
go srv.ServeConn(ctx, rw)
|
2020-09-25 17:06:56 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-22 18:06:05 -07:00
|
|
|
func (srv *Server) trackConn(conn *net.Conn) {
|
2021-02-21 14:05:08 -07:00
|
|
|
srv.mu.Lock()
|
|
|
|
defer srv.mu.Unlock()
|
2021-02-14 21:58:28 -07:00
|
|
|
if srv.conns == nil {
|
2021-02-22 18:06:05 -07:00
|
|
|
srv.conns = make(map[*net.Conn]struct{})
|
2021-02-14 21:58:28 -07:00
|
|
|
}
|
2021-02-22 18:06:05 -07:00
|
|
|
srv.conns[conn] = struct{}{}
|
2021-02-14 21:58:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (srv *Server) deleteConn(conn *net.Conn) {
|
|
|
|
srv.mu.Lock()
|
|
|
|
defer srv.mu.Unlock()
|
|
|
|
delete(srv.conns, conn)
|
|
|
|
}
|
|
|
|
|
2021-02-21 09:53:15 -07:00
|
|
|
// ServeConn serves a Gemini response over the provided connection.
|
2021-02-20 22:21:29 -07:00
|
|
|
// It closes the connection when the response has been completed.
|
2021-02-22 18:06:05 -07:00
|
|
|
// ServeConn can be used even after Shutdown or Close have been called.
|
2021-02-21 09:53:15 -07:00
|
|
|
func (srv *Server) ServeConn(ctx context.Context, conn net.Conn) error {
|
2020-12-17 23:41:14 -07:00
|
|
|
defer conn.Close()
|
2021-02-14 21:58:28 -07:00
|
|
|
|
2021-02-22 18:06:05 -07:00
|
|
|
srv.trackConn(&conn)
|
2021-02-20 22:21:29 -07:00
|
|
|
defer srv.tryFinishShutdown()
|
|
|
|
defer srv.deleteConn(&conn)
|
|
|
|
|
2021-02-14 13:50:41 -07:00
|
|
|
if d := srv.ReadTimeout; d != 0 {
|
|
|
|
conn.SetReadDeadline(time.Now().Add(d))
|
2020-10-31 19:07:02 -06:00
|
|
|
}
|
2021-02-14 13:50:41 -07:00
|
|
|
if d := srv.WriteTimeout; d != 0 {
|
|
|
|
conn.SetWriteDeadline(time.Now().Add(d))
|
2020-10-31 19:07:02 -06:00
|
|
|
}
|
|
|
|
|
2021-02-21 09:53:15 -07:00
|
|
|
errch := make(chan error, 1)
|
2021-02-20 22:21:29 -07:00
|
|
|
go func() {
|
2021-02-21 09:53:15 -07:00
|
|
|
errch <- srv.serveConn(ctx, conn)
|
2021-02-20 22:21:29 -07:00
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
2021-02-21 09:53:15 -07:00
|
|
|
return ctx.Err()
|
|
|
|
case err := <-errch:
|
|
|
|
return err
|
2021-02-20 22:21:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-21 09:53:15 -07:00
|
|
|
func (srv *Server) serveConn(ctx context.Context, conn net.Conn) error {
|
2021-02-17 09:44:11 -07:00
|
|
|
w := newResponseWriter(conn)
|
2020-12-17 23:41:14 -07:00
|
|
|
|
|
|
|
req, err := ReadRequest(conn)
|
2020-10-21 14:28:50 -06:00
|
|
|
if err != nil {
|
2021-02-17 11:36:16 -07:00
|
|
|
w.WriteHeader(StatusBadRequest, "Bad request")
|
2021-02-21 09:53:15 -07:00
|
|
|
return w.Flush()
|
2021-01-05 12:16:33 -07:00
|
|
|
}
|
|
|
|
|
2021-02-20 22:21:29 -07:00
|
|
|
// Store the TLS connection state
|
2021-01-05 12:16:33 -07:00
|
|
|
if tlsConn, ok := conn.(*tls.Conn); ok {
|
2021-02-08 10:32:47 -07:00
|
|
|
state := tlsConn.ConnectionState()
|
|
|
|
req.TLS = &state
|
2021-02-20 16:12:51 -07:00
|
|
|
req.Host = state.ServerName
|
2020-12-17 23:41:14 -07:00
|
|
|
}
|
2020-11-01 14:25:59 -07:00
|
|
|
|
2021-02-13 19:10:19 -07:00
|
|
|
// Store remote address
|
2021-02-20 14:27:33 -07:00
|
|
|
req.RemoteAddr = conn.RemoteAddr()
|
2021-02-13 19:10:19 -07:00
|
|
|
|
2021-02-17 18:30:59 -07:00
|
|
|
h := srv.Handler
|
2021-02-14 23:15:23 -07:00
|
|
|
if h == nil {
|
2021-02-17 11:36:16 -07:00
|
|
|
w.WriteHeader(StatusNotFound, "Not found")
|
2021-02-21 09:53:15 -07:00
|
|
|
return w.Flush()
|
2020-10-21 14:28:50 -06:00
|
|
|
}
|
2021-01-05 12:16:33 -07:00
|
|
|
|
2021-02-20 13:49:07 -07:00
|
|
|
h.ServeGemini(ctx, w, req)
|
2021-02-21 09:53:15 -07:00
|
|
|
return w.Flush()
|
2021-02-20 16:52:33 -07:00
|
|
|
}
|
|
|
|
|
2021-02-14 13:50:41 -07:00
|
|
|
func (srv *Server) logf(format string, args ...interface{}) {
|
|
|
|
if srv.ErrorLog != nil {
|
|
|
|
srv.ErrorLog.Printf(format, args...)
|
2020-11-03 14:11:31 -07:00
|
|
|
} else {
|
|
|
|
log.Printf(format, args...)
|
|
|
|
}
|
|
|
|
}
|