renames some api endpoints: e.g. /servers to /client-conns, /servers/xxx/peers to /client-conns/xxx/routes

This commit is contained in:
hyung-hwan 2024-11-30 00:19:39 +09:00
parent 9667a9b2e2
commit 4625ed3328
2 changed files with 249 additions and 62 deletions

View File

@ -16,6 +16,8 @@ import "strconv"
* GET list all peers * GET list all peers
* PUT create/replace * PUT create/replace
* PATCH partial update * PATCH partial update
* /servers/1112123/peers/1231344
* GET get info
*/ */
type json_errmsg struct { type json_errmsg struct {
@ -26,32 +28,37 @@ type json_in_peer_addrs struct {
PeerAddrs []string `json:"peer-addrs"` PeerAddrs []string `json:"peer-addrs"`
} }
type json_out_server struct { type json_out_client_conn struct {
Id uint32 `json:"id"` Id uint32 `json:"id"`
ServerAddr string `json:"server-addr"` ServerAddr string `json:"server-addr"`
PeerAddrs []json_out_server_peer `json:"peer-addrs"` Routes []json_out_client_route `json:"routes"`
} }
type json_out_server_peer struct { type json_out_client_route struct {
Id uint32 `json:"id"` Id uint32 `json:"id"`
ClientPeerAddr string `json:"peer-addr"` ClientPeerAddr string `json:"client-peer-addr"`
ServerPeerListenAddr string `json:"server-peer-listen-addr"` ServerPeerListenAddr string `json:"server-peer-listen-addr"`
} }
// ------------------------------------ // ------------------------------------
type client_ctl_servers struct { type client_ctl_client_conns struct {
c *Client c *Client
} }
type client_ctl_servers_id struct { type client_ctl_client_conns_id struct {
c *Client c *Client
} }
type client_ctl_servers_id_peers struct { type client_ctl_client_conns_id_routes struct {
c *Client c *Client
} }
type client_ctl_client_conns_id_routes_id struct {
c *Client
}
type client_ctl_clients struct { type client_ctl_clients struct {
c *Client c *Client
} }
@ -62,40 +69,41 @@ type client_ctl_clients_id struct {
// ------------------------------------ // ------------------------------------
func (ctl *client_ctl_servers) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (ctl *client_ctl_client_conns) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var c *Client var c *Client
var status_code int var status_code int
var err error var err error
var je *json.Encoder
c = ctl.c c = ctl.c
je = json.NewEncoder(w)
switch req.Method { switch req.Method {
case http.MethodGet: case http.MethodGet:
var je *json.Encoder
var cts *ClientConn var cts *ClientConn
var js []json_out_server var js []json_out_client_conn
status_code = http.StatusOK; w.WriteHeader(status_code)
je = json.NewEncoder(w)
js = make([]json_out_client_conn, 0)
c.cts_mtx.Lock() c.cts_mtx.Lock()
for _, cts = range c.cts_map_by_id { for _, cts = range c.cts_map_by_id {
var r *ClientRoute var r *ClientRoute
var jsp []json_out_server_peer var jsp []json_out_client_route
jsp = make([]json_out_client_route, 0)
cts.route_mtx.Lock() cts.route_mtx.Lock()
for _, r = range cts.route_map { for _, r = range cts.route_map {
jsp = append(jsp, json_out_server_peer{ jsp = append(jsp, json_out_client_route{
Id: r.id, Id: r.id,
ClientPeerAddr: r.peer_addr.String(), ClientPeerAddr: r.peer_addr.String(),
ServerPeerListenAddr: r.server_peer_listen_addr.String(), ServerPeerListenAddr: r.server_peer_listen_addr.String(),
}) })
} }
js = append(js, json_out_server{Id: cts.id, ServerAddr: cts.saddr.String(), PeerAddrs: jsp}) js = append(js, json_out_client_conn{Id: cts.id, ServerAddr: cts.saddr.String(), Routes: jsp})
cts.route_mtx.Unlock() cts.route_mtx.Unlock()
} }
c.cts_mtx.Unlock() c.cts_mtx.Unlock()
status_code = http.StatusOK; w.WriteHeader(status_code)
if err = je.Encode(js); err != nil { goto oops } if err = je.Encode(js); err != nil { goto oops }
case http.MethodPost: case http.MethodPost:
@ -106,22 +114,17 @@ func (ctl *client_ctl_servers) ServeHTTP(w http.ResponseWriter, req *http.Reques
err = json.NewDecoder(req.Body).Decode(&s) err = json.NewDecoder(req.Body).Decode(&s)
if err != nil { if err != nil {
status_code = http.StatusBadRequest status_code = http.StatusBadRequest; w.WriteHeader(status_code)
w.WriteHeader(status_code)
goto done goto done
} }
cc.ServerAddr = s.ServerAddr cc.ServerAddr = s.ServerAddr
cc.PeerAddrs = s.PeerAddrs cc.PeerAddrs = s.PeerAddrs
cts, err = c.start_service(&cc) // TODO: this can be blocking. do we have to resolve addresses before calling this? also not good because resolution succeed or fail at each attempt. however ok as ServeHTTP itself is in a goroutine? cts, err = c.start_service(&cc) // TODO: this can be blocking. do we have to resolve addresses before calling this? also not good because resolution succeed or fail at each attempt. however ok as ServeHTTP itself is in a goroutine?
if err != nil { if err != nil {
var je *json.Encoder
status_code = http.StatusInternalServerError; w.WriteHeader(status_code) status_code = http.StatusInternalServerError; w.WriteHeader(status_code)
je = json.NewEncoder(w)
if err = je.Encode(json_errmsg{Text: err.Error()}); err != nil { goto oops } if err = je.Encode(json_errmsg{Text: err.Error()}); err != nil { goto oops }
} else { } else {
var je *json.Encoder
status_code = http.StatusCreated; w.WriteHeader(status_code) status_code = http.StatusCreated; w.WriteHeader(status_code)
je = json.NewEncoder(w)
if err = je.Encode(cts.cfg); err != nil { goto oops } if err = je.Encode(cts.cfg); err != nil { goto oops }
} }
@ -131,7 +134,7 @@ func (ctl *client_ctl_servers) ServeHTTP(w http.ResponseWriter, req *http.Reques
c.cts_mtx.Lock() c.cts_mtx.Lock()
for _, cts = range c.cts_map { cts.ReqStop() } for _, cts = range c.cts_map { cts.ReqStop() }
c.cts_mtx.Unlock() c.cts_mtx.Unlock()
w.WriteHeader(http.StatusNoContent) status_code = http.StatusNoContent; w.WriteHeader(status_code)
default: default:
status_code = http.StatusBadRequest; w.WriteHeader(status_code) status_code = http.StatusBadRequest; w.WriteHeader(status_code)
@ -149,43 +152,136 @@ oops:
// ------------------------------------ // ------------------------------------
// servers/{id} // client-conns/{conn_id}
func (ctl *client_ctl_servers_id) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (ctl *client_ctl_client_conns_id) ServeHTTP(w http.ResponseWriter, req *http.Request) {
//req.PathValue("id")
switch req.Method {
case http.MethodGet:
case http.MethodPost:
case http.MethodPut: // update
goto bad_request
case http.MethodDelete:
}
return
bad_request:
w.WriteHeader(http.StatusBadRequest)
return
}
func (ctl *client_ctl_servers_id_peers) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var c *Client var c *Client
var status_code int var status_code int
var err error var err error
var id string var conn_id string
var conn_nid uint64
var je *json.Encoder
c = ctl.c c = ctl.c
je = json.NewEncoder(w)
conn_id = req.PathValue("conn_id")
conn_nid, err = strconv.ParseUint(conn_id, 10, 32)
if err != nil {
status_code = http.StatusBadRequest; w.WriteHeader(status_code)
if err = je.Encode(json_errmsg{Text: "wrong connection id - " + conn_id}); err != nil { goto oops }
goto done
}
id = req.PathValue("id")
switch req.Method { switch req.Method {
case http.MethodGet: case http.MethodGet:
var r *ClientRoute
var jsp []json_out_client_route
var js *json_out_client_conn
var cts *ClientConn
cts = c.FindClientConnById(uint32(conn_nid))
if cts == nil {
status_code = http.StatusNotFound; w.WriteHeader(status_code)
if err = je.Encode(json_errmsg{Text: "non-existent connection id - " + conn_id}); err != nil { goto oops }
goto done
}
jsp = make([]json_out_client_route, 0)
cts.route_mtx.Lock()
for _, r = range cts.route_map {
jsp = append(jsp, json_out_client_route{
Id: r.id,
ClientPeerAddr: r.peer_addr.String(),
ServerPeerListenAddr: r.server_peer_listen_addr.String(),
})
}
js = &json_out_client_conn{Id: cts.id, ServerAddr: cts.saddr.String(), Routes: jsp}
cts.route_mtx.Unlock()
status_code = http.StatusOK; w.WriteHeader(status_code)
if err = je.Encode(js); err != nil { goto oops }
case http.MethodDelete:
/*
err = c.RemoveClientConnById(uint32(conn_nid))
if err != nil {
status_code = http.StatusNotFound; w.WriteHeader(status_code)
if err = je.Encode(json_errmsg{Text: err.Error()}); err != nil { goto oops }
} else {
status_code = http.StatusNoContent; w.WriteHeader(status_code)
}
*/
default:
status_code = http.StatusBadRequest; w.WriteHeader(status_code)
}
return
done:
// TODO: need to handle x-forwarded-for and other stuff? this is not a real web service, though
c.log.Write("", LOG_DEBUG, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) // TODO: time taken
return
oops:
c.log.Write("", LOG_ERROR, "[%s] %s %s - %s", req.RemoteAddr, req.Method, req.URL.String(), err.Error())
return
}
func (ctl *client_ctl_client_conns_id_routes) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var c *Client
var status_code int
var err error
var conn_id string
var conn_nid uint64
var je *json.Encoder
var cts *ClientConn
c = ctl.c
je = json.NewEncoder(w)
conn_id = req.PathValue("conn_id")
conn_nid, err = strconv.ParseUint(conn_id, 10, 32)
if err != nil {
status_code = http.StatusBadRequest; w.WriteHeader(status_code)
if err = je.Encode(json_errmsg{Text: "wrong connection id - " + conn_id}); err != nil { goto oops }
goto done
}
cts = c.FindClientConnById(uint32(conn_nid))
if cts == nil {
status_code = http.StatusNotFound; w.WriteHeader(status_code)
if err = je.Encode(json_errmsg{Text: "non-existent connection id - " + conn_id}); err != nil { goto oops }
goto done
}
switch req.Method {
case http.MethodGet:
var r *ClientRoute
var jsp []json_out_client_route
jsp = make([]json_out_client_route, 0)
cts.route_mtx.Lock()
for _, r = range cts.route_map {
jsp = append(jsp, json_out_client_route{
Id: r.id,
ClientPeerAddr: r.peer_addr.String(),
ServerPeerListenAddr: r.server_peer_listen_addr.String(),
})
}
cts.route_mtx.Unlock()
status_code = http.StatusOK; w.WriteHeader(status_code)
if err = je.Encode(jsp); err != nil { goto oops }
case http.MethodPost: case http.MethodPost:
var pa json_in_peer_addrs var pa json_in_peer_addrs
var cts *ClientConn var cts *ClientConn
var nid uint64
err = json.NewDecoder(req.Body).Decode(&pa) err = json.NewDecoder(req.Body).Decode(&pa)
if err != nil { if err != nil {
@ -193,21 +289,14 @@ func (ctl *client_ctl_servers_id_peers) ServeHTTP(w http.ResponseWriter, req *ht
goto done goto done
} }
nid, err = strconv.ParseUint(id, 10, 32) cts = c.FindClientConnById(uint32(conn_nid))
if err != nil {
status_code = http.StatusBadRequest; w.WriteHeader(status_code)
goto done
}
cts = c.FindClientConnById(uint32(nid))
if cts == nil { if cts == nil {
status_code = http.StatusNotFound; w.WriteHeader(status_code) status_code = http.StatusNotFound; w.WriteHeader(status_code)
if err = je.Encode(json_errmsg{Text: "wrong connection id - " + conn_id}); err != nil { goto oops }
} else { } else {
err = cts.AddClientRoutes(pa.PeerAddrs) err = cts.AddClientRoutes(pa.PeerAddrs)
if err != nil { if err != nil {
var je *json.Encoder
status_code = http.StatusInternalServerError; w.WriteHeader(status_code) status_code = http.StatusInternalServerError; w.WriteHeader(status_code)
je = json.NewEncoder(w)
if err = je.Encode(json_errmsg{Text: err.Error()}); err != nil { goto oops } if err = je.Encode(json_errmsg{Text: err.Error()}); err != nil { goto oops }
} else { } else {
status_code = http.StatusCreated; w.WriteHeader(status_code) status_code = http.StatusCreated; w.WriteHeader(status_code)
@ -228,6 +317,86 @@ oops:
return return
} }
// ------------------------------------
func (ctl *client_ctl_client_conns_id_routes_id) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var c *Client
var status_code int
var err error
var conn_id string
var route_id string
var conn_nid uint64
var route_nid uint64
var je *json.Encoder
var cts *ClientConn
c = ctl.c
je = json.NewEncoder(w)
conn_id = req.PathValue("conn_id")
route_id = req.PathValue("route_id")
conn_nid, err = strconv.ParseUint(conn_id, 10, 32)
if err != nil {
status_code = http.StatusBadRequest; w.WriteHeader(status_code)
if err = je.Encode(json_errmsg{Text: "wrong connection id - " + conn_id}); err != nil { goto oops }
goto done
}
route_nid, err = strconv.ParseUint(route_id, 10, 32)
if err != nil {
status_code = http.StatusBadRequest; w.WriteHeader(status_code)
if err = je.Encode(json_errmsg{Text: "wrong route id - " + route_id}); err != nil { goto oops }
goto done
}
cts = c.FindClientConnById(uint32(conn_nid))
if cts == nil {
status_code = http.StatusNotFound; w.WriteHeader(status_code)
if err = je.Encode(json_errmsg{Text: "non-existent connection id - " + conn_id}); err != nil { goto oops }
goto done
}
switch req.Method {
case http.MethodGet:
var r *ClientRoute
r = cts.FindClientRouteById(uint32(route_nid))
if r == nil {
status_code = http.StatusNotFound; w.WriteHeader(status_code)
if err = je.Encode(json_errmsg{Text: "non-existent route id - " + conn_id}); err != nil { goto oops }
goto done
}
err = je.Encode(json_out_client_route{
Id: r.id,
ClientPeerAddr: r.peer_addr.String(),
ServerPeerListenAddr: r.server_peer_listen_addr.String(),
})
if err != nil { goto oops }
case http.MethodDelete:
err = cts.RemoveClientRouteById(uint32(route_nid))
if err != nil {
status_code = http.StatusNotFound; w.WriteHeader(status_code)
if err = je.Encode(json_errmsg{Text: err.Error()}); err != nil { goto oops }
} else {
status_code = http.StatusNoContent; w.WriteHeader(status_code)
}
default:
status_code = http.StatusBadRequest; w.WriteHeader(status_code)
}
done:
// TODO: need to handle x-forwarded-for and other stuff? this is not a real web service, though
c.log.Write("", LOG_DEBUG, "[%s] %s %s %d", req.RemoteAddr, req.Method, req.URL.String(), status_code) // TODO: time taken
return
oops:
c.log.Write("", LOG_ERROR, "[%s] %s %s - %s", req.RemoteAddr, req.Method, req.URL.String(), err.Error())
return
}
// ------------------------------------ // ------------------------------------
func (ctl *client_ctl_clients) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (ctl *client_ctl_clients) ServeHTTP(w http.ResponseWriter, req *http.Request) {

View File

@ -39,6 +39,7 @@ type Client struct {
ctx context.Context ctx context.Context
ctx_cancel context.CancelFunc ctx_cancel context.CancelFunc
tlscfg *tls.Config tlscfg *tls.Config
api_prefix string
ext_svcs []Service ext_svcs []Service
ctl *http.Server // control server ctl *http.Server // control server
@ -451,6 +452,21 @@ func (cts *ClientConn) RemoveClientRouteById(route_id uint32) error {
return nil return nil
} }
func (cts *ClientConn) FindClientRouteById(route_id uint32) *ClientRoute {
var r *ClientRoute
var ok bool
cts.route_mtx.Lock()
r, ok = cts.route_map[route_id]
if !ok {
cts.route_mtx.Unlock()
return nil
}
cts.route_mtx.Unlock()
return r
}
func (cts *ClientConn) AddClientRoutes(peer_addrs []string) error { func (cts *ClientConn) AddClientRoutes(peer_addrs []string) error {
var v string var v string
var addr *net.TCPAddr var addr *net.TCPAddr
@ -755,13 +771,15 @@ func NewClient(ctx context.Context, listen_on string, logger Logger, tlscfg *tls
c.stop_req.Store(false) c.stop_req.Store(false)
c.stop_chan = make(chan bool, 8) c.stop_chan = make(chan bool, 8)
c.log = logger c.log = logger
c.api_prefix = "" // TODO:
c.mux = http.NewServeMux() c.mux = http.NewServeMux()
c.mux.Handle("/servers", &client_ctl_servers{c: &c}) c.mux.Handle(c.api_prefix + "/client-conns", &client_ctl_client_conns{c: &c})
c.mux.Handle("/servers/{id}", &client_ctl_servers_id{c: &c}) c.mux.Handle(c.api_prefix + "/client-conns/{conn_id}", &client_ctl_client_conns_id{c: &c})
c.mux.Handle("/servers/{id}/peers", &client_ctl_servers_id_peers{c: &c}) c.mux.Handle(c.api_prefix + "/client-conns/{conn_id}/routes", &client_ctl_client_conns_id_routes{c: &c})
c.mux.Handle("/clients", &client_ctl_clients{c: &c}) c.mux.Handle(c.api_prefix + "/client-conns/{conn_id}/routes/{route_id}", &client_ctl_client_conns_id_routes_id{c: &c})
c.mux.Handle("/clients/{id}", &client_ctl_clients_id{c: &c}) c.mux.Handle(c.api_prefix + "/server-conns", &client_ctl_clients{c: &c})
c.mux.Handle(c.api_prefix + "/server-conns/{id}", &client_ctl_clients_id{c: &c})
c.ctl = &http.Server{ c.ctl = &http.Server{
Addr: listen_on, Addr: listen_on,