Skip to content

Instantly share code, notes, and snippets.

@chenset
Last active April 23, 2024 04:50
Show Gist options
  • Select an option

  • Save chenset/9ea0ffd9e1647d46eada5757b9db0f3f to your computer and use it in GitHub Desktop.

Select an option

Save chenset/9ea0ffd9e1647d46eada5757b9db0f3f to your computer and use it in GitHub Desktop.
http-proxy.go http proxy in go, Support socks5 forward proxy
package main
import (
"bufio"
"context"
"flag"
"fmt"
"golang.org/x/net/proxy"
"io"
"log"
"net"
"net/http"
"net/http/httputil"
"strings"
)
func init() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
func main() {
listen := flag.String("listen", "127.0.0.1:8080", "Http Proxy Listen address: client => http-proxy => server")
socks5 := flag.String("socks5", "", "Socks5 forward proxy, if not empty: client => http-proxy => socks5 proxy => server")
flag.Parse()
dialer, err := getDialer(*socks5)
if err != nil {
log.Fatal(err)
}
l, err := net.Listen("tcp", *listen)
if err != nil {
log.Fatal(err)
}
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
fmt.Println(err)
continue
}
go handleConnection(conn, dialer)
}
}
//handleConnection
func handleConnection(conn net.Conn, dialer proxy.Dialer) {
defer conn.Close()
request, err := http.ReadRequest(bufio.NewReader(conn))
if err != nil {
log.Println(err)
return
}
if strings.ToUpper(request.Method) == "CONNECT" {
responseTunnel(conn, request, dialer)
} else {
responseHttp(conn, request, dialer)
}
}
func getDialer(addr string) (dialer proxy.Dialer, err error) {
if addr != "" {
dialer, err = proxy.SOCKS5("tcp", addr, nil, proxy.Direct)
} else {
dialer, err = &net.Dialer{}, nil
}
return
}
//responseTunnel handle https ws etc..
func responseTunnel(conn net.Conn, request *http.Request, dialer proxy.Dialer) {
remote, err := dialer.Dial("tcp", request.URL.Host)
if err != nil {
log.Println(err)
return
}
conn.Write([]byte("HTTP/1.0 200 Connection Established\r\n\r\n"))
defer remote.Close()
ch := make(chan bool,1)
go func() {
io.Copy(remote, conn)
ch <- true
}()
io.Copy(conn, remote)
<-ch
}
//responseHttp get post, regular http request
func responseHttp(conn net.Conn, request *http.Request, dialer proxy.Dialer) {
request.RequestURI = ""
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Transport: func() http.RoundTripper {
t := http.DefaultTransport
t.(*http.Transport).Proxy = nil //disable system proxy
t.(*http.Transport).DialContext = func(ctx context.Context, network, addr string) (conn net.Conn, err error) {
return dialer.Dial(network, addr)
}
return t
}(),
}
resp, err := client.Do(request)
if err != nil {
log.Println(err)
return
}
responseDump, _ := httputil.DumpResponse(resp, true)
conn.Write(responseDump)
}
@chenset
Copy link
Author

chenset commented May 22, 2019

Usage:

# client => http proxy => server
go run http-proxy.go
# client => http proxy => socks5 proxy => server 
go run http-proxy.go -listen=:8080 -socks5=127.0.0.1:1080

Help:

go run http-proxy.go -h

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment