good morning!!!!

Skip to content
Snippets Groups Projects
acceptor.go 2.45 KiB
Newer Older
Garet Halliday's avatar
Garet Halliday committed
package gat

import (
Garet Halliday's avatar
hm  
Garet Halliday committed
	"errors"
	"io"
Garet Halliday's avatar
Garet Halliday committed
	"net"

Garet Halliday's avatar
hm  
Garet Halliday committed
	"tuxpa.in/a/zlog/log"

Garet Halliday's avatar
Garet Halliday committed
	"pggat/lib/bouncer/frontends/v0"
	"pggat/lib/fed"
	"pggat/lib/util/beforeexit"
Garet Halliday's avatar
Garet Halliday committed
)

type Acceptor struct {
	Listener net.Listener
	Options  frontends.AcceptOptions
}

Garet Halliday's avatar
Garet Halliday committed
func (T Acceptor) Accept() (fed.Conn, frontends.AcceptParams, error) {
Garet Halliday's avatar
Garet Halliday committed
	netConn, err := T.Listener.Accept()
	if err != nil {
		return nil, frontends.AcceptParams{}, err
	}
Garet Halliday's avatar
Garet Halliday committed
	conn := fed.WrapNetConn(netConn)
Garet Halliday's avatar
Garet Halliday committed
	params, err := frontends.Accept(conn, T.Options)
	if err != nil {
		_ = conn.Close()
		return nil, frontends.AcceptParams{}, err
	}
	return conn, params, nil
}

func Listen(network, address string, options frontends.AcceptOptions) (Acceptor, error) {
	listener, err := net.Listen(network, address)
	if err != nil {
		return Acceptor{}, err
	}
	if network == "unix" {
		// unix sockets are not cleaned up if process is stopped but i really wish they were
		beforeexit.Run(func() {
			_ = listener.Close()
		})
	}
Garet Halliday's avatar
Garet Halliday committed
	return Acceptor{
		Listener: listener,
		Options:  options,
	}, nil
}

Garet Halliday's avatar
Garet Halliday committed
func serve(client fed.Conn, acceptParams frontends.AcceptParams, pools Pools) error {
	defer func() {
		_ = client.Close()
	}()

	if acceptParams.CancelKey != [8]byte{} {
		p := pools.LookupKey(acceptParams.CancelKey)
		if p == nil {
			return nil
		}
		return p.Cancel(acceptParams.CancelKey)
	}

	p := pools.Lookup(acceptParams.User, acceptParams.Database)

Garet Halliday's avatar
Garet Halliday committed
	if p == nil {
Garet Halliday's avatar
hm  
Garet Halliday committed
		log.Printf("pool not found: user=%s database=%s", acceptParams.User, acceptParams.Database)
Garet Halliday's avatar
Garet Halliday committed
		return nil
	}

	authParams, err := frontends.Authenticate(client, frontends.AuthenticateOptions{
Garet Halliday's avatar
Garet Halliday committed
		Credentials: p.GetCredentials(),
	})
	if err != nil {
		return err
	}

	pools.RegisterKey(authParams.BackendKey, acceptParams.User, acceptParams.Database)
	defer pools.UnregisterKey(authParams.BackendKey)

Garet Halliday's avatar
Garet Halliday committed
	return p.Serve(client, acceptParams.InitialParameters, authParams.BackendKey)
}

func Serve(acceptor Acceptor, pools Pools) error {
Garet Halliday's avatar
Garet Halliday committed
	for {
		conn, acceptParams, err := acceptor.Accept()
Garet Halliday's avatar
Garet Halliday committed
		if err != nil {
Garet Halliday's avatar
hm  
Garet Halliday committed
			if errors.Is(err, net.ErrClosed) {
				return nil
			}
			log.Print("error accepting client: ", err)
Garet Halliday's avatar
Garet Halliday committed
			continue
		}
		go func() {
Garet Halliday's avatar
hm  
Garet Halliday committed
			err := serve(conn, acceptParams, pools)
			if err != nil && !errors.Is(err, io.EOF) {
				log.Print("error serving client: ", err)
			}
func ListenAndServe(network, address string, options frontends.AcceptOptions, pools Pools) error {
Garet Halliday's avatar
Garet Halliday committed
	listener, err := Listen(network, address, options)
	if err != nil {
		return err
	}
	return Serve(listener, pools)
Garet Halliday's avatar
Garet Halliday committed
}