-
Notifications
You must be signed in to change notification settings - Fork 38
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
fix(response): conflict when handler completed and concurrent map writes #55
base: master
Are you sure you want to change the base?
Conversation
Hello, I try as your code, and test failed.
There is still a possibility of concurrency. |
@x-lambda I'd like to test your code. Could you please provide more specific instructions on the testing process? |
@demouth Reproduce the testing code func TestLargeResponse(t *testing.T) {
r := gin.New()
r.GET("/slow", New(
WithTimeout(1*time.Second),
WithHandler(func(c *gin.Context) {
c.Next()
}),
WithResponse(func(c *gin.Context) {
c.String(http.StatusRequestTimeout, `{"error": "timeout error"}`)
}),
), func(c *gin.Context) {
time.Sleep(999*time.Millisecond + 500*time.Microsecond) // wait almost same as timeout
c.String(http.StatusRequestTimeout, `{"error": "handler error"}`)
})
wg := sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
w := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/slow", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusRequestTimeout, w.Code)
assert.Equal(t, `{"error": "timeout error"}`, w.Body.String())
}()
}
wg.Wait()
} |
fatal error: concurrent map writes
@demouth Any progress? |
@appleboy I don't have any good ideas at the moment. |
What's the reason that this PR can not yet be merged? |
Resolve the #15
fatal error: concurrent map writes
issueCause
In the current implementation, when a timeout occurs,
c.Writer
is replaced with&gin.responseWriter
and writing to the Body is performed by theTimeout.response
handler.Subsequently, additional writes to the
Body
are performed by the handler being executed in a goroutine.This results in writes being performed to the
Body
in a duplicate manner.Countermeasure
Before replacing
c.Writer
, use*gin.Context.Copy()
to duplicate the context. This ensures thatc.Writer
is only set to&gin.responseWriter
whenTimeout.response
handler is executed.Verification
Execute the following verification code.
Request 20 times consecutively using the following command.
Before the fix, there were instances of duplicated
Body
content. Additionally, the application occasionally encountered afatal error: concurrent map writes
and would stop.After the fix, there is no duplication of the
Body
, and the application does not encounter afatal error: concurrent map writes
that would cause it to stop.