Closed 0xfocu5 closed 3 months ago
fasthttpproxy.FasthttpSocksDialer
is very simple and passes proxy.Direct
to golang.org/x/net/proxy.FromURL
which is what you want to change. I would copy the implementation of fasthttpproxy.FasthttpSocksDialer
and try to pass a fasthttp.TCPDialer
as second argument so it uses that instead if net.Dial
.
fasthttpproxy.FasthttpSocksDialer
is very simple and passesproxy.Direct
togolang.org/x/net/proxy.FromURL
which is what you want to change. I would copy the implementation offasthttpproxy.FasthttpSocksDialer
and try to pass afasthttp.TCPDialer
as second argument so it uses that instead ifnet.Dial
.
Looking forward to your good news
support socks5 and http.
package fasthttpproxy
import (
"bufio"
"encoding/base64"
"errors"
"fmt"
"github.com/valyala/fasthttp"
"golang.org/x/net/proxy"
"net"
"net/url"
"strings"
"time"
)
type DialerFunc func(network, addr string) (net.Conn, error)
func (d DialerFunc) Dial(network, addr string) (net.Conn, error) {
return d(network, addr)
}
type HttpProxyDialer interface {
ConnectTimeout() time.Duration
}
func (d *Dialer) ConnectTimeout() time.Duration {
return d.connectTimeout
}
type Dialer struct {
fasthttp.TCPDialer
timeout time.Duration
connectTimeout time.Duration
}
func init() {
proxy.RegisterDialerType("http", HttpProxyDial)
}
func (d *Dialer) Dial(network, addr string) (conn net.Conn, err error) {
if network == "tcp4" {
if d.timeout > 0 {
return d.TCPDialer.DialTimeout(addr, d.timeout)
}
return d.TCPDialer.Dial(addr)
}
if network == "tcp" {
if d.timeout > 0 {
return d.TCPDialer.DialDualStackTimeout(addr, d.timeout)
}
return d.TCPDialer.DialDualStack(addr)
}
err = errors.New("don't know how to dial network:" + network)
return
}
func (d *Dialer) GetDialFunc(proxyAddr string) (dialFunc fasthttp.DialFunc, err error) {
u, err := url.Parse(proxyAddr)
if err != nil {
return
}
dialer, err := proxy.FromURL(u, d)
if err != nil {
return
}
dialFunc = func(addr string) (net.Conn, error) {
var network string
if strings.HasPrefix(addr, "[") {
network = "tcp"
} else {
network = "tcp4"
}
return dialer.Dial(network, addr)
}
return
}
func HttpProxyDial(u *url.URL, dialer proxy.Dialer) (proxy.Dialer, error) {
var proxyAddr string
if u.Scheme != "" {
proxyAddr = strings.TrimPrefix(u.String(), u.Scheme+"://")
}
var auth string
if strings.Contains(proxyAddr, "@") {
index := strings.LastIndex(proxyAddr, "@")
auth = base64.StdEncoding.EncodeToString([]byte(proxyAddr[:index]))
proxyAddr = proxyAddr[index+1:]
}
return DialerFunc(func(network, addr string) (conn net.Conn, err error) {
conn, err = dialer.Dial(network, proxyAddr)
if err != nil {
return
}
var connectTimeout time.Duration
hp, ok := dialer.(HttpProxyDialer)
if ok {
connectTimeout = hp.ConnectTimeout()
}
if connectTimeout > 0 {
if err = conn.SetDeadline(time.Now().Add(connectTimeout)); err != nil {
_ = conn.Close()
return nil, err
}
defer func() {
_ = conn.SetDeadline(time.Time{})
}()
}
req := "CONNECT " + addr + " HTTP/1.1\r\nHost: " + addr + "\r\n"
if auth != "" {
req += "Proxy-Authorization: Basic " + auth + "\r\n"
}
req += "\r\n"
_, err = conn.Write([]byte(req))
if err != nil {
_ = conn.Close()
return
}
res := fasthttp.AcquireResponse()
defer fasthttp.ReleaseResponse(res)
res.SkipBody = true
if err = res.Read(bufio.NewReader(conn)); err != nil {
_ = conn.Close()
return
}
if res.Header.StatusCode() != 200 {
_ = conn.Close()
err = fmt.Errorf("could not connect to proxyAddr: %s status code: %d", proxyAddr, res.Header.StatusCode())
return
}
return
}), nil
}
func TestDialProxy(t *testing.T) {
p := Dialer{
timeout: time.Second * 30,
}
dialer, err := p.GetDialFunc("http://127.0.0.1:8005")
if err != nil {
t.Fatal(err)
}
client := &fasthttp.Client{
ReadTimeout: time.Second * 5,
WriteTimeout: time.Second * 5,
MaxIdleConnDuration: time.Second * 5,
Dial: dialer,
}
statusCode, resp, err := client.Get(nil, "https://www.google.com")
if err != nil {
t.Fatal(err)
}
if statusCode != 200 {
t.Fatal(err)
}
_ = resp
}
Implement based on fasthttpproxy
support socks5 and http.
package fasthttpproxy import ( "bufio" "encoding/base64" "errors" "fmt" "github.com/valyala/fasthttp" "golang.org/x/net/proxy" "net" "net/url" "strings" "time" ) type DialerFunc func(network, addr string) (net.Conn, error) func (d DialerFunc) Dial(network, addr string) (net.Conn, error) { return d(network, addr) } type HttpProxyDialer interface { ConnectTimeout() time.Duration } func (d *Dialer) ConnectTimeout() time.Duration { return d.connectTimeout } type Dialer struct { fasthttp.TCPDialer timeout time.Duration connectTimeout time.Duration } func init() { proxy.RegisterDialerType("http", HttpProxyDial) } func (d *Dialer) Dial(network, addr string) (conn net.Conn, err error) { if network == "tcp4" { if d.timeout > 0 { return d.TCPDialer.DialTimeout(addr, d.timeout) } return d.TCPDialer.Dial(addr) } if network == "tcp" { if d.timeout > 0 { return d.TCPDialer.DialDualStackTimeout(addr, d.timeout) } return d.TCPDialer.DialDualStack(addr) } err = errors.New("don't know how to dial network:" + network) return } func (d *Dialer) GetDialFunc(proxyAddr string) (dialFunc fasthttp.DialFunc, err error) { u, err := url.Parse(proxyAddr) if err != nil { return } dialer, err := proxy.FromURL(u, d) if err != nil { return } dialFunc = func(addr string) (net.Conn, error) { var network string if strings.HasPrefix(addr, "[") { network = "tcp" } else { network = "tcp4" } return dialer.Dial(network, addr) } return } func HttpProxyDial(u *url.URL, dialer proxy.Dialer) (proxy.Dialer, error) { var proxyAddr string if u.Scheme != "" { proxyAddr = strings.TrimPrefix(u.String(), u.Scheme+"://") } var auth string if strings.Contains(proxyAddr, "@") { index := strings.LastIndex(proxyAddr, "@") auth = base64.StdEncoding.EncodeToString([]byte(proxyAddr[:index])) proxyAddr = proxyAddr[index+1:] } return DialerFunc(func(network, addr string) (conn net.Conn, err error) { conn, err = dialer.Dial(network, proxyAddr) if err != nil { return } var connectTimeout time.Duration hp, ok := dialer.(HttpProxyDialer) if ok { connectTimeout = hp.ConnectTimeout() } if connectTimeout > 0 { if err = conn.SetDeadline(time.Now().Add(connectTimeout)); err != nil { _ = conn.Close() return nil, err } defer func() { _ = conn.SetDeadline(time.Time{}) }() } req := "CONNECT " + addr + " HTTP/1.1\r\nHost: " + addr + "\r\n" if auth != "" { req += "Proxy-Authorization: Basic " + auth + "\r\n" } req += "\r\n" _, err = conn.Write([]byte(req)) if err != nil { _ = conn.Close() return } res := fasthttp.AcquireResponse() defer fasthttp.ReleaseResponse(res) res.SkipBody = true if err = res.Read(bufio.NewReader(conn)); err != nil { _ = conn.Close() return } if res.Header.StatusCode() != 200 { _ = conn.Close() err = fmt.Errorf("could not connect to proxyAddr: %s status code: %d", proxyAddr, res.Header.StatusCode()) return } return }), nil }
func TestDialProxy(t *testing.T) { p := Dialer{ timeout: time.Second * 30, } dialer, err := p.GetDialFunc("http://127.0.0.1:8005") if err != nil { t.Fatal(err) } client := &fasthttp.Client{ ReadTimeout: time.Second * 5, WriteTimeout: time.Second * 5, MaxIdleConnDuration: time.Second * 5, Dial: dialer, } statusCode, resp, err := client.Get(nil, "https://www.google.com") if err != nil { t.Fatal(err) } if statusCode != 200 { t.Fatal(err) } _ = resp }
thanks a lot,it worked.
I want these work together