Skip to content

Commit 58bb4d4

Browse files
committed
enhancement: use msgp for flash message encoding/decoding
1 parent e437633 commit 58bb4d4

File tree

4 files changed

+587
-242
lines changed

4 files changed

+587
-242
lines changed

ctx.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ type DefaultCtx struct {
6363
pathOriginal string // Original HTTP path
6464
pathBuffer []byte // HTTP path buffer
6565
detectionPathBuffer []byte // HTTP detectionPath buffer
66-
redirectionMessages []string // Messages of the previous redirect
66+
flashMessages redirectionMsgs // Flash messages
6767
indexRoute int // Index of the current route
6868
indexHandler int // Index of the current handler
6969
methodINT int // HTTP method INT equivalent
@@ -1896,7 +1896,7 @@ func (c *DefaultCtx) release() {
18961896
c.route = nil
18971897
c.fasthttp = nil
18981898
c.bind = nil
1899-
c.redirectionMessages = c.redirectionMessages[:0]
1899+
c.flashMessages = c.flashMessages[:0]
19001900
c.viewBindMap = sync.Map{}
19011901
if c.redirect != nil {
19021902
ReleaseRedirect(c.redirect)

redirect.go

+109-98
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package fiber
66

77
import (
88
"errors"
9-
"strings"
109
"sync"
1110

1211
"github.com/gofiber/fiber/v3/binder"
@@ -19,7 +18,7 @@ var redirectPool = sync.Pool{
1918
New: func() any {
2019
return &Redirect{
2120
status: StatusFound,
22-
oldInput: make(map[string]string, 0),
21+
messages: make(redirectionMsgs, 0),
2322
}
2423
},
2524
}
@@ -32,13 +31,37 @@ const (
3231
CookieDataAssigner = ":"
3332
)
3433

34+
// redirectionMsgs is a struct that used to store flash messages and old input data in cookie using MSGP.
35+
// msgp -file="redirect.go" -o="redirect_msgp.go" -tests=false -unexported
36+
//
37+
//msgp:ignore Redirect RedirectConfig OldInputData FlashMessage
38+
type redirectionMsg struct {
39+
key string
40+
value string
41+
level uint8
42+
isOldInput bool
43+
}
44+
45+
type redirectionMsgs []redirectionMsg
46+
47+
// OldInputData is a struct that holds the old input data.
48+
type OldInputData struct {
49+
Key string
50+
Value string
51+
}
52+
53+
// FlashMessage is a struct that holds the flash message data.
54+
type FlashMessage struct {
55+
Key string
56+
Value string
57+
Level uint8
58+
}
59+
3560
// Redirect is a struct that holds the redirect data.
3661
type Redirect struct {
37-
c *DefaultCtx // Embed ctx
38-
oldInput map[string]string // Old input data
39-
40-
messages []string // Flash messages
41-
status int // Status code of redirection. Default: StatusFound
62+
c *DefaultCtx // Embed ctx
63+
messages redirectionMsgs // Flash messages and old input data
64+
status int // Status code of redirection. Default: StatusFound
4265
}
4366

4467
// RedirectConfig A config to use with Redirect().Route()
@@ -71,10 +94,6 @@ func ReleaseRedirect(r *Redirect) {
7194
func (r *Redirect) release() {
7295
r.status = 302
7396
r.messages = r.messages[:0]
74-
// reset map
75-
for k := range r.oldInput {
76-
delete(r.oldInput, k)
77-
}
7897
r.c = nil
7998
}
8099

@@ -90,8 +109,18 @@ func (r *Redirect) Status(code int) *Redirect {
90109
// They will be sent as a cookie.
91110
// You can get them by using: Redirect().Messages(), Redirect().Message()
92111
// Note: You must use escape char before using ',' and ':' chars to avoid wrong parsing.
93-
func (r *Redirect) With(key, value string) *Redirect {
94-
r.messages = append(r.messages, key+CookieDataAssigner+value)
112+
func (r *Redirect) With(key, value string, level ...uint8) *Redirect {
113+
// Get level
114+
var msgLevel uint8
115+
if len(level) > 0 {
116+
msgLevel = level[0]
117+
}
118+
119+
r.messages = append(r.messages, redirectionMsg{
120+
key: key,
121+
value: value,
122+
level: msgLevel,
123+
})
95124

96125
return r
97126
}
@@ -105,76 +134,92 @@ func (r *Redirect) WithInput() *Redirect {
105134
ctype := utils.ToLower(utils.UnsafeString(r.c.Context().Request.Header.ContentType()))
106135
ctype = binder.FilterFlags(utils.ParseVendorSpecificContentType(ctype))
107136

137+
oldInput := make(map[string]string)
108138
switch ctype {
109139
case MIMEApplicationForm:
110-
_ = r.c.Bind().Form(r.oldInput) //nolint:errcheck // not needed
140+
_ = r.c.Bind().Form(oldInput) //nolint:errcheck // not needed
111141
case MIMEMultipartForm:
112-
_ = r.c.Bind().MultipartForm(r.oldInput) //nolint:errcheck // not needed
142+
_ = r.c.Bind().MultipartForm(oldInput) //nolint:errcheck // not needed
113143
default:
114-
_ = r.c.Bind().Query(r.oldInput) //nolint:errcheck // not needed
144+
_ = r.c.Bind().Query(oldInput) //nolint:errcheck // not needed
145+
}
146+
147+
// Add old input data
148+
for k, v := range oldInput {
149+
r.messages = append(r.messages, redirectionMsg{
150+
key: k,
151+
value: v,
152+
isOldInput: true,
153+
})
115154
}
116155

117156
return r
118157
}
119158

120159
// Messages Get flash messages.
121-
func (r *Redirect) Messages() map[string]string {
122-
msgs := r.c.redirectionMessages
123-
flashMessages := make(map[string]string, len(msgs))
124-
125-
for _, msg := range msgs {
126-
k, v := parseMessage(msg)
127-
128-
if !strings.HasPrefix(k, OldInputDataPrefix) {
129-
flashMessages[k] = v
160+
func (r *Redirect) Messages() []FlashMessage {
161+
flashMessages := make([]FlashMessage, 0)
162+
163+
for _, msg := range r.c.flashMessages {
164+
if !msg.isOldInput {
165+
flashMessages = append(flashMessages, FlashMessage{
166+
Key: msg.key,
167+
Value: msg.value,
168+
Level: msg.level,
169+
})
130170
}
131171
}
132172

133173
return flashMessages
134174
}
135175

136176
// Message Get flash message by key.
137-
func (r *Redirect) Message(key string) string {
138-
msgs := r.c.redirectionMessages
177+
func (r *Redirect) Message(key string) FlashMessage {
178+
msgs := r.c.flashMessages
139179

140180
for _, msg := range msgs {
141-
k, v := parseMessage(msg)
142-
143-
if !strings.HasPrefix(k, OldInputDataPrefix) && k == key {
144-
return v
181+
if msg.key == key && !msg.isOldInput {
182+
return FlashMessage{
183+
Key: msg.key,
184+
Value: msg.value,
185+
Level: msg.level,
186+
}
145187
}
146188
}
147-
return ""
189+
190+
return FlashMessage{}
148191
}
149192

150193
// OldInputs Get old input data.
151-
func (r *Redirect) OldInputs() map[string]string {
152-
msgs := r.c.redirectionMessages
153-
oldInputs := make(map[string]string, len(msgs))
154-
155-
for _, msg := range msgs {
156-
k, v := parseMessage(msg)
157-
158-
if strings.HasPrefix(k, OldInputDataPrefix) {
159-
// remove "old_input_data_" part from key
160-
oldInputs[k[len(OldInputDataPrefix):]] = v
194+
func (r *Redirect) OldInputs() []OldInputData {
195+
inputs := make([]OldInputData, 0)
196+
197+
for _, msg := range r.c.flashMessages {
198+
if msg.isOldInput {
199+
inputs = append(inputs, OldInputData{
200+
Key: msg.key,
201+
Value: msg.value,
202+
})
161203
}
162204
}
163-
return oldInputs
205+
206+
return inputs
164207
}
165208

166209
// OldInput Get old input data by key.
167-
func (r *Redirect) OldInput(key string) string {
168-
msgs := r.c.redirectionMessages
210+
func (r *Redirect) OldInput(key string) OldInputData {
211+
msgs := r.c.flashMessages
169212

170213
for _, msg := range msgs {
171-
k, v := parseMessage(msg)
172-
173-
if strings.HasPrefix(k, OldInputDataPrefix) && k[len(OldInputDataPrefix):] == key {
174-
return v
214+
if msg.key == key && msg.isOldInput {
215+
return OldInputData{
216+
Key: msg.key,
217+
Value: msg.value,
218+
}
175219
}
176220
}
177-
return ""
221+
222+
return OldInputData{}
178223
}
179224

180225
// To redirect to the URL derived from the specified path, with specified status.
@@ -240,66 +285,32 @@ func (r *Redirect) Back(fallback ...string) error {
240285
return r.To(location)
241286
}
242287

243-
// parseAndClearFlashMessages is a method to get flash messages before removing them
288+
// parseAndClearFlashMessages is a method to get flash messages before they are getting removed
244289
func (r *Redirect) parseAndClearFlashMessages() {
245290
// parse flash messages
246291
cookieValue := r.c.Cookies(FlashCookieName)
247292

248-
var commaPos int
249-
for {
250-
commaPos = findNextNonEscapedCharsetPosition(cookieValue, []byte(CookieDataSeparator))
251-
if commaPos == -1 {
252-
r.c.redirectionMessages = append(r.c.redirectionMessages, utils.Trim(cookieValue, ' '))
253-
break
254-
}
255-
r.c.redirectionMessages = append(r.c.redirectionMessages, utils.Trim(cookieValue[:commaPos], ' '))
256-
cookieValue = cookieValue[commaPos+1:]
293+
_, err := r.c.flashMessages.UnmarshalMsg(r.c.app.getBytes(cookieValue))
294+
if err != nil {
295+
return
257296
}
258-
259-
r.c.ClearCookie(FlashCookieName)
260297
}
261298

262299
// processFlashMessages is a helper function to process flash messages and old input data
263300
// and set them as cookies
264301
func (r *Redirect) processFlashMessages() {
265-
// Flash messages
266-
if len(r.messages) > 0 || len(r.oldInput) > 0 {
267-
messageText := bytebufferpool.Get()
268-
defer bytebufferpool.Put(messageText)
269-
270-
// flash messages
271-
for i, message := range r.messages {
272-
messageText.WriteString(message)
273-
// when there are more messages or oldInput -> add a comma
274-
if len(r.messages)-1 != i || (len(r.messages)-1 == i && len(r.oldInput) > 0) {
275-
messageText.WriteString(CookieDataSeparator)
276-
}
277-
}
278-
r.messages = r.messages[:0]
279-
280-
// old input data
281-
i := 1
282-
for k, v := range r.oldInput {
283-
messageText.WriteString(OldInputDataPrefix + k + CookieDataAssigner + v)
284-
if len(r.oldInput) != i {
285-
messageText.WriteString(CookieDataSeparator)
286-
}
287-
i++
288-
}
289-
290-
r.c.Cookie(&Cookie{
291-
Name: FlashCookieName,
292-
Value: r.c.app.getString(messageText.Bytes()),
293-
SessionOnly: true,
294-
})
302+
if len(r.messages) == 0 {
303+
return
295304
}
296-
}
297305

298-
// parseMessage is a helper function to parse flash messages and old input data
299-
func parseMessage(raw string) (string, string) { //nolint: revive // not necessary
300-
if i := findNextNonEscapedCharsetPosition(raw, []byte(CookieDataAssigner)); i != -1 {
301-
return RemoveEscapeChar(raw[:i]), RemoveEscapeChar(raw[i+1:])
306+
val, err := r.messages.MarshalMsg(nil)
307+
if err != nil {
308+
return
302309
}
303310

304-
return RemoveEscapeChar(raw), ""
311+
r.c.Cookie(&Cookie{
312+
Name: FlashCookieName,
313+
Value: r.c.app.getString(val),
314+
SessionOnly: true,
315+
})
305316
}

0 commit comments

Comments
 (0)