huma icon indicating copy to clipboard operation
huma copied to clipboard

Few notes about fiber adapter

Open x-user opened this issue 1 year ago • 1 comments

If i understand correctly fiberCtx.SetReadDeadline actually doesn't work and probably will not. Also if at same time we will add ability to get client IP address from huma.Context it will not work with current fiber adapter implementation.

Currently fiberAdapter.ServeHTTP calls App.Test to handle requests. This method uses testConn to mock incoming connection.

But testConn.Set*Deadline methods do nothing (source):

func (*testConn) SetDeadline(_ time.Time) error      { return nil }
func (*testConn) SetReadDeadline(_ time.Time) error  { return nil }
func (*testConn) SetWriteDeadline(_ time.Time) error { return nil }

And testConn.*Addr methods return hardcoded values (source):

func (*testConn) LocalAddr() net.Addr                { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} }
func (*testConn) RemoteAddr() net.Addr               { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} }

About using adaptor middleware

Closure returned by adaptor.handlerFunc serves as fiberAdapter.ServeHTTP, it declares fctx variable and initialize it by calling RequestCtx.Init method.

func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// ...
		var fctx fasthttp.RequestCtx
		fctx.Init(req, remoteAddr, nil)

RequestCtx.Init method calls RequestCtx.Init2 passing fakeAddrer struct as net.Conn value without setting underlying net.Conn to anything

func (ctx *RequestCtx) Init(req *Request, remoteAddr net.Addr, logger Logger) {
	// ...
	c := &fakeAddrer{
		laddr: zeroTCPAddr,
		raddr: remoteAddr,
	}
	// ...
	ctx.Init2(c, logger, true)
	// ...
}
// ...
type fakeAddrer struct {
	net.Conn
	laddr net.Addr
	raddr net.Addr
}

RequestCtx.Init2 sets c field to provided net.Conn value

func (ctx *RequestCtx) Init2(conn net.Conn, logger Logger, reduceMemoryUsage bool) {
	ctx.c = conn

RequestCtx.Conn returns it's c property value (fakeAddrer with underlying net.Conn set to nil)

func (ctx *RequestCtx) Conn() net.Conn {
	return ctx.c
}

fakeAddrer doesn't have it's own SetReadDeadline implementation so it calls SetReadDeadline method of it's underlying net.Conn (nil), because of that c.orig.Context().Conn().SetReadDeadline(deadline) in fiberCtx.SetReadDeadline panics.

IMHO: We can simply return nil from fiberCtx.SetReadDeadline without doing anything because it will not work anyway.

x-user avatar Mar 30 '24 06:03 x-user

@x-user hello!

I am maintaing humafiber adapter (not officially). I read your issue and comment, and I want to drag your attention to the following:

  • fasthttp and fiber has the very special way to deal with context (because of zero-allocation)
  • fiber.Ctx.Context and fiber.Ctx.UserContext works in the very specific way.

The long story short - to get expected behavior with Deadline, you need to alter "UserContext" inside middleware and you should use "UserContext" with altering to get it.

Take a look to this article from fiber documentation about timeout

  • https://docs.gofiber.io/api/middleware/timeout/

Also you could take a look to this pull request with fixes to huma.Context <=> Fiber integration

  • https://github.com/danielgtaylor/huma/pull/725

Bottom line - use fiber "Timeout" middleware + fixed version of humafiber from my PR, after that you will receive the expected behavior with timeout

Please ask questions if you have any

excavador avatar Feb 13 '25 08:02 excavador