diff --git a/main.go b/main.go index bb3e187..7bb34da 100644 --- a/main.go +++ b/main.go @@ -4,10 +4,17 @@ import ( "context" "errors" "fmt" + "log/slog" + "net" + "net/http" + "net/netip" "os" "os/signal" + "time" "github.com/urfave/cli/v3" + + "git.sunturtle.xyz/zephyr/chord/chord/httpnode" ) var app = cli.Command{ @@ -109,7 +116,57 @@ var ( } ) -func cliJoin(ctx context.Context, cmd *cli.Command) error { return errors.New("not implemented") } +func addrflag(ip string, p uint64) (string, error) { + ap, err := netip.ParseAddrPort(ip) + if err == nil { + // note inverted error check + if p != 0 { + ap = netip.AddrPortFrom(ap.Addr(), uint16(p)) + } + return ap.String(), nil + } + a, err := netip.ParseAddr(ip) + if err == nil { + // note inverted error check + return netip.AddrPortFrom(a, uint16(p)).String(), nil + } + return "", fmt.Errorf("couldn't parse %q as ip:port or ip", ip) +} + +func cliJoin(ctx context.Context, cmd *cli.Command) error { + addr, err := addrflag(cmd.String("ip"), cmd.Uint("p")) + if err != nil { + return err + } + l, err := net.Listen("tcp", addr) + if err != nil { + return err + } + defer l.Close() + cl := &httpnode.Client{HTTP: http.Client{Timeout: 5 * time.Second}} + s, err := httpnode.New(l, cl) + if err != nil { + return err + } + srv := http.Server{ + Handler: s.Router(), + ReadTimeout: 5 * time.Second, + BaseContext: func(l net.Listener) context.Context { return ctx }, + } + go func() { + slog.InfoContext(ctx, "HTTP API server", slog.Any("addr", l.Addr())) + err := srv.Serve(l) + if err == http.ErrServerClosed { + return + } + slog.ErrorContext(ctx, "HTTP API server closed", slog.Any("err", err)) + }() + <-ctx.Done() + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + return srv.Shutdown(ctx) +} + func cliLeave(ctx context.Context, cmd *cli.Command) error { return errors.New("not implemented") } func cliLookup(ctx context.Context, cmd *cli.Command) error { return errors.New("not implemented") } func cliPut(ctx context.Context, cmd *cli.Command) error { return errors.New("not implemented") }