commit 58fdc6c64daddd2e026f13c482609543494ff5c6 Author: kingecg Date: Wed Nov 13 00:00:06 2024 +0800 add code and complete first websocket frame diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..a0ddb39 --- /dev/null +++ b/Readme.md @@ -0,0 +1,3 @@ +# go http tunnel + +create tcp tunnel with websocket pipe \ No newline at end of file diff --git a/conf.json b/conf.json new file mode 100644 index 0000000..b2506de --- /dev/null +++ b/conf.json @@ -0,0 +1,17 @@ +{ + "host":"0.0.0.0", + "port":"8080", + "log":{ + "appenders":{ + "console":{ + "type":"console" + } + }, + "categories":{ + "default":{ + "appenders":["console"], + "level":"debug" + } + } + } +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..66495b2 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module git.pyer.club/kingecg/gotunnelserver + +go 1.23.1 + +require ( + git.pyer.club/kingecg/gologger v1.0.5 // indirect + github.com/gorilla/websocket v1.5.3 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..744a0bd --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +git.pyer.club/kingecg/gologger v1.0.5 h1:L/N/bleGHhEiaBYBf9U1z2ni0HfhaU71pk8ik/D11oo= +git.pyer.club/kingecg/gologger v1.0.5/go.mod h1:SNSl2jRHPzIpHSzdKOoVG798rtYMjPDPFyxUrEgivkY= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/main.go b/main.go new file mode 100644 index 0000000..7f0ac80 --- /dev/null +++ b/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "git.pyer.club/kingecg/gotunnelserver/server" +) + +func main() { + s := server.New("") + if s == nil { + return + } + s.Start() +} diff --git a/server/handleAgent.go b/server/handleAgent.go new file mode 100644 index 0000000..4fd6597 --- /dev/null +++ b/server/handleAgent.go @@ -0,0 +1,11 @@ +package server + +import ( + "net/http" + + "github.com/gorilla/websocket" +) + +func (s *Server) HandleAgent(c *websocket.Conn, r *http.Request) { + +} diff --git a/server/handleClient.go b/server/handleClient.go new file mode 100644 index 0000000..f78c64e --- /dev/null +++ b/server/handleClient.go @@ -0,0 +1,11 @@ +package server + +import ( + "net/http" + + "github.com/gorilla/websocket" +) + +func (s *Server) HandleClient(conn *websocket.Conn, r *http.Request) { + conn.WriteMessage(websocket.TextMessage, []byte("hello")) +} diff --git a/server/handlePipe.go b/server/handlePipe.go new file mode 100644 index 0000000..dd75c2c --- /dev/null +++ b/server/handlePipe.go @@ -0,0 +1,11 @@ +package server + +import ( + "net/http" + + "github.com/gorilla/websocket" +) + +func (s *Server) HandlePipe(conn *websocket.Conn, r *http.Request) { + conn.WriteMessage(websocket.TextMessage, []byte("hello")) +} diff --git a/server/main.go b/server/main.go new file mode 100644 index 0000000..68470e5 --- /dev/null +++ b/server/main.go @@ -0,0 +1,154 @@ +package server + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "net/http" + + "git.pyer.club/kingecg/gologger" + "github.com/gorilla/websocket" +) + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +type ServerConfig struct { + Port string `json:"port"` + Host string `json:"host"` + Cert string `json:"cert"` + Key string `json:"key"` + Logger *gologger.LoggersConfig `json:"log"` +} + +type Server struct { + *http.Server + mux *http.ServeMux + Config *ServerConfig + logger *gologger.Logger +} + +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.logger.Info("request:", r.URL.Path) + + if !s.auth(r) { + s.HandleUnAutherized(w, r) + } else { + s.mux.ServeHTTP(w, r) + } +} + +func (s *Server) auth(r *http.Request) bool { + s.logger.Info("auth:", r.RemoteAddr) + return true +} +func (s *Server) Shutdown() { + s.logger.Info("shutdown server") + s.Close() +} + +func (s *Server) HandleHello(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("hello")) +} + +func (s *Server) HandleUnAutherized(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusUnauthorized) +} +func (s *Server) HandleWs(w http.ResponseWriter, r *http.Request) { + lpath := r.URL.Path + lpath = strings.TrimPrefix(lpath, "/ws/") + c, err := upgrader.Upgrade(w, r, nil) + if err != nil { + s.logger.Error("upgrade failed:", err) + return + } + if strings.HasPrefix(lpath, "client") { + s.HandleClient(c, r) + } else if strings.HasPrefix(lpath, "agent") { + s.HandleAgent(c, r) + } else if strings.HasPrefix(lpath, "pipe") { + s.HandlePipe(c, r) + } else { + s.logger.Error("unknown path:", lpath) + c.Close() + } +} +func (s *Server) registHandler() { + if s.mux == nil { + s.mux = http.NewServeMux() + } + s.mux.HandleFunc("/hello", s.HandleHello) + s.mux.HandleFunc("/ws", s.HandleWs) +} +func (s *Server) Start() { + addr := s.Config.Host + ":" + s.Config.Port + s.Server.Addr = addr + + s.Server.Handler = s + s.registHandler() + if s.Config.Cert != "" && s.Config.Key != "" { + s.logger.Info("start https server") + s.ListenAndServeTLS(s.Config.Cert, s.Config.Key) + } else { + s.logger.Info("start http server") + s.ListenAndServe() + } +} + +func New(configFile string) *Server { + // load config from configFile + config, err := LoadConfig(configFile) + if err != nil { + return nil + } + gologger.Configure(*config.Logger) + logger := gologger.GetLogger("server") + logger.Info("create server") + server := &Server{ + Server: &http.Server{}, + Config: config, + logger: logger, + } + return server +} + +func LoadConfig(configFile string) (conf *ServerConfig, err error) { + aconfPath := configFile + if aconfPath == "" { + aconfPath = "conf.json" + } + if !filepath.IsAbs(aconfPath) { + aconfPath, _ = filepath.Abs(aconfPath) + } + fileConten, err := os.ReadFile(aconfPath) + if err != nil { + fmt.Printf("read config file failed: %s", err) + return nil, err + } + conf = new(ServerConfig) + err = json.Unmarshal(fileConten, conf) + if err != nil { + fmt.Printf("parse config file failed: %s", err) + return nil, err + } + if conf.Cert != "" && conf.Key != "" { + conf.Cert = Abs(filepath.Dir(aconfPath), conf.Cert) + conf.Key = Abs(filepath.Dir(aconfPath), conf.Key) + } else { + conf.Cert = "" + conf.Key = "" + } + return conf, nil +} + +func Abs(basePath string, path string) string { + if filepath.IsAbs(path) { + return path + } + return filepath.Join(basePath, path) +}