@@ -6,7 +6,6 @@ package fiber
6
6
7
7
import (
8
8
"errors"
9
- "strings"
10
9
"sync"
11
10
12
11
"github.com/gofiber/fiber/v3/binder"
@@ -19,7 +18,7 @@ var redirectPool = sync.Pool{
19
18
New : func () any {
20
19
return & Redirect {
21
20
status : StatusFound ,
22
- oldInput : make (map [ string ] string , 0 ),
21
+ messages : make (redirectionMsgs , 0 ),
23
22
}
24
23
},
25
24
}
@@ -32,13 +31,37 @@ const (
32
31
CookieDataAssigner = ":"
33
32
)
34
33
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
+
35
60
// Redirect is a struct that holds the redirect data.
36
61
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
42
65
}
43
66
44
67
// RedirectConfig A config to use with Redirect().Route()
@@ -71,10 +94,6 @@ func ReleaseRedirect(r *Redirect) {
71
94
func (r * Redirect ) release () {
72
95
r .status = 302
73
96
r .messages = r .messages [:0 ]
74
- // reset map
75
- for k := range r .oldInput {
76
- delete (r .oldInput , k )
77
- }
78
97
r .c = nil
79
98
}
80
99
@@ -90,8 +109,18 @@ func (r *Redirect) Status(code int) *Redirect {
90
109
// They will be sent as a cookie.
91
110
// You can get them by using: Redirect().Messages(), Redirect().Message()
92
111
// 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
+ })
95
124
96
125
return r
97
126
}
@@ -105,76 +134,92 @@ func (r *Redirect) WithInput() *Redirect {
105
134
ctype := utils .ToLower (utils .UnsafeString (r .c .Context ().Request .Header .ContentType ()))
106
135
ctype = binder .FilterFlags (utils .ParseVendorSpecificContentType (ctype ))
107
136
137
+ oldInput := make (map [string ]string )
108
138
switch ctype {
109
139
case MIMEApplicationForm :
110
- _ = r .c .Bind ().Form (r . oldInput ) //nolint:errcheck // not needed
140
+ _ = r .c .Bind ().Form (oldInput ) //nolint:errcheck // not needed
111
141
case MIMEMultipartForm :
112
- _ = r .c .Bind ().MultipartForm (r . oldInput ) //nolint:errcheck // not needed
142
+ _ = r .c .Bind ().MultipartForm (oldInput ) //nolint:errcheck // not needed
113
143
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
+ })
115
154
}
116
155
117
156
return r
118
157
}
119
158
120
159
// 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
+ })
130
170
}
131
171
}
132
172
133
173
return flashMessages
134
174
}
135
175
136
176
// 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
139
179
140
180
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
+ }
145
187
}
146
188
}
147
- return ""
189
+
190
+ return FlashMessage {}
148
191
}
149
192
150
193
// 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
+ })
161
203
}
162
204
}
163
- return oldInputs
205
+
206
+ return inputs
164
207
}
165
208
166
209
// 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
169
212
170
213
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
+ }
175
219
}
176
220
}
177
- return ""
221
+
222
+ return OldInputData {}
178
223
}
179
224
180
225
// To redirect to the URL derived from the specified path, with specified status.
@@ -240,66 +285,32 @@ func (r *Redirect) Back(fallback ...string) error {
240
285
return r .To (location )
241
286
}
242
287
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
244
289
func (r * Redirect ) parseAndClearFlashMessages () {
245
290
// parse flash messages
246
291
cookieValue := r .c .Cookies (FlashCookieName )
247
292
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
257
296
}
258
-
259
- r .c .ClearCookie (FlashCookieName )
260
297
}
261
298
262
299
// processFlashMessages is a helper function to process flash messages and old input data
263
300
// and set them as cookies
264
301
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
295
304
}
296
- }
297
305
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
302
309
}
303
310
304
- return RemoveEscapeChar (raw ), ""
311
+ r .c .Cookie (& Cookie {
312
+ Name : FlashCookieName ,
313
+ Value : r .c .app .getString (val ),
314
+ SessionOnly : true ,
315
+ })
305
316
}
0 commit comments