diff --git a/chord/httpnode/client.go b/chord/httpnode/client.go index f5962d0..3235f6a 100644 --- a/chord/httpnode/client.go +++ b/chord/httpnode/client.go @@ -2,11 +2,13 @@ package httpnode import ( "context" + "encoding/base64" "errors" "fmt" "net/http" "net/url" "path" + "strings" "git.sunturtle.xyz/zephyr/chord/chord" ) @@ -44,6 +46,29 @@ func (cl *Client) Find(ctx context.Context, s chord.Peer, id chord.ID) (chord.Pe return chord.Address(p.Peer), p.Value, nil } +func (cl *Client) Set(ctx context.Context, s chord.Peer, id chord.ID, v string) error { + _, addr := s.Values() + url := url.URL{ + Scheme: "http", + Host: addr.String(), + Path: path.Join("/", cl.APIBase, "key", id.String()), + } + body := strings.NewReader(base64.StdEncoding.EncodeToString([]byte(v))) + req, err := http.NewRequestWithContext(ctx, "POST", url.String(), body) + if err != nil { + return err + } + resp, err := cl.HTTP.Do(req) + if err != nil { + return err + } + if resp.StatusCode >= 400 { + _, err := readResponse[struct{}](resp) + return err + } + return nil +} + // Notify tells s we believe n to be its predecessor. func (cl *Client) Notify(ctx context.Context, n *chord.Node, s chord.Peer) error { _, addr := s.Values() diff --git a/chord/httpnode/server.go b/chord/httpnode/server.go index 944cb73..892f6a5 100644 --- a/chord/httpnode/server.go +++ b/chord/httpnode/server.go @@ -2,8 +2,10 @@ package httpnode import ( "context" + "encoding/base64" "errors" "fmt" + "io" "net" "net/http" "net/netip" @@ -46,6 +48,7 @@ func New(l net.Listener, cl chord.Client) (*Node, error) { func (n *Node) Router() http.Handler { m := http.NewServeMux() m.HandleFunc("GET /key/{id}", n.key) + m.HandleFunc("POST /key/{id}", n.set) m.HandleFunc("POST /pred", n.notify) m.HandleFunc("GET /neighbors", n.neighbors) return m @@ -82,6 +85,25 @@ func (n *Node) key(w http.ResponseWriter, r *http.Request) { writeOk(w, pv) } +func (n *Node) set(w http.ResponseWriter, r *http.Request) { + s := r.PathValue("id") + id, err := chord.ParseID(s) + if err != nil { + writeError(w, http.StatusBadRequest, err.Error()) + return + } + val, err := io.ReadAll(base64.NewDecoder(base64.StdEncoding, r.Body)) + if err != nil { + writeError(w, http.StatusInternalServerError, err.Error()) + return + } + if !n.self.SetLocal(id, string(val)) { + writeError(w, http.StatusNotFound, "id does not belong to this peer") + return + } + w.WriteHeader(http.StatusNoContent) +} + func (n *Node) notify(w http.ResponseWriter, r *http.Request) { s := r.FormValue("p") addr, err := netip.ParseAddrPort(s)