From f0c5f2d643323531f13d05c153961eff5febbc0a Mon Sep 17 00:00:00 2001 From: Wessie Date: Mon, 25 Mar 2024 22:51:13 +0000 Subject: [PATCH] proxy: implement User data passover for graceful restarts util: added RedirectBack that tries to read the URL the request originated from and modifies the request to go back there again. For use in website redirects graceful: added ReadJSONConn that converts the fd passed to a net.Conn directly --- proxy/mount.go | 9 ++------- proxy/source.go | 22 +++++++++++++--------- templates/middleware.go | 13 ++----------- util/graceful/json.go | 15 +++++++++++++++ util/util.go | 14 ++++++++++++++ website/admin/songs.go | 14 +++++++++++--- 6 files changed, 57 insertions(+), 30 deletions(-) diff --git a/proxy/mount.go b/proxy/mount.go index d84f99ea..590bae32 100644 --- a/proxy/mount.go +++ b/proxy/mount.go @@ -181,19 +181,13 @@ func (m *Mount) readSelf(ctx context.Context, cfg config.Config, src *net.UnixCo zerolog.Ctx(ctx).Debug().Msg("resume: reading mount") - file, err := graceful.ReadJSONFile(src, &wm) + conn, err := graceful.ReadJSONConn(src, &wm) if err != nil { return err } - defer file.Close() zerolog.Ctx(ctx).Debug().Any("wireMount", wm).Msg("resume") - conn, err := net.FileConn(file) - if err != nil { - return err - } - newmount := NewMount(ctx, cfg, m.pm, wm.Name, wm.ContentType, conn) *m = *newmount @@ -201,6 +195,7 @@ func (m *Mount) readSelf(ctx context.Context, cfg config.Config, src *net.UnixCo // this indicates the mount was probably in cleanup state and was gonna // close connections soon, we do the same m.pm.RemoveMount(newmount) + return nil } for i := 0; i < wm.SourceCount; i++ { diff --git a/proxy/source.go b/proxy/source.go index 6787a278..19704f21 100644 --- a/proxy/source.go +++ b/proxy/source.go @@ -12,6 +12,7 @@ import ( radio "github.com/R-a-dio/valkyrie" "github.com/R-a-dio/valkyrie/config" "github.com/R-a-dio/valkyrie/proxy/compat" + "github.com/R-a-dio/valkyrie/storage" "github.com/R-a-dio/valkyrie/util/graceful" "github.com/R-a-dio/valkyrie/website/middleware" "github.com/rs/xid" @@ -164,7 +165,7 @@ type wireSource struct { UserAgent string ContentType string MountName string - UserID radio.UserID + Username string Identifier Identifier Metadata *Metadata } @@ -175,7 +176,7 @@ func (sc *SourceClient) writeSelf(dst *net.UnixConn) error { UserAgent: sc.UserAgent, ContentType: sc.ContentType, MountName: sc.MountName, - UserID: sc.User.ID, + Username: sc.User.Username, Identifier: sc.Identifier, Metadata: sc.Metadata.Load(), } @@ -198,30 +199,33 @@ func (sc *SourceClient) readSelf(ctx context.Context, cfg config.Config, src *ne var ws wireSource zerolog.Ctx(ctx).Info().Msg("resume: reading source client") - file, err := graceful.ReadJSONFile(src, &ws) + conn, err := graceful.ReadJSONConn(src, &ws) if err != nil { return err } - defer file.Close() zerolog.Ctx(ctx).Info().Any("ws", ws).Msg("resume") - conn, err := net.FileConn(file) + // we only get the username over the wire, so we need to grab the real user struct + // from storage + ss, err := storage.Open(ctx, cfg) if err != nil { return err } - // TODO: implement this - var user radio.User - _ = user + user, err := ss.User(ctx).Get(ws.Username) + if err != nil { + return err + } + // construct the source client with the passed data new := NewSourceClient( ws.ID, ws.UserAgent, ws.ContentType, ws.MountName, conn, - user, + *user, ws.Identifier, ws.Metadata, ) diff --git a/templates/middleware.go b/templates/middleware.go index 6ae8521f..ca727691 100644 --- a/templates/middleware.go +++ b/templates/middleware.go @@ -3,7 +3,6 @@ package templates import ( "context" "net/http" - "net/url" "strings" "time" @@ -81,20 +80,12 @@ func SetThemeHandler(cookieName string, resolve func(string) string) http.Handle HttpOnly: true, }) - current, err := url.Parse(r.Header.Get("Hx-Current-Url")) - if err != nil { - r.URL = current - } else { - current, err = url.Parse(r.Header.Get("Referer")) - if err == nil { - r.URL = current - } - } + r = util.RedirectBack(r) if !util.IsHTMX(r) { // not a htmx request so probably no-js, send a http redirect to refresh // the page instead with the new cookie set - http.Redirect(w, r, current.String(), http.StatusFound) + http.Redirect(w, r, r.URL.String(), http.StatusFound) w.WriteHeader(http.StatusOK) return } diff --git a/util/graceful/json.go b/util/graceful/json.go index ef3bab69..d7a78dbf 100644 --- a/util/graceful/json.go +++ b/util/graceful/json.go @@ -37,6 +37,21 @@ func ReadJSONFile(src *net.UnixConn, msg any) (*os.File, error) { return handle, json.Unmarshal(buf[:n], msg) } +func ReadJSONConn(src *net.UnixConn, msg any) (net.Conn, error) { + file, err := ReadJSONFile(src, msg) + if err != nil { + return nil, err + } + defer file.Close() + + conn, err := net.FileConn(file) + if err != nil { + return nil, err + } + + return conn, nil +} + func ReadJSON(src *net.UnixConn, msg any) error { buf := make([]byte, 32*1024) tiny := make([]byte, 512) diff --git a/util/util.go b/util/util.go index c4a9122a..7b75c08f 100644 --- a/util/util.go +++ b/util/util.go @@ -3,6 +3,7 @@ package util import ( "context" "net/http" + "net/url" "sync/atomic" "time" @@ -15,6 +16,19 @@ func IsHTMX(r *http.Request) bool { return r.Header.Get("Hx-Request") == "true" } +func RedirectBack(r *http.Request) *http.Request { + current, err := url.Parse(r.Header.Get("Hx-Current-Url")) + if err != nil { + r.URL = current + } else { + current, err = url.Parse(r.Header.Get("Referer")) + if err == nil { + r.URL = current + } + } + return r +} + type StreamFn[T any] func(context.Context) (eventstream.Stream[T], error) type StreamCallbackFn[T any] func(context.Context, T) diff --git a/website/admin/songs.go b/website/admin/songs.go index ab3eb8c6..5b076985 100644 --- a/website/admin/songs.go +++ b/website/admin/songs.go @@ -101,12 +101,20 @@ func (s *State) PostSongs(w http.ResponseWriter, r *http.Request) { return } - if form == nil && util.IsHTMX(r) { - // delete operation that succeeded and htmx, return nothing + if util.IsHTMX(r) { + if form == nil { + return + } + err = s.TemplateExecutor.Execute(w, r, form) + if err != nil { + hlog.FromRequest(r).Error().Err(err).Msg("template failure") + return + } return } - // otherwise just return the new listing + // otherwise just return to the existing listing + r = util.RedirectBack(r) s.GetSongs(w, r) }