good morning!!!!

Skip to content
Snippets Groups Projects
acceptor.go 2.49 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
}

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
Garet Halliday's avatar
Garet Halliday committed
	ctx := frontends.AuthenticateContext{
		Conn: client,
		Options: frontends.AuthenticateOptions{
			Credentials: p.GetCredentials(),
		},
	}
	authParams, err := frontends.Authenticate(&ctx)
	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 {
Garet Halliday's avatar
Garet Halliday committed
		netConn, err := acceptor.Listener.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
			}
Garet Halliday's avatar
Garet Halliday committed
			log.Print("error accepting connection: ", err)
Garet Halliday's avatar
Garet Halliday committed
			continue
		}
Garet Halliday's avatar
Garet Halliday committed
		conn := fed.WrapNetConn(netConn)

Garet Halliday's avatar
Garet Halliday committed
		go func() {
Garet Halliday's avatar
Garet Halliday committed
			defer func() {
				_ = conn.Close()
			}()

			ctx := frontends.AcceptContext{
				Conn:    conn,
				Options: acceptor.Options,
			}
			acceptParams, acceptErr := frontends.Accept(&ctx)
			if acceptErr != nil {
				log.Print("error accepting client: ", acceptErr)
				return
			}

			err = serve(conn, acceptParams, pools)
Garet Halliday's avatar
hm  
Garet Halliday committed
			if err != nil && !errors.Is(err, io.EOF) {
				log.Print("error serving client: ", err)
Garet Halliday's avatar
Garet Halliday committed
				return
Garet Halliday's avatar
hm  
Garet Halliday committed
			}
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
}