http-proxy.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "log"
  6. "net"
  7. "net/http"
  8. "strings"
  9. "github.com/gin-gonic/gin"
  10. )
  11. const (
  12. TUNNEL_PACKET = `HTTP/1.1 200 Connection Established\r\nProxy-agent: spit-wizard.void\r\n\r\n`
  13. )
  14. func main() {
  15. r := gin.Default()
  16. r.NoRoute(routeProxy) // NO Route is every Route!!!
  17. r.Run(":8080") // listen and serve on 0.0.0.0:8080
  18. }
  19. // Then I can process all routes
  20. func routeProxy(c *gin.Context) {
  21. req := c.Request
  22. go resolveReq(req) // just print basic info. Remember you can't proxy youself.
  23. if req.Method == http.MethodConnect {
  24. // create http tunnel process https
  25. httpsProxy(c, req)
  26. } else {
  27. // process plain http
  28. httpProxy(c, req)
  29. }
  30. }
  31. func resolveReq(req *http.Request) {
  32. fmt.Printf("Method: %s, Host: %s, URL: %s, Version: %s\n", req.Method, req.Host, req.URL.Path, req.Proto)
  33. //fmt.Printf("%+v\n", req)
  34. }
  35. func httpProxy(c *gin.Context, req *http.Request) {
  36. newReq, _ := http.NewRequest(req.Method, req.URL.String(), req.Body)
  37. resp, err := http.DefaultClient.Do(newReq)
  38. if err != nil {
  39. log.Fatal(err)
  40. }
  41. defer resp.Body.Close()
  42. data, err := io.ReadAll(resp.Body)
  43. if err != nil {
  44. log.Fatal(err)
  45. }
  46. fmt.Printf("%s\n%s\n%s\n", req.Host, req.Method, req.RequestURI)
  47. code := resp.StatusCode
  48. c.Status(code) // change the status code, default is 404 !!!
  49. for k, v := range resp.Header {
  50. c.Header(k, strings.Join(v, ","))
  51. }
  52. c.Header("Server", "spit-wizard.void") // just change it, this is yours gin proxy.
  53. c.Writer.Write(data)
  54. }
  55. func httpsProxy(c *gin.Context, req *http.Request) {
  56. // established connect tunnel
  57. c.Status(200)
  58. c.Header("Server", "spit-wizard.void")
  59. c.Writer.Write([]byte(TUNNEL_PACKET))
  60. // c.Writer.Flush() // this may cause proble, but I don't know.
  61. address := req.URL.Host // it contains the port
  62. tunnelConn, err := net.Dial("tcp", address)
  63. if err != nil {
  64. log.Fatal(err)
  65. }
  66. //fmt.Printf("try to established Connect Tunnel to: %s has been successfully.\n", address)
  67. // But next is a TCP communication, but the tcp conn is a non-export variable,
  68. // so I can't get it, in the same, client's data is binary, so gin can't parse it.
  69. // so I can't do anything!!!
  70. // LOL, I find HTTP hijacker, it can make me take over the connection!!!
  71. hj, ok := c.Writer.(http.Hijacker)
  72. if !ok {
  73. http.Error(c.Writer, "webserver doesn't support hijacking", http.StatusInternalServerError)
  74. return
  75. }
  76. conn, bufrw, err := hj.Hijack()
  77. if err != nil {
  78. http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
  79. return
  80. }
  81. defer conn.Close()
  82. // first read request, then write response.
  83. // data flow direction:
  84. // client <---> proxy <---> server
  85. done := make(chan struct{})
  86. go func() {
  87. io.Copy(tunnelConn, bufrw)
  88. /*
  89. // this is my first write, but then I find other use io.Copy
  90. log.Println("client --> proxy --> server")
  91. data := make([]byte, 1024)
  92. for {
  93. log.Println("client --> proxy")
  94. n, err := bufrw.Read(data)
  95. if err != nil {
  96. log.Printf("%v\n", err)
  97. done <- struct{}{}
  98. }
  99. log.Println("proxy --> server")
  100. tunnelConn.Write(data[:n])
  101. }
  102. */
  103. }()
  104. go func() {
  105. io.Copy(bufrw, tunnelConn)
  106. /*
  107. log.Println("server --> proxy --> client")
  108. data := make([]byte, 1024)
  109. for {
  110. log.Println("server --> proxy")
  111. for {
  112. n, err := tunnelConn.Read(data)
  113. if err != nil {
  114. log.Printf("%v\n", err)
  115. done <- struct{}{}
  116. }
  117. log.Println("proxy --> client")
  118. bufrw.Write(data[:n])
  119. if n < 1024 {
  120. break
  121. }
  122. }
  123. bufrw.Flush()
  124. }
  125. */
  126. }()
  127. <-done
  128. //fmt.Println("The Tunnel has closed.")
  129. }