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  }