Skip to content
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

📢 v2 - Changelog #736

Closed
Fenny opened this issue Aug 21, 2020 · 19 comments
Closed

📢 v2 - Changelog #736

Fenny opened this issue Aug 21, 2020 · 19 comments

Comments

@Fenny
Copy link
Member

Fenny commented Aug 21, 2020

Updated September 14, 2020 ( 🚀 v2 benchmark results )

Fiber

Dear Gophers, after long discussions in our , we decided to release v2.0.0 on 15 September 2020. We will summarize our current API proposal in this post, feedback is more than welcome.


Breaking Changes

  • func(c *fiber.Ctx) error is the new Handler type accepting an error return, this will simplify error handling and create cleaner code because you can directly return outgoing methods like c.Send(), c.Next(), c.SendStatus(), c.Redirect() etc... 🔥 Support returning response from handler #218
// func(c *fiber.Ctx) {}
   func(c *fiber.Ctx) error {}
app.Use(func(c *fiber.Ctx) error {
    if c.Get("x-john") == "doe" {
        return c.Next()
    }
    return c.SendStatus(fiber.StatusForbidden)
})

app.Use("/teapot", func(c *fiber.Ctx) error {
    return c.Send([]byte("Helllo, Teapot ☕!"))
})

app.Get("/world", func(c *fiber.Ctx) error {
    return c.SendString("Hello, World 👋!")
})

app.Get("/redirect", func(c *fiber.Ctx) error {
    return c.Redirect("https://google.com")
})
  • fiber.New() takes an optional Config struct as value that will replace the *Settings pointer.
// func New(settings ...*Settings) *App {}
   func New(config... Config) *App {}
// func (app *App) Listen(address interface{}, tlsconfig ...*tls.Config) error {}
   func (app *App) Listen(addr string) error {}
  • app.Listener() will remove the optional *tls.Config argument, this should be set within the given listener. Prefork will be compatible with custom listeners.
// func (app *App) Listener(ln net.Listener, tlsconfig ...*tls.Config) error {}
   func (app *App) Listener(ln net.Listener) error {}
  • c.Next() returns an error to be used with the new Ctx handler signature. Passing an error through c.Next is not necessary anymore since we return all errors within the handler.
// func (c *Ctx) Next(err ...error) {}
   func (c *Ctx) Next() error {}
  • c.Body() now returns []byte type instead of a string
// func (c *Ctx) Body() string {}
   func (c *Ctx) Body() []byte {}
  • c.Write() will comply with the io.Writer interface.
// func (c *Ctx) Write(bodies ...interface{}) {}
   func (c *Ctx) Write(p []byte) (n int, err error) {}
// func (c *Ctx) Context() context.Context
   func (c *Ctx) Context() *fasthttp.RequestCtx
  • app.IsChild() will be available on the package layer
// func (app *App) IsChild() bool {}
   func IsChild() bool {}

🧹 Updates

  • c.Send() now contains a strong typed signature accepting a single []byte type
    Other outgoing Send methods also return an error to comply with the new Handler signature
// func (c *Ctx) Send(bodies ...interface{}) {}
   func (c *Ctx) Send(body []byte) error {}

// func (c *Ctx) SendStatus(status int) {}
   func (c *Ctx) SendStatus(status int) error {}

// func (c *Ctx) SendString(body string) {}
   func (c *Ctx) SendString(body string) error {}

// func (c *Ctx) SendStream(stream io.Reader, size ...int) {}
   func (c *Ctx) SendStream(stream io.Reader, size ...int) error {}
  • c.Redirect() now returns an error
// func (c *Ctx) Redirect(location string, status ...int) {}
   func (c *Ctx) Redirect(location string, status ...int) error {}
  • c.FormValue() now supports the optional defaultValue argument
// func (c *Ctx) FormValue(key string) string {}
   func (c *Ctx) FormValue(key string, defaultValue ...string) string {}

Removed

  • c.SendBytes() will be removed and is replaced by c.Send
// func (c *Ctx) SendBytes(body []byte) {}
  • c.Error() will be removed because errors can be accessed using the error return in c.Next() error
// func (c *Ctx) Error() error {}

🔥 New

// c.Fasthttp.Request.Header.Peek("x-version")
   c.Request().Header.Peek("x-version")
// c.Fasthttp.Response.Header.Peek("x-version")
   c.Response().Header.Peek("x-version")
  • app.Config.ErrorHandler will replace the app.Settings.ErrorHandler to override the default error handler.
app := fiber.New(fiber.Config{
	ErrorHandler: func(c *fiber.Ctx, err error) error {
		return c.Status(404).SendString("hi, i'm an custom error")
	},
})

app.Get("/", func(c *fiber.Ctx) error {
	return c.SendFile("../there/is/nothing/here")
})
  • Config.ProxyHeader will enable c.IP() to return the value of the given header key. By default c.IP() will return the Remote IP from the tcp connection, this property can be useful if you are behind a load balancer e.g. X-Forwarded-*.
app := fiber.New(fiber.Config{
	ProxyHeader: "CF-Connecting-IP",
})
app.Get("/", func(c *fiber.Ctx) error {
	return c.SendString(c.IP()) // value from CF-Connecting-IP header
})
  • Config.GETOnly rejects all non-GET requests if set to true. This option is useful as anti-DoS protection for servers accepting only GET requests and will return an ErrMethodNotAllowed to the error handler. The request size is limited by ReadBufferSize if GETOnly is set. Server accepts all the requests by default.
app := fiber.New(fiber.Config{
	GETOnly: true,
})
app.Post("/", func(c *fiber.Ctx) error {
	return c.SendString("This method is not allowed")
})

🚀 Routing

  • The logic for parsing the route now better recognizes the parts which are not parameters, so the parameter can be placed anywhere, it is only important that the normal non-optional parameters are terminated by a delimiter character:
// Route
app.Get("/test::param/", handler)
// GET /test:fiber/

// Route
app.Get("/shop/product/color::color/size::size", handler)
// GET /shop/product/color:blue/size:xs

// Route
app.Get("/@:name", handler)
// GET /@Fiber
  • Added support for the plus parameter, this is greedy like the wildcard parameter with the difference that it is required:
// Route
app.Get("/config/+.json", func(c *fiber.Ctx) error {
	ctx.Params("+1") //  abc
        ctx.Params("+")   //  abc
})
// GET /config/abc.json  // match
// GET /config/.json     // no match
  • Support for multiple wildcard and plus parameters has been added, they can now be accessed via wildcard or plus character with the counter in the route or normally for the first as in the current fiber version:
// GET /customer/v1/cart/proxy
app.Get("/*v1*/proxy", func(c *fiber.Ctx) error {
        c.Params("*")    //   customer/
	c.Params("*1")  //   customer/
	c.Params("*2")  //   /cart
})

📦 Middleware

🚀 Improvements

  • The new version will use the segmentio/encoding package to encode JSON, this will improve performance drastically.
// v1.14.x
Benchmark_Ctx_JSON-16            4596667               260 ns/op              64 B/op          2 allocs/op
Benchmark_Ctx_JSON-16            4603731               259 ns/op              64 B/op          2 allocs/op
Benchmark_Ctx_JSON-16            4652700               259 ns/op              64 B/op          2 allocs/op
Benchmark_Ctx_JSON-16            4598620               259 ns/op              64 B/op          2 allocs/op

// v2.0.0
Benchmark_Ctx_JSON-16            7186842               165 ns/op              64 B/op          2 allocs/op
Benchmark_Ctx_JSON-16            7214056               164 ns/op              64 B/op          2 allocs/op
Benchmark_Ctx_JSON-16            7227295               164 ns/op              64 B/op          2 allocs/op
Benchmark_Ctx_JSON-16            7227291               165 ns/op              64 B/op          2 allocs/op
  • 405 Method Not Allowed will be passed to the global error handler, in the old version this behavior was not controllable.
  • This version will ship with fasthttp v1.16.0 which contains the statusLine/statusMessage PR found by @ReneWerner87, @Fenny and @kiyonlin
  • v2.0.0 allows us to implement a hybrid radix tree router which will remove the remaining allocation when CaseSenstitive is disabled and increase performance drasticly. The reason why we went with the hybrid implementation is because we still want to respect the router stack order 😉 thnx @ReneWerner87
// v1.14.x
Benchmark_Router_NotFound-16                      235243              4843 ns/op              80 B/op          2 allocs/op
Benchmark_Router_Handler-16                      1721277               696 ns/op              16 B/op          1 allocs/op
Benchmark_Router_Handler_Strict_Case-16          1848582               651 ns/op               0 B/op          0 allocs/op
Benchmark_Router_Chain-16                        9300248               129 ns/op               1 B/op          1 allocs/op
Benchmark_Router_WithCompression-16              8693692               137 ns/op               1 B/op          1 allocs/op
Benchmark_Router_Next-16                         1960342               619 ns/op               0 B/op          0 allocs/op
Benchmark_Router_Handler_CaseSensitive-16        1862936               649 ns/op               0 B/op          0 allocs/op
Benchmark_Router_Handler_Unescape-16              255260              4573 ns/op              16 B/op          1 allocs/op
Benchmark_Router_Handler_StrictRouting-16        1857168               647 ns/op               0 B/op          0 allocs/op
Benchmark_Router_Github_API-16                      3634            317628 ns/op               0 B/op          0 allocs/op

// v2.0.0
Benchmark_Router_NotFound-16                     2919049               411 ns/op              48 B/op          1 allocs/op
Benchmark_Router_Handler-16                      8331446               145 ns/op               0 B/op          0 allocs/op
Benchmark_Router_Handler_Strict_Case-16          9675211               124 ns/op               0 B/op          0 allocs/op
Benchmark_Router_Chain-16                        9088828               132 ns/op               0 B/op          0 allocs/op
Benchmark_Router_WithCompression-16              9020534               133 ns/op               0 B/op          0 allocs/op
Benchmark_Router_Next-16                        14454469                81 ns/op               0 B/op          0 allocs/op
Benchmark_Router_Handler_CaseSensitive-16        9597826               124 ns/op               0 B/op          0 allocs/op
Benchmark_Router_Handler_Unescape-16             6059216               196 ns/op               0 B/op          0 allocs/op
Benchmark_Router_Handler_StrictRouting-16        9753892               123 ns/op               0 B/op          0 allocs/op
Benchmark_Router_Github_API-16                      5452            214098 ns/op               0 B/op          0 allocs/op

See: ( 🚀 v2 benchmark results )

@Fenny Fenny pinned this issue Aug 21, 2020
@Fenny
Copy link
Member Author

Fenny commented Aug 21, 2020

To be continued....

@Fenny Fenny changed the title 🚀 [Upcoming] v1.15.x 📢 v1.15.x design draft Aug 21, 2020
@Fenny Fenny changed the title 📢 v1.15.x design draft 📢 v1.15.x announcement Aug 22, 2020
@StarMonk
Copy link

StarMonk commented Aug 27, 2020

Since, c.Request() & c.Response() is coming, is it possible to pass custom function hook an argument inside "FastHttp FileServer PathNotFound handler ?

fiber/router.go

Line 285 in 0d31dc1

PathNotFound: func(ctx *fasthttp.RequestCtx) {
?

@Fenny
Copy link
Member Author

Fenny commented Aug 27, 2020

@StarMonk the following would achieve the same result.

func main() {
    app := fiber.New()

    app.Static("/", "./public")

    app.Use(func(c *fiber.Ctx) error {
        return c.Status(fiber.StatusNotFound).SendString("I find your lack of navigation disturbing")
    })

    log.Fatal(app.Listen(":3000"))
}

@songjiayang
Copy link
Contributor

API is clear more, can't wait to use it

@numToStr
Copy link

numToStr commented Sep 1, 2020

Error handling gonna be 🔥

@Fenny Fenny changed the title 📢 v1.15.x announcement 📢 v1.15.x - Changelog [Breaking changes] Sep 9, 2020
@pofl
Copy link

pofl commented Sep 10, 2020

Changes look good to me.
But why is it v1.15 and not v2.0?

I'm almost certain VSCode will ask people whether it should update the dependency in go.mod. And suddenly the code doesn't compile anymore. Is this the kind of surprise that you want to make? @Fenny

You're breaking like 80% of the API. That calls for an increment of the major version.

@Fenny
Copy link
Member Author

Fenny commented Sep 10, 2020

Hey @pofl, good question!

We definitely considered moving to v2 and eventually moved away from that idea after long discussions with the Community

It's a fact that Go has a problem when it comes to the v2+ ecosystem, @donatj wrote a good article about addressing these issues:
Go Modules have a v2+ Problem

Most and if not all repo's that moved to v2+ have issues regarding this matter:
labstack/echo#1475, labstack/echo#1427 labstack/echo#1382, labstack/echo#1374 labstack/echo#1329, labstack/echo#1291
labstack/echo#1272, labstack/echo#1244, go-chi/chi#462, go-chi/chi#490, go-chi/chi#381, go-chi/chi#366, go-chi/chi#349, micro/micro#1223 micro/micro#835, micro/micro#522 micro/micro#481, micro/micro#273 goadesign/goa#2567, goadesign/goa#2511 goadesign/goa#2488, goadesign/goa#2320 goadesign/goa#2181, goadesign/goa#2111
goadesign/goa#2106, goadesign/goa#1925
emicklei/go-restful#374, go-gorm/gorm#3356
go-gorm/gorm#3038, go-gorm/gorm#2886
go-gorm/gorm#3300, go-gorm/gorm#3310 argoproj/argo-workflows#2602, golang/go#35732 tensorflow/tensorflow#39307
... this is only a fraction I found searching modules in issues, but I think it's clear thatv2+ modules are not widely known and cause problems. This shows that the Go Team needs to do a better job of shining a light on this rule.

The official Go Blog tried to clear the situation up some and posted a write-up about it. Personally I found this write-up somewhat impenetrable. For how important and unusual of a requirement this is, the write-up is not accessible enough.

We think this is the right way to go at this point in time to avoid future problems, but please feel free to change my mind!

For what is worth, we try to make this transition as good as possible and stand by for anyone who needs assistance.
You can always join us on Discord if you have questions/ideas or need help with your project!

I maintain Fiber in my spare time and all the help is welcome, so any feedback or ideas are highly appreciated!

Update: Thanks for all the feedback! We are moving to v2, see: #issuecomment-691251128

@pofl
Copy link

pofl commented Sep 11, 2020

Okay, I mean you run this project as you think is best. And I want to express my gratefulness for this great library.

But the way I see it is that many projects introduce v2+ without major issues. I worked with one of those, https://github.com/jackc/pgx, and it was made quite obvious to me how I should use it. Just import "github.com/jackc/pgx/v4" and all works.

I just wanted to give you my opinion on this. I think getting 5 Github issues asking about problems with v2 is the lesser issue compared to surprising people with breaking changes. But that is just my opinion. I'll shut up now. It's off my chest and I'll leave it at that. Thanks again for the great work.

@e3b0c442
Copy link

e3b0c442 commented Sep 11, 2020

I understand that module versioning is not perfect, but @pofl is correct: by introducing breaking changes within a major version, you are going to cause more problems for users.

Looking through the issues that you've cherrypicked, they for the most part seem to be easily resolved, due to misunderstandings by the user on how to use v2+, or not even related to modules at all (there was one where somebody was complaining about dep not working). That's not to say there aren't legitimate issues, but most of the problems seem to be related to understanding and documentation rather than inherent failure of the system.

Please reconsider your decision. They're not perfect, but modules are the path forward, and it would be better to work with the Go team to make them better than stepping on the boat halfway (using modules, but not adhering to the backward compatibility promise).

@Fenny
Copy link
Member Author

Fenny commented Sep 11, 2020

Thanks, @pofl, and @e3b0c442 for taking the time to shine some light on this decision!
We became trending on Project introduces breaking changes to library and I'm glad to see all the opinions regarding this matter.

I'm always open to learn new things and change my decision accordingly, I always want to make the right call for the community.
After all the feedback I received in the last 6 hours, I can say that most people would mark this project as badly maintained if we introduce breaking changes to v1. So moving to v2 is the right decision to make, and here is my plan to do so:

  • We move the master to a v1 branch to be able to maintain it for security issues.
  • The master branch will be updated with the changes ( including correct go.mod ) and tagged under v2.0.0

I'm not sure if we can still support legacy pre-module users this way, but that's a sacrifice we have to make.

Thanks for sharing your opinions and idea's, this is what open-source is all about 💪

@Fenny Fenny changed the title 📢 v1.15.x - Changelog [Breaking changes] 📢 v2 - Changelog Sep 11, 2020
@komuw
Copy link

komuw commented Sep 11, 2020

We definitely considered moving to v2 and eventually moved away from that idea after long discussions with the Community

We became trending on Project introduces breaking changes to library and I'm glad to see all the opinions regarding this matter. So moving to v2 is the right decision to make

I do not use fiber and neither do I have a preference on whether to move to v2 or make breaking changes in v1, but if you have to choose either, I would say choose the option that the current users & maintainers of fiber are more comfortable with.

it's all trade-offs.

@peterbourgon
Copy link

peterbourgon commented Sep 11, 2020

If you're going to use modules, and "decide" to push breaking changes without bumping the major version, you're breaking a core requirement of both semantic versioning the package management machinery, and you're creating potentially un-fixable errors for downstream consumers. It is of course technically possible to do this — there's no way to programmatically determine if a change is or isn't backwards compatible — but it's incredibly hostile to all of your users. In short: this isn't a decision you get to make, you need to use v2.

edit: sorry 🤦 I didn't see the comment 5h ago

@marcuxyz
Copy link

marcuxyz commented Sep 12, 2020

its a greating. Thank you!

@Fenny
Copy link
Member Author

Fenny commented Sep 12, 2020

Could be cool, if , we can get header with contex. E.g:
c.Header["Authorization"]

@marcuxyz we have the ctx.Get method to fetch the request header.

// Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
app.Get("/", func(c *fiber.Ctx) {
  c.Get("Authorization") // "Basic YWxhZGRpbjpvcGVuc2VzYW1l"
})

@pofl
Copy link

pofl commented Sep 13, 2020

Yo @Fenny I created that Reddit thread. My aim was to kick off a discussion about the v2 methods in the larger Go community. I didn't want to put you on the spot. And I hate that you have been accused of doing a bad job as the maintainer of this project.

Nevertheless I love the fact that you are so open-minded to just change direction in this matter. This project is in good hands.

@marcuxyz
Copy link

Could be cool, if , we can get header with contex. E.g:
c.Header["Authorization"]

@marcuxyz we have the ctx.Get method to fetch the request header.

// Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
app.Get("/", func(c *fiber.Ctx) {
  c.Get("Authorization") // "Basic YWxhZGRpbjpvcGVuc2VzYW1l"
})

Yes... I think that using header before its a more expressive

@kinbiko
Copy link
Contributor

kinbiko commented Sep 13, 2020

Thanks for being so open minded about going to 2.0 after listening to the feedback from the community. This decision has increased my confidence in using and endorsing this package even further.

@mikeychowy
Copy link

Man, i really can't wait for 2.0 😍 . What timezone will the release be on, Fenny? EST? Because if so, i've got an extra one day of waiting to do then.... 😭

@Fenny
Copy link
Member Author

Fenny commented Sep 14, 2020

@mikeychowy v2 is tagged https://github.com/gofiber/fiber/releases/tag/v2.0.0 🥳

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests