implement cmd/proxy-client-service

master
Bel LaPointe 2023-10-11 16:18:42 -06:00
parent 874ff7ccaa
commit 49cd477db0
2 changed files with 141 additions and 0 deletions

View File

@ -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)
}
}

View File

@ -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)
}
}