ngrok/ngrok-go

fasthttp support?

tigerinus opened this issue · 3 comments

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

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

Thanks!

euank commented

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
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?

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.

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!