Fiber: 馃 Converts net/http request handlers to fiber request?

Created on 25 Apr 2020  路  9Comments  路  Source: gofiber/fiber

Question description

My use case is to user Fiber inside Google Cloud Functions or other places that enforce us to use the go http.Handler interface.

The code snippet below is an example using functions-framework-go. I know that I can probably go ahead and use App Engine or Cloud Run, but would be interesting to support also Cloud Functions.

Sorry if this is a silly question, I'm not that used to Go and I was trying to add more recipes to the repository and encourage people to use Fiber on all Google Cloud Go environments.

Code snippet

import (
    "net/http"
    "os"

    "github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
    "github.com/gofiber/fiber"
)

var app *fiber.App

func init() {   
    app = fiber.New()

    app.Get("/", func(c *fiber.Ctx) {
        c.Send("Hello World 馃殌")
    })
}

func MyFunction(w http.ResponseWriter, r *http.Request) {   
    app.ServeHTTP(w, r) // What to do here to feed app.Fiber with req and response ???
}

func main() {
    funcframework.RegisterHTTPFunction("/", MyFunction)
    port := "8080"
    if envPort := os.Getenv("PORT"); envPort != "" {
        port = envPort
    }

    if err := funcframework.Start(port); err != nil {
        log.Fatalf("app.Start: %v\n", err)
    }
}

Most helpful comment

you can try something like this

import (
    "github.com/gofiber/fiber"
    "github.com/valyala/fasthttp/fasthttpadaptor"
    "net/http"
)

...

g.Get("/", WrapHandler(pprof.Index))

func WrapHandler(f func(http.ResponseWriter, *http.Request)) func(ctx *fiber.Ctx) {
    return func(ctx *fiber.Ctx) {
        fasthttpadaptor.NewFastHTTPHandler(http.HandlerFunc(f))(ctx.Fasthttp)
    }
}

All 9 comments

Thanks for opening your first issue here! 馃帀 Be sure to follow the issue template! If you want to chat with us or need help, join us on our Discord server: https://gofiber.io/discord

you can try something like this

import (
    "github.com/gofiber/fiber"
    "github.com/valyala/fasthttp/fasthttpadaptor"
    "net/http"
)

...

g.Get("/", WrapHandler(pprof.Index))

func WrapHandler(f func(http.ResponseWriter, *http.Request)) func(ctx *fiber.Ctx) {
    return func(ctx *fiber.Ctx) {
        fasthttpadaptor.NewFastHTTPHandler(http.HandlerFunc(f))(ctx.Fasthttp)
    }
}

@Shareed2k is right, the fasthttpadaptor converts net/http request handlers to fasthttp request handlers. But keep in mind that this function may be used for easy switching from net/http to fasthttp, it has the following drawbacks comparing to using manually written fasthttp request handler:

  • A lot of useful functionality provided by fasthttp is missing
    from net/http handler.
  • net/http -> fasthttp handler conversion has some overhead,
    so the returned handler will be always slower than manually written
    fasthttp handler.

So it is advisable using this function only for development but then convert the net/http handlers to fiber when going into production.

Hey, thanks for the support. What I wanted to do was the opposite of that. I was able to do that by using an HTTP client calling the internal Fiber app.

It's super cumbersome but solves the problem on the Cloud Functions case that strictly requires us to use http.HandlerFunc. For Cloud Run and App Engine that will not be required. I'll try to wrap up some basic examples for each Serverless platform on Google Cloud and open a PR on the recipes repository if you guys think that's valuable.

package functions

import (
    "bytes"
    "context"
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "net"
    "net/http"
    "os"

    "github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
    "github.com/gofiber/fiber"
    "github.com/valyala/fasthttp/fasthttputil"
)

var app *fiber.App

func init() {
    app = fiber.New()
    group := app.Group("/api")

    group.Get("/hello", func(c *fiber.Ctx) {
        c.Send("Hello World 馃殌")
    })

    group.Get("/ola", func(c *fiber.Ctx) {
        c.Send("Ol谩 Mundo 馃殌")
    })
}

func MyCloudFunction(w http.ResponseWriter, r *http.Request) {
    ln := fasthttputil.NewInmemoryListener()
    defer ln.Close()

    // Copy request
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    url := fmt.Sprintf("%s://%s%s", "http", "0.0.0.0", r.RequestURI)
    proxyReq, err := http.NewRequest(r.Method, url, bytes.NewReader(body))
    proxyReq.Header = r.Header

    // Create http client
    client := http.Client{
        Transport: &http.Transport{
            DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
                return ln.Dial()
            },
        },
    }

    // Serve request to internal HTTP client
    go func() {
        err := app.Serve(ln)
        if err != nil {
            log.Fatalf("server err : %v", err)
            panic(err)
        }
    }()

    // Call internal Fiber API
    response, err := client.Do(proxyReq)
    if err != nil {
        fmt.Fprintf(w, "err : %v", err)
        return
    }

    // Copy response and headers
    for k, values := range response.Header {
        for _, v := range values {
            w.Header().Set(k, v)
        }
    }
    w.WriteHeader(response.StatusCode)

    io.Copy(w, response.Body)
    response.Body.Close()
}

func main() {
    funcframework.RegisterHTTPFunction("/", MyCloudFunction)

    port := "8080"
    if envPort := os.Getenv("PORT"); envPort != "" {
        port = envPort
    }

    if err := funcframework.Start(port); err != nil {
        log.Fatalf("app.Start: %v\n", err)
    }
}

you can try something like this

import (
  "github.com/gofiber/fiber"
  "github.com/valyala/fasthttp/fasthttpadaptor"
  "net/http"
)

...

g.Get("/", WrapHandler(pprof.Index))

func WrapHandler(f func(http.ResponseWriter, *http.Request)) func(ctx *fiber.Ctx) {
  return func(ctx *fiber.Ctx) {
      fasthttpadaptor.NewFastHTTPHandler(http.HandlerFunc(f))(ctx.Fasthttp)
  }
}

Is there any way to prevent that pprof dependency?

pprof is just example

Just submitted the PR adding the Google Cloud examples https://github.com/gofiber/recipes/pull/16

@alvarowolfx, I'm happy you got this working. Too bad Google Cloud forces you to use a http.HandlerFunc instead of a fixed port.

I will work on a simple adapter middleware for fiber when I have some spare time left. Thanks for sharing your problem and solution, I will mention this issue in an upcoming TODO issue.

PS: @alvarowolfx are you able to listen on a second port? If so you could proxy the request to the fiber port.

I'll take a look on starting it on a separated port and proxying the request, but I think the current workaround that I sent do almost that.

But at least this is only required on Cloud functions, maybe on the recipes repo we encourage people to use app engine or cloud run for now, as this is not needed on those environments.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

peterbourgon picture peterbourgon  路  4Comments

lucasmdomingues picture lucasmdomingues  路  3Comments

Ivan-Feofanov picture Ivan-Feofanov  路  3Comments

Badrouu17 picture Badrouu17  路  4Comments

petersephrin picture petersephrin  路  4Comments