Golang封装一个request类支持socks和http代理
1. 需要需用国外服务器做代理的时候
2. 需要使用代理服务器做白名单的时候
3. 代码还支持重试机制
封装代码如下
package util
import (
"bytes"
"crypto/tls"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
"strings"
"sync"
"time"
"golang.org/x/net/proxy"
)
const (
ContentTypeFormData = 0
ContentTypeJson = 1
)
type SendRequest struct {
client *http.Client
retryNum int // 重试次数
retryTime int // 重试间隔时间
headers http.Header
boundary string
Lock sync.Mutex
}
func NewSendRequest(contentType int) *SendRequest {
headers := http.Header{}
if contentType == ContentTypeFormData {
headers.Set("Content-Type", "application/x-www-form-urlencoded")
} else if contentType == ContentTypeJson {
headers.Set("Content-Type", "application/json")
}
return &SendRequest{
client: &http.Client{
Timeout: 30 * time.Second,
},
retryTime: 10,
headers: headers,
}
}
func (s *SendRequest) SetBoundary(_boundary string) *SendRequest {
s.boundary = _boundary
return s
}
func (s *SendRequest) SetHeaders(headers http.Header) *SendRequest {
s.headers = headers
return s
}
func (s *SendRequest) UpdateHeaders(headers map[string]string) *SendRequest {
s.Lock.Lock()
defer s.Lock.Unlock()
// 设置请求头
for key, value := range headers {
s.headers.Set(key, value)
}
return s
}
func (s *SendRequest) SetProxy(proxyAddr string) {
// 创建代理 URL
proxyURL, err := url.Parse(proxyAddr)
if err != nil {
fmt.Println("Failed to parse proxy URL:", err)
return
}
var transport *http.Transport
if strings.Contains(proxyAddr, "sock") {
dialer, err := proxy.FromURL(proxyURL, proxy.Direct)
if err != nil {
fmt.Println("Failed to create proxy dialer:", err)
return
}
// 创建自定义的 HTTP 客户端,使用 SOCKS5 代理进行请求
transport = &http.Transport{
Dial: dialer.Dial,
}
// proxy := func(_ *http.Request) (*url.URL, error) {
// return url.Parse(proxyAddr)
// }
// transport = &http.Transport{
// Proxy: proxy,
// }
} else {
// 创建自定义的 Transport
transport = &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
}
s.client.Transport = transport
}
func (s *SendRequest) send(method string, url string, reqBody io.Reader, headers http.Header) ([]byte, *http.Response, error) {
req, err := http.NewRequest(method, url, reqBody)
if err != nil {
return nil, nil, err
}
// 设置请求头
if len(headers) > 0 {
req.Header = headers
} else {
req.Header = s.headers
}
resp, err := s.client.Do(req)
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()
// s.Lock.Lock()
body, err := io.ReadAll(resp.Body)
// s.Lock.Unlock()
if err != nil {
return nil, nil, err
}
if resp.StatusCode != http.StatusOK {
// return nil, nil, errors.New(fmt.Sprintf("状态码:%d,内容:%s", resp.StatusCode, string(body)))
return nil, nil, fmt.Errorf("状态码:%d", resp.StatusCode)
}
return body, resp, nil
}
func (s *SendRequest) Post(url string, param url.Values) ([]byte, *http.Response, error) {
reqBody := strings.NewReader(param.Encode())
// 设置请求参数
if s.boundary != "" {
// 创建请求体
buf := &bytes.Buffer{}
writer := multipart.NewWriter(buf)
// 设置分割符号(boundary)
writer.SetBoundary(s.boundary)
// 添加表单字段到请求体
for key, value := range param {
for _, v := range value {
_ = writer.WriteField(key, v)
}
}
// 关闭 multipart.Writer,以写入结尾标识符
_ = writer.Close()
reqBody = strings.NewReader(buf.String())
}
return s.send("POST", url, reqBody, nil)
}
func (s *SendRequest) PostJSON(url string, data []byte) ([]byte, *http.Response, error) {
var reqBody *bytes.Reader
if data != nil {
reqBody = bytes.NewReader(data)
}
return s.send("POST", url, reqBody, nil)
}
func (s *SendRequest) RepeatPost(url string, param url.Values) ([]byte, *http.Response, error) {
// fmt.Printf("[%s][%d]请求地址:%s\n", uuid, num, url)
// fmt.Printf("[%s][%d]本次发送:%v\n", uuid, num, param)
result, resp, err := s.Post(url, param)
if err != nil && s.retryNum < 5 {
time.Sleep(time.Second * time.Duration(s.retryTime))
// fmt.Printf("[%s][%d]请求返回错误:%v\n", uuid, num, err)
s.retryNum++
s.retryNum = s.retryNum * 2
// 进行重发
return s.RepeatPost(url, param)
}
return result, resp, err
}
func (s *SendRequest) Get(url string, headers http.Header) ([]byte, *http.Response, error) {
return s.send("GET", url, nil, headers)
}
// func main() {
// req := NewSendRequest()
// // 测试 POST 请求
// postURL := "http://example.com/api"
// postParam := url.Values{
// "key1": []string{"value1"},
// "key2": []string{"value2"},
// }
// postHeaders := map[string]string{
// "Content-Type": "application/x-www-form-urlencoded",
// }
// postResult, err := req.Post(postURL, postParam, postHeaders)
// if err != nil {
// fmt.Println("POST 请求错误:", err)
// } else {
// fmt.Println("POST 请求结果:", postResult)
// }
// // 测试重复发送 POST 请求
// repeatUUID := "12345"
// repeatURL := "http://example.com/api"
// repeatParam := url.Values{
// "key1": []string{"value1"},
// "key2": []string{"value2"},
// }
// repeatTimeout := 0 * time.Second
// repeatNum := 1
// repeatHeaders := map[string]string{
// "Content-Type": "application/x-www-form-urlencoded",
// }
// req.RepeatPost(repeatUUID, repeatURL, repeatParam, repeatTimeout, repeatNum, repeatHeaders)
// // 测试 GET 请求
// getURL := "http://example.com/api"
// getHeaders := map[string]string{
// "Content-Type": "application/x-www-form-urlencoded",
// }
// getResult, err := req.Get(getURL, getHeaders)
// if err != nil {
// fmt.Println("GET 请求错误:", err)
// } else {
// fmt.Println("GET 请求结果:", getResult)
// }
// }
使用代理请求
package main
import (
"log"
"xxx/utils"
)
func main() {
// 这个网站有很多代理ip,但是大部分都用不了会超时或者连接拒绝
// https://geonode.com/free-proxy-list
// 付费的三方https://ip.huashengdaili.com/
sendRequest := utils.NewSendRequest(nil, "")
sendRequest.SetProxy("socks4://169.239.49.118:5678")
// https://api.ip.sb/ip
// https://myip.top
body, _, err := sendRequest.Get("https://myip.top", nil)
if err != nil {
log.Fatal(err)
}
log.Println(string(body))
}
当然可以自己搭建一个socks5和http的代理服务器
自己搭建代码如下:
package main
import (
"io"
"log"
"net"
"net/http"
"os"
"github.com/armon/go-socks5"
)
func handleRequest(w http.ResponseWriter, r *http.Request) {
// 建立与后端服务器的连接
destConn, err := net.Dial("tcp", r.Host)
if err != nil {
log.Println("Failed to connect to destination:", err)
http.Error(w, "Failed to connect to destination", http.StatusInternalServerError)
return
}
defer destConn.Close()
// 将客户端的请求发送到后端服务器
err = r.Write(destConn)
if err != nil {
log.Println("Failed to send request to destination:", err)
http.Error(w, "Failed to send request to destination", http.StatusInternalServerError)
return
}
// 将后端服务器的响应返回给客户端
_, err = io.Copy(w, destConn)
if err != nil {
log.Println("Failed to send response to client:", err)
http.Error(w, "Failed to send response to client", http.StatusInternalServerError)
return
}
}
func main() {
// 创建代理服务器处理程序
proxyHandler := http.HandlerFunc(handleRequest)
// 启动HTTPS代理服务器
go func() {
log.Println("proxy 8080")
err := http.ListenAndServeTLS(":8080", "./pem/cert.pem", "./pem/key.pem", proxyHandler)
if err != nil {
log.Fatal("Failed to start proxy server:", err)
}
}()
// 启动SOCKS5代理服务器
socksConf := &socks5.Config{}
socksServer, err := socks5.New(socksConf)
if err != nil {
log.Fatal("Failed to create SOCKS5 server:", err)
}
socksListener, err := net.Listen("tcp", ":1080")
if err != nil {
log.Fatal("Failed to start SOCKS5 server:", err)
}
log.Println("SOCKS5 proxy 1080")
err = http.Serve(socksListener, socksServer)
if err != nil {
log.Fatal("Failed to start SOCKS5 server:", err)
}
}
证书生成
1. openssl生成参考:https://cloud.tencent.com/developer/article/1548350
2. 在线生成:https://www.lddgo.net/encrypt/ssl