ngrok / ngrok-go

Embed ngrok secure ingress into your Go apps as a net.Listener with a single line of code.
MIT License
684 stars 65 forks source link

fasthttp support? #65

Closed tigerinus closed 1 year ago

tigerinus commented 1 year ago

My project is https://github.com/valyala/fasthttp based.

It'd be nice to serve ngrok tun via fasthttp.

Thanks!

euank commented 1 year ago

Due to the library exposing connections directly, I think it's already possible to do this!

The following code (omitting error handling etc) worked for me:

tun, _ := ngrok.Listen(ctx,
    config.HTTPEndpoint(),
    ngrok.WithAuthtokenFromEnv(),
)
log.Println("tunnel created:", tun.URL())

var serv fasthttp.Server
serv.Handler = func(ctx *fasthttp.RequestCtx) {
    fmt.Fprintf(ctx, "Hello! You're requesting %q", ctx.RequestURI())
}

for {
    conn, _ := tun.Accept()
    go serv.ServeConn(conn)
}
Complete example code ```go package main import ( "context" "fmt" "log" "github.com/valyala/fasthttp" "golang.ngrok.com/ngrok" "golang.ngrok.com/ngrok/config" ) func main() { if err := run(context.Background()); err != nil { log.Fatalf("main: %v", err) } } func run(ctx context.Context) error { tun, err := ngrok.Listen(ctx, config.HTTPEndpoint(), ngrok.WithAuthtokenFromEnv(), ) if err != nil { return err } log.Println("tunnel created:", tun.URL()) var serv fasthttp.Server serv.Handler = func(ctx *fasthttp.RequestCtx) { fmt.Fprintf(ctx, "Hello! You're requesting %q", ctx.RequestURI()) } for { conn, err := tun.Accept() if err != nil { return err } go func() { err := serv.ServeConn(conn) if err != nil { log.Printf("error serving connection %v: %v", conn, err) } }() } } ```

Basically, fasthttp already has fasthttp.Server.ServeConn, which can let us plumb in the net.Conn this library's Tunnel.Accept() exposes.

This ngrok library does have some sugar for http.Handler, and we could consider adding sugar for fasthttp, but having an example showing the above might also be enough.

That said, I haven't used fasthttp much. Does using the fasthttp.Server directly have other caveats or is there something the above approach doesn't work for?

tigerinus commented 1 year ago

I am not sure if there is any caveats - it's something to find out.

But this is certainly promising. Theoratically it should serve ngrok requests faster.

jrobsonchase commented 1 year ago

It looks like you can also use the fasthttp.Server.Serve method to serve from a net.Listener directly, which the ngrok.Tunnel type implements.

This simplifies the example to:

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/valyala/fasthttp"
    "golang.ngrok.com/ngrok"
    "golang.ngrok.com/ngrok/config"
)

func main() {
    if err := run(context.Background()); err != nil {
        log.Fatalf("main: %v", err)
    }
}

func run(ctx context.Context) error {
    tun, err := ngrok.Listen(ctx,
        config.HTTPEndpoint(),
        ngrok.WithAuthtokenFromEnv(),
    )
    if err != nil {
        return err
    }
    log.Println("tunnel created:", tun.URL())

    var serv fasthttp.Server

    serv.Handler = func(ctx *fasthttp.RequestCtx) {
        fmt.Fprintf(ctx, "Hello! You're requesting %q", ctx.RequestURI())
    }

        serv.Serve(tun)
}

Which should give you all the usual concurrency for free without having to worry about individual connections.

Going to create an issue for adding this example to the repo, but otherwise closing this as solved!