cookie.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package httphead
  2. import (
  3. "bytes"
  4. )
  5. // ScanCookie scans cookie pairs from data using DefaultCookieScanner.Scan()
  6. // method.
  7. func ScanCookie(data []byte, it func(key, value []byte) bool) bool {
  8. return DefaultCookieScanner.Scan(data, it)
  9. }
  10. // DefaultCookieScanner is a CookieScanner which is used by ScanCookie().
  11. // Note that it is intended to have the same behavior as http.Request.Cookies()
  12. // has.
  13. var DefaultCookieScanner = CookieScanner{}
  14. // CookieScanner contains options for scanning cookie pairs.
  15. // See https://tools.ietf.org/html/rfc6265#section-4.1.1
  16. type CookieScanner struct {
  17. // DisableNameValidation disables name validation of a cookie. If false,
  18. // only RFC2616 "tokens" are accepted.
  19. DisableNameValidation bool
  20. // DisableValueValidation disables value validation of a cookie. If false,
  21. // only RFC6265 "cookie-octet" characters are accepted.
  22. //
  23. // Note that Strict option also affects validation of a value.
  24. //
  25. // If Strict is false, then scanner begins to allow space and comma
  26. // characters inside the value for better compatibility with non standard
  27. // cookies implementations.
  28. DisableValueValidation bool
  29. // BreakOnPairError sets scanner to immediately return after first pair syntax
  30. // validation error.
  31. // If false, scanner will try to skip invalid pair bytes and go ahead.
  32. BreakOnPairError bool
  33. // Strict enables strict RFC6265 mode scanning. It affects name and value
  34. // validation, as also some other rules.
  35. // If false, it is intended to bring the same behavior as
  36. // http.Request.Cookies().
  37. Strict bool
  38. }
  39. // Scan maps data to name and value pairs. Usually data represents value of the
  40. // Cookie header.
  41. func (c CookieScanner) Scan(data []byte, it func(name, value []byte) bool) bool {
  42. lexer := &Scanner{data: data}
  43. const (
  44. statePair = iota
  45. stateBefore
  46. )
  47. state := statePair
  48. for lexer.Buffered() > 0 {
  49. switch state {
  50. case stateBefore:
  51. // Pairs separated by ";" and space, according to the RFC6265:
  52. // cookie-pair *( ";" SP cookie-pair )
  53. //
  54. // Cookie pairs MUST be separated by (";" SP). So our only option
  55. // here is to fail as syntax error.
  56. a, b := lexer.Peek2()
  57. if a != ';' {
  58. return false
  59. }
  60. state = statePair
  61. advance := 1
  62. if b == ' ' {
  63. advance++
  64. } else if c.Strict {
  65. return false
  66. }
  67. lexer.Advance(advance)
  68. case statePair:
  69. if !lexer.FetchUntil(';') {
  70. return false
  71. }
  72. var value []byte
  73. name := lexer.Bytes()
  74. if i := bytes.IndexByte(name, '='); i != -1 {
  75. value = name[i+1:]
  76. name = name[:i]
  77. } else if c.Strict {
  78. if !c.BreakOnPairError {
  79. goto nextPair
  80. }
  81. return false
  82. }
  83. if !c.Strict {
  84. trimLeft(name)
  85. }
  86. if !c.DisableNameValidation && !ValidCookieName(name) {
  87. if !c.BreakOnPairError {
  88. goto nextPair
  89. }
  90. return false
  91. }
  92. if !c.Strict {
  93. value = trimRight(value)
  94. }
  95. value = stripQuotes(value)
  96. if !c.DisableValueValidation && !ValidCookieValue(value, c.Strict) {
  97. if !c.BreakOnPairError {
  98. goto nextPair
  99. }
  100. return false
  101. }
  102. if !it(name, value) {
  103. return true
  104. }
  105. nextPair:
  106. state = stateBefore
  107. }
  108. }
  109. return true
  110. }
  111. // ValidCookieValue reports whether given value is a valid RFC6265
  112. // "cookie-octet" bytes.
  113. //
  114. // cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
  115. // ; US-ASCII characters excluding CTLs,
  116. // ; whitespace DQUOTE, comma, semicolon,
  117. // ; and backslash
  118. //
  119. // Note that the false strict parameter disables errors on space 0x20 and comma
  120. // 0x2c. This could be useful to bring some compatibility with non-compliant
  121. // clients/servers in the real world.
  122. // It acts the same as standard library cookie parser if strict is false.
  123. func ValidCookieValue(value []byte, strict bool) bool {
  124. if len(value) == 0 {
  125. return true
  126. }
  127. for _, c := range value {
  128. switch c {
  129. case '"', ';', '\\':
  130. return false
  131. case ',', ' ':
  132. if strict {
  133. return false
  134. }
  135. default:
  136. if c <= 0x20 {
  137. return false
  138. }
  139. if c >= 0x7f {
  140. return false
  141. }
  142. }
  143. }
  144. return true
  145. }
  146. // ValidCookieName reports wheter given bytes is a valid RFC2616 "token" bytes.
  147. func ValidCookieName(name []byte) bool {
  148. for _, c := range name {
  149. if !OctetTypes[c].IsToken() {
  150. return false
  151. }
  152. }
  153. return true
  154. }
  155. func stripQuotes(bts []byte) []byte {
  156. if last := len(bts) - 1; last > 0 && bts[0] == '"' && bts[last] == '"' {
  157. return bts[1:last]
  158. }
  159. return bts
  160. }
  161. func trimLeft(p []byte) []byte {
  162. var i int
  163. for i < len(p) && OctetTypes[p[i]].IsSpace() {
  164. i++
  165. }
  166. return p[i:]
  167. }
  168. func trimRight(p []byte) []byte {
  169. j := len(p)
  170. for j > 0 && OctetTypes[p[j-1]].IsSpace() {
  171. j--
  172. }
  173. return p[:j]
  174. }