120 lines
2.5 KiB
Go
120 lines
2.5 KiB
Go
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)
|
|
}
|
|
}
|