...

Source file src/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go

Documentation: github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg

     1  /*
     2  Copyright (c) 2011, Open Knowledge Foundation Ltd.
     3  All rights reserved.
     4  
     5  HTTP Content-Type Autonegotiation.
     6  
     7  The functions in this package implement the behaviour specified in
     8  http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
     9  
    10  Redistribution and use in source and binary forms, with or without
    11  modification, are permitted provided that the following conditions are
    12  met:
    13  
    14  	Redistributions of source code must retain the above copyright
    15  	notice, this list of conditions and the following disclaimer.
    16  
    17  	Redistributions in binary form must reproduce the above copyright
    18  	notice, this list of conditions and the following disclaimer in
    19  	the documentation and/or other materials provided with the
    20  	distribution.
    21  
    22  	Neither the name of the Open Knowledge Foundation Ltd. nor the
    23  	names of its contributors may be used to endorse or promote
    24  	products derived from this software without specific prior written
    25  	permission.
    26  
    27  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    28  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    29  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    30  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    31  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    32  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    33  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    34  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    35  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    36  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    37  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    38  */
    39  package goautoneg
    40  
    41  import (
    42  	"sort"
    43  	"strconv"
    44  	"strings"
    45  )
    46  
    47  // Structure to represent a clause in an HTTP Accept Header
    48  type Accept struct {
    49  	Type, SubType string
    50  	Q             float64
    51  	Params        map[string]string
    52  }
    53  
    54  // For internal use, so that we can use the sort interface
    55  type accept_slice []Accept
    56  
    57  func (accept accept_slice) Len() int {
    58  	slice := []Accept(accept)
    59  	return len(slice)
    60  }
    61  
    62  func (accept accept_slice) Less(i, j int) bool {
    63  	slice := []Accept(accept)
    64  	ai, aj := slice[i], slice[j]
    65  	if ai.Q > aj.Q {
    66  		return true
    67  	}
    68  	if ai.Type != "*" && aj.Type == "*" {
    69  		return true
    70  	}
    71  	if ai.SubType != "*" && aj.SubType == "*" {
    72  		return true
    73  	}
    74  	return false
    75  }
    76  
    77  func (accept accept_slice) Swap(i, j int) {
    78  	slice := []Accept(accept)
    79  	slice[i], slice[j] = slice[j], slice[i]
    80  }
    81  
    82  // Parse an Accept Header string returning a sorted list
    83  // of clauses
    84  func ParseAccept(header string) (accept []Accept) {
    85  	parts := strings.Split(header, ",")
    86  	accept = make([]Accept, 0, len(parts))
    87  	for _, part := range parts {
    88  		part := strings.Trim(part, " ")
    89  
    90  		a := Accept{}
    91  		a.Params = make(map[string]string)
    92  		a.Q = 1.0
    93  
    94  		mrp := strings.Split(part, ";")
    95  
    96  		media_range := mrp[0]
    97  		sp := strings.Split(media_range, "/")
    98  		a.Type = strings.Trim(sp[0], " ")
    99  
   100  		switch {
   101  		case len(sp) == 1 && a.Type == "*":
   102  			a.SubType = "*"
   103  		case len(sp) == 2:
   104  			a.SubType = strings.Trim(sp[1], " ")
   105  		default:
   106  			continue
   107  		}
   108  
   109  		if len(mrp) == 1 {
   110  			accept = append(accept, a)
   111  			continue
   112  		}
   113  
   114  		for _, param := range mrp[1:] {
   115  			sp := strings.SplitN(param, "=", 2)
   116  			if len(sp) != 2 {
   117  				continue
   118  			}
   119  			token := strings.Trim(sp[0], " ")
   120  			if token == "q" {
   121  				a.Q, _ = strconv.ParseFloat(sp[1], 32)
   122  			} else {
   123  				a.Params[token] = strings.Trim(sp[1], " ")
   124  			}
   125  		}
   126  
   127  		accept = append(accept, a)
   128  	}
   129  
   130  	slice := accept_slice(accept)
   131  	sort.Sort(slice)
   132  
   133  	return
   134  }
   135  
   136  // Negotiate the most appropriate content_type given the accept header
   137  // and a list of alternatives.
   138  func Negotiate(header string, alternatives []string) (content_type string) {
   139  	asp := make([][]string, 0, len(alternatives))
   140  	for _, ctype := range alternatives {
   141  		asp = append(asp, strings.SplitN(ctype, "/", 2))
   142  	}
   143  	for _, clause := range ParseAccept(header) {
   144  		for i, ctsp := range asp {
   145  			if clause.Type == ctsp[0] && clause.SubType == ctsp[1] {
   146  				content_type = alternatives[i]
   147  				return
   148  			}
   149  			if clause.Type == ctsp[0] && clause.SubType == "*" {
   150  				content_type = alternatives[i]
   151  				return
   152  			}
   153  			if clause.Type == "*" && clause.SubType == "*" {
   154  				content_type = alternatives[i]
   155  				return
   156  			}
   157  		}
   158  	}
   159  	return
   160  }
   161  

View as plain text