Implement loading screen for slow requests

This commit is contained in:
Anders Pitman 2020-10-16 11:21:41 -06:00
parent 9aafa18254
commit cca211de0e
2 changed files with 110 additions and 20 deletions

View File

@ -1,7 +1,6 @@
package main
import (
"errors"
"fmt"
"github.com/GeertJohan/go.rice"
"html/template"
@ -9,6 +8,8 @@ import (
"log"
"net/http"
"strings"
"sync"
"time"
)
type WebUiHandler struct {
@ -19,6 +20,8 @@ type WebUiHandler struct {
tunMan *TunnelManager
box *rice.Box
headHtml template.HTML
pendingRequests map[string]chan string
mutex *sync.Mutex
}
type IndexData struct {
@ -41,6 +44,11 @@ type ConfirmData struct {
CancelUrl string
}
type LoadingData struct {
Head template.HTML
TargetUrl string
}
type AlertData struct {
Head template.HTML
Message string
@ -81,6 +89,8 @@ func NewWebUiHandler(config *BoringProxyConfig, db *Database, api *Api, auth *Au
api: api,
auth: auth,
tunMan: tunMan,
pendingRequests: make(map[string]chan string),
mutex: &sync.Mutex{},
}
}
@ -291,6 +301,8 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
cookie := &http.Cookie{Name: "access_token", Value: "", Secure: true, HttpOnly: true}
http.SetCookie(w, cookie)
http.Redirect(w, r, "/#/tunnels", 303)
case "/loading":
h.handleLoading(w, r)
default:
w.WriteHeader(404)
h.alertDialog(w, r, "Unknown page "+r.URL.Path, "/#/tunnels")
@ -377,16 +389,57 @@ func (h *WebUiHandler) handleTunnels(w http.ResponseWriter, r *http.Request, tok
func (h *WebUiHandler) handleCreateTunnel(w http.ResponseWriter, r *http.Request, tokenData TokenData) {
pendingId, err := genRandomCode(16)
if err != nil {
w.WriteHeader(400)
h.alertDialog(w, r, err.Error(), "/#/tunnels")
}
doneSignal := make(chan string)
h.mutex.Lock()
h.pendingRequests[pendingId] = doneSignal
h.mutex.Unlock()
go func() {
r.ParseForm()
_, err := h.api.CreateTunnel(tokenData, r.Form)
if err != nil {
w.WriteHeader(400)
h.alertDialog(w, r, err.Error(), "/#/tunnels")
}
doneSignal <- "/#/tunnels"
}()
timeout := make(chan bool, 1)
go func() {
time.Sleep(100 * time.Millisecond)
timeout <- true
}()
select {
case <-timeout:
url := fmt.Sprintf("/loading?id=%s", pendingId)
tmpl, err := h.loadTemplate("loading.tmpl")
if err != nil {
w.WriteHeader(500)
h.alertDialog(w, r, err.Error(), "/#/tunnels")
return
}
data := &LoadingData{
Head: h.headHtml,
TargetUrl: url,
}
tmpl.Execute(w, data)
case <-doneSignal:
http.Redirect(w, r, "/#/tunnels", 303)
}
}
func (h *WebUiHandler) sendLoginPage(w http.ResponseWriter, r *http.Request, code int) {
@ -555,16 +608,36 @@ func (h *WebUiHandler) alertDialog(w http.ResponseWriter, r *http.Request, messa
return nil
}
func (h *WebUiHandler) handleLoading(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
w.WriteHeader(405)
h.alertDialog(w, r, "Invalid method for users", "/#/tunnels")
}
r.ParseForm()
pendingId := r.Form.Get("id")
h.mutex.Lock()
doneSignal := h.pendingRequests[pendingId]
h.mutex.Unlock()
redirUrl := <-doneSignal
http.Redirect(w, r, redirUrl, 303)
}
func (h *WebUiHandler) loadTemplate(name string) (*template.Template, error) {
tmplStr, err := h.box.String(name)
if err != nil {
return nil, errors.New("Error reading template " + name)
return nil, err
}
tmpl, err := template.New(name).Parse(tmplStr)
if err != nil {
return nil, errors.New("Error compiling template " + name)
return nil, err
}
return tmpl, nil

17
webui/loading.tmpl Normal file
View File

@ -0,0 +1,17 @@
<!doctype html>
<html>
<head>
<meta http-equiv="refresh" content="0; URL='{{$.TargetUrl}}'" />
{{.Head}}
</head>
<body>
<main>
<div class='dialog'>
<p>
Loading...
</p>
</div>
</main>
</body>
</html>