implement cmd/proxy-client-service
parent
874ff7ccaa
commit
49cd477db0
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue