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 }