github.com/bazelbuild/rules_webtesting@v0.2.0/go/httphelper/http_helper.go (about) 1 // Copyright 2016 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package httphelper provides simple wrappers for working with HTTP. 16 package httphelper 17 18 import ( 19 "bytes" 20 "context" 21 "crypto/tls" 22 "fmt" 23 "io" 24 "net" 25 "net/http" 26 "net/url" 27 "os" 28 "sort" 29 "strings" 30 "time" 31 ) 32 33 var client = &http.Client{ 34 Transport: &http.Transport{ 35 TLSClientConfig: &tls.Config{ 36 InsecureSkipVerify: true, 37 }, 38 Proxy: http.ProxyFromEnvironment, 39 DialContext: (&net.Dialer{ 40 Timeout: 30 * time.Second, 41 KeepAlive: 30 * time.Second, 42 DualStack: true, 43 }).DialContext, 44 MaxIdleConns: 100, 45 IdleConnTimeout: 90 * time.Second, 46 TLSHandshakeTimeout: 10 * time.Second, 47 ExpectContinueTimeout: 1 * time.Second, 48 }, 49 } 50 51 // Forward forwards r to host and writes the response from host to w. 52 func Forward(ctx context.Context, host, trimPrefix string, w http.ResponseWriter, r *http.Request) error { 53 url, err := constructURL(host, r.URL.Path, trimPrefix) 54 if err != nil { 55 return err 56 } 57 58 buffer := &bytes.Buffer{} 59 60 if r.ContentLength > 0 { 61 buffer.Grow(int(r.ContentLength)) 62 } 63 64 length, err := io.Copy(buffer, r.Body) 65 if err != nil { 66 return err 67 } 68 69 // Construct request based on Method, URL Path, and Body from r 70 request, err := http.NewRequest(r.Method, url.String(), buffer) 71 if err != nil { 72 return err 73 } 74 request = request.WithContext(ctx) 75 request.ContentLength = length 76 77 request.Header["Content-Type"] = r.Header["Content-Type"] 78 request.Header["Accept"] = r.Header["Accept"] 79 request.Header["Accept-Encoding"] = r.Header["Accept-Encoding"] 80 81 resp, err := client.Do(request) 82 if err != nil { 83 return err 84 } 85 defer resp.Body.Close() 86 87 // Copy response headers from resp to w 88 for k, vs := range resp.Header { 89 w.Header().Del(k) 90 for _, v := range vs { 91 w.Header().Add(k, v) 92 } 93 } 94 95 SetDefaultResponseHeaders(w.Header()) 96 97 // Copy status code from resp to w 98 w.WriteHeader(resp.StatusCode) 99 _, err = io.Copy(w, resp.Body) 100 return err 101 } 102 103 // Get returns the contents located at url. 104 func Get(ctx context.Context, url string) (*http.Response, error) { 105 request, err := http.NewRequest(http.MethodGet, url, nil) 106 if err != nil { 107 return nil, err 108 } 109 request = request.WithContext(ctx) 110 return client.Do(request) 111 } 112 113 func constructURL(base, path, prefix string) (*url.URL, error) { 114 u, err := url.Parse(base) 115 if err != nil { 116 return nil, err 117 } 118 119 if !strings.HasPrefix(prefix, "/") { 120 prefix = "/" + prefix 121 } 122 123 if !strings.HasPrefix(path, prefix) { 124 return nil, fmt.Errorf("%q does not have expected prefix %q", path, prefix) 125 } 126 127 ref, err := url.Parse(strings.TrimPrefix(path, prefix)) 128 if err != nil { 129 return nil, err 130 } 131 132 return u.ResolveReference(ref), err 133 } 134 135 type longestToShortest []string 136 137 func (s longestToShortest) Len() int { 138 return len(s) 139 } 140 141 func (s longestToShortest) Swap(i, j int) { 142 s[i], s[j] = s[j], s[i] 143 } 144 145 func (s longestToShortest) Less(i, j int) bool { 146 return len(s[i]) > len(s[j]) 147 } 148 149 // FQDN returns the fully-qualified domain name (or localhost if lookup 150 // according to the hostname fails). 151 func FQDN() (string, error) { 152 hostname, err := os.Hostname() 153 if err != nil { 154 // Fail if the kernel fails to report a hostname. 155 return "", err 156 } 157 158 addrs, err := net.LookupHost(hostname) 159 if err != nil { 160 return "localhost", nil 161 } 162 163 for _, addr := range addrs { 164 if names, err := net.LookupAddr(addr); err == nil && len(names) > 0 { 165 sort.Sort(longestToShortest(names)) 166 for _, name := range names { 167 name = strings.TrimRight(name, ".") 168 if strings.HasPrefix(name, hostname) { 169 return name, nil 170 } 171 } 172 return names[0], nil 173 } 174 } 175 176 return "localhost", nil 177 } 178 179 // SetDefaultResponseHeaders sets headers that appear in all WebDriver responses. 180 func SetDefaultResponseHeaders(h http.Header) { 181 h.Set("Access-Control-Allow-Origin", "*") 182 h.Set("Access-Control-Allow-Methods", "CONNECT,DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT,TRACE") 183 // Adding here in case any other allow headers are already set. 184 h.Add("Access-Control-Allow-Headers", "Accept,Content-Type") 185 h.Set("Cache-Control", "no-cache") 186 }