Skip to content

Commit f67d7a9

Browse files
rbeuque74thinkerou
authored andcommitted
context: inherits context cancelation and deadline from http.Request context for Go>=1.7 (#1690)
*gin.Context implements standard context.Context methods, but always returns data as context is still valid. Since Go 1.7, http.Request now contains a context.Context object, which can be controlled by the http.Server to indicates that the context is now closed, and persue of request should be canceled. This implements the propagation of http.Request context methods inside gin.Context to have HTTP context cancelation information at gin.Context level. Signed-off-by: Romain Beuque <romain.beuque@corp.ovh.com>
1 parent 59695e7 commit f67d7a9

File tree

4 files changed

+118
-28
lines changed

4 files changed

+118
-28
lines changed

context.go

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -942,34 +942,6 @@ func (c *Context) SetAccepted(formats ...string) {
942942
c.Accepted = formats
943943
}
944944

945-
/************************************/
946-
/***** GOLANG.ORG/X/NET/CONTEXT *****/
947-
/************************************/
948-
949-
// Deadline returns the time when work done on behalf of this context
950-
// should be canceled. Deadline returns ok==false when no deadline is
951-
// set. Successive calls to Deadline return the same results.
952-
func (c *Context) Deadline() (deadline time.Time, ok bool) {
953-
return
954-
}
955-
956-
// Done returns a channel that's closed when work done on behalf of this
957-
// context should be canceled. Done may return nil if this context can
958-
// never be canceled. Successive calls to Done return the same value.
959-
func (c *Context) Done() <-chan struct{} {
960-
return nil
961-
}
962-
963-
// Err returns a non-nil error value after Done is closed,
964-
// successive calls to Err return the same error.
965-
// If Done is not yet closed, Err returns nil.
966-
// If Done is closed, Err returns a non-nil error explaining why:
967-
// Canceled if the context was canceled
968-
// or DeadlineExceeded if the context's deadline passed.
969-
func (c *Context) Err() error {
970-
return nil
971-
}
972-
973945
// Value returns the value associated with this context for key, or nil
974946
// if no value is associated with key. Successive calls to Value with
975947
// the same key returns the same result.

context_17.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
package gin
88

99
import (
10+
"time"
11+
1012
"github.com/gin-gonic/gin/render"
1113
)
1214

@@ -15,3 +17,31 @@ import (
1517
func (c *Context) PureJSON(code int, obj interface{}) {
1618
c.Render(code, render.PureJSON{Data: obj})
1719
}
20+
21+
/************************************/
22+
/***** GOLANG.ORG/X/NET/CONTEXT *****/
23+
/************************************/
24+
25+
// Deadline returns the time when work done on behalf of this context
26+
// should be canceled. Deadline returns ok==false when no deadline is
27+
// set. Successive calls to Deadline return the same results.
28+
func (c *Context) Deadline() (time.Time, bool) {
29+
return c.Request.Context().Deadline()
30+
}
31+
32+
// Done returns a channel that's closed when work done on behalf of this
33+
// context should be canceled. Done may return nil if this context can
34+
// never be canceled. Successive calls to Done return the same value.
35+
func (c *Context) Done() <-chan struct{} {
36+
return c.Request.Context().Done()
37+
}
38+
39+
// Err returns a non-nil error value after Done is closed,
40+
// successive calls to Err return the same error.
41+
// If Done is not yet closed, Err returns nil.
42+
// If Done is closed, Err returns a non-nil error explaining why:
43+
// Canceled if the context was canceled
44+
// or DeadlineExceeded if the context's deadline passed.
45+
func (c *Context) Err() error {
46+
return c.Request.Context().Err()
47+
}

context_17_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
package gin
88

99
import (
10+
"bytes"
11+
"context"
1012
"net/http"
1113
"net/http/httptest"
1214
"testing"
15+
"time"
1316

1417
"github.com/stretchr/testify/assert"
1518
)
@@ -25,3 +28,49 @@ func TestContextRenderPureJSON(t *testing.T) {
2528
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
2629
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
2730
}
31+
32+
func TestContextHTTPContext(t *testing.T) {
33+
c, _ := CreateTestContext(httptest.NewRecorder())
34+
req, _ := http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
35+
ctx, cancelFunc := context.WithCancel(context.Background())
36+
defer cancelFunc()
37+
c.Request = req.WithContext(ctx)
38+
39+
assert.NoError(t, c.Err())
40+
assert.NotNil(t, c.Done())
41+
select {
42+
case <-c.Done():
43+
assert.Fail(t, "context should not be canceled")
44+
default:
45+
}
46+
47+
ti, ok := c.Deadline()
48+
assert.Equal(t, ti, time.Time{})
49+
assert.False(t, ok)
50+
assert.Equal(t, c.Value(0), c.Request)
51+
52+
cancelFunc()
53+
assert.NotNil(t, c.Done())
54+
select {
55+
case <-c.Done():
56+
default:
57+
assert.Fail(t, "context should be canceled")
58+
}
59+
}
60+
61+
func TestContextHTTPContextWithDeadline(t *testing.T) {
62+
c, _ := CreateTestContext(httptest.NewRecorder())
63+
req, _ := http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
64+
location, _ := time.LoadLocation("Europe/Paris")
65+
assert.NotNil(t, location)
66+
date := time.Date(2031, 12, 27, 16, 00, 00, 00, location)
67+
ctx, cancelFunc := context.WithDeadline(context.Background(), date)
68+
defer cancelFunc()
69+
c.Request = req.WithContext(ctx)
70+
71+
assert.NoError(t, c.Err())
72+
73+
ti, ok := c.Deadline()
74+
assert.Equal(t, ti, date)
75+
assert.True(t, ok)
76+
}

context_pre17.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2018 Gin Core Team. All rights reserved.
2+
// Use of this source code is governed by a MIT style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build !go1.7
6+
7+
package gin
8+
9+
import (
10+
"time"
11+
)
12+
13+
/************************************/
14+
/***** GOLANG.ORG/X/NET/CONTEXT *****/
15+
/************************************/
16+
17+
// Deadline returns the time when work done on behalf of this context
18+
// should be canceled. Deadline returns ok==false when no deadline is
19+
// set. Successive calls to Deadline return the same results.
20+
func (c *Context) Deadline() (deadline time.Time, ok bool) {
21+
return
22+
}
23+
24+
// Done returns a channel that's closed when work done on behalf of this
25+
// context should be canceled. Done may return nil if this context can
26+
// never be canceled. Successive calls to Done return the same value.
27+
func (c *Context) Done() <-chan struct{} {
28+
return nil
29+
}
30+
31+
// Err returns a non-nil error value after Done is closed,
32+
// successive calls to Err return the same error.
33+
// If Done is not yet closed, Err returns nil.
34+
// If Done is closed, Err returns a non-nil error explaining why:
35+
// Canceled if the context was canceled
36+
// or DeadlineExceeded if the context's deadline passed.
37+
func (c *Context) Err() error {
38+
return nil
39+
}

0 commit comments

Comments
 (0)