gofiber / fiber

⚑️ Express inspired web framework written in Go

Home Page:https://gofiber.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

🐞 Document clearly that properties of the request ctx are invalid after the handler returns

yossizahn opened this issue Β· comments

Fiber version/commit: Latest
Issue description
Not sure if this is intentional or somewhat of a bug.
Currently, many properties of the request context are returned as strings but are unsafe to persist after the handler has returned (due to unsafe conversion here).

Expected behavior
It should be possible to safely store request properties for reuse after the handler has returned.

Steps to reproduce
See code snippet. Send a few requests in quick succession, e.g.:

for (( c=1; c<=10; c++ )); do   curl "http://localhost:3000/$c";   done

The second fmt.Printf will not output the correct value.

Code snippet

package main
 
import (
        "fmt"
        "time"
 
        "github.com/gofiber/fiber"
)
 
func main() {
        app := fiber.New()
 
        app.Get("/:number", func(c *fiber.Ctx) {
                number := c.Params("number")
                go myfunc(number)
                c.Send(number)
        })
        app.Listen(3000)
}
func myfunc(number string) {
        fmt.Printf("number is %s \n", number)
        time.Sleep(1 * time.Second)
        fmt.Printf("number is now %s \n", number)
}

Thanks for opening your first issue here! πŸŽ‰ Be sure to follow the issue template!

commented

We use the same method as Fasthttp to convert strings and bytes without memory allocation, but proper use requires you to never change the original byte array, but that's a reasonable contract in many cases. I did not see many use-cases to persist values after returning from the handler, so this is an interesting topic.

It is true that it is possible to reproduce the same behavior in Fasthttp, but in Fasthttp it is more forgivable since a) it is clearly documented and b) there is no implicit contract implied in returning a []byte saying that it won't change under your feet.
It would seem to me that returning a string constitutes an implicit contract that it will follow string conventions such as being immutable.

commented

@yossizahn, you have a good point and we can provide an Immutable option so return values within the handler are immutable. Update the docs that by default all context values are valid until you return from the handler. This way we can keep zero memory allocation conversion enabled by default.

func main() {
  app := fiber.New(&fiber.Settings{
    Immutable: true, 
  })
  app.Get("/:number", func(c *fiber.Ctx) {
    number := c.Params("number") // immutable
    go func() {
      time.Sleep(1 * time.Second)
      fmt.Println("number: ", number)
    }()
  })
  app.Listen(3000)
}
commented

I'm closing this issue because it will be addressed in v1.8.0, feel free to re-open!

I wish the strings returned from the context were wrapped in a new unsafestring struct so when you wrote the code you are forced to call getUnsafeString() or something so it's obvious what you're doing. That way I would pass around unsafestring into functions so I know the string is mutable! All the other go docs say strings are immutable.