package main import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "os/signal" "path" "render231011/internal/thestore" "strings" "syscall" "time" ) type Server struct { store *thestore.Store } func NewServer() Server { s := Server{ store: thestore.NewStore(), } // todo timeout via context if err := s.pollCacheServiceForUpdates(context.Background()); err != nil { panic(err) } go s.PollCacheServiceForUpdates() return s } // todo add a context to spin this goroutine-method down func (server Server) PollCacheServiceForUpdates() { ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT) defer can() for ctx.Err() == nil { if err := server.pollCacheServiceForUpdates(ctx); err != nil { log.Println("failed to PollCacheServiceForUpdates:", err) } // poll cache service select { case <-ctx.Done(): case <-time.After(time.Second): // make configurable } } } var lastEventsPoll time.Time func (server Server) pollCacheServiceForUpdates(ctx context.Context) error { c := http.Client{Timeout: time.Minute} req, err := http.NewRequest( http.MethodGet, // todo configurable cache-service addr fmt.Sprintf("http://localhost:8080/events?since=%v", lastEventsPoll.Unix()), // todo not a global nil, ) if err != nil { panic(err) } log.Printf("+%v", req) resp, err := c.Do(req) if err != nil { return err } defer resp.Body.Close() defer io.Copy(io.Discard, resp.Body) // todo check status code var pollResult []thestore.Event if err := json.NewDecoder(resp.Body).Decode(&pollResult); err != nil { return fmt.Errorf("failed to read events from cache-service: %v", err) } for i := range pollResult { server.store.Push(pollResult[i]) } // !!!!!!!todo cache-service needs to emit lastEventsPoll value lastEventsPoll = time.Now() return nil } func (server Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // todo middleware: rate limit // todo middleware: timeout requests // todo: replace with an actual router if strings.HasPrefix(r.URL.Path, "/lookup") && r.Method == http.MethodGet { server.serveGetLookup(w, r) } } // GET /lookup/xyz func (server Server) serveGetLookup(w http.ResponseWriter, r *http.Request) { k := path.Base(r.URL.Path) if k == "" { // log err here w.WriteHeader(http.StatusBadRequest) return } state, ok := server.store.Get(k) if !ok { // todo something clearer w.WriteHeader(http.StatusNoContent) return } if err := json.NewEncoder(w).Encode(state); err != nil { w.WriteHeader(499) log.Println("failed to responsd", err) } }