github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/core/httpclient/client.go (about)

     1  package httpclient
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"io"
     7  	"net"
     8  	"net/http"
     9  	neturl "net/url"
    10  	"regexp"
    11  	"time"
    12  
    13  	"github.com/lmorg/murex/config"
    14  	"github.com/lmorg/murex/lang/types"
    15  	"github.com/lmorg/murex/utils/json"
    16  )
    17  
    18  const (
    19  	enableTimeout  = true
    20  	disableTimeout = false
    21  )
    22  
    23  var rxHttpProto = regexp.MustCompile(`(?i)^http(s)?://`)
    24  
    25  // Request generates a HTTP request
    26  func Request(ctx context.Context, method, url string, body io.Reader, conf *config.Config, setTimeout bool) (response *http.Response, err error) {
    27  	toStr, err := conf.Get("http", "timeout", types.String)
    28  	if err != nil {
    29  		return
    30  	}
    31  	toDur, err := time.ParseDuration(toStr.(string) + "s")
    32  	if err != nil {
    33  		return
    34  	}
    35  
    36  	insecure, err := conf.Get("http", "insecure", types.Boolean)
    37  	if err != nil {
    38  		return
    39  	}
    40  
    41  	var client *http.Client
    42  	if setTimeout {
    43  		tr := http.Transport{
    44  			Dial:            dialTimeout(toDur, toDur, conf),
    45  			TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure.(bool)},
    46  		}
    47  
    48  		client = &http.Client{
    49  			Timeout:   toDur,
    50  			Transport: &tr,
    51  		}
    52  
    53  	} else {
    54  		tr := http.Transport{
    55  			TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure.(bool)},
    56  		}
    57  
    58  		client = &http.Client{
    59  			Transport: &tr,
    60  		}
    61  	}
    62  
    63  	userAgent, err := conf.Get("http", "user-agent", types.String)
    64  	if err != nil {
    65  		return
    66  	}
    67  
    68  	request, err := http.NewRequest(method, url, body)
    69  	if err != nil {
    70  		return
    71  	}
    72  
    73  	request = request.WithContext(ctx)
    74  
    75  	urlParsed, err := neturl.Parse(url)
    76  	if err != nil {
    77  		return
    78  	}
    79  
    80  	// code in meta functions
    81  	setHeaders(request, conf, &urlParsed.Host)
    82  	setCookies(request, conf, &urlParsed.Host)
    83  
    84  	// Set important headers (these will override anything set in the 'headers' config)
    85  	request.Header.Set("Host", urlParsed.Host)
    86  	request.Header.Set("User-Agent", userAgent.(string))
    87  
    88  	redirects, err := conf.Get("http", "redirect", types.Boolean)
    89  	if err != nil {
    90  		return
    91  	}
    92  
    93  	if redirects.(bool) {
    94  		response, err = client.Do(request)
    95  	} else {
    96  		response, err = client.Transport.RoundTrip(request)
    97  	}
    98  
    99  	return
   100  }
   101  
   102  // dialTimeout function unashamedly copy and pasted from:
   103  // https://stackoverflow.com/questions/16895294/how-to-set-timeout-for-http-get-requests-in-golang#16930649
   104  func dialTimeout(cTimeout time.Duration, rwTimeout time.Duration, conf *config.Config) func(net, addr string) (c net.Conn, err error) {
   105  	return func(netw, addr string) (net.Conn, error) {
   106  		conn, err := net.DialTimeout(netw, addr, cTimeout)
   107  		if err != nil {
   108  			return nil, err
   109  		}
   110  		conn.SetDeadline(time.Now().Add(rwTimeout))
   111  		return conn, nil
   112  	}
   113  }
   114  
   115  func validateURL(url *string, conf *config.Config) {
   116  	if !rxHttpProto.MatchString(*url) {
   117  		v, err := conf.Get("http", "default-https", types.Boolean)
   118  		if err != nil {
   119  			v = false
   120  		}
   121  
   122  		if v.(bool) {
   123  			*url = "https://" + *url
   124  			return
   125  		}
   126  
   127  		*url = "http://" + *url
   128  	}
   129  }
   130  
   131  func getMeta(conf *config.Config, meta string, domain *string) (metaValues, error) {
   132  	s, err := conf.Get("http", meta, "json")
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	var domains metaDomains
   138  	err = json.UnmarshalMurex([]byte(s.(string)), &domains)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	if len(domains) > 0 && len(domains[*domain]) > 0 {
   144  		return domains[*domain], nil
   145  	}
   146  
   147  	return nil, err
   148  }
   149  
   150  func setHeaders(request *http.Request, conf *config.Config, domain *string) error {
   151  	headers, err := getMeta(conf, "headers", domain)
   152  	if err != nil {
   153  		return err
   154  	}
   155  
   156  	for name := range headers {
   157  		request.Header.Add(name, headers[name])
   158  	}
   159  
   160  	return nil
   161  }
   162  
   163  func setCookies(request *http.Request, conf *config.Config, domain *string) error {
   164  	cookies, err := getMeta(conf, "cookies", domain)
   165  	if err != nil {
   166  		return err
   167  	}
   168  
   169  	for name := range cookies {
   170  		request.AddCookie(&http.Cookie{
   171  			Name:  name,
   172  			Value: cookies[name],
   173  		})
   174  	}
   175  
   176  	return nil
   177  }