Skip to content

Commit 19ecca9

Browse files
committed
feat(callback): RetryAfter
1 parent 4836ef1 commit 19ecca9

File tree

4 files changed

+75
-0
lines changed

4 files changed

+75
-0
lines changed

callback/callback.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"log"
1313
"net/http"
1414
"strconv"
15+
"time"
1516

1617
"github.com/SevereCloud/vksdk/v2/events"
1718
"github.com/SevereCloud/vksdk/v2/internal"
@@ -81,13 +82,31 @@ func (cb *Callback) HandleFunc(w http.ResponseWriter, r *http.Request) {
8182
retryCounter, _ := strconv.Atoi(r.Header.Get("X-Retry-Counter"))
8283
ctx = context.WithValue(ctx, internal.CallbackRetryCounterKey, retryCounter)
8384

85+
var (
86+
code int
87+
date time.Time
88+
)
89+
90+
retryAfter := func(c int, d time.Time) {
91+
code = c
92+
date = d
93+
}
94+
ctx = context.WithValue(ctx, internal.CallbackRetryAfterKey, retryAfter)
95+
8496
if err := cb.Handler(ctx, e); err != nil {
8597
cb.logf("callback: %v", err)
8698
http.Error(w, "Bad Request", http.StatusBadRequest)
8799

88100
return
89101
}
90102

103+
if code != 0 {
104+
w.Header().Set("Retry-After", date.Format(http.TimeFormat)) // RFC 7231, 7.1.3
105+
http.Error(w, http.StatusText(code), code)
106+
107+
return
108+
}
109+
91110
_, _ = w.Write([]byte("ok"))
92111
}
93112

callback/context.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package callback // import "github.com/SevereCloud/vksdk/v2/callback"
22

33
import (
44
"context"
5+
"time"
56

67
"github.com/SevereCloud/vksdk/v2/internal"
78
)
@@ -10,3 +11,22 @@ import (
1011
func RetryCounterFromContext(ctx context.Context) int {
1112
return ctx.Value(internal.CallbackRetryCounterKey).(int)
1213
}
14+
15+
// RetryAfter send the "Retry-After" header field to indicate how long the
16+
// VK Callback ought to wait before making a repeated request.
17+
// Not work with goroutine mode!
18+
//
19+
// Possible HTTP status codes:
20+
//
21+
// http.StatusGone
22+
// http.StatusTooManyRequests
23+
// http.StatusServiceUnavailable
24+
//
25+
// The resend time range must be less than 3 hours. The actual time of
26+
// forwarding an event notification may be longer than the specified time.
27+
func RetryAfter(ctx context.Context, code int, date time.Time) {
28+
ctx.Value(internal.CallbackRetryAfterKey).(func(int, time.Time))(
29+
code,
30+
date,
31+
)
32+
}

callback/context_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package callback_test
22

33
import (
4+
"bytes"
45
"context"
6+
"net/http"
7+
"net/http/httptest"
58
"testing"
9+
"time"
610

711
"github.com/SevereCloud/vksdk/v2/callback"
12+
"github.com/SevereCloud/vksdk/v2/events"
813
"github.com/SevereCloud/vksdk/v2/internal"
914
"github.com/stretchr/testify/assert"
1015
)
@@ -18,3 +23,33 @@ func TestRetryCounterFromContext(t *testing.T) {
1823
)
1924
assert.Equal(t, retryCounter, callback.RetryCounterFromContext(ctx))
2025
}
26+
27+
func TestRetryAfter(t *testing.T) {
28+
code := http.StatusGone
29+
date := time.Now().Add(time.Minute * 5)
30+
31+
cb := callback.NewCallback()
32+
cb.MessageNew(func(ctx context.Context, obj events.MessageNewObject) {
33+
callback.RetryAfter(
34+
ctx,
35+
code,
36+
date,
37+
)
38+
})
39+
40+
jsonStr := []byte(`{"type": "message_new","object": {}}`)
41+
42+
req, err := http.NewRequest("POST", "/callback", bytes.NewBuffer(jsonStr))
43+
if err != nil {
44+
t.Fatal(err)
45+
}
46+
47+
rr := httptest.NewRecorder()
48+
handler := http.HandlerFunc(cb.HandleFunc)
49+
50+
handler.ServeHTTP(rr, req)
51+
52+
assert.Equal(t, http.StatusText(code)+"\n", rr.Body.String())
53+
assert.Equal(t, code, rr.Code)
54+
assert.Equal(t, date.Format(http.TimeFormat), rr.Header().Get("Retry-After"))
55+
}

internal/transport.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const (
2626
EventIDKey
2727
LongPollTsKey
2828
CallbackRetryCounterKey
29+
CallbackRetryAfterKey
2930
)
3031

3132
// ContextClient return *http.Client.

0 commit comments

Comments
 (0)