// Run from the framework root; open the printed URLs and click Run checks. // All listeners are loopback-only and stop after five minutes or SIGINT/SIGTERM. package main import ( "context" "encoding/json" "fmt" "io" "net" "net/http" "os" "os/signal" "strings" "syscall" "time" omni "git.yuntuo.hk/k2safe/go-omni" ) func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func run() error { var listeners []net.Listener defer func() { for _, l := range listeners { l.Close() } }() for i := 0; i < 4; i++ { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { return err } listeners = append(listeners, l) } url := func(i int) string { return "http://" + listeners[i].Addr().String() } app := omni.New(omni.WithCORS(omni.CORSConfig{Enabled: true, AllowedOrigins: []string{url(0)}, AllowedMethods: []string{"GET", "POST"}, AllowedHeaders: []string{"Authorization", "Content-Type"}, ExposedHeaders: []string{"Content-Disposition"}, AllowCredentials: true})) defer app.Close() app.GET("/text", func(c *omni.Context) error { return c.Text(200, "hello") }) app.GET("/private", func(c *omni.Context) error { if c.Header("Authorization") != "Bearer example-only" { return omni.Unauthorized("LOGIN_REQUIRED", "login required") } return c.Text(200, "authenticated") }) app.GET("/download", func(c *omni.Context) error { c.SetResponseHeader("Content-Disposition", "attachment; filename=example.txt") return c.Text(200, "file bytes") }) public := omni.New(omni.WithCORS(omni.CORSConfig{Enabled: true, AllowedOrigins: []string{"*"}})) defer public.Close() public.GET("/text", func(c *omni.Context) error { return c.Text(200, "public") }) page := func(allowed bool) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } data, _ := json.Marshal(map[string]any{"api": url(2), "publicApi": url(3), "allowed": allowed}) w.Header().Set("Content-Type", "text/html; charset=utf-8") io.WriteString(w, strings.Replace(pageSource, "CONFIG_PLACEHOLDER", string(data), 1)) }) } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() errors := make(chan error, 4) var servers []*http.Server for i, h := range []http.Handler{page(true), page(false), app, public} { s := &http.Server{Handler: h, ReadHeaderTimeout: 3 * time.Second, ReadTimeout: 5 * time.Second, WriteTimeout: 5 * time.Second} servers = append(servers, s) go func(l net.Listener) { errors <- s.Serve(l) }(listeners[i]) } defer func() { for _, s := range servers { s.Close() } }() fmt.Printf("Allowed page: %s\nDenied page: %s\n", url(0), url(1)) select { case <-ctx.Done(): return nil case err := <-errors: if err != http.ErrServerClosed { return err } return nil } } const pageSource = `
Loopback-only, synthetic data; no accounts or credentials required.
Not started`