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