• With issues:
  • Use the search tool before opening a new issue.
  • Please provide source code and commit sha if you found a bug.
  • Review existing issues and provide feedback or react to them.

Description

context.Copy() does not duplicate the request body source. So reading in the copied context closes the stream in the original context as well

How to reproduce

package main

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

func main() {
    g := gin.Default()
    g.POST("/hello", func(c *gin.Context) {
        go func(ccp *gin.Context) {
            _, _ = ioutil.ReadAll(ccp.Request.Body)
        }(c.Copy())
        time.Sleep(50) // Let goroutine read the body
        body, _ := ioutil.ReadAll(c.Request.Body)

        c.String(200, "Hello. I got this request from you: %s", string(body))
    })
    g.Run(":9000")
}

Expectations

$ curl -X POST --location "http://localhost:9000/hello" \
    -H "Content-Type: application/json" \
    -d "{\"test\": \"value\"}"
Hello. I got this request from you: {"test": "value"}

Actual result

$ curl -X POST --location "http://localhost:9000/hello" \
    -H "Content-Type: application/json" \
    -d "{\"test\": \"value\"}"
Hello. I got this request from you: 

Environment

  • go version: 1.19
  • gin version (or commit ref): v1.6.3
  • operating system: Mac

Comment From: timandy

I think the author does not copy the body to save memory, and I agree with the author's design. If you want to pass the content of the body, you should read it first, and then pass the readed result in different coroutines.