Initial commit with basic version of Ngkrok
This commit is contained in:
commit
f730bdbc74
1
src/.env.dist
Normal file
1
src/.env.dist
Normal file
@ -0,0 +1 @@
|
||||
SERVER_PORT=:4040
|
18
src/.gitignore
vendored
Normal file
18
src/.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# ---> Go
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
vendor/
|
||||
|
||||
.env
|
67
src/cmd/client/main.go
Normal file
67
src/cmd/client/main.go
Normal file
@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/progrium/qmux/golang/session"
|
||||
)
|
||||
|
||||
const (
|
||||
CLIENT_VERSION = "v0.2"
|
||||
HEADER_CLIENT_TOKEN = "X-Client-App-Token"
|
||||
HEADER_USER_AGENT = "User-Agent"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
srvPort, srvHost, clientToken := "18181", "hop.pbiernat.dev", "ghd7H8H*&6tg8*tg68"
|
||||
targetPort := flag.Arg(0)
|
||||
|
||||
if targetPort == "" {
|
||||
log.Fatal("Missing port!")
|
||||
}
|
||||
|
||||
conn, err := net.Dial("tcp", net.JoinHostPort(srvHost, srvPort))
|
||||
fatal("main net.Dial", err)
|
||||
client := httputil.NewClientConn(conn, bufio.NewReader(conn))
|
||||
req, err := http.NewRequest("GET", "/register", nil)
|
||||
fatal("http.NewRequest", err)
|
||||
req.Host = net.JoinHostPort(srvHost, srvPort)
|
||||
req.Header.Add(HEADER_CLIENT_TOKEN, clientToken)
|
||||
req.Header.Add(HEADER_USER_AGENT, "HopClient/"+CLIENT_VERSION)
|
||||
client.Write(req)
|
||||
resp, _ := client.Read(req)
|
||||
fmt.Printf("http port %s available at:\n", targetPort)
|
||||
fmt.Printf("https://%s\n", resp.Header.Get("X-Public-Host"))
|
||||
c, _ := client.Hijack()
|
||||
sess := session.New(c)
|
||||
defer sess.Close()
|
||||
for {
|
||||
ch, err := sess.Accept()
|
||||
fatal("Accept", err)
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:"+targetPort)
|
||||
fatal("net.Dial", err)
|
||||
go join(conn, ch)
|
||||
}
|
||||
}
|
||||
|
||||
func join(a io.ReadWriteCloser, b io.ReadWriteCloser) {
|
||||
go io.Copy(b, a)
|
||||
io.Copy(a, b)
|
||||
a.Close()
|
||||
b.Close()
|
||||
}
|
||||
|
||||
func fatal(title string, err error) {
|
||||
if err != nil {
|
||||
log.Fatal(title+": ", err)
|
||||
}
|
||||
}
|
39
src/cmd/server/main.go
Normal file
39
src/cmd/server/main.go
Normal file
@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"git.pbiernat.dev/golang/ngkrok/internal/ngkrok"
|
||||
vhost "github.com/inconshreveable/go-vhost"
|
||||
)
|
||||
|
||||
func main() {
|
||||
addr, host, port := "::", "hop.pbiernat.dev", "18181"
|
||||
|
||||
lst, err := net.Listen("tcp", net.JoinHostPort(addr, port))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
vmux, err := vhost.NewHTTPMuxer(lst, 3*time.Second)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer vmux.Close()
|
||||
|
||||
srv := ngkrok.NewServer(addr, host, port, vmux)
|
||||
go srv.Serve()
|
||||
|
||||
log.Printf("NgKrok server [%s] ready!\n", host)
|
||||
|
||||
for {
|
||||
conn, err := vmux.NextError()
|
||||
fmt.Println("vmux error:", err)
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
}
|
13
src/go.mod
Normal file
13
src/go.mod
Normal file
@ -0,0 +1,13 @@
|
||||
module git.pbiernat.dev/golang/ngkrok
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/inconshreveable/go-vhost v1.0.0 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/klauspost/compress v1.16.5 // indirect
|
||||
github.com/progrium/qmux/golang v0.0.0-20210721211401-475935a675d8 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.46.0 // indirect
|
||||
)
|
21
src/go.sum
Normal file
21
src/go.sum
Normal file
@ -0,0 +1,21 @@
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/inconshreveable/go-vhost v1.0.0 h1:IK4VZTlXL4l9vz2IZoiSFbYaaqUW7dXJAiPriUN5Ur8=
|
||||
github.com/inconshreveable/go-vhost v1.0.0/go.mod h1:aA6DnFhALT3zH0y+A39we+zbrdMC2N0X/q21e6FI0LU=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
|
||||
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
|
||||
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/progrium/qmux/golang v0.0.0-20210721211401-475935a675d8 h1:hLnTL/51vGU9IB4yFFExFSQgRlqFh0GHIJxCcOh7LT0=
|
||||
github.com/progrium/qmux/golang v0.0.0-20210721211401-475935a675d8/go.mod h1:Z2EPtydgPrcZxO50GhkzTGgdWjA5PPHsZkoq6KTxPVE=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.46.0 h1:6ZRhrFg8zBXTRYY6vdzbFhqsBd7FVv123pV2m9V87U4=
|
||||
github.com/valyala/fasthttp v1.46.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
|
||||
golang.org/x/net v0.0.0-20210420210106-798c2154c571/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
118
src/internal/ngkrok/server.go
Normal file
118
src/internal/ngkrok/server.go
Normal file
@ -0,0 +1,118 @@
|
||||
package ngkrok
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
vhost "github.com/inconshreveable/go-vhost"
|
||||
"github.com/progrium/qmux/golang/session"
|
||||
)
|
||||
|
||||
const (
|
||||
SERVER_VERSION = "v0.5"
|
||||
HEADER_CLIENT_TOKEN = "X-Client-App-Token"
|
||||
HEADER_PUBLIC_HOST = "X-Public-Host"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
addr string
|
||||
host string
|
||||
port string
|
||||
VMux *vhost.HTTPMuxer
|
||||
}
|
||||
|
||||
func NewServer(addr, host, port string, vmux *vhost.HTTPMuxer) *Server {
|
||||
return &Server{addr, host, port, vmux}
|
||||
}
|
||||
|
||||
func (s *Server) Serve() {
|
||||
ml, err := s.VMux.Listen(net.JoinHostPort(s.host, s.port))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
srv := &http.Server{}
|
||||
// http.HandleFunc("/", s.mainHandler)
|
||||
http.HandleFunc("/register", s.registerHandler)
|
||||
|
||||
srv.Serve(ml)
|
||||
}
|
||||
|
||||
// func (s *Server) mainHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// log.Println("mainHandler")
|
||||
// return
|
||||
// }
|
||||
|
||||
func (s *Server) registerHandler(w http.ResponseWriter, r *http.Request) {
|
||||
subdomain := newSubdomain(r) + s.host
|
||||
publicHost := strings.TrimSuffix(net.JoinHostPort(subdomain, s.port), ":80")
|
||||
pl, err := s.VMux.Listen(publicHost)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer pl.Close()
|
||||
|
||||
w.Header().Add(HEADER_PUBLIC_HOST, subdomain)
|
||||
w.Header().Add("Connection", "close")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
conn, _, _ := w.(http.Hijacker).Hijack()
|
||||
sess := session.New(conn)
|
||||
defer sess.Close()
|
||||
log.Printf("%s: start session", subdomain)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := pl.Accept()
|
||||
if err != nil {
|
||||
log.Println("Accept:", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
ch, err := sess.Open(context.Background())
|
||||
if err != nil {
|
||||
log.Println("sess.Open:", err)
|
||||
return
|
||||
}
|
||||
defer ch.Close()
|
||||
|
||||
go join(ch, conn)
|
||||
}
|
||||
}()
|
||||
|
||||
sess.Wait()
|
||||
log.Printf("%s: end session", subdomain)
|
||||
}
|
||||
|
||||
func join(a io.ReadWriteCloser, b io.ReadWriteCloser) {
|
||||
go io.Copy(b, a)
|
||||
io.Copy(a, b)
|
||||
a.Close()
|
||||
b.Close()
|
||||
}
|
||||
|
||||
func newSubdomain(r *http.Request) string {
|
||||
clientToken := r.Header.Get(HEADER_CLIENT_TOKEN)
|
||||
|
||||
if clientToken == "ghd7H8H*&6tg8*tg68" { // hardcoded token for dev tests....
|
||||
return "test."
|
||||
}
|
||||
|
||||
b := make([]byte, 10)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
letters := []rune("abcdefghijklmnopqrstuvwxyz1234567890")
|
||||
rune := make([]rune, 10)
|
||||
for i := range rune {
|
||||
rune[i] = letters[int(b[i])*len(letters)/256]
|
||||
}
|
||||
|
||||
return string(rune) + "."
|
||||
}
|
Loading…
Reference in New Issue
Block a user