Skip to content

Commit 651afe9

Browse files
committed
add docs for retry and circuit breaker
- update docs for curl cmd and upgrading v3 - update license file
1 parent 2682428 commit 651afe9

File tree

5 files changed

+229
-24
lines changed

5 files changed

+229
-24
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
same "printed page" as the copyright notice for easier
187187
identification within third-party archives.
188188

189-
Copyright [yyyy] [name of copyright owner]
189+
Copyright 2024-Present Jeevanandam M ([email protected])
190190

191191
Licensed under the Apache License, Version 2.0 (the "License");
192192
you may not use this file except in compliance with the License.

content/docs/circuit-breaker.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
# Circuit Breaker
3+
4+
A circuit breaker is used to improve system stability and resiliency. It is different from the retry mechanism.
5+
6+
{{% hint info %}}
7+
**Hint:** Combining the Circuit Breaker with [Retry Mechanism]({{% relref "retry-mechanism" %}}) typically provides a comprehensive approach to handling failures.
8+
{{% /hint %}}
9+
10+
## Default Values
11+
12+
* Timeout is `10s`
13+
* Failure threshold is `3`
14+
* Success threshold is `1`
15+
* Circuir break policy
16+
* Status Code `500` and above
17+
18+
19+
## Example
20+
21+
```go
22+
// create circuit breaker with required values, override as required
23+
cb := resty.NewCircuitBreaker().
24+
SetTimeout(15 * time.Second).
25+
SetFailThreshold(10).
26+
SetSuccessThreshold(5)
27+
28+
// create Resty client
29+
client := resty.New().
30+
SetCircuitBreaker(cb)
31+
defer client.Close()
32+
33+
// start using the client ...
34+
```
35+
36+
37+
## Methods
38+
39+
* [CircuitBreaker.SetTimeout]({{% param Resty.V3.GoDocLinkPrefix %}}CircuitBreaker.SetTimeout)
40+
* [CircuitBreaker.SetFailThreshold]({{% param Resty.V3.GoDocLinkPrefix %}}CircuitBreaker.SetFailThreshold)
41+
* [CircuitBreaker.SetSuccessThreshold]({{% param Resty.V3.GoDocLinkPrefix %}}CircuitBreaker.SetSuccessThreshold)
42+
* [CircuitBreaker.SetPolicies]({{% param Resty.V3.GoDocLinkPrefix %}}CircuitBreaker.SetPolicies)

content/docs/curl-command.md

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,53 @@
1-
---
2-
title: "CURL Command Generation"
3-
bookHidden: true
4-
---
51

6-
# CURL Command Generation
2+
# Curl Command
73

8-
Resty provides a way to generate the CURL command in debug mode.
4+
Resty provides an option to generate the curl command for the request.
5+
6+
By default, Resty does not log the curl command in the debug log since it has the potential to leak sensitive data unless explicitly enabled via [Client.SetDebugLogCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetDebugLogCurlCmd).
97

108
{{% hint info %}}
119
**NOTE:** Client-level settings can be overridden at the request level.
1210
{{% /hint %}}
1311

1412
{{% hint warning %}}
1513
**NOTE:**
16-
17-
- Potential to leak sensitive data from [Request]() and [Response]() in the debug log.
18-
- Beware of memory usage since the request body is reread.
14+
- Potential to leak sensitive data from [Request]({{% param Resty.V3.GoDocLinkPrefix %}}Request) and [Response]({{% param Resty.V3.GoDocLinkPrefix %}}Response) in the debug log when the debug log option is enabled.
15+
- Additional memory usage since the request body was reread.
16+
- curl body is not generated for `io.Reader` and multipart request flow.
1917
{{% /hint %}}
2018

21-
## Methods
22-
* [Client.EnableGenerateCurlOnDebug]({{% param Resty.V3.GoDocLinkPrefix %}}Client.EnableGenerateCurlOnDebug)
23-
* [Client.DisableGenerateCurlOnDebug]({{% param Resty.V3.GoDocLinkPrefix %}}Client.DisableGenerateCurlOnDebug)
24-
* [Client.SetGenerateCurlOnDebug]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetGenerateCurlOnDebug)
25-
* [Request.EnableGenerateCurlOnDebug]({{% param Resty.V3.GoDocLinkPrefix %}}Request.EnableGenerateCurlOnDebug)
26-
* [Request.DisableGenerateCurlOnDebug]({{% param Resty.V3.GoDocLinkPrefix %}}Request.DisableGenerateCurlOnDebug)
27-
* [Request.SetGenerateCurlOnDebug]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetGenerateCurlOnDebug)
19+
## Example
2820

2921
```go
3022
c := resty.New()
3123
defer c.Close()
3224

3325
res, err := c.R().
34-
EnableDebug().
35-
EnableGenerateCurlOnDebug().
26+
SetGenerateCurlCmd(true).
3627
SetBody(map[string]string{
3728
"name": "Alex",
3829
}).
3930
Post("https://httpbin.org/post")
4031

41-
curlCmdStr := res.Request.GenerateCurlCommand()
32+
curlCmdStr := res.Request.CurlCommand()
4233
fmt.Println(err, curlCmdStr)
4334

4435
// Result:
4536
// curl -X POST -H 'Content-Type: application/json' -H 'User-Agent: go-resty/3.0.0 (https://resty.dev)' -d '{"name":"Alex"}' https://httpbin.org/post
4637
```
38+
39+
## Methods
40+
41+
### Client
42+
43+
* [Client.EnableGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Client.EnableGenerateCurlCmd)
44+
* [Client.DisableGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Client.DisableGenerateCurlCmd)
45+
* [Client.SetGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetGenerateCurlCmd)
46+
* [Client.SetDebugLogCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetDebugLogCurlCmd)
47+
48+
### Request
49+
50+
* [Request.EnableGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Request.EnableGenerateCurlCmd)
51+
* [Request.DisableGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Request.DisableGenerateCurlCmd)
52+
* [Request.SetGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetGenerateCurlCmd)
53+
* [Request.SetDebugLogCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetDebugLogCurlCmd)

content/docs/retry-mechanism.md

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
2+
# Retry Mechanism
3+
4+
The retry mechanism plays a crucial role in modern system integration by enabling effective handling of failures.
5+
6+
Resty provides exponential backoff with a jitter strategy out of the box; a custom retry strategy could be employed to override this default.
7+
8+
{{% hint info %}}
9+
**Hint:** Combining the Retry strategy with [Circuit Breaker]({{% relref "circuit-breaker" %}}) typically provides a comprehensive approach to handling failures.
10+
{{% /hint %}}
11+
12+
13+
## Default Values
14+
15+
* Retry count is `0`
16+
* Total retry attempts = `first attempt + retry count`
17+
* Retry minimum wait time is `100ms`
18+
* Retry maximum wait time is `2000ms`
19+
* Retry strategy is exponential backoff with a jitter
20+
21+
22+
## Default Behavior
23+
24+
* Respects header `Retry-After` if present
25+
* Resets reader on retry request if the `io.ReadSeeker` interface is supported.
26+
* Retries only on Idempotent HTTP Verb - GET, HEAD, PUT, DELETE, OPTIONS, and TRACE ([RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110.html#name-method-registration), [RFC 5789](https://datatracker.ietf.org/doc/html/rfc5789.html))
27+
* Use [Client.SetAllowNonIdempotentRetry]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetAllowNonIdempotentRetry) or [Request.SetAllowNonIdempotentRetry]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetAllowNonIdempotentRetry). If additional control is necessary, utilize the custom retry condition.
28+
* Applies [default retry conditions]({{% relref "#default-conditions" %}})
29+
* It can be disabled via [Client.SetRetryDefaultConditions]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetRetryDefaultConditions) or [Request.SetRetryDefaultConditions]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryDefaultConditions)
30+
31+
32+
## Default Conditions
33+
34+
* Condition gets applied in the following order
35+
* No Retry
36+
* TLS certificate verification error
37+
* Too many redirects error
38+
* Scheme error
39+
* Invalid header error
40+
* Response is nil
41+
* Retry
42+
* Status Code is 429 Too Many Requests
43+
* Status Code is 500 or above (but not Status Code 501 Not Implemented)
44+
* Status Code is 0
45+
46+
47+
## Examples
48+
49+
```go
50+
// Retry configuration can be set at the client or request level
51+
client.
52+
SetRetryCount(3).
53+
SetRetryWaitTime(2 * time.Second).
54+
SetRetryMaxWaitTime(5 * time.Second)
55+
```
56+
57+
### Constant Delay
58+
59+
Use a custom retry strategy approach to perform constant/fixed delay.
60+
61+
```go
62+
// Retry configuration can be set at the client or request level
63+
client.
64+
SetRetryStrategy(func(_ *resty.Response, _ error) (time.Duration, error) {
65+
return 3 * time.Second, nil
66+
})
67+
```
68+
69+
### Retry Hook
70+
71+
Utilize the retry hook(s) to perform logic between retries.
72+
73+
```go
74+
// Retry configuration can be set at the client or request level
75+
client.
76+
AddRetryHook(func(res *resty.Response, err error) {
77+
// perform logic here
78+
})
79+
```
80+
81+
### Retry Conditions
82+
83+
```go
84+
// Retry configuration can be set at the client or request level
85+
// NOTE: first default retry conditions get applied
86+
// before user-defined retry conditions
87+
client.
88+
AddRetryCondition(func(res *resty.Response, err error) bool {
89+
// perform logic here
90+
91+
// return true if retry is required otherwise, return false
92+
return true
93+
})
94+
```
95+
96+
97+
## Methods
98+
99+
### Client
100+
101+
* [Client.SetRetryCount]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetRetryCount)
102+
* [Client.SetRetryWaitTime]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetRetryWaitTime)
103+
* [Client.SetRetryMaxWaitTime]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetRetryMaxWaitTime)
104+
* [Client.SetRetryStrategy]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetRetryStrategy)
105+
* [Client.SetRetryDefaultConditions]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetRetryDefaultConditions)
106+
* [Client.AddRetryHook]({{% param Resty.V3.GoDocLinkPrefix %}}Client.AddRetryHook)
107+
* [Client.AddRetryCondition]({{% param Resty.V3.GoDocLinkPrefix %}}Client.AddRetryCondition)
108+
109+
110+
### Request
111+
112+
* [Request.SetRetryCount]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryCount)
113+
* [Request.SetRetryWaitTime]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryWaitTime)
114+
* [Request.SetRetryMaxWaitTime]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryMaxWaitTime)
115+
* [Request.SetRetryStrategy]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryStrategy)
116+
* [Request.SetRetryDefaultConditions]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryDefaultConditions)
117+
* [Request.AddRetryHook]({{% param Resty.V3.GoDocLinkPrefix %}}Request.AddRetryHook)
118+
* [Request.AddRetryCondition]({{% param Resty.V3.GoDocLinkPrefix %}}Request.AddRetryCondition)

content/docs/upgrading-to-v3.md

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ I made necessary breaking changes to improve Resty and open up future growth pos
2727

2828
* Set Content length is not applicable for `io.Reader` flow.
2929
* By default, payload is not supported in HTTP verb DELETE. Use [Client.AllowMethodDeletePayload]({{% param Resty.V3.GoDocLinkPrefix %}}Client.AllowMethodDeletePayload) or [Request.AllowMethodDeletePayload]({{% param Resty.V3.GoDocLinkPrefix %}}Request.AllowMethodDeletePayload).
30+
* Retry Mechanism
31+
* Respects header `Retry-After` if present
32+
* Resets reader on retry request if the `io.ReadSeeker` interface is supported.
33+
* Retries only on Idempotent HTTP Verb - GET, HEAD, PUT, DELETE, OPTIONS, and TRACE ([RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110.html#name-method-registration), [RFC 5789](https://datatracker.ietf.org/doc/html/rfc5789.html)),
34+
* Use [Client.SetAllowNonIdempotentRetry]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetAllowNonIdempotentRetry) or [Request.SetAllowNonIdempotentRetry]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetAllowNonIdempotentRetry). If additional control is necessary, utilize the custom retry condition.
35+
* Applies [default retry conditions]({{% relref "retry-mechanism#default-conditions" %}})
36+
* It can be disabled via [Client.SetRetryDefaultConditions]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetRetryDefaultConditions) or [Request.SetRetryDefaultConditions]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryDefaultConditions)
37+
*
3038

3139
#### Client
3240

@@ -40,6 +48,8 @@ I made necessary breaking changes to improve Resty and open up future growth pos
4048
* [Client.ResponseBodyLimit]({{% param Resty.V3.GoDocLinkPrefix %}}Client.ResponseBodyLimit) - datatype changed from `int` to `int64`
4149
* `Client.SetAllowGetMethodPayload` => [Client.SetAllowMethodGetPayload]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetAllowMethodGetPayload)
4250
* `Client.Clone()` - use [Client.Clone(ctx context.Context)]({{% param Resty.V3.GoDocLinkPrefix %}}Client.Clone) instead.
51+
* `Client.EnableGenerateCurlOnDebug` => [Client.EnableGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Client.EnableGenerateCurlCmd)
52+
* `Client.DisableGenerateCurlOnDebug` => [Client.DisableGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Client.DisableGenerateCurlCmd)
4353

4454
#### Request
4555

@@ -48,19 +58,27 @@ I made necessary breaking changes to improve Resty and open up future growth pos
4858
* `Request.NotParseResponse` => [Request.DoNotParseResponse]({{% param Resty.V3.GoDocLinkPrefix %}}Request.DoNotParseResponse)
4959
* `Request.ExpectContentType` => [Request.SetExpectResponseContentType]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetExpectResponseContentType)
5060
* `Request.ForceContentType` => [Request.SetForceResponseContentType]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetForceResponseContentType)
61+
* `Request.SetOutput` => [Request.SetOutputFileName]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetOutputFileName)
62+
* `Request.EnableGenerateCurlOnDebug` => [Request.EnableGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Request.EnableGenerateCurlCmd)
63+
* `Request.DisableGenerateCurlOnDebug` => [Request.DisableGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Request.DisableGenerateCurlCmd)
64+
* `Request.GenerateCurlCommand` => [Request.CurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Request.CurlCmd)
5165

5266

5367
### Removed
5468

5569
#### Client
5670

71+
* `Client.SetHostURL` - use [Client.SetBaseURL]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetBaseURL) instead.
5772
* `Client.{SetJSONMarshaler, SetJSONUnmarshaler, SetXMLMarshaler, SetXMLUnmarshaler}` - use [Client.AddContentTypeEncoder]({{% param Resty.V3.GoDocLinkPrefix %}}Client.AddContentTypeEncoder) and [Client.AddContentTypeDecoder]({{% param Resty.V3.GoDocLinkPrefix %}}Client.AddContentTypeDecoder) instead.
58-
* `Client.RawPathParams` - use `Client.PathParams()` instead
73+
* `Client.RawPathParams` - use `Client.PathParams()` instead.
5974
* `Client.UserInfo`
75+
* `Client.SetRetryResetReaders` - it happens automatically.
76+
* `Client.SetRetryAfter` - use [Client.SetRetryStrategy]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetRetryStrategy) or [Request.SetRetryStrategy]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryStrategy) instead.
6077

6178
#### Request
6279

6380
* `Request.RawPathParams` - use [Request.PathParams]({{% param Resty.V3.GoDocLinkPrefix %}}Request.PathParams) instead
81+
*
6482

6583
#### Response
6684

@@ -79,6 +97,10 @@ I made necessary breaking changes to improve Resty and open up future growth pos
7997

8098
## New Features and Enhancements
8199

100+
* Override all transport settings and timeout values used by Resty using [NewWithTransportSettings]({{% param Resty.V3.GoDocLinkPrefix %}}NewWithTransportSettings)
101+
* [Circuit Breaker]({{% relref "circuit-breaker" %}})
102+
* Set retry settings on Request instance refer to [Retry Mechanism]({{% relref "retry-mechanism" %}})
103+
82104
### New ways to create Client
83105

84106
* [NewWithTransportSettings]({{% param Resty.V3.GoDocLinkPrefix %}}NewWithTransportSettings)
@@ -104,6 +126,12 @@ I made necessary breaking changes to improve Resty and open up future growth pos
104126
* [Client.IsDisableWarn]({{% param Resty.V3.GoDocLinkPrefix %}}Client.IsDisableWarn)
105127
* [Client.AllowMethodDeletePayload]({{% param Resty.V3.GoDocLinkPrefix %}}Client.AllowMethodDeletePayload)
106128
* [Client.SetAllowMethodDeletePayload]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetAllowMethodDeletePayload)
129+
* [Client.SetRetryStrategy]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetRetryStrategy)
130+
* [Client.SetRetryDefaultConditions]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetRetryDefaultConditions)
131+
* [Client.IsSaveResponse]({{% param Resty.V3.GoDocLinkPrefix %}}Client.IsSaveResponse)
132+
* [Client.SetSaveResponse]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetSaveResponse)
133+
* [Client.SetGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetGenerateCurlCmd)
134+
* [Client.SetDebugLogCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Client.SetDebugLogCurlCmd)
107135

108136
### Request
109137

@@ -113,7 +141,7 @@ I made necessary breaking changes to improve Resty and open up future growth pos
113141
* [Request.DebugBodyLimit]({{% param Resty.V3.GoDocLinkPrefix %}}Request.DebugBodyLimit)
114142
* [Request.EnableDebug]({{% param Resty.V3.GoDocLinkPrefix %}}Request.EnableDebug)
115143
* [Request.DisableDebug]({{% param Resty.V3.GoDocLinkPrefix %}}Request.DisableDebug)
116-
* [Request.IsTrace]({{% param Resty.V3.GoDocLinkPrefix %}}Request.IsTrace)
144+
* [Request.IsTrace]({{% param Resty.V3.GoDocLinkPrefix %}}Request)
117145
* [Request.SetTrace]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetTrace)
118146
* [Request.DisableTrace]({{% param Resty.V3.GoDocLinkPrefix %}}Request.DisableTrace)
119147
* [Request.Patch]({{% param Resty.V3.GoDocLinkPrefix %}}Request.Patch)
@@ -122,11 +150,21 @@ I made necessary breaking changes to improve Resty and open up future growth pos
122150
* [Request.SetURL](R{{% param Resty.V3.GoDocLinkPrefix %}}equest.SetURL)
123151
* [Request.SetAllowMethodGetPayload]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetAllowMethodGetPayload)
124152
* [Request.SetAllowMethodDeletePayload]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetAllowMethodDeletePayload)
153+
* [Request.SetRetryCount]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryCount)
154+
* [Request.SetRetryWaitTime]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryWaitTime)
155+
* [Request.SetRetryMaxWaitTime]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryMaxWaitTime)
156+
* [Request.SetRetryStrategy]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryStrategy)
157+
* [Request.SetRetryDefaultConditions]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetRetryDefaultConditions)
158+
* [Request.IsSaveResponse]({{% param Resty.V3.GoDocLinkPrefix %}}Request)
159+
* [Request.SetSaveResponse]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetSaveResponse)
160+
* [Request.SetGenerateCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetGenerateCurlCmd)
161+
* [Request.SetDebugLogCurlCmd]({{% param Resty.V3.GoDocLinkPrefix %}}Request.SetDebugLogCurlCmd)
162+
125163

126164
### Response
127165

128166
* [Response.Body]({{% param Resty.V3.GoDocLinkPrefix %}}Response)
129-
* [Response.SetBodyBytes]({{% param Resty.V3.GoDocLinkPrefix %}}Response.SetBodyBytes)
130167
* [Response.Bytes]({{% param Resty.V3.GoDocLinkPrefix %}}Response.Bytes)
131168
* [Response.IsRead]({{% param Resty.V3.GoDocLinkPrefix %}}Response)
132-
* [Response.Err]({{% param Resty.V3.GoDocLinkPrefix %}}Response)
169+
* [Response.Err]({{% param Resty.V3.GoDocLinkPrefix %}}Response)
170+
* [Response.RedirectHistory]({{% param Resty.V3.GoDocLinkPrefix %}}Response.RedirectHistory)

0 commit comments

Comments
 (0)