headers.go raw

   1  package rest
   2  
   3  import (
   4  	"net/url"
   5  	"regexp"
   6  	"strings"
   7  )
   8  
   9  var (
  10  	commaRegexp      = regexp.MustCompile(`,\s{0,}`)
  11  	valueCommaRegexp = regexp.MustCompile(`([^"]),`)
  12  	equalRegexp      = regexp.MustCompile(` *= *`)
  13  	keyRegexp        = regexp.MustCompile(`[a-z*]+`)
  14  	linkRegexp       = regexp.MustCompile(`\A<(.+)>;(.+)\z`)
  15  	semiRegexp       = regexp.MustCompile(`; +`)
  16  	valRegexp        = regexp.MustCompile(`"+([^"]+)"+`)
  17  )
  18  
  19  // Links represents a Link Header, keyed by the Rel attribute
  20  type Links map[string]*Link
  21  
  22  // Link has a URI and its relation (next/prev/last/etc)
  23  type Link struct {
  24  	URI   string
  25  	Rel   string
  26  	Extra map[string]string
  27  }
  28  
  29  // Next gets the URI for "next", if present
  30  func (l Links) Next() string {
  31  	for k, v := range l {
  32  		if k == "next" {
  33  			return v.URI
  34  		}
  35  	}
  36  	return ""
  37  }
  38  
  39  // ParseLink parses a Link header value into a Links, mainly cribbed from:
  40  // https://github.com/peterhellberg/link/blob/master/link.go
  41  // The forceHTTPS parameter will rewrite any HTTP URLs it finds to HTTPS.
  42  func ParseLink(s string, forceHTTPS bool) Links {
  43  	if s == "" {
  44  		return nil
  45  	}
  46  
  47  	links := Links{}
  48  
  49  	s = valueCommaRegexp.ReplaceAllString(s, "$1")
  50  
  51  	for _, l := range commaRegexp.Split(s, -1) {
  52  		linkMatches := linkRegexp.FindAllStringSubmatch(l, -1)
  53  
  54  		if len(linkMatches) == 0 {
  55  			return nil
  56  		}
  57  
  58  		pieces := linkMatches[0]
  59  
  60  		// Make sure we have a reasonable URL
  61  		uri := ""
  62  		if url, err := url.ParseRequestURI(pieces[1]); err == nil {
  63  
  64  			// See PLAT-188
  65  			if forceHTTPS && url.Scheme == "http" {
  66  				url.Scheme = "https"
  67  			}
  68  
  69  			uri = url.String()
  70  		}
  71  
  72  		link := &Link{URI: uri, Extra: map[string]string{}}
  73  
  74  		for _, extra := range semiRegexp.Split(pieces[2], -1) {
  75  			vals := equalRegexp.Split(extra, -1)
  76  
  77  			key := keyRegexp.FindString(vals[0])
  78  			val := valRegexp.FindStringSubmatch(vals[1])[1]
  79  
  80  			if key == "rel" {
  81  				vals := strings.Split(val, " ")
  82  				rels := []string{vals[0]}
  83  
  84  				if len(vals) > 1 {
  85  					for _, v := range vals[1:] {
  86  						if !strings.HasPrefix(v, "http") {
  87  							rels = append(rels, v)
  88  						}
  89  					}
  90  				}
  91  
  92  				rel := strings.Join(rels, " ")
  93  
  94  				link.Rel = rel
  95  				links[rel] = link
  96  			} else {
  97  				link.Extra[key] = val
  98  			}
  99  		}
 100  	}
 101  
 102  	return links
 103  }
 104