198 lines
5.1 KiB
Go
198 lines
5.1 KiB
Go
package starnet
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
HEADER_FORM_URLENCODE = `application/x-www-form-urlencoded`
|
|
HEADER_FORM_DATA = `multipart/form-data`
|
|
HEADER_JSON = `application/json`
|
|
HEADER_PLAIN = `text/plain`
|
|
)
|
|
|
|
var (
|
|
DefaultDialTimeout = 5 * time.Second
|
|
DefaultTimeout = 10 * time.Second
|
|
DefaultFetchRespBody = false
|
|
)
|
|
|
|
func UrlEncodeRaw(str string) string {
|
|
strs := strings.Replace(url.QueryEscape(str), "+", "%20", -1)
|
|
return strs
|
|
}
|
|
|
|
func UrlEncode(str string) string {
|
|
return url.QueryEscape(str)
|
|
}
|
|
|
|
func UrlDecode(str string) (string, error) {
|
|
return url.QueryUnescape(str)
|
|
}
|
|
|
|
func BuildQuery(queryData map[string]string) string {
|
|
query := url.Values{}
|
|
for k, v := range queryData {
|
|
query.Add(k, v)
|
|
}
|
|
return query.Encode()
|
|
}
|
|
|
|
// BuildPostForm takes a map of string keys and values, converts it into a URL-encoded query string,
|
|
// and then converts that string into a byte slice. This function is useful for preparing data for HTTP POST requests,
|
|
// where the server expects the request body to be URL-encoded form data.
|
|
//
|
|
// Parameters:
|
|
// queryMap: A map where the key-value pairs represent the form data to be sent in the HTTP POST request.
|
|
//
|
|
// Returns:
|
|
// A byte slice representing the URL-encoded form data.
|
|
func BuildPostForm(queryMap map[string]string) []byte {
|
|
return []byte(BuildQuery(queryMap))
|
|
}
|
|
|
|
func Get(uri string, opts ...RequestOpt) (*Response, error) {
|
|
return NewSimpleRequest(uri, "GET", opts...).Do()
|
|
}
|
|
|
|
func Post(uri string, opts ...RequestOpt) (*Response, error) {
|
|
return NewSimpleRequest(uri, "POST", opts...).Do()
|
|
}
|
|
|
|
func Options(uri string, opts ...RequestOpt) (*Response, error) {
|
|
return NewSimpleRequest(uri, "OPTIONS", opts...).Do()
|
|
}
|
|
|
|
func Put(uri string, opts ...RequestOpt) (*Response, error) {
|
|
return NewSimpleRequest(uri, "PUT", opts...).Do()
|
|
}
|
|
|
|
func Delete(uri string, opts ...RequestOpt) (*Response, error) {
|
|
return NewSimpleRequest(uri, "DELETE", opts...).Do()
|
|
}
|
|
|
|
func Head(uri string, opts ...RequestOpt) (*Response, error) {
|
|
return NewSimpleRequest(uri, "HEAD", opts...).Do()
|
|
}
|
|
|
|
func Patch(uri string, opts ...RequestOpt) (*Response, error) {
|
|
return NewSimpleRequest(uri, "PATCH", opts...).Do()
|
|
}
|
|
|
|
func Trace(uri string, opts ...RequestOpt) (*Response, error) {
|
|
return NewSimpleRequest(uri, "TRACE", opts...).Do()
|
|
}
|
|
|
|
func Connect(uri string, opts ...RequestOpt) (*Response, error) {
|
|
return NewSimpleRequest(uri, "CONNECT", opts...).Do()
|
|
}
|
|
|
|
func DefaultCheckRedirectFunc(req *http.Request, via []*http.Request) error {
|
|
return http.ErrUseLastResponse
|
|
}
|
|
|
|
func DefaultDialFunc(ctx context.Context, netType, addr string) (net.Conn, error) {
|
|
var lastErr error
|
|
var addrs []string
|
|
if dialFn, ok := ctx.Value("dialFunc").(func(context.Context, string, string) (net.Conn, error)); ok {
|
|
if dialFn != nil {
|
|
return dialFn(ctx, netType, addr)
|
|
}
|
|
}
|
|
customIP, ok := ctx.Value("customIP").([]string)
|
|
if !ok {
|
|
customIP = nil
|
|
}
|
|
dialTimeout, ok := ctx.Value("dialTimeout").(time.Duration)
|
|
if !ok {
|
|
dialTimeout = DefaultDialTimeout
|
|
}
|
|
timeout, ok := ctx.Value("timeout").(time.Duration)
|
|
if !ok {
|
|
timeout = DefaultTimeout
|
|
}
|
|
lookUpIPfn, ok := ctx.Value("lookUpIP").(func(context.Context, string) ([]net.IPAddr, error))
|
|
if !ok {
|
|
lookUpIPfn = net.DefaultResolver.LookupIPAddr
|
|
}
|
|
host, port, err := net.SplitHostPort(addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
proxy, ok := ctx.Value("proxy").(string)
|
|
if !ok {
|
|
proxy = ""
|
|
}
|
|
if proxy == "" && len(customIP) > 0 {
|
|
for _, v := range customIP {
|
|
ipAddr := net.ParseIP(v)
|
|
if ipAddr == nil {
|
|
return nil, fmt.Errorf("invalid custom ip: %s", customIP)
|
|
}
|
|
tmpAddr := net.JoinHostPort(v, port)
|
|
addrs = append(addrs, tmpAddr)
|
|
}
|
|
} else {
|
|
ipLists, err := lookUpIPfn(ctx, host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, v := range ipLists {
|
|
tmpAddr := net.JoinHostPort(v.String(), port)
|
|
addrs = append(addrs, tmpAddr)
|
|
}
|
|
}
|
|
for _, addr := range addrs {
|
|
c, err := net.DialTimeout(netType, addr, dialTimeout)
|
|
if err != nil {
|
|
lastErr = err
|
|
continue
|
|
}
|
|
if timeout != 0 {
|
|
err = c.SetDeadline(time.Now().Add(timeout))
|
|
}
|
|
return c, nil
|
|
}
|
|
return nil, lastErr
|
|
}
|
|
|
|
func DefaultDialTlsFunc(ctx context.Context, netType, addr string) (net.Conn, error) {
|
|
conn, err := DefaultDialFunc(ctx, netType, addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tlsConfig, ok := ctx.Value("tlsConfig").(*tls.Config)
|
|
if !ok || tlsConfig == nil {
|
|
return nil, fmt.Errorf("tlsConfig is not set in context")
|
|
}
|
|
tlsConn := tls.Client(conn, tlsConfig)
|
|
if err := tlsConn.Handshake(); err != nil {
|
|
return nil, fmt.Errorf("tls handshake failed: %w", err)
|
|
}
|
|
return tlsConn, nil
|
|
}
|
|
|
|
func DefaultProxyURL() func(*http.Request) (*url.URL, error) {
|
|
return func(req *http.Request) (*url.URL, error) {
|
|
if req == nil {
|
|
return nil, fmt.Errorf("request is nil")
|
|
}
|
|
proxyURL, ok := req.Context().Value("proxy").(string)
|
|
if !ok || proxyURL == "" {
|
|
return nil, nil
|
|
}
|
|
parsedURL, err := url.Parse(proxyURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse proxy URL: %w", err)
|
|
}
|
|
return parsedURL, nil
|
|
}
|
|
}
|