mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	PR#76518 introduced a change to limit the the read on various calls with utilio.ReadAtMost. By design, ReadAtMost will return an error if there is a truncation, but the calling code needs to know to handle it.
		
			
				
	
	
		
			151 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2015 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package http
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto/tls"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"net/http"
 | 
						|
	"net/url"
 | 
						|
	"time"
 | 
						|
 | 
						|
	utilnet "k8s.io/apimachinery/pkg/util/net"
 | 
						|
	"k8s.io/kubernetes/pkg/probe"
 | 
						|
	"k8s.io/kubernetes/pkg/version"
 | 
						|
 | 
						|
	"k8s.io/klog"
 | 
						|
	utilio "k8s.io/utils/io"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	maxRespBodyLength = 10 * 1 << 10 // 10KB
 | 
						|
)
 | 
						|
 | 
						|
// New creates Prober that will skip TLS verification while probing.
 | 
						|
// followNonLocalRedirects configures whether the prober should follow redirects to a different hostname.
 | 
						|
//   If disabled, redirects to other hosts will trigger a warning result.
 | 
						|
func New(followNonLocalRedirects bool) Prober {
 | 
						|
	tlsConfig := &tls.Config{InsecureSkipVerify: true}
 | 
						|
	return NewWithTLSConfig(tlsConfig, followNonLocalRedirects)
 | 
						|
}
 | 
						|
 | 
						|
// NewWithTLSConfig takes tls config as parameter.
 | 
						|
// followNonLocalRedirects configures whether the prober should follow redirects to a different hostname.
 | 
						|
//   If disabled, redirects to other hosts will trigger a warning result.
 | 
						|
func NewWithTLSConfig(config *tls.Config, followNonLocalRedirects bool) Prober {
 | 
						|
	// We do not want the probe use node's local proxy set.
 | 
						|
	transport := utilnet.SetTransportDefaults(
 | 
						|
		&http.Transport{
 | 
						|
			TLSClientConfig:   config,
 | 
						|
			DisableKeepAlives: true,
 | 
						|
			Proxy:             http.ProxyURL(nil),
 | 
						|
		})
 | 
						|
	return httpProber{transport, followNonLocalRedirects}
 | 
						|
}
 | 
						|
 | 
						|
// Prober is an interface that defines the Probe function for doing HTTP readiness/liveness checks.
 | 
						|
type Prober interface {
 | 
						|
	Probe(url *url.URL, headers http.Header, timeout time.Duration) (probe.Result, string, error)
 | 
						|
}
 | 
						|
 | 
						|
type httpProber struct {
 | 
						|
	transport               *http.Transport
 | 
						|
	followNonLocalRedirects bool
 | 
						|
}
 | 
						|
 | 
						|
// Probe returns a ProbeRunner capable of running an HTTP check.
 | 
						|
func (pr httpProber) Probe(url *url.URL, headers http.Header, timeout time.Duration) (probe.Result, string, error) {
 | 
						|
	client := &http.Client{
 | 
						|
		Timeout:       timeout,
 | 
						|
		Transport:     pr.transport,
 | 
						|
		CheckRedirect: redirectChecker(pr.followNonLocalRedirects),
 | 
						|
	}
 | 
						|
	return DoHTTPProbe(url, headers, client)
 | 
						|
}
 | 
						|
 | 
						|
// GetHTTPInterface is an interface for making HTTP requests, that returns a response and error.
 | 
						|
type GetHTTPInterface interface {
 | 
						|
	Do(req *http.Request) (*http.Response, error)
 | 
						|
}
 | 
						|
 | 
						|
// DoHTTPProbe checks if a GET request to the url succeeds.
 | 
						|
// If the HTTP response code is successful (i.e. 400 > code >= 200), it returns Success.
 | 
						|
// If the HTTP response code is unsuccessful or HTTP communication fails, it returns Failure.
 | 
						|
// This is exported because some other packages may want to do direct HTTP probes.
 | 
						|
func DoHTTPProbe(url *url.URL, headers http.Header, client GetHTTPInterface) (probe.Result, string, error) {
 | 
						|
	req, err := http.NewRequest("GET", url.String(), nil)
 | 
						|
	if err != nil {
 | 
						|
		// Convert errors into failures to catch timeouts.
 | 
						|
		return probe.Failure, err.Error(), nil
 | 
						|
	}
 | 
						|
	if _, ok := headers["User-Agent"]; !ok {
 | 
						|
		if headers == nil {
 | 
						|
			headers = http.Header{}
 | 
						|
		}
 | 
						|
		// explicitly set User-Agent so it's not set to default Go value
 | 
						|
		v := version.Get()
 | 
						|
		headers.Set("User-Agent", fmt.Sprintf("kube-probe/%s.%s", v.Major, v.Minor))
 | 
						|
	}
 | 
						|
	req.Header = headers
 | 
						|
	if headers.Get("Host") != "" {
 | 
						|
		req.Host = headers.Get("Host")
 | 
						|
	}
 | 
						|
	res, err := client.Do(req)
 | 
						|
	if err != nil {
 | 
						|
		// Convert errors into failures to catch timeouts.
 | 
						|
		return probe.Failure, err.Error(), nil
 | 
						|
	}
 | 
						|
	defer res.Body.Close()
 | 
						|
	b, err := utilio.ReadAtMost(res.Body, maxRespBodyLength)
 | 
						|
	if err != nil {
 | 
						|
		if err == utilio.ErrLimitReached {
 | 
						|
			klog.V(4).Infof("Non fatal body truncation for %s, Response: %v", url.String(), *res)
 | 
						|
		} else {
 | 
						|
			return probe.Failure, "", err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	body := string(b)
 | 
						|
	if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusBadRequest {
 | 
						|
		if res.StatusCode >= http.StatusMultipleChoices { // Redirect
 | 
						|
			klog.V(4).Infof("Probe terminated redirects for %s, Response: %v", url.String(), *res)
 | 
						|
			return probe.Warning, body, nil
 | 
						|
		}
 | 
						|
		klog.V(4).Infof("Probe succeeded for %s, Response: %v", url.String(), *res)
 | 
						|
		return probe.Success, body, nil
 | 
						|
	}
 | 
						|
	klog.V(4).Infof("Probe failed for %s with request headers %v, response body: %v", url.String(), headers, body)
 | 
						|
	return probe.Failure, fmt.Sprintf("HTTP probe failed with statuscode: %d", res.StatusCode), nil
 | 
						|
}
 | 
						|
 | 
						|
func redirectChecker(followNonLocalRedirects bool) func(*http.Request, []*http.Request) error {
 | 
						|
	if followNonLocalRedirects {
 | 
						|
		return nil // Use the default http client checker.
 | 
						|
	}
 | 
						|
 | 
						|
	return func(req *http.Request, via []*http.Request) error {
 | 
						|
		if req.URL.Hostname() != via[0].URL.Hostname() {
 | 
						|
			return http.ErrUseLastResponse
 | 
						|
		}
 | 
						|
		// Default behavior: stop after 10 redirects.
 | 
						|
		if len(via) >= 10 {
 | 
						|
			return errors.New("stopped after 10 redirects")
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
}
 |