@@ -2959,6 +2959,127 @@ func Test_Ctx_Subdomains(t *testing.T) {
29592959 utils .AssertEqual (t , []string {"localhost:3000" }, c .Subdomains ())
29602960}
29612961
2962+ // go test -run Test_Ctx_Immutable_AfterHandler
2963+ func Test_Ctx_Immutable_AfterHandler (t * testing.T ) {
2964+ t .Parallel ()
2965+
2966+ app := New (Config {
2967+ Immutable : true ,
2968+ ProxyHeader : HeaderXForwardedFor ,
2969+ EnableIPValidation : true ,
2970+ })
2971+
2972+ type ctxSnapshot struct {
2973+ method string
2974+ path string
2975+ originalURL string
2976+ baseURL string
2977+ protocol string
2978+ hostname string
2979+ paramName string
2980+ queryFoo string
2981+ cookieSession string
2982+ headerUserAgent string
2983+ ip string
2984+ ips []string
2985+ subdomains []string
2986+ body []byte
2987+ routePath string
2988+ }
2989+
2990+ snapshots := make ([]ctxSnapshot , 0 , 2 )
2991+
2992+ app .All ("/v1/:name" , func (c * Ctx ) error {
2993+ snapshots = append (snapshots , ctxSnapshot {
2994+ method : c .Method (),
2995+ path : c .Path (),
2996+ originalURL : c .OriginalURL (),
2997+ baseURL : c .BaseURL (),
2998+ protocol : c .Protocol (),
2999+ hostname : c .Hostname (),
3000+ paramName : c .Params ("name" ),
3001+ queryFoo : c .Query ("foo" ),
3002+ cookieSession : c .Cookies ("session" ),
3003+ headerUserAgent : c .Get (HeaderUserAgent ),
3004+ ip : c .IP (),
3005+ ips : c .IPs (),
3006+ subdomains : c .Subdomains (),
3007+ body : c .Body (),
3008+ routePath : c .Route ().Path ,
3009+ })
3010+
3011+ return c .SendString ("ok" )
3012+ })
3013+
3014+ req := httptest .NewRequest (MethodPost , "https://initial.invalid/v1/alpha?foo=bar" , strings .NewReader ("body-one" ))
3015+ req .Header .Set (HeaderXForwardedHost , "p1.api.example.com" )
3016+ req .Header .Set (HeaderXForwardedProto , "https" )
3017+ req .Header .Set (HeaderXForwardedFor , "10.0.0.1, 10.0.0.2" )
3018+ req .Header .Set (HeaderUserAgent , "agent-one" )
3019+ req .Header .Set (HeaderCookie , "session=alpha" )
3020+
3021+ originalFirst := req .URL .String ()
3022+
3023+ resp , err := app .Test (req )
3024+ utils .AssertEqual (t , nil , err )
3025+ utils .AssertEqual (t , StatusOK , resp .StatusCode )
3026+ utils .AssertEqual (t , nil , resp .Body .Close ())
3027+
3028+ utils .AssertEqual (t , 1 , len (snapshots ))
3029+ first := snapshots [0 ]
3030+
3031+ follow := httptest .NewRequest (MethodPatch , "http://secondary.invalid/v1/beta?foo=qux" , strings .NewReader ("body-two" ))
3032+ follow .Header .Set (HeaderXForwardedHost , "edge.stage.example.org" )
3033+ follow .Header .Set (HeaderXForwardedProto , "http" )
3034+ follow .Header .Set (HeaderXForwardedFor , "192.168.1.50" )
3035+ follow .Header .Set (HeaderUserAgent , "agent-two" )
3036+ follow .Header .Set (HeaderCookie , "session=beta" )
3037+
3038+ originalSecond := follow .URL .String ()
3039+
3040+ resp , err = app .Test (follow )
3041+ utils .AssertEqual (t , nil , err )
3042+ utils .AssertEqual (t , StatusOK , resp .StatusCode )
3043+ utils .AssertEqual (t , nil , resp .Body .Close ())
3044+
3045+ utils .AssertEqual (t , 2 , len (snapshots ))
3046+ second := snapshots [1 ]
3047+
3048+ // Ensure the first request's state stays valid after the context is released.
3049+ utils .AssertEqual (t , MethodPost , first .method )
3050+ utils .AssertEqual (t , "/v1/alpha" , first .path )
3051+ utils .AssertEqual (t , originalFirst , first .originalURL )
3052+ utils .AssertEqual (t , "https://p1.api.example.com" , first .baseURL )
3053+ utils .AssertEqual (t , "https" , first .protocol )
3054+ utils .AssertEqual (t , "p1.api.example.com" , first .hostname )
3055+ utils .AssertEqual (t , "alpha" , first .paramName )
3056+ utils .AssertEqual (t , "bar" , first .queryFoo )
3057+ utils .AssertEqual (t , "alpha" , first .cookieSession )
3058+ utils .AssertEqual (t , "agent-one" , first .headerUserAgent )
3059+ utils .AssertEqual (t , "10.0.0.1" , first .ip )
3060+ utils .AssertEqual (t , []string {"10.0.0.1" , "10.0.0.2" }, first .ips )
3061+ utils .AssertEqual (t , []string {"p1" , "api" }, first .subdomains )
3062+ utils .AssertEqual (t , "body-one" , string (first .body ))
3063+ utils .AssertEqual (t , "/v1/:name" , first .routePath )
3064+
3065+ // Verify the second request collected distinct immutable copies.
3066+ utils .AssertEqual (t , MethodPatch , second .method )
3067+ utils .AssertEqual (t , "/v1/beta" , second .path )
3068+ utils .AssertEqual (t , originalSecond , second .originalURL )
3069+ utils .AssertEqual (t , "http://edge.stage.example.org" , second .baseURL )
3070+ utils .AssertEqual (t , "http" , second .protocol )
3071+ utils .AssertEqual (t , "edge.stage.example.org" , second .hostname )
3072+ utils .AssertEqual (t , "beta" , second .paramName )
3073+ utils .AssertEqual (t , "qux" , second .queryFoo )
3074+ utils .AssertEqual (t , "beta" , second .cookieSession )
3075+ utils .AssertEqual (t , "agent-two" , second .headerUserAgent )
3076+ utils .AssertEqual (t , "192.168.1.50" , second .ip )
3077+ utils .AssertEqual (t , []string {"192.168.1.50" }, second .ips )
3078+ utils .AssertEqual (t , []string {"edge" , "stage" }, second .subdomains )
3079+ utils .AssertEqual (t , "body-two" , string (second .body ))
3080+ utils .AssertEqual (t , "/v1/:name" , second .routePath )
3081+ }
3082+
29623083// go test -v -run=^$ -bench=Benchmark_Ctx_Subdomains -benchmem -count=4
29633084func Benchmark_Ctx_Subdomains (b * testing.B ) {
29643085 app := New ()
0 commit comments