2020-09-08 10:11:32 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-09-08 16:24:16 +00:00
|
|
|
"context"
|
2020-09-08 15:13:39 +00:00
|
|
|
"fmt"
|
2020-09-08 10:11:32 +00:00
|
|
|
"log"
|
2020-09-08 15:13:39 +00:00
|
|
|
"net"
|
|
|
|
"net/url"
|
|
|
|
"strings"
|
2020-09-08 10:11:32 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
2020-09-09 09:00:00 +00:00
|
|
|
cfg, err := Load("config")
|
2020-09-08 10:11:32 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to load config file: %v", err)
|
|
|
|
}
|
2020-09-08 15:13:39 +00:00
|
|
|
|
2020-09-08 16:24:16 +00:00
|
|
|
srv := NewServer()
|
2020-09-08 15:13:39 +00:00
|
|
|
|
2020-09-09 09:00:00 +00:00
|
|
|
for _, d := range cfg.ChildrenByName("frontend") {
|
2020-09-08 15:13:39 +00:00
|
|
|
if err := parseFrontend(srv, d); err != nil {
|
|
|
|
log.Fatalf("failed to parse frontend: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
select {}
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseFrontend(srv *Server, d *Directive) error {
|
|
|
|
frontend := &Frontend{Server: srv}
|
|
|
|
srv.Frontends = append(srv.Frontends, frontend)
|
|
|
|
|
|
|
|
// TODO: support multiple backends
|
|
|
|
backendDirective := d.ChildByName("backend")
|
|
|
|
if backendDirective == nil {
|
|
|
|
return fmt.Errorf("missing backend directive in frontend block")
|
|
|
|
}
|
|
|
|
if err := parseBackend(&frontend.Backend, backendDirective); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-09-08 16:24:16 +00:00
|
|
|
var listenNames []string
|
2020-09-08 15:13:39 +00:00
|
|
|
for _, listenDirective := range d.ChildrenByName("listen") {
|
|
|
|
var listenAddr string
|
|
|
|
if err := listenDirective.ParseParams(&listenAddr); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
host, port, err := net.SplitHostPort(listenAddr)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to parse listen address %q: %v", listenAddr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: come up with something more robust
|
|
|
|
if host != "localhost" && net.ParseIP(host) == nil {
|
2020-09-08 16:24:16 +00:00
|
|
|
listenNames = append(listenNames, host)
|
2020-09-08 15:13:39 +00:00
|
|
|
host = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
ln, err := net.Listen("tcp", net.JoinHostPort(host, port))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to listen on %q: %v", listenAddr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
if err := frontend.Serve(ln); err != nil {
|
|
|
|
log.Fatalf("failed to serve: %v", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2020-09-08 16:24:16 +00:00
|
|
|
if err := srv.certmagic.ManageAsync(context.Background(), listenNames); err != nil {
|
|
|
|
return fmt.Errorf("failed to manage TLS certificates: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-09-08 15:13:39 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseBackend(backend *Backend, d *Directive) error {
|
|
|
|
var backendURI string
|
|
|
|
if err := d.ParseParams(&backendURI); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !strings.Contains(backendURI, ":/") {
|
|
|
|
// This is a raw domain name, make it an URL with an empty scheme
|
|
|
|
backendURI = "//" + backendURI
|
|
|
|
}
|
|
|
|
|
|
|
|
u, err := url.Parse(backendURI)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to parse backend URI %q: %v", backendURI, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: +proxy to use the PROXY protocol
|
|
|
|
|
|
|
|
switch u.Scheme {
|
|
|
|
case "", "tcp":
|
|
|
|
backend.Network = "tcp"
|
|
|
|
backend.Address = u.Host
|
|
|
|
case "unix":
|
|
|
|
backend.Network = "unix"
|
|
|
|
backend.Address = u.Host
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("failed to setup backend %q: unsupported URI scheme", backendURI)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2020-09-08 10:11:32 +00:00
|
|
|
}
|