github.com/aclisp/heapster@v0.19.2-0.20160613100040-51756f899a96/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/net/http.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package net 18 19 import ( 20 "crypto/tls" 21 "fmt" 22 "io" 23 "net" 24 "net/http" 25 "net/url" 26 "os" 27 "strconv" 28 "strings" 29 ) 30 31 // IsProbableEOF returns true if the given error resembles a connection termination 32 // scenario that would justify assuming that the watch is empty. 33 // These errors are what the Go http stack returns back to us which are general 34 // connection closure errors (strongly correlated) and callers that need to 35 // differentiate probable errors in connection behavior between normal "this is 36 // disconnected" should use the method. 37 func IsProbableEOF(err error) bool { 38 if uerr, ok := err.(*url.Error); ok { 39 err = uerr.Err 40 } 41 switch { 42 case err == io.EOF: 43 return true 44 case err.Error() == "http: can't write HTTP request on broken connection": 45 return true 46 case strings.Contains(err.Error(), "connection reset by peer"): 47 return true 48 case strings.Contains(strings.ToLower(err.Error()), "use of closed network connection"): 49 return true 50 } 51 return false 52 } 53 54 var defaultTransport = http.DefaultTransport.(*http.Transport) 55 56 // SetTransportDefaults applies the defaults from http.DefaultTransport 57 // for the Proxy, Dial, and TLSHandshakeTimeout fields if unset 58 func SetTransportDefaults(t *http.Transport) *http.Transport { 59 if t.Proxy == nil || isDefault(t.Proxy) { 60 // http.ProxyFromEnvironment doesn't respect CIDRs and that makes it impossible to exclude things like pod and service IPs from proxy settings 61 // ProxierWithNoProxyCIDR allows CIDR rules in NO_PROXY 62 t.Proxy = NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment) 63 } 64 if t.Dial == nil { 65 t.Dial = defaultTransport.Dial 66 } 67 if t.TLSHandshakeTimeout == 0 { 68 t.TLSHandshakeTimeout = defaultTransport.TLSHandshakeTimeout 69 } 70 return t 71 } 72 73 type RoundTripperWrapper interface { 74 http.RoundTripper 75 WrappedRoundTripper() http.RoundTripper 76 } 77 78 type DialFunc func(net, addr string) (net.Conn, error) 79 80 func Dialer(transport http.RoundTripper) (DialFunc, error) { 81 if transport == nil { 82 return nil, nil 83 } 84 85 switch transport := transport.(type) { 86 case *http.Transport: 87 return transport.Dial, nil 88 case RoundTripperWrapper: 89 return Dialer(transport.WrappedRoundTripper()) 90 default: 91 return nil, fmt.Errorf("unknown transport type: %v", transport) 92 } 93 } 94 95 func TLSClientConfig(transport http.RoundTripper) (*tls.Config, error) { 96 if transport == nil { 97 return nil, nil 98 } 99 100 switch transport := transport.(type) { 101 case *http.Transport: 102 return transport.TLSClientConfig, nil 103 case RoundTripperWrapper: 104 return TLSClientConfig(transport.WrappedRoundTripper()) 105 default: 106 return nil, fmt.Errorf("unknown transport type: %v", transport) 107 } 108 } 109 110 func FormatURL(scheme string, host string, port int, path string) *url.URL { 111 return &url.URL{ 112 Scheme: scheme, 113 Host: net.JoinHostPort(host, strconv.Itoa(port)), 114 Path: path, 115 } 116 } 117 118 func GetHTTPClient(req *http.Request) string { 119 if userAgent, ok := req.Header["User-Agent"]; ok { 120 if len(userAgent) > 0 { 121 return userAgent[0] 122 } 123 } 124 return "unknown" 125 } 126 127 // Extracts and returns the clients IP from the given request. 128 // Looks at X-Forwarded-For header, X-Real-Ip header and request.RemoteAddr in that order. 129 // Returns nil if none of them are set or is set to an invalid value. 130 func GetClientIP(req *http.Request) net.IP { 131 hdr := req.Header 132 // First check the X-Forwarded-For header for requests via proxy. 133 hdrForwardedFor := hdr.Get("X-Forwarded-For") 134 if hdrForwardedFor != "" { 135 // X-Forwarded-For can be a csv of IPs in case of multiple proxies. 136 // Use the first valid one. 137 parts := strings.Split(hdrForwardedFor, ",") 138 for _, part := range parts { 139 ip := net.ParseIP(strings.TrimSpace(part)) 140 if ip != nil { 141 return ip 142 } 143 } 144 } 145 146 // Try the X-Real-Ip header. 147 hdrRealIp := hdr.Get("X-Real-Ip") 148 if hdrRealIp != "" { 149 ip := net.ParseIP(hdrRealIp) 150 if ip != nil { 151 return ip 152 } 153 } 154 155 // Fallback to Remote Address in request, which will give the correct client IP when there is no proxy. 156 ip := net.ParseIP(req.RemoteAddr) 157 return ip 158 } 159 160 var defaultProxyFuncPointer = fmt.Sprintf("%p", http.ProxyFromEnvironment) 161 162 // isDefault checks to see if the transportProxierFunc is pointing to the default one 163 func isDefault(transportProxier func(*http.Request) (*url.URL, error)) bool { 164 transportProxierPointer := fmt.Sprintf("%p", transportProxier) 165 return transportProxierPointer == defaultProxyFuncPointer 166 } 167 168 // NewProxierWithNoProxyCIDR constructs a Proxier function that respects CIDRs in NO_PROXY and delegates if 169 // no matching CIDRs are found 170 func NewProxierWithNoProxyCIDR(delegate func(req *http.Request) (*url.URL, error)) func(req *http.Request) (*url.URL, error) { 171 // we wrap the default method, so we only need to perform our check if the NO_PROXY envvar has a CIDR in it 172 noProxyEnv := os.Getenv("NO_PROXY") 173 noProxyRules := strings.Split(noProxyEnv, ",") 174 175 cidrs := []*net.IPNet{} 176 for _, noProxyRule := range noProxyRules { 177 _, cidr, _ := net.ParseCIDR(noProxyRule) 178 if cidr != nil { 179 cidrs = append(cidrs, cidr) 180 } 181 } 182 183 if len(cidrs) == 0 { 184 return delegate 185 } 186 187 return func(req *http.Request) (*url.URL, error) { 188 host := req.URL.Host 189 // for some urls, the Host is already the host, not the host:port 190 if net.ParseIP(host) == nil { 191 var err error 192 host, _, err = net.SplitHostPort(req.URL.Host) 193 if err != nil { 194 return delegate(req) 195 } 196 } 197 198 ip := net.ParseIP(host) 199 if ip == nil { 200 return delegate(req) 201 } 202 203 for _, cidr := range cidrs { 204 if cidr.Contains(ip) { 205 return nil, nil 206 } 207 } 208 209 return delegate(req) 210 } 211 }