Skip to content

A library for establishing outbound WebSocket connections from HTTP servers, overcoming ingress restrictions.

License

Notifications You must be signed in to change notification settings

grepplabs/backstream

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

backstream

Release License Build

Overview

The backstream is a library designed for environments with restricted ingress connections. It enables HTTP servers to establish outbound WebSocket connections to a central HTTP service. Once connected, users can send HTTP requests to the central service, which efficiently forwards them downstream to the appropriate WebSocket-enabled servers, effectively bypassing ingress restrictions.

backstream

Usage

Write a proxy server.

package main

import (
	"context"
	"flag"
	"log/slog"
	"net/http"
	"os"
	"time"

	"github.com/grepplabs/backstream/handler"
	"github.com/grepplabs/backstream/ws"
)

func main() {
	addr := flag.String("addr", ":8080", "Listen address")
	flag.Parse()

	codec := handler.NewHttpProtoCodec()
	proxyHandler := handler.NewProxyHandler(codec, handler.WithProxyDefaultRequestTimeout(3*time.Second))
	logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug}))
	serve := ws.NewServe(context.Background(), proxyHandler, codec.MessageCodec(), ws.WithServeLogger(logger), ws.WithRequireClientId(true))

	mux := http.NewServeMux()
	mux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
		serve.HandleWS(w, r)
	})
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		serve.HandleProxy(w, r)
	})
	server := &http.Server{
		Addr:              *addr,
		ReadHeaderTimeout: 3 * time.Second,
		Handler:           mux,
	}
	if err := server.ListenAndServe(); err != nil {
		logger.Error(err.Error())
	}
}

Write a HTTP service server.

package main

import (
	"context"
	"flag"
	"log/slog"
	"net/http"
	"os"

	"github.com/grepplabs/backstream/handler"
	"github.com/grepplabs/backstream/ws"
)

func main() {
	addr := flag.String("addr", ":8081", "Listen address")
	proxyUrl := flag.String("proxy-url", "ws://localhost:8080/ws", "Proxy websocket endpoint")
	clientID := flag.String("client-id", "4711", "Client ID")
	flag.Parse()

	codec := handler.NewHttpProtoCodec()
	logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug})).With(slog.String("client-id", *clientID))
	mux := http.NewServeMux()
	wsHandler := handler.NewRecoveryHandler(handler.NewHTTPHandler(func(w http.ResponseWriter, r *http.Request) {
		mux.ServeHTTP(w, r)
	}, codec), logger)
	client := ws.NewClient(context.Background(), *proxyUrl, wsHandler, codec.MessageCodec(), ws.WithClientLogger(logger), ws.WithClientID(*clientID))
	client.Start()

	mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
	})
	server := &http.Server{
		Addr:    *addr,
		Handler: mux,
	}
	if err := server.ListenAndServe(); err != nil {
		logger.Error(err.Error())
	}
}

Invoke test endpoint against the proxy server.

curl -v -H 'x-backstream-client-id: 4711' http://localhost:8080/test
curl -v -H 'x-backstream-client-id: 4711' -H 'x-backstream-request-timeout: 12s' http://localhost:8080/test