http.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package net
  14. import (
  15. "bufio"
  16. "bytes"
  17. "context"
  18. "crypto/tls"
  19. "fmt"
  20. "io"
  21. "net"
  22. "net/http"
  23. "net/url"
  24. "os"
  25. "path"
  26. "strconv"
  27. "strings"
  28. "golang.org/x/net/http2"
  29. "k8s.io/klog"
  30. )
  31. // JoinPreservingTrailingSlash does a path.Join of the specified elements,
  32. // preserving any trailing slash on the last non-empty segment
  33. func JoinPreservingTrailingSlash(elem ...string) string {
  34. // do the basic path join
  35. result := path.Join(elem...)
  36. // find the last non-empty segment
  37. for i := len(elem) - 1; i >= 0; i-- {
  38. if len(elem[i]) > 0 {
  39. // if the last segment ended in a slash, ensure our result does as well
  40. if strings.HasSuffix(elem[i], "/") && !strings.HasSuffix(result, "/") {
  41. result += "/"
  42. }
  43. break
  44. }
  45. }
  46. return result
  47. }
  48. // IsProbableEOF returns true if the given error resembles a connection termination
  49. // scenario that would justify assuming that the watch is empty.
  50. // These errors are what the Go http stack returns back to us which are general
  51. // connection closure errors (strongly correlated) and callers that need to
  52. // differentiate probable errors in connection behavior between normal "this is
  53. // disconnected" should use the method.
  54. func IsProbableEOF(err error) bool {
  55. if err == nil {
  56. return false
  57. }
  58. if uerr, ok := err.(*url.Error); ok {
  59. err = uerr.Err
  60. }
  61. msg := err.Error()
  62. switch {
  63. case err == io.EOF:
  64. return true
  65. case msg == "http: can't write HTTP request on broken connection":
  66. return true
  67. case strings.Contains(msg, "http2: server sent GOAWAY and closed the connection"):
  68. return true
  69. case strings.Contains(msg, "connection reset by peer"):
  70. return true
  71. case strings.Contains(strings.ToLower(msg), "use of closed network connection"):
  72. return true
  73. }
  74. return false
  75. }
  76. var defaultTransport = http.DefaultTransport.(*http.Transport)
  77. // SetOldTransportDefaults applies the defaults from http.DefaultTransport
  78. // for the Proxy, Dial, and TLSHandshakeTimeout fields if unset
  79. func SetOldTransportDefaults(t *http.Transport) *http.Transport {
  80. if t.Proxy == nil || isDefault(t.Proxy) {
  81. // http.ProxyFromEnvironment doesn't respect CIDRs and that makes it impossible to exclude things like pod and service IPs from proxy settings
  82. // ProxierWithNoProxyCIDR allows CIDR rules in NO_PROXY
  83. t.Proxy = NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment)
  84. }
  85. // If no custom dialer is set, use the default context dialer
  86. if t.DialContext == nil && t.Dial == nil {
  87. t.DialContext = defaultTransport.DialContext
  88. }
  89. if t.TLSHandshakeTimeout == 0 {
  90. t.TLSHandshakeTimeout = defaultTransport.TLSHandshakeTimeout
  91. }
  92. return t
  93. }
  94. // SetTransportDefaults applies the defaults from http.DefaultTransport
  95. // for the Proxy, Dial, and TLSHandshakeTimeout fields if unset
  96. func SetTransportDefaults(t *http.Transport) *http.Transport {
  97. t = SetOldTransportDefaults(t)
  98. // Allow clients to disable http2 if needed.
  99. if s := os.Getenv("DISABLE_HTTP2"); len(s) > 0 {
  100. klog.Infof("HTTP2 has been explicitly disabled")
  101. } else {
  102. if err := http2.ConfigureTransport(t); err != nil {
  103. klog.Warningf("Transport failed http2 configuration: %v", err)
  104. }
  105. }
  106. return t
  107. }
  108. type RoundTripperWrapper interface {
  109. http.RoundTripper
  110. WrappedRoundTripper() http.RoundTripper
  111. }
  112. type DialFunc func(ctx context.Context, net, addr string) (net.Conn, error)
  113. func DialerFor(transport http.RoundTripper) (DialFunc, error) {
  114. if transport == nil {
  115. return nil, nil
  116. }
  117. switch transport := transport.(type) {
  118. case *http.Transport:
  119. // transport.DialContext takes precedence over transport.Dial
  120. if transport.DialContext != nil {
  121. return transport.DialContext, nil
  122. }
  123. // adapt transport.Dial to the DialWithContext signature
  124. if transport.Dial != nil {
  125. return func(ctx context.Context, net, addr string) (net.Conn, error) {
  126. return transport.Dial(net, addr)
  127. }, nil
  128. }
  129. // otherwise return nil
  130. return nil, nil
  131. case RoundTripperWrapper:
  132. return DialerFor(transport.WrappedRoundTripper())
  133. default:
  134. return nil, fmt.Errorf("unknown transport type: %T", transport)
  135. }
  136. }
  137. type TLSClientConfigHolder interface {
  138. TLSClientConfig() *tls.Config
  139. }
  140. func TLSClientConfig(transport http.RoundTripper) (*tls.Config, error) {
  141. if transport == nil {
  142. return nil, nil
  143. }
  144. switch transport := transport.(type) {
  145. case *http.Transport:
  146. return transport.TLSClientConfig, nil
  147. case TLSClientConfigHolder:
  148. return transport.TLSClientConfig(), nil
  149. case RoundTripperWrapper:
  150. return TLSClientConfig(transport.WrappedRoundTripper())
  151. default:
  152. return nil, fmt.Errorf("unknown transport type: %T", transport)
  153. }
  154. }
  155. func FormatURL(scheme string, host string, port int, path string) *url.URL {
  156. return &url.URL{
  157. Scheme: scheme,
  158. Host: net.JoinHostPort(host, strconv.Itoa(port)),
  159. Path: path,
  160. }
  161. }
  162. func GetHTTPClient(req *http.Request) string {
  163. if ua := req.UserAgent(); len(ua) != 0 {
  164. return ua
  165. }
  166. return "unknown"
  167. }
  168. // SourceIPs splits the comma separated X-Forwarded-For header or returns the X-Real-Ip header or req.RemoteAddr,
  169. // in that order, ignoring invalid IPs. It returns nil if all of these are empty or invalid.
  170. func SourceIPs(req *http.Request) []net.IP {
  171. hdr := req.Header
  172. // First check the X-Forwarded-For header for requests via proxy.
  173. hdrForwardedFor := hdr.Get("X-Forwarded-For")
  174. forwardedForIPs := []net.IP{}
  175. if hdrForwardedFor != "" {
  176. // X-Forwarded-For can be a csv of IPs in case of multiple proxies.
  177. // Use the first valid one.
  178. parts := strings.Split(hdrForwardedFor, ",")
  179. for _, part := range parts {
  180. ip := net.ParseIP(strings.TrimSpace(part))
  181. if ip != nil {
  182. forwardedForIPs = append(forwardedForIPs, ip)
  183. }
  184. }
  185. }
  186. if len(forwardedForIPs) > 0 {
  187. return forwardedForIPs
  188. }
  189. // Try the X-Real-Ip header.
  190. hdrRealIp := hdr.Get("X-Real-Ip")
  191. if hdrRealIp != "" {
  192. ip := net.ParseIP(hdrRealIp)
  193. if ip != nil {
  194. return []net.IP{ip}
  195. }
  196. }
  197. // Fallback to Remote Address in request, which will give the correct client IP when there is no proxy.
  198. // Remote Address in Go's HTTP server is in the form host:port so we need to split that first.
  199. host, _, err := net.SplitHostPort(req.RemoteAddr)
  200. if err == nil {
  201. if remoteIP := net.ParseIP(host); remoteIP != nil {
  202. return []net.IP{remoteIP}
  203. }
  204. }
  205. // Fallback if Remote Address was just IP.
  206. if remoteIP := net.ParseIP(req.RemoteAddr); remoteIP != nil {
  207. return []net.IP{remoteIP}
  208. }
  209. return nil
  210. }
  211. // Extracts and returns the clients IP from the given request.
  212. // Looks at X-Forwarded-For header, X-Real-Ip header and request.RemoteAddr in that order.
  213. // Returns nil if none of them are set or is set to an invalid value.
  214. func GetClientIP(req *http.Request) net.IP {
  215. ips := SourceIPs(req)
  216. if len(ips) == 0 {
  217. return nil
  218. }
  219. return ips[0]
  220. }
  221. // Prepares the X-Forwarded-For header for another forwarding hop by appending the previous sender's
  222. // IP address to the X-Forwarded-For chain.
  223. func AppendForwardedForHeader(req *http.Request) {
  224. // Copied from net/http/httputil/reverseproxy.go:
  225. if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
  226. // If we aren't the first proxy retain prior
  227. // X-Forwarded-For information as a comma+space
  228. // separated list and fold multiple headers into one.
  229. if prior, ok := req.Header["X-Forwarded-For"]; ok {
  230. clientIP = strings.Join(prior, ", ") + ", " + clientIP
  231. }
  232. req.Header.Set("X-Forwarded-For", clientIP)
  233. }
  234. }
  235. var defaultProxyFuncPointer = fmt.Sprintf("%p", http.ProxyFromEnvironment)
  236. // isDefault checks to see if the transportProxierFunc is pointing to the default one
  237. func isDefault(transportProxier func(*http.Request) (*url.URL, error)) bool {
  238. transportProxierPointer := fmt.Sprintf("%p", transportProxier)
  239. return transportProxierPointer == defaultProxyFuncPointer
  240. }
  241. // NewProxierWithNoProxyCIDR constructs a Proxier function that respects CIDRs in NO_PROXY and delegates if
  242. // no matching CIDRs are found
  243. func NewProxierWithNoProxyCIDR(delegate func(req *http.Request) (*url.URL, error)) func(req *http.Request) (*url.URL, error) {
  244. // we wrap the default method, so we only need to perform our check if the NO_PROXY (or no_proxy) envvar has a CIDR in it
  245. noProxyEnv := os.Getenv("NO_PROXY")
  246. if noProxyEnv == "" {
  247. noProxyEnv = os.Getenv("no_proxy")
  248. }
  249. noProxyRules := strings.Split(noProxyEnv, ",")
  250. cidrs := []*net.IPNet{}
  251. for _, noProxyRule := range noProxyRules {
  252. _, cidr, _ := net.ParseCIDR(noProxyRule)
  253. if cidr != nil {
  254. cidrs = append(cidrs, cidr)
  255. }
  256. }
  257. if len(cidrs) == 0 {
  258. return delegate
  259. }
  260. return func(req *http.Request) (*url.URL, error) {
  261. ip := net.ParseIP(req.URL.Hostname())
  262. if ip == nil {
  263. return delegate(req)
  264. }
  265. for _, cidr := range cidrs {
  266. if cidr.Contains(ip) {
  267. return nil, nil
  268. }
  269. }
  270. return delegate(req)
  271. }
  272. }
  273. // DialerFunc implements Dialer for the provided function.
  274. type DialerFunc func(req *http.Request) (net.Conn, error)
  275. func (fn DialerFunc) Dial(req *http.Request) (net.Conn, error) {
  276. return fn(req)
  277. }
  278. // Dialer dials a host and writes a request to it.
  279. type Dialer interface {
  280. // Dial connects to the host specified by req's URL, writes the request to the connection, and
  281. // returns the opened net.Conn.
  282. Dial(req *http.Request) (net.Conn, error)
  283. }
  284. // ConnectWithRedirects uses dialer to send req, following up to 10 redirects (relative to
  285. // originalLocation). It returns the opened net.Conn and the raw response bytes.
  286. // If requireSameHostRedirects is true, only redirects to the same host are permitted.
  287. func ConnectWithRedirects(originalMethod string, originalLocation *url.URL, header http.Header, originalBody io.Reader, dialer Dialer, requireSameHostRedirects bool) (net.Conn, []byte, error) {
  288. const (
  289. maxRedirects = 9 // Fail on the 10th redirect
  290. maxResponseSize = 16384 // play it safe to allow the potential for lots of / large headers
  291. )
  292. var (
  293. location = originalLocation
  294. method = originalMethod
  295. intermediateConn net.Conn
  296. rawResponse = bytes.NewBuffer(make([]byte, 0, 256))
  297. body = originalBody
  298. )
  299. defer func() {
  300. if intermediateConn != nil {
  301. intermediateConn.Close()
  302. }
  303. }()
  304. redirectLoop:
  305. for redirects := 0; ; redirects++ {
  306. if redirects > maxRedirects {
  307. return nil, nil, fmt.Errorf("too many redirects (%d)", redirects)
  308. }
  309. req, err := http.NewRequest(method, location.String(), body)
  310. if err != nil {
  311. return nil, nil, err
  312. }
  313. req.Header = header
  314. intermediateConn, err = dialer.Dial(req)
  315. if err != nil {
  316. return nil, nil, err
  317. }
  318. // Peek at the backend response.
  319. rawResponse.Reset()
  320. respReader := bufio.NewReader(io.TeeReader(
  321. io.LimitReader(intermediateConn, maxResponseSize), // Don't read more than maxResponseSize bytes.
  322. rawResponse)) // Save the raw response.
  323. resp, err := http.ReadResponse(respReader, nil)
  324. if err != nil {
  325. // Unable to read the backend response; let the client handle it.
  326. klog.Warningf("Error reading backend response: %v", err)
  327. break redirectLoop
  328. }
  329. switch resp.StatusCode {
  330. case http.StatusFound:
  331. // Redirect, continue.
  332. default:
  333. // Don't redirect.
  334. break redirectLoop
  335. }
  336. // Redirected requests switch to "GET" according to the HTTP spec:
  337. // https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3
  338. method = "GET"
  339. // don't send a body when following redirects
  340. body = nil
  341. resp.Body.Close() // not used
  342. // Prepare to follow the redirect.
  343. redirectStr := resp.Header.Get("Location")
  344. if redirectStr == "" {
  345. return nil, nil, fmt.Errorf("%d response missing Location header", resp.StatusCode)
  346. }
  347. // We have to parse relative to the current location, NOT originalLocation. For example,
  348. // if we request http://foo.com/a and get back "http://bar.com/b", the result should be
  349. // http://bar.com/b. If we then make that request and get back a redirect to "/c", the result
  350. // should be http://bar.com/c, not http://foo.com/c.
  351. location, err = location.Parse(redirectStr)
  352. if err != nil {
  353. return nil, nil, fmt.Errorf("malformed Location header: %v", err)
  354. }
  355. // Only follow redirects to the same host. Otherwise, propagate the redirect response back.
  356. if requireSameHostRedirects && location.Hostname() != originalLocation.Hostname() {
  357. break redirectLoop
  358. }
  359. // Reset the connection.
  360. intermediateConn.Close()
  361. intermediateConn = nil
  362. }
  363. connToReturn := intermediateConn
  364. intermediateConn = nil // Don't close the connection when we return it.
  365. return connToReturn, rawResponse.Bytes(), nil
  366. }
  367. // CloneRequest creates a shallow copy of the request along with a deep copy of the Headers.
  368. func CloneRequest(req *http.Request) *http.Request {
  369. r := new(http.Request)
  370. // shallow clone
  371. *r = *req
  372. // deep copy headers
  373. r.Header = CloneHeader(req.Header)
  374. return r
  375. }
  376. // CloneHeader creates a deep copy of an http.Header.
  377. func CloneHeader(in http.Header) http.Header {
  378. out := make(http.Header, len(in))
  379. for key, values := range in {
  380. newValues := make([]string, len(values))
  381. copy(newValues, values)
  382. out[key] = newValues
  383. }
  384. return out
  385. }