123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- 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.")
- }
|