Description

The Nginx logged below: [error] 34#34: *172 readv() failed (104: Connection reset by peer) while reading upstream, client: x.x.x.x, server: , request: "POST /api/xxx HTTP/1.1", upstream: "http://127.0.0.1/api/xxx", host: "xxx.com"

this log occurs after the middleware of the gin checking token is expired, and returns directly without reading the HTTP body. With the further test, we found if the length of the post request body is large than 10k, we can 100 percent get the problem. but if the length of the HTTP body is small,then it won't happen.

If we add c.ShouldBindJson() into the Checktoken() function, the problem will also disappear. We guess the 104 problems caused by the HTTP body data are not read completely by the gin,because auth function returns forward. so the Nginx will receive a reset for this connection.

How to reproduce

package main

import ( "github.com/gin-gonic/gin" )

func main() { engine = gin.New() api := engine.Group("api") v1 := api.Group("v1",checkToken) v1.POST("/something", CreateHandler)

}

func checkToken(c *gin.Context) { token = c.GetString("token") if len(token) == 0 { // if we add the ShouldBindJson below, the 104 problem wont happen // var request map[string]interface{} // _ = c.ShouldBindJson(request) c.Abort() c.String(http.StatusUnauthorized,"token is invalid") return } }

type information struct { Hobby string }

type createRequest struct { Name string Age int Hobbies []infromation }

func CreateHandler(c *gin.Context) { var request createRequest if err := c.ShouleBindJson(&request);err != nil { c.Json(http.StatusBadRequest,nil) return } c.Json(http.StatusOk,nil) }

Expectations

$ curl http://localhost:8201/api/v1/xxx 
 401 token is invalid

Actual result

$ curl -i http://localhost:8201/api/v1/xxx/ -d '{"more than 10k data"}'
401 token is valid
but ngingx will logger below:
 [error] 34#34: *172 readv() failed (104: Connection reset by peer) while reading upstream, client: x.x.x.x, server: , request: "POST /api/xxx HTTP/1.1", upstream: "http://127.0.0.1/api/v1/xxx", host: "xxx.com"

Environment

  • go version: 1.16
  • gin version (or commit ref): v1.6.3
  • operating system: aws linux

Comment From: yonggong-chowbus

Is there any approach to avoid this problem, not to read body when token is expired?

Comment From: Bisstocuz

Did you use c.Abort() and c.Next() to write middlewares?

Comment From: yonggong-chowbus

Did you use c.Abort() and c.Next() to write middlewares?

Sorry, I lacked the c.abort in the checkAuth middleware.
Yes, I used c.Abort to abort the execution of the next handler in the checkAuth function.

Do you mean I should use c.abort and c.next together in the CheckAuth function if token is invalid?

Comment From: Arktische

Classic problem. Connection reset by peer means your Golang server close this conn actively. You might modify nginx config like this.

upstream {
       keepalive: 100;
}
location /your/path {
        proxy_pass http://xxxxx;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
}

Comment From: yonggong-chowbus

Classic problem. Connection reset by peer means your Golang server close this conn actively. You might modify nginx config like this.

upstream { keepalive: 100; } location /your/path { proxy_pass http://xxxxx; proxy_http_version 1.1; proxy_set_header Connection ""; }

we already used the above config in the Nginx. but our keepalive is 1000.

I think the problem is caused by the read buffer of gin.