Skip to content

Listener multiplexing to run web servers with different protocols on the same port

Notifications You must be signed in to change notification settings

shubhang93/lnmux

Repository files navigation

lnmux [v0.2.0]

Listener multiplexing to run multiple web services on a single port (HTTP / HTTP2 / GRPC)

For go docs please run

godoc -http :6060
open http://localhost:6060/pkg/github.com/shubhang93/lnmux/

This library enables you to run multiple web services on the port using a single listener. The routing is decided by using connection matcher functions which peek into the connection and decide which server to route the connection to

Usage

package main

import (
	"context"
	"fmt"
	"github.com/shubhang93/lnmux"
	"github.com/shubhang93/lnmux/matchers"
	"golang.org/x/net/http2"
	"net"
	"net/http"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

func main() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		panic(err)
	}

	ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
	defer cancel()

	mux := lnmux.Listener{Root: ln, ConnReadTimeout: 100 * time.Millisecond}

	httpOneListener := mux.ListenFor("http-one-listener", matchers.AutoMatchHTTP)
	httpFastListener := mux.ListenFor("http-fast-listener", matchers.AutoMatchHTTPFast)
	HTTP2Listener := mux.ListenFor("grpc", matchers.MatchHTTP2Preface)
	// same matcher can be used for GRPC as well

	var wg sync.WaitGroup

	wg.Add(3)
	go func() {
		defer wg.Done()
		startServer(httpOneListener, func(writer http.ResponseWriter, request *http.Request) {
			_, _ = fmt.Fprintf(writer, "OK")
		}, false)
	}()

	go func() {
		defer wg.Done()
		startServer(httpFastListener, func(writer http.ResponseWriter, request *http.Request) {
			_, _ = fmt.Fprintf(writer, "JSON_OK")
		}, false)
	}()

	go func() {
		defer wg.Done()
		startServer(HTTP2Listener, func(writer http.ResponseWriter, request *http.Request) {
			_, _ = fmt.Fprintf(writer, "HTTP2_OK")
		}, true)
	}()

	if err := mux.Serve(ctx); err != nil {
		fmt.Println(err)
	}

	wg.Wait()
	fmt.Println("terminated")
}

func startServer(ln net.Listener, h http.HandlerFunc, useHTTP2 bool) {

	// shutdown server after 10s

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
	defer cancel()
	srvr := &http.Server{
		Handler: h,
	}

	if useHTTP2 {
		_ = http2.ConfigureServer(srvr, &http2.Server{})
	}

	done := make(chan struct{})
	go func() {
		defer close(done)
		<-ctx.Done()
		srvr.Shutdown(ctx)
	}()

	_ = srvr.Serve(ln)
	<-done

}

Note

Connection matching happens in the order they are registered. First matching connection wins. Order of ListenFor(s) matter

About

Listener multiplexing to run web servers with different protocols on the same port

Resources

Stars

Watchers

Forks

Packages

No packages published