PageRenderTime 53ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/third_party/gofrontend/libgo/go/net/http/response_test.go

http://github.com/axw/llgo
Go | 725 lines | 622 code | 73 blank | 30 comment | 74 complexity | cd24a99ef919f3f7d4c7f10d7fbccee5 MD5 | raw file
Possible License(s): BSD-3-Clause, MIT
  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 http
  5. import (
  6. "bufio"
  7. "bytes"
  8. "compress/gzip"
  9. "crypto/rand"
  10. "fmt"
  11. "io"
  12. "io/ioutil"
  13. "net/http/internal"
  14. "net/url"
  15. "reflect"
  16. "regexp"
  17. "strings"
  18. "testing"
  19. )
  20. type respTest struct {
  21. Raw string
  22. Resp Response
  23. Body string
  24. }
  25. func dummyReq(method string) *Request {
  26. return &Request{Method: method}
  27. }
  28. func dummyReq11(method string) *Request {
  29. return &Request{Method: method, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1}
  30. }
  31. var respTests = []respTest{
  32. // Unchunked response without Content-Length.
  33. {
  34. "HTTP/1.0 200 OK\r\n" +
  35. "Connection: close\r\n" +
  36. "\r\n" +
  37. "Body here\n",
  38. Response{
  39. Status: "200 OK",
  40. StatusCode: 200,
  41. Proto: "HTTP/1.0",
  42. ProtoMajor: 1,
  43. ProtoMinor: 0,
  44. Request: dummyReq("GET"),
  45. Header: Header{
  46. "Connection": {"close"}, // TODO(rsc): Delete?
  47. },
  48. Close: true,
  49. ContentLength: -1,
  50. },
  51. "Body here\n",
  52. },
  53. // Unchunked HTTP/1.1 response without Content-Length or
  54. // Connection headers.
  55. {
  56. "HTTP/1.1 200 OK\r\n" +
  57. "\r\n" +
  58. "Body here\n",
  59. Response{
  60. Status: "200 OK",
  61. StatusCode: 200,
  62. Proto: "HTTP/1.1",
  63. ProtoMajor: 1,
  64. ProtoMinor: 1,
  65. Header: Header{},
  66. Request: dummyReq("GET"),
  67. Close: true,
  68. ContentLength: -1,
  69. },
  70. "Body here\n",
  71. },
  72. // Unchunked HTTP/1.1 204 response without Content-Length.
  73. {
  74. "HTTP/1.1 204 No Content\r\n" +
  75. "\r\n" +
  76. "Body should not be read!\n",
  77. Response{
  78. Status: "204 No Content",
  79. StatusCode: 204,
  80. Proto: "HTTP/1.1",
  81. ProtoMajor: 1,
  82. ProtoMinor: 1,
  83. Header: Header{},
  84. Request: dummyReq("GET"),
  85. Close: false,
  86. ContentLength: 0,
  87. },
  88. "",
  89. },
  90. // Unchunked response with Content-Length.
  91. {
  92. "HTTP/1.0 200 OK\r\n" +
  93. "Content-Length: 10\r\n" +
  94. "Connection: close\r\n" +
  95. "\r\n" +
  96. "Body here\n",
  97. Response{
  98. Status: "200 OK",
  99. StatusCode: 200,
  100. Proto: "HTTP/1.0",
  101. ProtoMajor: 1,
  102. ProtoMinor: 0,
  103. Request: dummyReq("GET"),
  104. Header: Header{
  105. "Connection": {"close"},
  106. "Content-Length": {"10"},
  107. },
  108. Close: true,
  109. ContentLength: 10,
  110. },
  111. "Body here\n",
  112. },
  113. // Chunked response without Content-Length.
  114. {
  115. "HTTP/1.1 200 OK\r\n" +
  116. "Transfer-Encoding: chunked\r\n" +
  117. "\r\n" +
  118. "0a\r\n" +
  119. "Body here\n\r\n" +
  120. "09\r\n" +
  121. "continued\r\n" +
  122. "0\r\n" +
  123. "\r\n",
  124. Response{
  125. Status: "200 OK",
  126. StatusCode: 200,
  127. Proto: "HTTP/1.1",
  128. ProtoMajor: 1,
  129. ProtoMinor: 1,
  130. Request: dummyReq("GET"),
  131. Header: Header{},
  132. Close: false,
  133. ContentLength: -1,
  134. TransferEncoding: []string{"chunked"},
  135. },
  136. "Body here\ncontinued",
  137. },
  138. // Chunked response with Content-Length.
  139. {
  140. "HTTP/1.1 200 OK\r\n" +
  141. "Transfer-Encoding: chunked\r\n" +
  142. "Content-Length: 10\r\n" +
  143. "\r\n" +
  144. "0a\r\n" +
  145. "Body here\n\r\n" +
  146. "0\r\n" +
  147. "\r\n",
  148. Response{
  149. Status: "200 OK",
  150. StatusCode: 200,
  151. Proto: "HTTP/1.1",
  152. ProtoMajor: 1,
  153. ProtoMinor: 1,
  154. Request: dummyReq("GET"),
  155. Header: Header{},
  156. Close: false,
  157. ContentLength: -1,
  158. TransferEncoding: []string{"chunked"},
  159. },
  160. "Body here\n",
  161. },
  162. // Chunked response in response to a HEAD request
  163. {
  164. "HTTP/1.1 200 OK\r\n" +
  165. "Transfer-Encoding: chunked\r\n" +
  166. "\r\n",
  167. Response{
  168. Status: "200 OK",
  169. StatusCode: 200,
  170. Proto: "HTTP/1.1",
  171. ProtoMajor: 1,
  172. ProtoMinor: 1,
  173. Request: dummyReq("HEAD"),
  174. Header: Header{},
  175. TransferEncoding: []string{"chunked"},
  176. Close: false,
  177. ContentLength: -1,
  178. },
  179. "",
  180. },
  181. // Content-Length in response to a HEAD request
  182. {
  183. "HTTP/1.0 200 OK\r\n" +
  184. "Content-Length: 256\r\n" +
  185. "\r\n",
  186. Response{
  187. Status: "200 OK",
  188. StatusCode: 200,
  189. Proto: "HTTP/1.0",
  190. ProtoMajor: 1,
  191. ProtoMinor: 0,
  192. Request: dummyReq("HEAD"),
  193. Header: Header{"Content-Length": {"256"}},
  194. TransferEncoding: nil,
  195. Close: true,
  196. ContentLength: 256,
  197. },
  198. "",
  199. },
  200. // Content-Length in response to a HEAD request with HTTP/1.1
  201. {
  202. "HTTP/1.1 200 OK\r\n" +
  203. "Content-Length: 256\r\n" +
  204. "\r\n",
  205. Response{
  206. Status: "200 OK",
  207. StatusCode: 200,
  208. Proto: "HTTP/1.1",
  209. ProtoMajor: 1,
  210. ProtoMinor: 1,
  211. Request: dummyReq("HEAD"),
  212. Header: Header{"Content-Length": {"256"}},
  213. TransferEncoding: nil,
  214. Close: false,
  215. ContentLength: 256,
  216. },
  217. "",
  218. },
  219. // No Content-Length or Chunked in response to a HEAD request
  220. {
  221. "HTTP/1.0 200 OK\r\n" +
  222. "\r\n",
  223. Response{
  224. Status: "200 OK",
  225. StatusCode: 200,
  226. Proto: "HTTP/1.0",
  227. ProtoMajor: 1,
  228. ProtoMinor: 0,
  229. Request: dummyReq("HEAD"),
  230. Header: Header{},
  231. TransferEncoding: nil,
  232. Close: true,
  233. ContentLength: -1,
  234. },
  235. "",
  236. },
  237. // explicit Content-Length of 0.
  238. {
  239. "HTTP/1.1 200 OK\r\n" +
  240. "Content-Length: 0\r\n" +
  241. "\r\n",
  242. Response{
  243. Status: "200 OK",
  244. StatusCode: 200,
  245. Proto: "HTTP/1.1",
  246. ProtoMajor: 1,
  247. ProtoMinor: 1,
  248. Request: dummyReq("GET"),
  249. Header: Header{
  250. "Content-Length": {"0"},
  251. },
  252. Close: false,
  253. ContentLength: 0,
  254. },
  255. "",
  256. },
  257. // Status line without a Reason-Phrase, but trailing space.
  258. // (permitted by RFC 2616)
  259. {
  260. "HTTP/1.0 303 \r\n\r\n",
  261. Response{
  262. Status: "303 ",
  263. StatusCode: 303,
  264. Proto: "HTTP/1.0",
  265. ProtoMajor: 1,
  266. ProtoMinor: 0,
  267. Request: dummyReq("GET"),
  268. Header: Header{},
  269. Close: true,
  270. ContentLength: -1,
  271. },
  272. "",
  273. },
  274. // Status line without a Reason-Phrase, and no trailing space.
  275. // (not permitted by RFC 2616, but we'll accept it anyway)
  276. {
  277. "HTTP/1.0 303\r\n\r\n",
  278. Response{
  279. Status: "303 ",
  280. StatusCode: 303,
  281. Proto: "HTTP/1.0",
  282. ProtoMajor: 1,
  283. ProtoMinor: 0,
  284. Request: dummyReq("GET"),
  285. Header: Header{},
  286. Close: true,
  287. ContentLength: -1,
  288. },
  289. "",
  290. },
  291. // golang.org/issue/4767: don't special-case multipart/byteranges responses
  292. {
  293. `HTTP/1.1 206 Partial Content
  294. Connection: close
  295. Content-Type: multipart/byteranges; boundary=18a75608c8f47cef
  296. some body`,
  297. Response{
  298. Status: "206 Partial Content",
  299. StatusCode: 206,
  300. Proto: "HTTP/1.1",
  301. ProtoMajor: 1,
  302. ProtoMinor: 1,
  303. Request: dummyReq("GET"),
  304. Header: Header{
  305. "Content-Type": []string{"multipart/byteranges; boundary=18a75608c8f47cef"},
  306. },
  307. Close: true,
  308. ContentLength: -1,
  309. },
  310. "some body",
  311. },
  312. // Unchunked response without Content-Length, Request is nil
  313. {
  314. "HTTP/1.0 200 OK\r\n" +
  315. "Connection: close\r\n" +
  316. "\r\n" +
  317. "Body here\n",
  318. Response{
  319. Status: "200 OK",
  320. StatusCode: 200,
  321. Proto: "HTTP/1.0",
  322. ProtoMajor: 1,
  323. ProtoMinor: 0,
  324. Header: Header{
  325. "Connection": {"close"}, // TODO(rsc): Delete?
  326. },
  327. Close: true,
  328. ContentLength: -1,
  329. },
  330. "Body here\n",
  331. },
  332. // 206 Partial Content. golang.org/issue/8923
  333. {
  334. "HTTP/1.1 206 Partial Content\r\n" +
  335. "Content-Type: text/plain; charset=utf-8\r\n" +
  336. "Accept-Ranges: bytes\r\n" +
  337. "Content-Range: bytes 0-5/1862\r\n" +
  338. "Content-Length: 6\r\n\r\n" +
  339. "foobar",
  340. Response{
  341. Status: "206 Partial Content",
  342. StatusCode: 206,
  343. Proto: "HTTP/1.1",
  344. ProtoMajor: 1,
  345. ProtoMinor: 1,
  346. Request: dummyReq("GET"),
  347. Header: Header{
  348. "Accept-Ranges": []string{"bytes"},
  349. "Content-Length": []string{"6"},
  350. "Content-Type": []string{"text/plain; charset=utf-8"},
  351. "Content-Range": []string{"bytes 0-5/1862"},
  352. },
  353. ContentLength: 6,
  354. },
  355. "foobar",
  356. },
  357. // Both keep-alive and close, on the same Connection line. (Issue 8840)
  358. {
  359. "HTTP/1.1 200 OK\r\n" +
  360. "Content-Length: 256\r\n" +
  361. "Connection: keep-alive, close\r\n" +
  362. "\r\n",
  363. Response{
  364. Status: "200 OK",
  365. StatusCode: 200,
  366. Proto: "HTTP/1.1",
  367. ProtoMajor: 1,
  368. ProtoMinor: 1,
  369. Request: dummyReq("HEAD"),
  370. Header: Header{
  371. "Content-Length": {"256"},
  372. },
  373. TransferEncoding: nil,
  374. Close: true,
  375. ContentLength: 256,
  376. },
  377. "",
  378. },
  379. // Both keep-alive and close, on different Connection lines. (Issue 8840)
  380. {
  381. "HTTP/1.1 200 OK\r\n" +
  382. "Content-Length: 256\r\n" +
  383. "Connection: keep-alive\r\n" +
  384. "Connection: close\r\n" +
  385. "\r\n",
  386. Response{
  387. Status: "200 OK",
  388. StatusCode: 200,
  389. Proto: "HTTP/1.1",
  390. ProtoMajor: 1,
  391. ProtoMinor: 1,
  392. Request: dummyReq("HEAD"),
  393. Header: Header{
  394. "Content-Length": {"256"},
  395. },
  396. TransferEncoding: nil,
  397. Close: true,
  398. ContentLength: 256,
  399. },
  400. "",
  401. },
  402. }
  403. func TestReadResponse(t *testing.T) {
  404. for i, tt := range respTests {
  405. resp, err := ReadResponse(bufio.NewReader(strings.NewReader(tt.Raw)), tt.Resp.Request)
  406. if err != nil {
  407. t.Errorf("#%d: %v", i, err)
  408. continue
  409. }
  410. rbody := resp.Body
  411. resp.Body = nil
  412. diff(t, fmt.Sprintf("#%d Response", i), resp, &tt.Resp)
  413. var bout bytes.Buffer
  414. if rbody != nil {
  415. _, err = io.Copy(&bout, rbody)
  416. if err != nil {
  417. t.Errorf("#%d: %v", i, err)
  418. continue
  419. }
  420. rbody.Close()
  421. }
  422. body := bout.String()
  423. if body != tt.Body {
  424. t.Errorf("#%d: Body = %q want %q", i, body, tt.Body)
  425. }
  426. }
  427. }
  428. func TestWriteResponse(t *testing.T) {
  429. for i, tt := range respTests {
  430. resp, err := ReadResponse(bufio.NewReader(strings.NewReader(tt.Raw)), tt.Resp.Request)
  431. if err != nil {
  432. t.Errorf("#%d: %v", i, err)
  433. continue
  434. }
  435. err = resp.Write(ioutil.Discard)
  436. if err != nil {
  437. t.Errorf("#%d: %v", i, err)
  438. continue
  439. }
  440. }
  441. }
  442. var readResponseCloseInMiddleTests = []struct {
  443. chunked, compressed bool
  444. }{
  445. {false, false},
  446. {true, false},
  447. {true, true},
  448. }
  449. // TestReadResponseCloseInMiddle tests that closing a body after
  450. // reading only part of its contents advances the read to the end of
  451. // the request, right up until the next request.
  452. func TestReadResponseCloseInMiddle(t *testing.T) {
  453. for _, test := range readResponseCloseInMiddleTests {
  454. fatalf := func(format string, args ...interface{}) {
  455. args = append([]interface{}{test.chunked, test.compressed}, args...)
  456. t.Fatalf("on test chunked=%v, compressed=%v: "+format, args...)
  457. }
  458. checkErr := func(err error, msg string) {
  459. if err == nil {
  460. return
  461. }
  462. fatalf(msg+": %v", err)
  463. }
  464. var buf bytes.Buffer
  465. buf.WriteString("HTTP/1.1 200 OK\r\n")
  466. if test.chunked {
  467. buf.WriteString("Transfer-Encoding: chunked\r\n")
  468. } else {
  469. buf.WriteString("Content-Length: 1000000\r\n")
  470. }
  471. var wr io.Writer = &buf
  472. if test.chunked {
  473. wr = internal.NewChunkedWriter(wr)
  474. }
  475. if test.compressed {
  476. buf.WriteString("Content-Encoding: gzip\r\n")
  477. wr = gzip.NewWriter(wr)
  478. }
  479. buf.WriteString("\r\n")
  480. chunk := bytes.Repeat([]byte{'x'}, 1000)
  481. for i := 0; i < 1000; i++ {
  482. if test.compressed {
  483. // Otherwise this compresses too well.
  484. _, err := io.ReadFull(rand.Reader, chunk)
  485. checkErr(err, "rand.Reader ReadFull")
  486. }
  487. wr.Write(chunk)
  488. }
  489. if test.compressed {
  490. err := wr.(*gzip.Writer).Close()
  491. checkErr(err, "compressor close")
  492. }
  493. if test.chunked {
  494. buf.WriteString("0\r\n\r\n")
  495. }
  496. buf.WriteString("Next Request Here")
  497. bufr := bufio.NewReader(&buf)
  498. resp, err := ReadResponse(bufr, dummyReq("GET"))
  499. checkErr(err, "ReadResponse")
  500. expectedLength := int64(-1)
  501. if !test.chunked {
  502. expectedLength = 1000000
  503. }
  504. if resp.ContentLength != expectedLength {
  505. fatalf("expected response length %d, got %d", expectedLength, resp.ContentLength)
  506. }
  507. if resp.Body == nil {
  508. fatalf("nil body")
  509. }
  510. if test.compressed {
  511. gzReader, err := gzip.NewReader(resp.Body)
  512. checkErr(err, "gzip.NewReader")
  513. resp.Body = &readerAndCloser{gzReader, resp.Body}
  514. }
  515. rbuf := make([]byte, 2500)
  516. n, err := io.ReadFull(resp.Body, rbuf)
  517. checkErr(err, "2500 byte ReadFull")
  518. if n != 2500 {
  519. fatalf("ReadFull only read %d bytes", n)
  520. }
  521. if test.compressed == false && !bytes.Equal(bytes.Repeat([]byte{'x'}, 2500), rbuf) {
  522. fatalf("ReadFull didn't read 2500 'x'; got %q", string(rbuf))
  523. }
  524. resp.Body.Close()
  525. rest, err := ioutil.ReadAll(bufr)
  526. checkErr(err, "ReadAll on remainder")
  527. if e, g := "Next Request Here", string(rest); e != g {
  528. g = regexp.MustCompile(`(xx+)`).ReplaceAllStringFunc(g, func(match string) string {
  529. return fmt.Sprintf("x(repeated x%d)", len(match))
  530. })
  531. fatalf("remainder = %q, expected %q", g, e)
  532. }
  533. }
  534. }
  535. func diff(t *testing.T, prefix string, have, want interface{}) {
  536. hv := reflect.ValueOf(have).Elem()
  537. wv := reflect.ValueOf(want).Elem()
  538. if hv.Type() != wv.Type() {
  539. t.Errorf("%s: type mismatch %v want %v", prefix, hv.Type(), wv.Type())
  540. }
  541. for i := 0; i < hv.NumField(); i++ {
  542. hf := hv.Field(i).Interface()
  543. wf := wv.Field(i).Interface()
  544. if !reflect.DeepEqual(hf, wf) {
  545. t.Errorf("%s: %s = %v want %v", prefix, hv.Type().Field(i).Name, hf, wf)
  546. }
  547. }
  548. }
  549. type responseLocationTest struct {
  550. location string // Response's Location header or ""
  551. requrl string // Response.Request.URL or ""
  552. want string
  553. wantErr error
  554. }
  555. var responseLocationTests = []responseLocationTest{
  556. {"/foo", "http://bar.com/baz", "http://bar.com/foo", nil},
  557. {"http://foo.com/", "http://bar.com/baz", "http://foo.com/", nil},
  558. {"", "http://bar.com/baz", "", ErrNoLocation},
  559. }
  560. func TestLocationResponse(t *testing.T) {
  561. for i, tt := range responseLocationTests {
  562. res := new(Response)
  563. res.Header = make(Header)
  564. res.Header.Set("Location", tt.location)
  565. if tt.requrl != "" {
  566. res.Request = &Request{}
  567. var err error
  568. res.Request.URL, err = url.Parse(tt.requrl)
  569. if err != nil {
  570. t.Fatalf("bad test URL %q: %v", tt.requrl, err)
  571. }
  572. }
  573. got, err := res.Location()
  574. if tt.wantErr != nil {
  575. if err == nil {
  576. t.Errorf("%d. err=nil; want %q", i, tt.wantErr)
  577. continue
  578. }
  579. if g, e := err.Error(), tt.wantErr.Error(); g != e {
  580. t.Errorf("%d. err=%q; want %q", i, g, e)
  581. continue
  582. }
  583. continue
  584. }
  585. if err != nil {
  586. t.Errorf("%d. err=%q", i, err)
  587. continue
  588. }
  589. if g, e := got.String(), tt.want; g != e {
  590. t.Errorf("%d. Location=%q; want %q", i, g, e)
  591. }
  592. }
  593. }
  594. func TestResponseStatusStutter(t *testing.T) {
  595. r := &Response{
  596. Status: "123 some status",
  597. StatusCode: 123,
  598. ProtoMajor: 1,
  599. ProtoMinor: 3,
  600. }
  601. var buf bytes.Buffer
  602. r.Write(&buf)
  603. if strings.Contains(buf.String(), "123 123") {
  604. t.Errorf("stutter in status: %s", buf.String())
  605. }
  606. }
  607. func TestResponseContentLengthShortBody(t *testing.T) {
  608. const shortBody = "Short body, not 123 bytes."
  609. br := bufio.NewReader(strings.NewReader("HTTP/1.1 200 OK\r\n" +
  610. "Content-Length: 123\r\n" +
  611. "\r\n" +
  612. shortBody))
  613. res, err := ReadResponse(br, &Request{Method: "GET"})
  614. if err != nil {
  615. t.Fatal(err)
  616. }
  617. if res.ContentLength != 123 {
  618. t.Fatalf("Content-Length = %d; want 123", res.ContentLength)
  619. }
  620. var buf bytes.Buffer
  621. n, err := io.Copy(&buf, res.Body)
  622. if n != int64(len(shortBody)) {
  623. t.Errorf("Copied %d bytes; want %d, len(%q)", n, len(shortBody), shortBody)
  624. }
  625. if buf.String() != shortBody {
  626. t.Errorf("Read body %q; want %q", buf.String(), shortBody)
  627. }
  628. if err != io.ErrUnexpectedEOF {
  629. t.Errorf("io.Copy error = %#v; want io.ErrUnexpectedEOF", err)
  630. }
  631. }
  632. func TestReadResponseUnexpectedEOF(t *testing.T) {
  633. br := bufio.NewReader(strings.NewReader("HTTP/1.1 301 Moved Permanently\r\n" +
  634. "Location: http://example.com"))
  635. _, err := ReadResponse(br, nil)
  636. if err != io.ErrUnexpectedEOF {
  637. t.Errorf("ReadResponse = %v; want io.ErrUnexpectedEOF", err)
  638. }
  639. }
  640. func TestNeedsSniff(t *testing.T) {
  641. // needsSniff returns true with an empty response.
  642. r := &response{}
  643. if got, want := r.needsSniff(), true; got != want {
  644. t.Errorf("needsSniff = %t; want %t", got, want)
  645. }
  646. // needsSniff returns false when Content-Type = nil.
  647. r.handlerHeader = Header{"Content-Type": nil}
  648. if got, want := r.needsSniff(), false; got != want {
  649. t.Errorf("needsSniff empty Content-Type = %t; want %t", got, want)
  650. }
  651. }