package gopher import ( "context" "crypto/tls" "errors" "fmt" "io" "net" "tildegit.org/tjp/sliderule/internal" "tildegit.org/tjp/sliderule/internal/types" "tildegit.org/tjp/sliderule/logging" ) type gopherServer struct { internal.Server handler types.Handler } func (gs gopherServer) Protocol() string { return "GOPHER" } // NewServer builds a gopher server. func NewServer( ctx context.Context, hostname string, network string, address string, handler types.Handler, baseLog logging.Logger, ) (types.Server, error) { gs := &gopherServer{handler: handler} hostname = internal.JoinDefaultPort(hostname, "70") address = internal.JoinDefaultPort(address, "70") var err error gs.Server, err = internal.NewServer(ctx, hostname, network, address, baseLog, gs.handleConn) if err != nil { return nil, err } return gs, nil } // NewTLSServer builds a gopher server which serves gopher over tls-encrypted connections. func NewTLSServer( ctx context.Context, hostname string, network string, address string, handler types.Handler, baseLog logging.Logger, tlsConfig *tls.Config, ) (types.Server, error) { gs := &gopherServer{handler: handler} hostname = internal.JoinDefaultPort(hostname, "70") address = internal.JoinDefaultPort(address, "70") var err error gs.Server, err = internal.NewServer(ctx, hostname, network, address, baseLog, gs.handleConn) if err != nil { return nil, err } gs.Listener = tls.NewListener(gs.Listener, tlsConfig) return gs, nil } func (gs *gopherServer) handleConn(conn net.Conn) { var response *types.Response request, err := ParseRequest(conn) if err != nil { response = Error(errors.New("Malformed request.")).Response() } else { request.Server = gs request.RemoteAddr = conn.RemoteAddr() request.Host = gs.Host 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) _ = gs.LogError("msg", "panic in handler", "err", err) rdr := NewResponseReader(Error(errors.New("Server error.")).Response()) _, _ = io.Copy(conn, rdr) } }() response = gs.handler.Handle(gs.Ctx, request) if response == nil { response = Error(errors.New("Resource does not exist.")).Response() } } defer response.Close() _, _ = io.Copy(conn, NewResponseReader(response)) }