package main import ( "context" _ "embed" "encoding/json" "errors" "flag" "fmt" "log" "net/http" "os" "os/signal" "sync" "syscall" ) var ( Config struct { Port int SessionD string Debug bool Semaphore sync.Mutex } //go:embed template.d/login.html htmlLogin []byte //go:embed template.d/index.html htmlIndex []byte ) func main() { ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT) defer can() ctx, cleanup := contextWithCleanup(ctx) defer cleanup() config(ctx) log.Printf("%+v", Config) listenAndServe(ctx) log.Println("done") } func contextWithCleanup(ctx context.Context) (context.Context, func()) { m := map[int]func(){} ctx = context.WithValue(ctx, "__cleanup__", m) return ctx, func() { defer func() { recover() }() for _, v := range m { v() } } } func contextWithCleanupFunc(ctx context.Context, foo func()) { v := ctx.Value("__cleanup__") if v == nil { panic("cannot get context with cleanup func that doesnt have cleanup init") } m := v.(map[int]func()) m[len(m)] = foo } func config(ctx context.Context) { d, err := os.MkdirTemp(os.TempDir(), "ai.*") if err != nil { panic(err) } contextWithCleanupFunc(ctx, func() { os.RemoveAll(d) }) flag.IntVar(&Config.Port, "p", 37070, "port to listen on") flag.StringVar(&Config.SessionD, "d", d, "dir to store sessions") flag.BoolVar(&Config.Debug, "debug", false, "debug mode") flag.Parse() } func listenAndServe(ctx context.Context) { wg := &sync.WaitGroup{} s := &http.Server{ Addr: fmt.Sprintf(":%d", Config.Port), Handler: http.HandlerFunc(handle), } wg.Add(1) go func() { defer wg.Done() if err := s.ListenAndServe(); err != nil && ctx.Err() == nil { panic(err) } }() <-ctx.Done() s.Close() wg.Wait() } func handle(w http.ResponseWriter, r *http.Request) { if err := _handle(w, r); err != nil { u, _, _ := r.BasicAuth() log.Printf("%s: %s: %v", u, r.URL.Path, err) } } func _handle(w http.ResponseWriter, r *http.Request) error { u, p, ok := r.BasicAuth() if !ok { w.Header().Set("WWW-Authenticate", "Basic") w.WriteHeader(http.StatusUnauthorized) return errors.New("no auth") } _, _ = u, p Config.Semaphore.Lock() defer Config.Semaphore.Unlock() return errors.New("not impl") } type Cookie struct { Name string } func ParseCookie(r *http.Request) (Cookie, error) { cookie, err := r.Cookie("root") if err != nil { return Cookie{}, err } var result Cookie if err := json.Unmarshal([]byte(cookie.Value), &result); err != nil { return Cookie{}, err } if result.Name == "" { return Cookie{}, errors.New("incomplete cookie") } return result, nil } func (cookie Cookie) Serialize(w http.ResponseWriter) { b, _ := json.Marshal(cookie) c := &http.Cookie{ Name: "root", Value: string(b), } http.SetCookie(w, c) }