param.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package param
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "strings"
  7. )
  8. // Param is a key/value header parameter
  9. type Param struct {
  10. Key string
  11. Value string
  12. Quote bool
  13. }
  14. // String returns the formatted parameter
  15. func (p Param) String() string {
  16. if p.Quote {
  17. return fmt.Sprintf("%s=%q", p.Key, p.Value)
  18. }
  19. return fmt.Sprintf("%s=%s", p.Key, p.Value)
  20. }
  21. // Format formats the parameters to be included in the header
  22. func Format(pp ...Param) string {
  23. var b strings.Builder
  24. for i, p := range pp {
  25. if i > 0 {
  26. b.WriteString(", ")
  27. }
  28. b.WriteString(p.String())
  29. }
  30. return b.String()
  31. }
  32. // Parse parses the header parameters
  33. func Parse(s string) ([]Param, error) {
  34. var pp []Param
  35. br := bufio.NewReader(strings.NewReader(s))
  36. for i := 0; true; i++ {
  37. // skip whitespace
  38. if err := skipWhite(br); err != nil {
  39. return nil, err
  40. }
  41. // see if there's more to read
  42. if _, err := br.Peek(1); err == io.EOF {
  43. break
  44. }
  45. // read key/value pair
  46. p, err := parseParam(br, i == 0)
  47. if err != nil {
  48. return nil, fmt.Errorf("param: %w", err)
  49. }
  50. pp = append(pp, p)
  51. }
  52. return pp, nil
  53. }
  54. func parseIdent(br *bufio.Reader) (string, error) {
  55. var ident []byte
  56. for {
  57. b, err := br.ReadByte()
  58. if err == io.EOF {
  59. break
  60. }
  61. if err != nil {
  62. return "", err
  63. }
  64. if !(('a' <= b && b <= 'z') || ('A' <= b && b <= 'Z') || '0' <= b && b <= '9' || b == '-') {
  65. if err := br.UnreadByte(); err != nil {
  66. return "", err
  67. }
  68. break
  69. }
  70. ident = append(ident, b)
  71. }
  72. return string(ident), nil
  73. }
  74. func parseByte(br *bufio.Reader, expect byte) error {
  75. b, err := br.ReadByte()
  76. if err != nil {
  77. if err == io.EOF {
  78. return fmt.Errorf("expected '%c', got EOF", expect)
  79. }
  80. return err
  81. }
  82. if b != expect {
  83. return fmt.Errorf("expected '%c', got '%c'", expect, b)
  84. }
  85. return nil
  86. }
  87. func parseString(br *bufio.Reader) (string, error) {
  88. var s []rune
  89. // read the open quote
  90. if err := parseByte(br, '"'); err != nil {
  91. return "", err
  92. }
  93. // read the string
  94. var escaped bool
  95. for {
  96. r, _, err := br.ReadRune()
  97. if err != nil {
  98. return "", err
  99. }
  100. if escaped {
  101. s = append(s, r)
  102. escaped = false
  103. continue
  104. }
  105. if r == '\\' {
  106. escaped = true
  107. continue
  108. }
  109. // closing quote
  110. if r == '"' {
  111. break
  112. }
  113. s = append(s, r)
  114. }
  115. return string(s), nil
  116. }
  117. func skipWhite(br *bufio.Reader) error {
  118. for {
  119. b, err := br.ReadByte()
  120. if err != nil {
  121. if err == io.EOF {
  122. return nil
  123. }
  124. return err
  125. }
  126. if b != ' ' {
  127. return br.UnreadByte()
  128. }
  129. }
  130. }
  131. func parseParam(br *bufio.Reader, first bool) (Param, error) {
  132. // skip whitespace
  133. if err := skipWhite(br); err != nil {
  134. return Param{}, err
  135. }
  136. if !first {
  137. // read the comma separator
  138. if err := parseByte(br, ','); err != nil {
  139. return Param{}, err
  140. }
  141. // skip whitespace
  142. if err := skipWhite(br); err != nil {
  143. return Param{}, err
  144. }
  145. }
  146. // read the key
  147. key, err := parseIdent(br)
  148. if err != nil {
  149. return Param{}, err
  150. }
  151. // skip whitespace
  152. if err := skipWhite(br); err != nil {
  153. return Param{}, err
  154. }
  155. // read the equals sign
  156. if err := parseByte(br, '='); err != nil {
  157. return Param{}, err
  158. }
  159. // skip whitespace
  160. if err := skipWhite(br); err != nil {
  161. return Param{}, err
  162. }
  163. // read the value
  164. var value string
  165. var quote bool
  166. if b, _ := br.Peek(1); len(b) == 1 && b[0] == '"' {
  167. quote = true
  168. value, err = parseString(br)
  169. } else {
  170. value, err = parseIdent(br)
  171. }
  172. if err != nil {
  173. return Param{}, err
  174. }
  175. return Param{Key: key, Value: value, Quote: quote}, nil
  176. }