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)
}
}
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:
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.
Most helpful comment
you can try something like this