chord/chord/httpnode/server.go
2025-03-11 09:15:18 -04:00

91 lines
2.1 KiB
Go

package httpnode
import (
"errors"
"net"
"net/http"
"net/netip"
"git.sunturtle.xyz/zephyr/chord/chord"
)
type Node struct {
// self is the node topology this server represents.
self *chord.Node
// client is the Chord client for forwarding queries to other nodes.
client chord.Client
}
// New creates an instance of a Chord network that responds on HTTP.
// The listener must be the same one used for the HTTP server that routes to
// [Node.ServeHTTP]. It must be bound to a single interface and port.
func New(l net.Listener, cl chord.Client) (*Node, error) {
addr, err := netip.ParseAddrPort(l.Addr().String())
if err != nil {
return nil, err
}
if addr.Addr().IsUnspecified() || addr.Port() == 0 {
return nil, errors.New("listener must be bound to a single interface and port")
}
n, err := chord.Create(addr)
if err != nil {
return nil, err
}
r := &Node{
self: n,
client: cl,
}
return r, nil
}
// Router creates a handler for the Chord HTTP endpoints.
func (n *Node) Router() http.Handler {
m := http.NewServeMux()
m.HandleFunc("GET /succ", n.successor)
m.HandleFunc("POST /pred", n.notify)
m.HandleFunc("GET /neighbors", n.neighbors)
return m
}
func (n *Node) successor(w http.ResponseWriter, r *http.Request) {
s := r.FormValue("s")
id, err := chord.ParseID(s)
if err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
}
p, err := chord.Find(r.Context(), n.client, n.self, id)
if err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
_, addr := p.Values()
writeOk(w, addr)
}
func (n *Node) notify(w http.ResponseWriter, r *http.Request) {
s := r.FormValue("p")
addr, err := netip.ParseAddrPort(s)
if err != nil {
writeError(w, http.StatusBadRequest, err.Error())
return
}
np := chord.Address(addr)
chord.Notify(n.self, np)
w.WriteHeader(http.StatusNoContent)
}
func (n *Node) neighbors(w http.ResponseWriter, r *http.Request) {
pred, succ := n.self.Neighbors(nil)
_, paddr := pred.Values()
u := neighbors{
Succ: make([]netip.AddrPort, 0, len(succ)),
Pred: paddr,
}
for _, s := range succ {
_, addr := s.Values()
u.Succ = append(u.Succ, addr)
}
writeOk(w, &u)
}