reader_test.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package textproto
  5. import (
  6. "bufio"
  7. "bytes"
  8. "io"
  9. "reflect"
  10. "strings"
  11. "testing"
  12. )
  13. type canonicalHeaderKeyTest struct {
  14. in, out string
  15. }
  16. var canonicalHeaderKeyTests = []canonicalHeaderKeyTest{
  17. {"a-b-c", "A-B-C"},
  18. {"a-1-c", "A-1-C"},
  19. {"User-Agent", "User-Agent"},
  20. {"uSER-aGENT", "User-Agent"},
  21. {"user-agent", "User-Agent"},
  22. {"USER-AGENT", "User-Agent"},
  23. {"üser-agenT", "üser-Agent"}, // non-ASCII unchanged
  24. // This caused a panic due to mishandling of a space:
  25. {"C Ontent-Transfer-Encoding", "C-Ontent-Transfer-Encoding"},
  26. {"foo bar", "Foo-Bar"},
  27. }
  28. func TestCanonicalMIMEHeaderKey(t *testing.T) {
  29. for _, tt := range canonicalHeaderKeyTests {
  30. if s := CanonicalMIMEHeaderKey(tt.in); s != tt.out {
  31. t.Errorf("CanonicalMIMEHeaderKey(%q) = %q, want %q", tt.in, s, tt.out)
  32. }
  33. }
  34. }
  35. func reader(s string) *Reader {
  36. return NewReader(bufio.NewReader(strings.NewReader(s)))
  37. }
  38. func TestReadLine(t *testing.T) {
  39. r := reader("line1\nline2\n")
  40. s, err := r.ReadLine()
  41. if s != "line1" || err != nil {
  42. t.Fatalf("Line 1: %s, %v", s, err)
  43. }
  44. s, err = r.ReadLine()
  45. if s != "line2" || err != nil {
  46. t.Fatalf("Line 2: %s, %v", s, err)
  47. }
  48. s, err = r.ReadLine()
  49. if s != "" || err != io.EOF {
  50. t.Fatalf("EOF: %s, %v", s, err)
  51. }
  52. }
  53. func TestReadContinuedLine(t *testing.T) {
  54. r := reader("line1\nline\n 2\nline3\n")
  55. s, err := r.ReadContinuedLine()
  56. if s != "line1" || err != nil {
  57. t.Fatalf("Line 1: %s, %v", s, err)
  58. }
  59. s, err = r.ReadContinuedLine()
  60. if s != "line 2" || err != nil {
  61. t.Fatalf("Line 2: %s, %v", s, err)
  62. }
  63. s, err = r.ReadContinuedLine()
  64. if s != "line3" || err != nil {
  65. t.Fatalf("Line 3: %s, %v", s, err)
  66. }
  67. s, err = r.ReadContinuedLine()
  68. if s != "" || err != io.EOF {
  69. t.Fatalf("EOF: %s, %v", s, err)
  70. }
  71. }
  72. func TestReadCodeLine(t *testing.T) {
  73. r := reader("123 hi\n234 bye\n345 no way\n")
  74. code, msg, err := r.ReadCodeLine(0)
  75. if code != 123 || msg != "hi" || err != nil {
  76. t.Fatalf("Line 1: %d, %s, %v", code, msg, err)
  77. }
  78. code, msg, err = r.ReadCodeLine(23)
  79. if code != 234 || msg != "bye" || err != nil {
  80. t.Fatalf("Line 2: %d, %s, %v", code, msg, err)
  81. }
  82. code, msg, err = r.ReadCodeLine(346)
  83. if code != 345 || msg != "no way" || err == nil {
  84. t.Fatalf("Line 3: %d, %s, %v", code, msg, err)
  85. }
  86. if e, ok := err.(*Error); !ok || e.Code != code || e.Msg != msg {
  87. t.Fatalf("Line 3: wrong error %v\n", err)
  88. }
  89. code, msg, err = r.ReadCodeLine(1)
  90. if code != 0 || msg != "" || err != io.EOF {
  91. t.Fatalf("EOF: %d, %s, %v", code, msg, err)
  92. }
  93. }
  94. func TestReadDotLines(t *testing.T) {
  95. r := reader("dotlines\r\n.foo\r\n..bar\n...baz\nquux\r\n\r\n.\r\nanother\n")
  96. s, err := r.ReadDotLines()
  97. want := []string{"dotlines", "foo", ".bar", "..baz", "quux", ""}
  98. if !reflect.DeepEqual(s, want) || err != nil {
  99. t.Fatalf("ReadDotLines: %v, %v", s, err)
  100. }
  101. s, err = r.ReadDotLines()
  102. want = []string{"another"}
  103. if !reflect.DeepEqual(s, want) || err != io.ErrUnexpectedEOF {
  104. t.Fatalf("ReadDotLines2: %v, %v", s, err)
  105. }
  106. }
  107. func TestReadDotBytes(t *testing.T) {
  108. r := reader("dotlines\r\n.foo\r\n..bar\n...baz\nquux\r\n\r\n.\r\nanot.her\r\n")
  109. b, err := r.ReadDotBytes()
  110. want := []byte("dotlines\nfoo\n.bar\n..baz\nquux\n\n")
  111. if !reflect.DeepEqual(b, want) || err != nil {
  112. t.Fatalf("ReadDotBytes: %q, %v", b, err)
  113. }
  114. b, err = r.ReadDotBytes()
  115. want = []byte("anot.her\n")
  116. if !reflect.DeepEqual(b, want) || err != io.ErrUnexpectedEOF {
  117. t.Fatalf("ReadDotBytes2: %q, %v", b, err)
  118. }
  119. }
  120. func TestReadMIMEHeader(t *testing.T) {
  121. r := reader("my-key: Value 1 \r\nLong-key: Even \n Longer Value\r\nmy-Key: Value 2\r\n\n")
  122. m, err := r.ReadMIMEHeader()
  123. want := MIMEHeader{
  124. "My-Key": {"Value 1", "Value 2"},
  125. "Long-Key": {"Even Longer Value"},
  126. }
  127. if !reflect.DeepEqual(m, want) || err != nil {
  128. t.Fatalf("ReadMIMEHeader: %v, %v; want %v", m, err, want)
  129. }
  130. }
  131. func TestReadMIMEHeaderSingle(t *testing.T) {
  132. r := reader("Foo: bar\n\n")
  133. m, err := r.ReadMIMEHeader()
  134. want := MIMEHeader{"Foo": {"bar"}}
  135. if !reflect.DeepEqual(m, want) || err != nil {
  136. t.Fatalf("ReadMIMEHeader: %v, %v; want %v", m, err, want)
  137. }
  138. }
  139. func TestLargeReadMIMEHeader(t *testing.T) {
  140. data := make([]byte, 16*1024)
  141. for i := 0; i < len(data); i++ {
  142. data[i] = 'x'
  143. }
  144. sdata := string(data)
  145. r := reader("Cookie: " + sdata + "\r\n\n")
  146. m, err := r.ReadMIMEHeader()
  147. if err != nil {
  148. t.Fatalf("ReadMIMEHeader: %v", err)
  149. }
  150. cookie := m.Get("Cookie")
  151. if cookie != sdata {
  152. t.Fatalf("ReadMIMEHeader: %v bytes, want %v bytes", len(cookie), len(sdata))
  153. }
  154. }
  155. // Test that we read slightly-bogus MIME headers seen in the wild,
  156. // with spaces before colons, and spaces in keys.
  157. func TestReadMIMEHeaderNonCompliant(t *testing.T) {
  158. // Invalid HTTP response header as sent by an Axis security
  159. // camera: (this is handled by IE, Firefox, Chrome, curl, etc.)
  160. r := reader("Foo: bar\r\n" +
  161. "Content-Language: en\r\n" +
  162. "SID : 0\r\n" +
  163. "Audio Mode : None\r\n" +
  164. "Privilege : 127\r\n\r\n")
  165. m, err := r.ReadMIMEHeader()
  166. want := MIMEHeader{
  167. "Foo": {"bar"},
  168. "Content-Language": {"en"},
  169. "Sid": {"0"},
  170. "Audio-Mode": {"None"},
  171. "Privilege": {"127"},
  172. }
  173. if !reflect.DeepEqual(m, want) || err != nil {
  174. t.Fatalf("ReadMIMEHeader =\n%v, %v; want:\n%v", m, err, want)
  175. }
  176. }
  177. type readResponseTest struct {
  178. in string
  179. inCode int
  180. wantCode int
  181. wantMsg string
  182. }
  183. var readResponseTests = []readResponseTest{
  184. {"230-Anonymous access granted, restrictions apply\n" +
  185. "Read the file README.txt,\n" +
  186. "230 please",
  187. 23,
  188. 230,
  189. "Anonymous access granted, restrictions apply\nRead the file README.txt,\n please",
  190. },
  191. {"230 Anonymous access granted, restrictions apply\n",
  192. 23,
  193. 230,
  194. "Anonymous access granted, restrictions apply",
  195. },
  196. {"400-A\n400-B\n400 C",
  197. 4,
  198. 400,
  199. "A\nB\nC",
  200. },
  201. {"400-A\r\n400-B\r\n400 C\r\n",
  202. 4,
  203. 400,
  204. "A\nB\nC",
  205. },
  206. }
  207. // See http://www.ietf.org/rfc/rfc959.txt page 36.
  208. func TestRFC959Lines(t *testing.T) {
  209. for i, tt := range readResponseTests {
  210. r := reader(tt.in + "\nFOLLOWING DATA")
  211. code, msg, err := r.ReadResponse(tt.inCode)
  212. if err != nil {
  213. t.Errorf("#%d: ReadResponse: %v", i, err)
  214. continue
  215. }
  216. if code != tt.wantCode {
  217. t.Errorf("#%d: code=%d, want %d", i, code, tt.wantCode)
  218. }
  219. if msg != tt.wantMsg {
  220. t.Errorf("#%d: msg=%q, want %q", i, msg, tt.wantMsg)
  221. }
  222. }
  223. }
  224. func TestCommonHeaders(t *testing.T) {
  225. for h := range commonHeader {
  226. if h != CanonicalMIMEHeaderKey(h) {
  227. t.Errorf("Non-canonical header %q in commonHeader", h)
  228. }
  229. }
  230. t.Skip("gccgo escape analysis")
  231. b := []byte("content-Length")
  232. want := "Content-Length"
  233. n := testing.AllocsPerRun(200, func() {
  234. if x := canonicalMIMEHeaderKey(b); x != want {
  235. t.Fatalf("canonicalMIMEHeaderKey(%q) = %q; want %q", b, x, want)
  236. }
  237. })
  238. if n > 0 {
  239. t.Errorf("canonicalMIMEHeaderKey allocs = %v; want 0", n)
  240. }
  241. }
  242. var clientHeaders = strings.Replace(`Host: golang.org
  243. Connection: keep-alive
  244. Cache-Control: max-age=0
  245. Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
  246. User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3
  247. Accept-Encoding: gzip,deflate,sdch
  248. Accept-Language: en-US,en;q=0.8,fr-CH;q=0.6
  249. Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
  250. COOKIE: __utma=000000000.0000000000.0000000000.0000000000.0000000000.00; __utmb=000000000.0.00.0000000000; __utmc=000000000; __utmz=000000000.0000000000.00.0.utmcsr=code.google.com|utmccn=(referral)|utmcmd=referral|utmcct=/p/go/issues/detail
  251. Non-Interned: test
  252. `, "\n", "\r\n", -1)
  253. var serverHeaders = strings.Replace(`Content-Type: text/html; charset=utf-8
  254. Content-Encoding: gzip
  255. Date: Thu, 27 Sep 2012 09:03:33 GMT
  256. Server: Google Frontend
  257. Cache-Control: private
  258. Content-Length: 2298
  259. VIA: 1.1 proxy.example.com:80 (XXX/n.n.n-nnn)
  260. Connection: Close
  261. Non-Interned: test
  262. `, "\n", "\r\n", -1)
  263. func BenchmarkReadMIMEHeader(b *testing.B) {
  264. b.ReportAllocs()
  265. var buf bytes.Buffer
  266. br := bufio.NewReader(&buf)
  267. r := NewReader(br)
  268. for i := 0; i < b.N; i++ {
  269. var want int
  270. var find string
  271. if (i & 1) == 1 {
  272. buf.WriteString(clientHeaders)
  273. want = 10
  274. find = "Cookie"
  275. } else {
  276. buf.WriteString(serverHeaders)
  277. want = 9
  278. find = "Via"
  279. }
  280. h, err := r.ReadMIMEHeader()
  281. if err != nil {
  282. b.Fatal(err)
  283. }
  284. if len(h) != want {
  285. b.Fatalf("wrong number of headers: got %d, want %d", len(h), want)
  286. }
  287. if _, ok := h[find]; !ok {
  288. b.Fatalf("did not find key %s", find)
  289. }
  290. }
  291. }
  292. func BenchmarkUncommon(b *testing.B) {
  293. b.ReportAllocs()
  294. var buf bytes.Buffer
  295. br := bufio.NewReader(&buf)
  296. r := NewReader(br)
  297. for i := 0; i < b.N; i++ {
  298. buf.WriteString("uncommon-header-for-benchmark: foo\r\n\r\n")
  299. h, err := r.ReadMIMEHeader()
  300. if err != nil {
  301. b.Fatal(err)
  302. }
  303. if _, ok := h["Uncommon-Header-For-Benchmark"]; !ok {
  304. b.Fatal("Missing result header.")
  305. }
  306. }
  307. }