add room wait handler.

This commit is contained in:
Miroslav Šedivý
2022-08-05 01:02:59 +02:00
parent 41facda0bc
commit 7b71067da0
2 changed files with 117 additions and 4 deletions
+46
View File
@@ -6,6 +6,40 @@ import (
"github.com/m1k1o/neko-rooms/internal/utils"
)
func roomWait(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`<script>
(async function() {
document.querySelector(".swal2-loader").style.display = 'block'
document.querySelector(".swal2-loader").style.visibility = 'hidden'
let lastAttempt = (new Date()).getTime()
while(true) {
try {
lastAttempt = (new Date()).getTime()
document.querySelector(".swal2-loader").style.visibility = 'visible'
await fetch("?wait")
location.href = location.href
} catch {
let now = (new Date()).getTime()
let diff = now - lastAttempt
// if the gap between last attempt and now
// is gt 20s, do reconnect immediatly
if ((now - lastAttempt) > 20*1000) {
continue
}
// wait for 10 sec
await new Promise(res => setTimeout(res, 2500))
document.querySelector(".swal2-loader").style.visibility = 'hidden'
await new Promise(res => setTimeout(res, 7500))
}
}
}())
</script>`))
}
func RoomNotFound(w http.ResponseWriter, r *http.Request) {
utils.Swal2Response(w, `
<div class="swal2-header">
@@ -16,8 +50,14 @@ func RoomNotFound(w http.ResponseWriter, r *http.Request) {
</div>
<div class="swal2-content">
<div>The room you are trying to join does not exist.</div>
<div>You can wait on this page until it will be created.</div>
</div>
<div class="swal2-actions">
<div class="swal2-loader" style="display:none;"></div>
</div>
`)
roomWait(w, r)
}
func RoomNotRunning(w http.ResponseWriter, r *http.Request) {
@@ -30,8 +70,14 @@ func RoomNotRunning(w http.ResponseWriter, r *http.Request) {
</div>
<div class="swal2-content">
<div>The room you are trying to join is not running.</div>
<div>You can wait on this page until it will be started.</div>
</div>
<div class="swal2-actions">
<div class="swal2-loader" style="display:none;"></div>
</div>
`)
roomWait(w, r)
}
func RoomNotReady(w http.ResponseWriter, r *http.Request) {
+71 -4
View File
@@ -6,6 +6,7 @@ import (
"net/http"
"net/http/httputil"
"net/url"
"path"
"strconv"
"strings"
"sync"
@@ -23,12 +24,20 @@ type entry struct {
handler *httputil.ReverseProxy
}
type wait struct {
subs int
signal chan struct{}
}
type ProxyManagerCtx struct {
logger zerolog.Logger
mu sync.RWMutex
ctx context.Context
cancel func()
waitMu sync.RWMutex
waitChans map[string]*wait
client *dockerClient.Client
instanceName string
handlers *prefixHandler[*entry]
@@ -36,7 +45,8 @@ type ProxyManagerCtx struct {
func New(client *dockerClient.Client, instanceName string) *ProxyManagerCtx {
return &ProxyManagerCtx{
logger: log.With().Str("module", "proxy").Logger(),
logger: log.With().Str("module", "proxy").Logger(),
waitChans: map[string]*wait{},
client: client,
instanceName: instanceName,
@@ -83,6 +93,15 @@ func (p *ProxyManagerCtx) Start() {
Str("host", host).
Msg("got docker event")
// terminate waiting for any events
p.waitMu.Lock()
ch, ok := p.waitChans[path]
if ok {
close(ch.signal)
delete(p.waitChans, path)
}
p.waitMu.Unlock()
p.mu.Lock()
switch msg.Action {
case "create":
@@ -222,27 +241,75 @@ func (p *ProxyManagerCtx) parseLabels(labels map[string]string) (enabled bool, p
return
}
func (p *ProxyManagerCtx) waitForPath(w http.ResponseWriter, r *http.Request, path string) {
p.logger.Debug().Str("path", path).Msg("adding new wait handler")
p.waitMu.Lock()
ch, ok := p.waitChans[path]
if !ok {
ch = &wait{
subs: 1,
signal: make(chan struct{}),
}
p.waitChans[path] = ch
} else {
p.waitChans[path].subs += 1
}
p.waitMu.Unlock()
select {
case <-ch.signal:
w.Write([]byte("ready"))
case <-r.Context().Done():
http.Error(w, r.Context().Err().Error(), http.StatusRequestTimeout)
p.waitMu.Lock()
ch.subs -= 1
if ch.subs <= 0 {
delete(p.waitChans, path)
}
p.waitMu.Unlock()
p.logger.Debug().Str("path", path).Msg("wait handler removed")
case <-p.ctx.Done():
w.Write([]byte("shutdown"))
}
}
func (p *ProxyManagerCtx) ServeHTTP(w http.ResponseWriter, r *http.Request) {
cleanPath := path.Clean(r.URL.Path)
// get proxy by room name
p.mu.RLock()
proxy, prefix, ok := p.handlers.Match(r.URL.Path)
proxy, prefix, ok := p.handlers.Match(cleanPath)
p.mu.RUnlock()
// if room not found
if !ok {
// blocking until room is created
if r.URL.Query().Has("wait") {
p.waitForPath(w, r, cleanPath)
return
}
RoomNotFound(w, r)
return
}
// redirect to room ending with /
if r.URL.Path == prefix {
r.URL.Path += "/"
if cleanPath == prefix && !strings.HasSuffix(r.URL.Path, "/") {
r.URL.Path = cleanPath + "/"
http.Redirect(w, r, r.URL.String(), http.StatusTemporaryRedirect)
return
}
// if room not running
if !proxy.running {
// blocking until room is running
if r.URL.Query().Has("wait") {
p.waitForPath(w, r, cleanPath)
return
}
RoomNotRunning(w, r)
return
}