Extend the config, database schema, and server internals to support per-route L4/L7 mode selection and PROXY protocol fields. This is the foundation for L7 HTTP/2 reverse proxying and multi-hop PROXY protocol support described in the updated ARCHITECTURE.md. Config: Listener gains ProxyProtocol; Route gains Mode, TLSCert, TLSKey, BackendTLS, SendProxyProtocol. L7 routes validated at load time (cert/key pair must exist and parse). Mode defaults to "l4". DB: Migration v2 adds columns to listeners and routes tables. CRUD and seeding updated to persist all new fields. Server: RouteInfo replaces bare backend string in route lookup. handleConn dispatches on route.Mode (L7 path stubbed with error). ListenerState and ListenerData carry ProxyProtocol flag. All existing L4 tests pass unchanged. New tests cover migration v2, L7 field persistence, config validation for mode/cert/key, and proxy_protocol flag round-tripping. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
70 lines
2.0 KiB
Go
70 lines
2.0 KiB
Go
package db
|
|
|
|
import "fmt"
|
|
|
|
// Route is a database route record.
|
|
type Route struct {
|
|
ID int64
|
|
ListenerID int64
|
|
Hostname string
|
|
Backend string
|
|
Mode string // "l4" or "l7"
|
|
TLSCert string
|
|
TLSKey string
|
|
BackendTLS bool
|
|
SendProxyProtocol bool
|
|
}
|
|
|
|
// ListRoutes returns all routes for a listener.
|
|
func (s *Store) ListRoutes(listenerID int64) ([]Route, error) {
|
|
rows, err := s.db.Query(
|
|
`SELECT id, listener_id, hostname, backend, mode, tls_cert, tls_key, backend_tls, send_proxy_protocol
|
|
FROM routes WHERE listener_id = ? ORDER BY hostname`,
|
|
listenerID,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("querying routes: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var routes []Route
|
|
for rows.Next() {
|
|
var r Route
|
|
if err := rows.Scan(&r.ID, &r.ListenerID, &r.Hostname, &r.Backend,
|
|
&r.Mode, &r.TLSCert, &r.TLSKey, &r.BackendTLS, &r.SendProxyProtocol); err != nil {
|
|
return nil, fmt.Errorf("scanning route: %w", err)
|
|
}
|
|
routes = append(routes, r)
|
|
}
|
|
return routes, rows.Err()
|
|
}
|
|
|
|
// CreateRoute inserts a route and returns its ID.
|
|
func (s *Store) CreateRoute(listenerID int64, hostname, backend, mode, tlsCert, tlsKey string, backendTLS, sendProxyProtocol bool) (int64, error) {
|
|
result, err := s.db.Exec(
|
|
`INSERT INTO routes (listener_id, hostname, backend, mode, tls_cert, tls_key, backend_tls, send_proxy_protocol)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
listenerID, hostname, backend, mode, tlsCert, tlsKey, backendTLS, sendProxyProtocol,
|
|
)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("inserting route: %w", err)
|
|
}
|
|
return result.LastInsertId()
|
|
}
|
|
|
|
// DeleteRoute deletes a route by listener ID and hostname.
|
|
func (s *Store) DeleteRoute(listenerID int64, hostname string) error {
|
|
result, err := s.db.Exec(
|
|
"DELETE FROM routes WHERE listener_id = ? AND hostname = ?",
|
|
listenerID, hostname,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("deleting route: %w", err)
|
|
}
|
|
n, _ := result.RowsAffected()
|
|
if n == 0 {
|
|
return fmt.Errorf("route %q not found on listener %d", hostname, listenerID)
|
|
}
|
|
return nil
|
|
}
|