Skip to content

add MiddlewareNeverDoneCtx for package ghttp #3250

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions net/ghttp/ghttp_middleware_never_done_ctx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp

// MiddlewareNeverDoneCtx sets the context never done for current process.
func MiddlewareNeverDoneCtx(r *Request) {
r.SetCtx(r.GetNeverDoneCtx())
r.Middleware.Next()
}
90 changes: 90 additions & 0 deletions net/ghttp/ghttp_z_unit_feature_request_ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"testing"
"time"

"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/encoding/gbase64"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
Expand Down Expand Up @@ -345,3 +346,92 @@ func Test_Request_Form(t *testing.T) {
}), "john")
})
}

func Test_Request_NeverDoneCtx_Done(t *testing.T) {
var array = garray.New(true)
s := g.Server(guid.S())
s.BindHandler("/done", func(r *ghttp.Request) {
var (
ctx = r.Context()
ticker = time.NewTimer(time.Millisecond * 1500)
)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
array.Append(1)
return
case <-ticker.C:
array.Append(1)
return
}
}
})
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()

time.Sleep(100 * time.Millisecond)

c := g.Client()
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
gtest.C(t, func(t *gtest.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
go func() {
result := c.GetContent(ctx, "/done")
fmt.Println(result)
}()
time.Sleep(time.Millisecond * 100)

t.Assert(array.Len(), 0)
cancel()

time.Sleep(time.Millisecond * 500)
t.Assert(array.Len(), 1)
})
}

func Test_Request_NeverDoneCtx_NeverDone(t *testing.T) {
var array = garray.New(true)
s := g.Server(guid.S())
s.Use(ghttp.MiddlewareNeverDoneCtx)
s.BindHandler("/never-done", func(r *ghttp.Request) {
var (
ctx = r.Context()
ticker = time.NewTimer(time.Millisecond * 1500)
)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
array.Append(1)
return
case <-ticker.C:
array.Append(1)
return
}
}
})
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()

time.Sleep(100 * time.Millisecond)

c := g.Client()
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
gtest.C(t, func(t *gtest.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
go func() {
result := c.GetContent(ctx, "/never-done")
fmt.Println(result)
}()
time.Sleep(time.Millisecond * 100)

t.Assert(array.Len(), 0)
cancel()

time.Sleep(time.Millisecond * 1500)
t.Assert(array.Len(), 1)
})
}
6 changes: 5 additions & 1 deletion os/gctx/gctx_never_done.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ func (c *neverDoneCtx) Err() error {
}

// NeverDone wraps and returns a new context object that will be never done,
// which forbids the context manually done, to make the context can be propagated to asynchronous goroutines.
// which forbids the context manually done, to make the context can be propagated
// to asynchronous goroutines.
//
// Note that, it does not affect the closing (canceling) of the parent context,
// as it is a wrapper for its parent, which only affects the next context handling.
func NeverDone(ctx context.Context) context.Context {
return &neverDoneCtx{ctx}
}