package main import ( "fmt" "io" "log" "net" "net/http" "strings" "github.com/gin-gonic/gin" ) const ( TUNNEL_PACKET = `HTTP/1.1 200 Connection Established\r\nProxy-agent: spit-wizard.void\r\n\r\n` ) func main() { r := gin.Default() r.NoRoute(routeProxy) // NO Route is every Route!!! r.Run(":8080") // listen and serve on 0.0.0.0:8080 } // Then I can process all routes func routeProxy(c *gin.Context) { req := c.Request go resolveReq(req) // just print basic info. Remember you can't proxy youself. if req.Method == http.MethodConnect { // create http tunnel process https httpsProxy(c, req) } else { // process plain http httpProxy(c, req) } } func resolveReq(req *http.Request) { fmt.Printf("Method: %s, Host: %s, URL: %s, Version: %s\n", req.Method, req.Host, req.URL.Path, req.Proto) //fmt.Printf("%+v\n", req) } func httpProxy(c *gin.Context, req *http.Request) { newReq, _ := http.NewRequest(req.Method, req.URL.String(), req.Body) resp, err := http.DefaultClient.Do(newReq) if err != nil { log.Fatal(err) } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%s\n%s\n%s\n", req.Host, req.Method, req.RequestURI) code := resp.StatusCode c.Status(code) // change the status code, default is 404 !!! for k, v := range resp.Header { c.Header(k, strings.Join(v, ",")) } c.Header("Server", "spit-wizard.void") // just change it, this is yours gin proxy. c.Writer.Write(data) } func httpsProxy(c *gin.Context, req *http.Request) { // established connect tunnel c.Status(200) c.Header("Server", "spit-wizard.void") c.Writer.Write([]byte(TUNNEL_PACKET)) // c.Writer.Flush() // this may cause proble, but I don't know. address := req.URL.Host // it contains the port tunnelConn, err := net.Dial("tcp", address) if err != nil { log.Fatal(err) } //fmt.Printf("try to established Connect Tunnel to: %s has been successfully.\n", address) // But next is a TCP communication, but the tcp conn is a non-export variable, // so I can't get it, in the same, client's data is binary, so gin can't parse it. // so I can't do anything!!! // LOL, I find HTTP hijacker, it can make me take over the connection!!! hj, ok := c.Writer.(http.Hijacker) if !ok { http.Error(c.Writer, "webserver doesn't support hijacking", http.StatusInternalServerError) return } conn, bufrw, err := hj.Hijack() if err != nil { http.Error(c.Writer, err.Error(), http.StatusInternalServerError) return } defer conn.Close() // first read request, then write response. // data flow direction: // client <---> proxy <---> server done := make(chan struct{}) go func() { io.Copy(tunnelConn, bufrw) /* // this is my first write, but then I find other use io.Copy log.Println("client --> proxy --> server") data := make([]byte, 1024) for { log.Println("client --> proxy") n, err := bufrw.Read(data) if err != nil { log.Printf("%v\n", err) done <- struct{}{} } log.Println("proxy --> server") tunnelConn.Write(data[:n]) } */ }() go func() { io.Copy(bufrw, tunnelConn) /* log.Println("server --> proxy --> client") data := make([]byte, 1024) for { log.Println("server --> proxy") for { n, err := tunnelConn.Read(data) if err != nil { log.Printf("%v\n", err) done <- struct{}{} } log.Println("proxy --> client") bufrw.Write(data[:n]) if n < 1024 { break } } bufrw.Flush() } */ }() <-done //fmt.Println("The Tunnel has closed.") }