package gemini import ( "bufio" "context" "crypto/tls" "fmt" "io" "net" "tildegit.org/tjp/sliderule/internal" "tildegit.org/tjp/sliderule/internal/types" "tildegit.org/tjp/sliderule/logging" ) type server struct { internal.Server handler types.Handler } func (s server) Protocol() string { return "GEMINI" } // NewServer builds a gemini server. func NewServer( ctx context.Context, hostname string, network string, address string, handler types.Handler, baseLog logging.Logger, tlsConfig *tls.Config, ) (types.Server, error) { s := &server{handler: handler} hostname = internal.JoinDefaultPort(hostname, "1965") address = internal.JoinDefaultPort(address, "1965") internalServer, err := internal.NewServer(ctx, hostname, network, address, baseLog, s.handleConn) if err != nil { return nil, err } s.Server = internalServer s.Listener = tls.NewListener(s.Listener, tlsConfig) return s, nil } func (s *server) handleConn(conn net.Conn) { buf := bufio.NewReader(conn) var response *types.Response request, err := ParseRequest(buf) if err != nil { response = BadRequest(err.Error()) } else { request.Server = s request.RemoteAddr = conn.RemoteAddr() if tlsconn, ok := conn.(*tls.Conn); ok { state := tlsconn.ConnectionState() request.TLSState = &state } defer func() { if r := recover(); r != nil { err := fmt.Errorf("%s", r) _ = s.LogError("msg", "panic in handler", "err", err) _, _ = io.Copy(conn, NewResponseReader(Failure(err))) } }() response = s.handler.Handle(s.Ctx, request) if response == nil { response = NotFound("Resource does not exist.") } } defer response.Close() _, _ = io.Copy(conn, NewResponseReader(response)) } // GeminiOnly filters requests down to just those on the gemini:// protocol. // // Optionally, it will also allow through titan:// requests. // // Filtered requests will be turned away with a 53 response "proxy request refused". func GeminiOnly(allowTitan bool) types.Middleware { return func(inner types.Handler) types.Handler { return types.HandlerFunc(func(ctx context.Context, request *types.Request) *types.Response { if request.Scheme == "gemini" || (allowTitan && request.Scheme == "titan") { return inner.Handle(ctx, request) } return RefuseProxy("Non-gemini protocol requests are not supported.") }) } }