writer.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package httphead
  2. import "io"
  3. var (
  4. comma = []byte{','}
  5. equality = []byte{'='}
  6. semicolon = []byte{';'}
  7. quote = []byte{'"'}
  8. escape = []byte{'\\'}
  9. )
  10. // WriteOptions write options list to the dest.
  11. // It uses the same form as {Scan,Parse}Options functions:
  12. // values = 1#value
  13. // value = token *( ";" param )
  14. // param = token [ "=" (token | quoted-string) ]
  15. //
  16. // It wraps valuse into the quoted-string sequence if it contains any
  17. // non-token characters.
  18. func WriteOptions(dest io.Writer, options []Option) (n int, err error) {
  19. w := writer{w: dest}
  20. for i, opt := range options {
  21. if i > 0 {
  22. w.write(comma)
  23. }
  24. writeTokenSanitized(&w, opt.Name)
  25. for _, p := range opt.Parameters.data() {
  26. w.write(semicolon)
  27. writeTokenSanitized(&w, p.key)
  28. if len(p.value) != 0 {
  29. w.write(equality)
  30. writeTokenSanitized(&w, p.value)
  31. }
  32. }
  33. }
  34. return w.result()
  35. }
  36. // writeTokenSanitized writes token as is or as quouted string if it contains
  37. // non-token characters.
  38. //
  39. // Note that is is not expects LWS sequnces be in s, cause LWS is used only as
  40. // header field continuation:
  41. // "A CRLF is allowed in the definition of TEXT only as part of a header field
  42. // continuation. It is expected that the folding LWS will be replaced with a
  43. // single SP before interpretation of the TEXT value."
  44. // See https://tools.ietf.org/html/rfc2616#section-2
  45. //
  46. // That is we sanitizing s for writing, so there could not be any header field
  47. // continuation.
  48. // That is any CRLF will be escaped as any other control characters not allowd in TEXT.
  49. func writeTokenSanitized(bw *writer, bts []byte) {
  50. var qt bool
  51. var pos int
  52. for i := 0; i < len(bts); i++ {
  53. c := bts[i]
  54. if !OctetTypes[c].IsToken() && !qt {
  55. qt = true
  56. bw.write(quote)
  57. }
  58. if OctetTypes[c].IsControl() || c == '"' {
  59. if !qt {
  60. qt = true
  61. bw.write(quote)
  62. }
  63. bw.write(bts[pos:i])
  64. bw.write(escape)
  65. bw.write(bts[i : i+1])
  66. pos = i + 1
  67. }
  68. }
  69. if !qt {
  70. bw.write(bts)
  71. } else {
  72. bw.write(bts[pos:])
  73. bw.write(quote)
  74. }
  75. }
  76. type writer struct {
  77. w io.Writer
  78. n int
  79. err error
  80. }
  81. func (w *writer) write(p []byte) {
  82. if w.err != nil {
  83. return
  84. }
  85. var n int
  86. n, w.err = w.w.Write(p)
  87. w.n += n
  88. return
  89. }
  90. func (w *writer) result() (int, error) {
  91. return w.n, w.err
  92. }