github.com/google/cloudprober@v0.11.3/probes/http/request.go (about) 1 // Copyright 2019-2020 The Cloudprober Authors. 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 http 16 17 import ( 18 "fmt" 19 "io" 20 "net" 21 "net/http" 22 "strings" 23 24 "github.com/google/cloudprober/targets/endpoint" 25 ) 26 27 const relURLLabel = "relative_url" 28 29 // requestBody encapsulates the request body and implements the io.Reader() 30 // interface. 31 type requestBody struct { 32 b []byte 33 } 34 35 // Read implements the io.Reader interface. Instead of using buffered read, 36 // it simply copies the bytes to the provided slice in one go (depending on 37 // the input slice capacity) and returns io.EOF. Buffered reads require 38 // resetting the buffer before re-use, restricting our ability to use the 39 // request object concurrently. 40 func (rb *requestBody) Read(p []byte) (int, error) { 41 return copy(p, rb.b), io.EOF 42 } 43 44 // resolveFunc resolves the given host for the IP version. 45 // This type is mainly used for testing. For all other cases, a nil function 46 // should be passed to the httpRequestForTarget function. 47 type resolveFunc func(host string, ipVer int) (net.IP, error) 48 49 func hostWithPort(host string, port int) string { 50 if port == 0 { 51 return host 52 } 53 return fmt.Sprintf("%s:%d", host, port) 54 } 55 56 // hostHeaderForTarget computes request's Host header for a target. 57 // - If host header is set in the probe, it overrides everything else. 58 // - If target's fqdn is provided in its labels, use that along with the port. 59 // - Finally, use target's name with port. 60 func hostHeaderForTarget(target endpoint.Endpoint, probeHostHeader string, port int) string { 61 if probeHostHeader != "" { 62 return probeHostHeader 63 } 64 65 if target.Labels["fqdn"] != "" { 66 return hostWithPort(target.Labels["fqdn"], port) 67 } 68 69 return hostWithPort(target.Name, port) 70 } 71 72 func urlHostForTarget(target endpoint.Endpoint) string { 73 if target.Labels["fqdn"] != "" { 74 return target.Labels["fqdn"] 75 } 76 77 return target.Name 78 } 79 80 func relURLForTarget(target endpoint.Endpoint, probeURL string) string { 81 if probeURL != "" { 82 return probeURL 83 } 84 85 if target.Labels[relURLLabel] != "" { 86 return target.Labels[relURLLabel] 87 } 88 89 return "" 90 } 91 92 func (p *Probe) httpRequestForTarget(target endpoint.Endpoint, resolveF resolveFunc) *http.Request { 93 // Prepare HTTP.Request for Client.Do 94 port := int(p.c.GetPort()) 95 // If port is not configured explicitly, use target's port if available. 96 if port == 0 { 97 port = target.Port 98 } 99 100 urlHost := urlHostForTarget(target) 101 102 if p.c.GetResolveFirst() { 103 if resolveF == nil { 104 resolveF = p.opts.Targets.Resolve 105 } 106 107 ip, err := resolveF(target.Name, p.opts.IPVersion) 108 if err != nil { 109 p.l.Error("target: ", target.Name, ", resolve error: ", err.Error()) 110 return nil 111 } 112 urlHost = ip.String() 113 } 114 115 // Put square brackets around literal IPv6 hosts. This is the same logic as 116 // net.JoinHostPort, but we cannot use net.JoinHostPort as it works only for 117 // non default ports. 118 if strings.IndexByte(urlHost, ':') >= 0 { 119 urlHost = "[" + urlHost + "]" 120 } 121 122 url := fmt.Sprintf("%s://%s%s", p.protocol, hostWithPort(urlHost, port), relURLForTarget(target, p.url)) 123 124 // Prepare request body 125 var body io.Reader 126 if len(p.requestBody) > 0 { 127 body = &requestBody{p.requestBody} 128 } 129 req, err := http.NewRequest(p.method, url, body) 130 if err != nil { 131 p.l.Error("target: ", target.Name, ", error creating HTTP request: ", err.Error()) 132 return nil 133 } 134 135 var probeHostHeader string 136 for _, header := range p.c.GetHeaders() { 137 if header.GetName() == "Host" { 138 probeHostHeader = header.GetValue() 139 continue 140 } 141 req.Header.Set(header.GetName(), header.GetValue()) 142 } 143 144 // Host header is set by http.NewRequest based on the URL, update it based 145 // on various conditions. 146 req.Host = hostHeaderForTarget(target, probeHostHeader, port) 147 148 if p.bearerToken != "" { 149 req.Header.Set("Authorization", "Bearer "+p.bearerToken) 150 } 151 152 return req 153 }