From 49cd477db0d75b16e9960d4333a832dddc3c2862 Mon Sep 17 00:00:00 2001 From: Bel LaPointe Date: Wed, 11 Oct 2023 16:18:42 -0600 Subject: [PATCH] implement cmd/proxy-client-service --- .../cmd/proxy-client-service/main.go | 22 ++++ .../cmd/proxy-client-service/server.go | 119 ++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 render-2023-10-11/cmd/proxy-client-service/main.go create mode 100644 render-2023-10-11/cmd/proxy-client-service/server.go diff --git a/render-2023-10-11/cmd/proxy-client-service/main.go b/render-2023-10-11/cmd/proxy-client-service/main.go new file mode 100644 index 0000000..3dfb5ef --- /dev/null +++ b/render-2023-10-11/cmd/proxy-client-service/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "flag" + "fmt" + "log" + "net/http" +) + +func main() { + port := flag.Int("p", 8080, "port to listen on") + flag.Parse() + + server := NewServer() + + addr := fmt.Sprintf(":%d", *port) // check port >0 // accept IP for mvp security + // add signal catching for clean shutdown via context and httpServer or the like + log.Printf("listening on %s", addr) + if err := http.ListenAndServe(addr, server); err != nil { + panic(err) + } +} diff --git a/render-2023-10-11/cmd/proxy-client-service/server.go b/render-2023-10-11/cmd/proxy-client-service/server.go new file mode 100644 index 0000000..48dec35 --- /dev/null +++ b/render-2023-10-11/cmd/proxy-client-service/server.go @@ -0,0 +1,119 @@ +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) + } +}