replace lobby with proxy.

This commit is contained in:
Miroslav Šedivý
2022-08-02 01:26:46 +02:00
parent 917dea5cbb
commit ae2582deaf
6 changed files with 138 additions and 89 deletions
-35
View File
@@ -1,35 +0,0 @@
package api
import (
"net/http"
"strings"
"github.com/go-chi/chi"
"github.com/m1k1o/neko-rooms/internal/proxy"
)
func (manager *ApiManagerCtx) RoomLobby(w http.ResponseWriter, r *http.Request) {
if !manager.conf.Lobby {
http.NotFound(w, r)
}
roomName := chi.URLParam(r, "roomName")
response, err := manager.rooms.FindByName(roomName)
if err != nil || response.Name != roomName {
proxy.RoomNotFound(w, r)
return
}
if !response.Running {
proxy.RoomNotRunning(w, r)
return
}
if strings.Contains(response.Status, "starting") {
proxy.RoomNotReady(w, r)
return
}
proxy.RoomReady(w, r)
}
+92 -23
View File
@@ -6,6 +6,8 @@ import (
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"strings"
"sync"
dockerTypes "github.com/docker/docker/api/types"
@@ -15,6 +17,11 @@ import (
"github.com/rs/zerolog/log"
)
type entry struct {
running bool
handler *httputil.ReverseProxy
}
type ProxyManagerCtx struct {
logger zerolog.Logger
mu sync.RWMutex
@@ -23,7 +30,7 @@ type ProxyManagerCtx struct {
client *dockerClient.Client
instanceName string
handlers *prefixHandler[*httputil.ReverseProxy]
handlers *prefixHandler[*entry]
}
func New(client *dockerClient.Client, instanceName string) *ProxyManagerCtx {
@@ -32,7 +39,7 @@ func New(client *dockerClient.Client, instanceName string) *ProxyManagerCtx {
client: client,
instanceName: instanceName,
handlers: &prefixHandler[*httputil.ReverseProxy]{},
handlers: &prefixHandler[*entry]{},
}
}
@@ -49,7 +56,6 @@ func (p *ProxyManagerCtx) Start() {
Filters: filters.NewArgs(
filters.Arg("type", "container"),
filters.Arg("label", fmt.Sprintf("m1k1o.neko_rooms.instance=%s", p.instanceName)),
filters.Arg("label", "m1k1o.neko_rooms.proxy=true"),
filters.Arg("event", "create"),
filters.Arg("event", "start"),
//filters.Arg("event", "health_status"),
@@ -63,7 +69,7 @@ func (p *ProxyManagerCtx) Start() {
case err := <-errs:
p.logger.Info().Interface("err", err).Msg("eee")
case msg := <-msgs:
path, port, ok := p.parseLabels(msg.Actor.Attributes)
enabled, path, port, ok := p.parseLabels(msg.Actor.Attributes)
if !ok {
break
}
@@ -79,16 +85,27 @@ func (p *ProxyManagerCtx) Start() {
p.mu.Lock()
switch msg.Action {
case "create":
p.handlers.Set(path, nil)
case "start":
proxy := httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: "http",
Host: host,
p.handlers.Set(path, &entry{
running: false,
})
case "start":
e := &entry{
running: true,
}
p.handlers.Set(path, proxy)
// if proxying is disabled
if enabled {
e.handler = httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: "http",
Host: host,
})
}
p.handlers.Set(path, e)
case "stop":
p.handlers.Set(path, nil)
p.handlers.Set(path, &entry{
running: false,
})
case "destroy":
p.handlers.Remove(path)
}
@@ -118,31 +135,77 @@ func (p *ProxyManagerCtx) Refresh() error {
return err
}
p.handlers = &prefixHandler[*httputil.ReverseProxy]{}
p.handlers = &prefixHandler[*entry]{}
for _, cont := range containers {
path, port, ok := p.parseLabels(cont.Labels)
enabled, path, port, ok := p.parseLabels(cont.Labels)
if !ok {
continue
}
host := cont.ID[:12] + ":" + port
var proxy *httputil.ReverseProxy
entry := &entry{}
if cont.State == "running" {
proxy = httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: "http",
Host: host,
})
entry.running = true
// if proxying is disabled
if enabled {
entry.handler = httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: "http",
Host: host,
})
}
}
p.handlers.Set(path, proxy)
p.handlers.Set(path, entry)
}
return nil
}
func (p *ProxyManagerCtx) parseLabels(labels map[string]string) (path string, port string, ok bool) {
func (p *ProxyManagerCtx) parseLabels(labels map[string]string) (enabled bool, path string, port string, ok bool) {
enabledStr, found := labels["m1k1o.neko_rooms.proxy.enabled"]
if !found {
//
// workaround for legacy traefik labels
//
// get room name
var roomName string
roomName, ok = labels["m1k1o.neko_rooms.name"]
if !ok {
return
}
// get container name
containerName := p.instanceName + "-" + roomName
// get path
path, ok = labels["traefik.http.middlewares."+containerName+"-prf.stripprefix.prefixes"]
if !ok {
return
}
// remove last /
path = strings.TrimSuffix(path, "/")
// get port
port, ok = labels["traefik.http.services."+containerName+"-frontend.loadbalancer.server.port"]
if !ok {
return
}
//
// workaround for legacy traefik labels
//
return
}
enabledBool, err := strconv.ParseBool(enabledStr)
enabled = enabledBool && err == nil
path, ok = labels["m1k1o.neko_rooms.proxy.path"]
if !ok {
return
@@ -172,13 +235,19 @@ func (p *ProxyManagerCtx) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// if room not running
if proxy == nil {
if !proxy.running {
RoomNotRunning(w, r)
return
}
// if not proxying
if proxy.handler == nil {
RoomReady(w, r)
return
}
// handle not ready room
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
proxy.handler.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
if r.URL.Path == "/" {
RoomNotReady(w, r)
}
@@ -187,7 +256,7 @@ func (p *ProxyManagerCtx) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// strip prefix from proxy
handler := http.StripPrefix(prefix, proxy)
handler := http.StripPrefix(prefix, proxy.handler)
// handle by proxy
handler.ServeHTTP(w, r)
+33 -2
View File
@@ -41,6 +41,37 @@ func (p *prefixHandler[T]) Set(prefix string, value T) {
}
}
func (p *prefixHandler[T]) Get(prefix string) (value T, ok bool) {
arr := strings.Split(prefix, "/")
len := len(arr)
for i, a := range arr {
if a == "" {
continue
}
if p.Children == nil {
p.Children = map[string]*prefixHandler[T]{}
}
dat, found := p.Children[a]
if !found {
dat = &prefixHandler[T]{}
}
if i == len-1 {
value = dat.Value
ok = dat.Children == nil
return
}
p.Children[a] = dat
p = dat
}
return
}
func (p *prefixHandler[T]) Remove(prefix string) {
arr := strings.Split(prefix, "/")
len := len(arr)
@@ -54,8 +85,8 @@ func (p *prefixHandler[T]) Remove(prefix string) {
delete(p.Children, a)
}
dat, ok := p.Children[a]
if !ok {
dat, found := p.Children[a]
if !found {
break
}
p = dat
+1 -1
View File
@@ -217,7 +217,7 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro
labels["traefik.http.routers."+containerName+".tls.certresolver"] = t.Certresolver
}
} else {
labels["m1k1o.neko_rooms.proxy"] = "true"
labels["m1k1o.neko_rooms.proxy.enabled"] = "true"
labels["m1k1o.neko_rooms.proxy.path"] = pathPrefix
labels["m1k1o.neko_rooms.proxy.port"] = fmt.Sprintf("%d", frontendPort)
}
+12 -24
View File
@@ -55,20 +55,6 @@ func New(ApiManager types.ApiManager, roomConfig *config.Room, config *config.Se
logger.Info().Msgf("with pprof endpoint at %s", pprofPath)
}
//
// rooms page
//
router.Group(func(r chi.Router) {
if !roomConfig.Traefik.Enabled {
r.Handle("/*", proxyHandler)
} else {
// add simple lobby room
r.Get("/{roomName}", ApiManager.RoomLobby)
r.Get("/{roomName}/", ApiManager.RoomLobby)
}
})
//
// admin page
//
@@ -94,25 +80,27 @@ func New(ApiManager types.ApiManager, roomConfig *config.Room, config *config.Se
// serve static files
if config.Admin.Static != "" {
prefix := config.Admin.PathPrefix
dir := http.Dir(config.Admin.Static)
fs := http.FileServer(http.Dir(config.Admin.Static))
fs = http.StripPrefix(config.Admin.PathPrefix, fs)
fs := http.FileServer(dir)
fs = http.StripPrefix(prefix, fs)
router.Handle(prefix+"/", fs)
router.Handle(path.Join("/", config.Admin.PathPrefix, "*"), fs)
}
})
// redirect / to admin path prefix
if config.Admin.PathPrefix != "/" {
// redirect / to admin path prefix
router.Get("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, strings.TrimPrefix(config.Admin.PathPrefix, "/"), http.StatusTemporaryRedirect)
http.Redirect(w, r, strings.TrimPrefix(config.Admin.PathPrefix, "/")+"/", http.StatusTemporaryRedirect)
})
// redirect force admin path prefix ending with /
router.Get(config.Admin.PathPrefix, func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, strings.TrimPrefix(config.Admin.PathPrefix, "/")+"/", http.StatusTemporaryRedirect)
})
}
// we could use custom 404
router.NotFound(http.NotFound)
// handle all remaining paths with proxy
router.Handle("/*", proxyHandler)
return &ServerManagerCtx{
logger: logger,
-4
View File
@@ -1,13 +1,9 @@
package types
import (
"net/http"
"github.com/go-chi/chi"
)
type ApiManager interface {
Mount(r chi.Router)
RoomLobby(w http.ResponseWriter, r *http.Request)
}