github.com/cayleygraph/cayley@v0.7.7/server/http/accept.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file or at 5 // https://developers.google.com/open-source/licenses/bsd. 6 7 package cayleyhttp 8 9 import ( 10 "net/http" 11 "strings" 12 ) 13 14 // Octet types from RFC 2616. 15 var octetTypes [256]octetType 16 17 type octetType byte 18 19 const ( 20 isToken octetType = 1 << iota 21 isSpace 22 ) 23 24 func init() { 25 // OCTET = <any 8-bit sequence of data> 26 // CHAR = <any US-ASCII character (octets 0 - 127)> 27 // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)> 28 // CR = <US-ASCII CR, carriage return (13)> 29 // LF = <US-ASCII LF, linefeed (10)> 30 // SP = <US-ASCII SP, space (32)> 31 // HT = <US-ASCII HT, horizontal-tab (9)> 32 // <"> = <US-ASCII double-quote mark (34)> 33 // CRLF = CR LF 34 // LWS = [CRLF] 1*( SP | HT ) 35 // TEXT = <any OCTET except CTLs, but including LWS> 36 // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> 37 // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT 38 // token = 1*<any CHAR except CTLs or separators> 39 // qdtext = <any TEXT except <">> 40 41 for c := 0; c < 256; c++ { 42 var t octetType 43 isCtl := c <= 31 || c == 127 44 isChar := 0 <= c && c <= 127 45 isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 46 if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { 47 t |= isSpace 48 } 49 if isChar && !isCtl && !isSeparator { 50 t |= isToken 51 } 52 octetTypes[c] = t 53 } 54 } 55 56 // AcceptSpec describes an Accept* header. 57 type AcceptSpec struct { 58 Value string 59 Q float64 60 } 61 62 // ParseAccept parses Accept* headers. 63 func ParseAccept(header http.Header, key string) (specs []AcceptSpec) { 64 loop: 65 for _, s := range header[key] { 66 for { 67 var spec AcceptSpec 68 spec.Value, s = expectTokenSlash(s) 69 if spec.Value == "" { 70 continue loop 71 } 72 spec.Q = 1.0 73 s = skipSpace(s) 74 if strings.HasPrefix(s, ";") { 75 s = skipSpace(s[1:]) 76 if !strings.HasPrefix(s, "q=") { 77 continue loop 78 } 79 spec.Q, s = expectQuality(s[2:]) 80 if spec.Q < 0.0 { 81 continue loop 82 } 83 } 84 specs = append(specs, spec) 85 s = skipSpace(s) 86 if !strings.HasPrefix(s, ",") { 87 continue loop 88 } 89 s = skipSpace(s[1:]) 90 } 91 } 92 return 93 } 94 95 func skipSpace(s string) (rest string) { 96 i := 0 97 for ; i < len(s); i++ { 98 if octetTypes[s[i]]&isSpace == 0 { 99 break 100 } 101 } 102 return s[i:] 103 } 104 105 func expectTokenSlash(s string) (token, rest string) { 106 i := 0 107 for ; i < len(s); i++ { 108 b := s[i] 109 if (octetTypes[b]&isToken == 0) && b != '/' { 110 break 111 } 112 } 113 return s[:i], s[i:] 114 } 115 116 func expectQuality(s string) (q float64, rest string) { 117 switch { 118 case len(s) == 0: 119 return -1, "" 120 case s[0] == '0': 121 q = 0 122 case s[0] == '1': 123 q = 1 124 default: 125 return -1, "" 126 } 127 s = s[1:] 128 if !strings.HasPrefix(s, ".") { 129 return q, s 130 } 131 s = s[1:] 132 i := 0 133 n := 0 134 d := 1 135 for ; i < len(s); i++ { 136 b := s[i] 137 if b < '0' || b > '9' { 138 break 139 } 140 n = n*10 + int(b) - '0' 141 d *= 10 142 } 143 return q + float64(n)/float64(d), s[i:] 144 }