chore: initial commit
This commit is contained in:
87
cmd/server/main.go
Normal file
87
cmd/server/main.go
Normal file
@ -0,0 +1,87 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"forge.cadoles.com/wpetit/go-tunnel"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
)
|
||||
|
||||
const sharedKey = "go-tunnel"
|
||||
const salt = "go-tunnel"
|
||||
|
||||
var registry = NewRegistry()
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
logger.SetLevel(slog.LevelDebug)
|
||||
|
||||
server := tunnel.NewServer(
|
||||
tunnel.WithServerAESBlockCrypt(sharedKey, salt),
|
||||
tunnel.WithServerOnClientAuth(registry.OnClientAuth),
|
||||
tunnel.WithServerOnClientDisconnect(registry.OnClientDisconnect),
|
||||
)
|
||||
|
||||
go func() {
|
||||
if err := server.Listen(ctx); err != nil {
|
||||
logger.Fatal(ctx, "error while listening", logger.E(err))
|
||||
}
|
||||
}()
|
||||
|
||||
if err := http.ListenAndServe(":3003", http.HandlerFunc(handleRequest)); err != nil {
|
||||
logger.Fatal(ctx, "error while listening", logger.E(err))
|
||||
}
|
||||
}
|
||||
|
||||
func handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
subdomains := strings.SplitN(r.Host, ".", 2)
|
||||
|
||||
if len(subdomains) < 2 {
|
||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
clientID := subdomains[0]
|
||||
remoteClient := registry.Get(clientID)
|
||||
|
||||
if remoteClient == nil {
|
||||
http.Error(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
target, err := url.Parse("http://localhost:3000")
|
||||
if err != nil {
|
||||
logger.Fatal(r.Context(), "could not parse url", logger.E(err))
|
||||
}
|
||||
|
||||
reverse := httputil.NewSingleHostReverseProxy(target)
|
||||
reverse.Transport = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {
|
||||
conn, err := remoteClient.Proxy(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
},
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
reverse.ServeHTTP(w, r)
|
||||
}
|
77
cmd/server/registry.go
Normal file
77
cmd/server/registry.go
Normal file
@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"forge.cadoles.com/wpetit/go-tunnel"
|
||||
cmap "github.com/streamrail/concurrent-map"
|
||||
)
|
||||
|
||||
type Registry struct {
|
||||
clientIDByRemoteAddr cmap.ConcurrentMap
|
||||
clientByClientID cmap.ConcurrentMap
|
||||
}
|
||||
|
||||
func (r *Registry) OnClientAuth(ctx context.Context, rc *tunnel.RemoteClient, credentials interface{}) (bool, error) {
|
||||
clientID, ok := credentials.(string)
|
||||
if !ok {
|
||||
return false, errors.New("unexpected credentials")
|
||||
}
|
||||
|
||||
remoteAddr := rc.RemoteAddr().String()
|
||||
|
||||
r.Add(clientID, remoteAddr, rc)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *Registry) OnClientDisconnect(ctx context.Context, rc *tunnel.RemoteClient) error {
|
||||
remoteAddr := rc.RemoteAddr().String()
|
||||
|
||||
r.RemoveByRemoteAddr(remoteAddr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) Get(clientID string) *tunnel.RemoteClient {
|
||||
rawRemoteClient, exists := r.clientByClientID.Get(clientID)
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
remoteClient, ok := rawRemoteClient.(*tunnel.RemoteClient)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return remoteClient
|
||||
}
|
||||
|
||||
func (r *Registry) Add(clientID, remoteAddr string, rc *tunnel.RemoteClient) {
|
||||
r.clientByClientID.Set(clientID, rc)
|
||||
r.clientIDByRemoteAddr.Set(remoteAddr, clientID)
|
||||
}
|
||||
|
||||
func (r *Registry) RemoveByRemoteAddr(remoteAddr string) {
|
||||
rawClientID, exists := r.clientIDByRemoteAddr.Get(remoteAddr)
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
|
||||
r.clientIDByRemoteAddr.Remove(remoteAddr)
|
||||
|
||||
clientID, ok := rawClientID.(string)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
r.clientByClientID.Remove(clientID)
|
||||
}
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
clientIDByRemoteAddr: cmap.New(),
|
||||
clientByClientID: cmap.New(),
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user