mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 22:01:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			190 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| HTTP Content-Type Autonegotiation.
 | |
| 
 | |
| The functions in this package implement the behaviour specified in
 | |
| http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
 | |
| 
 | |
| Copyright (c) 2011, Open Knowledge Foundation Ltd.
 | |
| All rights reserved.
 | |
| 
 | |
| Redistribution and use in source and binary forms, with or without
 | |
| modification, are permitted provided that the following conditions are
 | |
| met:
 | |
| 
 | |
|     Redistributions of source code must retain the above copyright
 | |
|     notice, this list of conditions and the following disclaimer.
 | |
| 
 | |
|     Redistributions in binary form must reproduce the above copyright
 | |
|     notice, this list of conditions and the following disclaimer in
 | |
|     the documentation and/or other materials provided with the
 | |
|     distribution.
 | |
| 
 | |
|     Neither the name of the Open Knowledge Foundation Ltd. nor the
 | |
|     names of its contributors may be used to endorse or promote
 | |
|     products derived from this software without specific prior written
 | |
|     permission.
 | |
| 
 | |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | |
| HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
| */
 | |
| 
 | |
| package goautoneg
 | |
| 
 | |
| import (
 | |
| 	"sort"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| // Structure to represent a clause in an HTTP Accept Header
 | |
| type Accept struct {
 | |
| 	Type, SubType string
 | |
| 	Q             float64
 | |
| 	Params        map[string]string
 | |
| }
 | |
| 
 | |
| // acceptSlice is defined to implement sort interface.
 | |
| type acceptSlice []Accept
 | |
| 
 | |
| func (slice acceptSlice) Len() int {
 | |
| 	return len(slice)
 | |
| }
 | |
| 
 | |
| func (slice acceptSlice) Less(i, j int) bool {
 | |
| 	ai, aj := slice[i], slice[j]
 | |
| 	if ai.Q > aj.Q {
 | |
| 		return true
 | |
| 	}
 | |
| 	if ai.Type != "*" && aj.Type == "*" {
 | |
| 		return true
 | |
| 	}
 | |
| 	if ai.SubType != "*" && aj.SubType == "*" {
 | |
| 		return true
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func (slice acceptSlice) Swap(i, j int) {
 | |
| 	slice[i], slice[j] = slice[j], slice[i]
 | |
| }
 | |
| 
 | |
| func stringTrimSpaceCutset(r rune) bool {
 | |
| 	return r == ' '
 | |
| }
 | |
| 
 | |
| func nextSplitElement(s, sep string) (item string, remaining string) {
 | |
| 	if index := strings.Index(s, sep); index != -1 {
 | |
| 		return s[:index], s[index+1:]
 | |
| 	}
 | |
| 	return s, ""
 | |
| }
 | |
| 
 | |
| // Parse an Accept Header string returning a sorted list
 | |
| // of clauses
 | |
| func ParseAccept(header string) acceptSlice {
 | |
| 	partsCount := 0
 | |
| 	remaining := header
 | |
| 	for len(remaining) > 0 {
 | |
| 		partsCount++
 | |
| 		_, remaining = nextSplitElement(remaining, ",")
 | |
| 	}
 | |
| 	accept := make(acceptSlice, 0, partsCount)
 | |
| 
 | |
| 	remaining = header
 | |
| 	var part string
 | |
| 	for len(remaining) > 0 {
 | |
| 		part, remaining = nextSplitElement(remaining, ",")
 | |
| 		part = strings.TrimFunc(part, stringTrimSpaceCutset)
 | |
| 
 | |
| 		a := Accept{
 | |
| 			Q: 1.0,
 | |
| 		}
 | |
| 
 | |
| 		sp, remainingPart := nextSplitElement(part, ";")
 | |
| 
 | |
| 		sp0, spRemaining := nextSplitElement(sp, "/")
 | |
| 		a.Type = strings.TrimFunc(sp0, stringTrimSpaceCutset)
 | |
| 
 | |
| 		switch {
 | |
| 		case len(spRemaining) == 0:
 | |
| 			if a.Type == "*" {
 | |
| 				a.SubType = "*"
 | |
| 			} else {
 | |
| 				continue
 | |
| 			}
 | |
| 		default:
 | |
| 			var sp1 string
 | |
| 			sp1, spRemaining = nextSplitElement(spRemaining, "/")
 | |
| 			if len(spRemaining) > 0 {
 | |
| 				continue
 | |
| 			}
 | |
| 			a.SubType = strings.TrimFunc(sp1, stringTrimSpaceCutset)
 | |
| 		}
 | |
| 
 | |
| 		if len(remainingPart) == 0 {
 | |
| 			accept = append(accept, a)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		a.Params = make(map[string]string)
 | |
| 		for len(remainingPart) > 0 {
 | |
| 			sp, remainingPart = nextSplitElement(remainingPart, ";")
 | |
| 			sp0, spRemaining = nextSplitElement(sp, "=")
 | |
| 			if len(spRemaining) == 0 {
 | |
| 				continue
 | |
| 			}
 | |
| 			var sp1 string
 | |
| 			sp1, spRemaining = nextSplitElement(spRemaining, "=")
 | |
| 			if len(spRemaining) != 0 {
 | |
| 				continue
 | |
| 			}
 | |
| 			token := strings.TrimFunc(sp0, stringTrimSpaceCutset)
 | |
| 			if token == "q" {
 | |
| 				a.Q, _ = strconv.ParseFloat(sp1, 32)
 | |
| 			} else {
 | |
| 				a.Params[token] = strings.TrimFunc(sp1, stringTrimSpaceCutset)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		accept = append(accept, a)
 | |
| 	}
 | |
| 
 | |
| 	sort.Sort(accept)
 | |
| 	return accept
 | |
| }
 | |
| 
 | |
| // Negotiate the most appropriate content_type given the accept header
 | |
| // and a list of alternatives.
 | |
| func Negotiate(header string, alternatives []string) (content_type string) {
 | |
| 	asp := make([][]string, 0, len(alternatives))
 | |
| 	for _, ctype := range alternatives {
 | |
| 		asp = append(asp, strings.SplitN(ctype, "/", 2))
 | |
| 	}
 | |
| 	for _, clause := range ParseAccept(header) {
 | |
| 		for i, ctsp := range asp {
 | |
| 			if clause.Type == ctsp[0] && clause.SubType == ctsp[1] {
 | |
| 				content_type = alternatives[i]
 | |
| 				return
 | |
| 			}
 | |
| 			if clause.Type == ctsp[0] && clause.SubType == "*" {
 | |
| 				content_type = alternatives[i]
 | |
| 				return
 | |
| 			}
 | |
| 			if clause.Type == "*" && clause.SubType == "*" {
 | |
| 				content_type = alternatives[i]
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return
 | |
| }
 |