mirror of
https://github.com/gin-gonic/gin.git
synced 2025-04-06 03:57:46 +08:00
Merge 4eecebf8bfcfdf2bde13f2757931a570e4d38529 into 8763f33c65f7df8be5b9fe7504ab7fcf20abb41d
This commit is contained in:
commit
bd1d2ec7c7
46
context.go
46
context.go
@ -6,9 +6,11 @@ package gin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net"
|
"net"
|
||||||
@ -47,6 +49,20 @@ const ContextKey = "_gin-gonic/gin/contextkey"
|
|||||||
|
|
||||||
type ContextKeyType int
|
type ContextKeyType int
|
||||||
|
|
||||||
|
type CookieOption func(*http.Cookie)
|
||||||
|
|
||||||
|
func WithPartitionedCookie(partitioned bool) CookieOption {
|
||||||
|
return func(cookie *http.Cookie) {
|
||||||
|
cookie.Partitioned = partitioned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithSameSiteCookie(sameSite http.SameSite) CookieOption {
|
||||||
|
return func(cookie *http.Cookie) {
|
||||||
|
cookie.SameSite = sameSite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const ContextRequestKey ContextKeyType = 0
|
const ContextRequestKey ContextKeyType = 0
|
||||||
|
|
||||||
// abortIndex represents a typical value used in abort functions.
|
// abortIndex represents a typical value used in abort functions.
|
||||||
@ -87,9 +103,9 @@ type Context struct {
|
|||||||
// or PUT body parameters.
|
// or PUT body parameters.
|
||||||
formCache url.Values
|
formCache url.Values
|
||||||
|
|
||||||
// SameSite allows a server to define a cookie attribute making it impossible for
|
// cookieOptions set up additional cookie parameters to be used with http.Cookie
|
||||||
// the browser to send this cookie along with cross-site requests.
|
// this is needed to prevent breaking existing implementations
|
||||||
sameSite http.SameSite
|
cookieOptions []CookieOption
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************/
|
/************************************/
|
||||||
@ -108,7 +124,7 @@ func (c *Context) reset() {
|
|||||||
c.Accepted = nil
|
c.Accepted = nil
|
||||||
c.queryCache = nil
|
c.queryCache = nil
|
||||||
c.formCache = nil
|
c.formCache = nil
|
||||||
c.sameSite = 0
|
c.cookieOptions = make([]CookieOption, 0)
|
||||||
*c.params = (*c.params)[:0]
|
*c.params = (*c.params)[:0]
|
||||||
*c.skippedNodes = (*c.skippedNodes)[:0]
|
*c.skippedNodes = (*c.skippedNodes)[:0]
|
||||||
}
|
}
|
||||||
@ -1005,26 +1021,38 @@ func (c *Context) GetRawData() ([]byte, error) {
|
|||||||
|
|
||||||
// SetSameSite with cookie
|
// SetSameSite with cookie
|
||||||
func (c *Context) SetSameSite(samesite http.SameSite) {
|
func (c *Context) SetSameSite(samesite http.SameSite) {
|
||||||
c.sameSite = samesite
|
c.cookieOptions = append(c.cookieOptions, WithSameSiteCookie(samesite))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
|
// SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
|
||||||
// The provided cookie must have a valid Name. Invalid cookies may be
|
// The provided cookie must have a valid Name. Invalid cookies may be
|
||||||
// silently dropped.
|
// silently dropped.
|
||||||
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
|
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool, options ...CookieOption) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = "/"
|
path = "/"
|
||||||
}
|
}
|
||||||
http.SetCookie(c.Writer, &http.Cookie{
|
|
||||||
|
cookie := &http.Cookie{
|
||||||
Name: name,
|
Name: name,
|
||||||
Value: url.QueryEscape(value),
|
Value: url.QueryEscape(value),
|
||||||
MaxAge: maxAge,
|
MaxAge: maxAge,
|
||||||
Path: path,
|
Path: path,
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
SameSite: c.sameSite,
|
|
||||||
Secure: secure,
|
Secure: secure,
|
||||||
HttpOnly: httpOnly,
|
HttpOnly: httpOnly,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
for _, option := range c.cookieOptions {
|
||||||
|
option(cookie)
|
||||||
|
}
|
||||||
|
for _, option := range options {
|
||||||
|
option(cookie)
|
||||||
|
}
|
||||||
|
if err := cookie.Valid(); err != nil {
|
||||||
|
slog.Error(fmt.Sprintf("invalid cookie: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
http.SetCookie(c.Writer, cookie)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cookie returns the named cookie provided in the request or
|
// Cookie returns the named cookie provided in the request or
|
||||||
|
@ -206,6 +206,7 @@ func TestContextReset(t *testing.T) {
|
|||||||
c.Params = Params{Param{}}
|
c.Params = Params{Param{}}
|
||||||
c.Error(errors.New("test")) //nolint: errcheck
|
c.Error(errors.New("test")) //nolint: errcheck
|
||||||
c.Set("foo", "bar")
|
c.Set("foo", "bar")
|
||||||
|
c.SetSameSite(http.SameSiteLaxMode)
|
||||||
c.reset()
|
c.reset()
|
||||||
|
|
||||||
assert.False(t, c.IsAborted())
|
assert.False(t, c.IsAborted())
|
||||||
@ -215,6 +216,7 @@ func TestContextReset(t *testing.T) {
|
|||||||
assert.Empty(t, c.Errors.Errors())
|
assert.Empty(t, c.Errors.Errors())
|
||||||
assert.Empty(t, c.Errors.ByType(ErrorTypeAny))
|
assert.Empty(t, c.Errors.ByType(ErrorTypeAny))
|
||||||
assert.Empty(t, c.Params)
|
assert.Empty(t, c.Params)
|
||||||
|
assert.Empty(t, c.cookieOptions)
|
||||||
assert.EqualValues(t, c.index, -1)
|
assert.EqualValues(t, c.index, -1)
|
||||||
assert.Equal(t, c.Writer.(*responseWriter), &c.writermem)
|
assert.Equal(t, c.Writer.(*responseWriter), &c.writermem)
|
||||||
}
|
}
|
||||||
@ -866,6 +868,18 @@ func TestContextSetCookie(t *testing.T) {
|
|||||||
assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie"))
|
assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextSetCookieWithOptions(t *testing.T) {
|
||||||
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
|
c.SetCookie("user", "gin", 1, "/", "localhost", true, true, WithSameSiteCookie(http.SameSiteLaxMode), WithPartitionedCookie(true))
|
||||||
|
assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax; Partitioned", c.Writer.Header().Get("Set-Cookie"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextSetCookieWithNonSecurePartition(t *testing.T) {
|
||||||
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
|
c.SetCookie("user", "gin", 1, "/", "localhost", false, true, WithPartitionedCookie(true))
|
||||||
|
assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Partitioned", c.Writer.Header().Get("Set-Cookie"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestContextSetCookiePathEmpty(t *testing.T) {
|
func TestContextSetCookiePathEmpty(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
c.SetSameSite(http.SameSiteLaxMode)
|
c.SetSameSite(http.SameSiteLaxMode)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user