client_test.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. package h2quic
  2. import (
  3. "bytes"
  4. "compress/gzip"
  5. "context"
  6. "crypto/tls"
  7. "errors"
  8. "io"
  9. "net/http"
  10. "golang.org/x/net/http2"
  11. "golang.org/x/net/http2/hpack"
  12. quic "github.com/lucas-clemente/quic-go"
  13. "github.com/lucas-clemente/quic-go/internal/protocol"
  14. "github.com/lucas-clemente/quic-go/internal/utils"
  15. "github.com/lucas-clemente/quic-go/qerr"
  16. "time"
  17. . "github.com/onsi/ginkgo"
  18. . "github.com/onsi/gomega"
  19. )
  20. var _ = Describe("Client", func() {
  21. var (
  22. client *client
  23. session *mockSession
  24. headerStream *mockStream
  25. req *http.Request
  26. origDialAddr = dialAddr
  27. )
  28. injectResponse := func(id protocol.StreamID, rsp *http.Response) {
  29. EventuallyWithOffset(0, func() bool {
  30. client.mutex.Lock()
  31. defer client.mutex.Unlock()
  32. _, ok := client.responses[id]
  33. return ok
  34. }).Should(BeTrue())
  35. rspChan := client.responses[5]
  36. ExpectWithOffset(0, rspChan).ToNot(BeClosed())
  37. rspChan <- rsp
  38. }
  39. BeforeEach(func() {
  40. origDialAddr = dialAddr
  41. hostname := "quic.clemente.io:1337"
  42. client = newClient(hostname, nil, &roundTripperOpts{}, nil, nil)
  43. Expect(client.hostname).To(Equal(hostname))
  44. session = newMockSession()
  45. session.ctx, session.ctxCancel = context.WithCancel(context.Background())
  46. client.session = session
  47. headerStream = newMockStream(3)
  48. client.headerStream = headerStream
  49. client.requestWriter = newRequestWriter(headerStream, utils.DefaultLogger)
  50. var err error
  51. req, err = http.NewRequest("GET", "https://localhost:1337", nil)
  52. Expect(err).ToNot(HaveOccurred())
  53. })
  54. AfterEach(func() {
  55. dialAddr = origDialAddr
  56. })
  57. It("saves the TLS config", func() {
  58. tlsConf := &tls.Config{InsecureSkipVerify: true}
  59. client = newClient("", tlsConf, &roundTripperOpts{}, nil, nil)
  60. Expect(client.tlsConf).To(Equal(tlsConf))
  61. })
  62. It("saves the QUIC config", func() {
  63. quicConf := &quic.Config{HandshakeTimeout: time.Nanosecond}
  64. client = newClient("", &tls.Config{}, &roundTripperOpts{}, quicConf, nil)
  65. Expect(client.config).To(Equal(quicConf))
  66. })
  67. It("uses the default QUIC config if none is give", func() {
  68. client = newClient("", &tls.Config{}, &roundTripperOpts{}, nil, nil)
  69. Expect(client.config).ToNot(BeNil())
  70. Expect(client.config).To(Equal(defaultQuicConfig))
  71. })
  72. It("adds the port to the hostname, if none is given", func() {
  73. client = newClient("quic.clemente.io", nil, &roundTripperOpts{}, nil, nil)
  74. Expect(client.hostname).To(Equal("quic.clemente.io:443"))
  75. })
  76. It("dials", func() {
  77. client = newClient("localhost:1337", nil, &roundTripperOpts{}, nil, nil)
  78. session.streamsToOpen = []quic.Stream{newMockStream(3), newMockStream(5)}
  79. dialAddr = func(hostname string, _ *tls.Config, _ *quic.Config) (quic.Session, error) {
  80. return session, nil
  81. }
  82. close(headerStream.unblockRead)
  83. done := make(chan struct{})
  84. go func() {
  85. defer GinkgoRecover()
  86. _, err := client.RoundTrip(req)
  87. Expect(err).ToNot(HaveOccurred())
  88. close(done)
  89. // fmt.Println("done")
  90. }()
  91. Eventually(func() quic.Session { return client.session }).Should(Equal(session))
  92. // make the go routine return
  93. injectResponse(5, &http.Response{})
  94. Eventually(done).Should(BeClosed())
  95. })
  96. It("errors when dialing fails", func() {
  97. testErr := errors.New("handshake error")
  98. client = newClient("localhost:1337", nil, &roundTripperOpts{}, nil, nil)
  99. dialAddr = func(hostname string, _ *tls.Config, _ *quic.Config) (quic.Session, error) {
  100. return nil, testErr
  101. }
  102. _, err := client.RoundTrip(req)
  103. Expect(err).To(MatchError(testErr))
  104. })
  105. It("uses the custom dialer, if provided", func() {
  106. var tlsCfg *tls.Config
  107. var qCfg *quic.Config
  108. session.streamsToOpen = []quic.Stream{newMockStream(3), newMockStream(5)}
  109. dialer := func(_, _ string, tlsCfgP *tls.Config, cfg *quic.Config) (quic.Session, error) {
  110. tlsCfg = tlsCfgP
  111. qCfg = cfg
  112. return session, nil
  113. }
  114. client = newClient("localhost:1337", nil, &roundTripperOpts{}, nil, dialer)
  115. done := make(chan struct{})
  116. go func() {
  117. defer GinkgoRecover()
  118. _, err := client.RoundTrip(req)
  119. Expect(err).ToNot(HaveOccurred())
  120. close(done)
  121. }()
  122. Eventually(func() quic.Session { return client.session }).Should(Equal(session))
  123. Expect(qCfg).To(Equal(client.config))
  124. Expect(tlsCfg).To(Equal(client.tlsConf))
  125. // make the go routine return
  126. injectResponse(5, &http.Response{})
  127. Eventually(done).Should(BeClosed())
  128. })
  129. It("errors if it can't open a stream", func() {
  130. testErr := errors.New("you shall not pass")
  131. client = newClient("localhost:1337", nil, &roundTripperOpts{}, nil, nil)
  132. session.streamOpenErr = testErr
  133. dialAddr = func(hostname string, _ *tls.Config, _ *quic.Config) (quic.Session, error) {
  134. return session, nil
  135. }
  136. _, err := client.RoundTrip(req)
  137. Expect(err).To(MatchError(testErr))
  138. })
  139. It("returns a request when dial fails", func() {
  140. testErr := errors.New("dial error")
  141. dialAddr = func(hostname string, _ *tls.Config, _ *quic.Config) (quic.Session, error) {
  142. return nil, testErr
  143. }
  144. request, err := http.NewRequest("https", "https://quic.clemente.io:1337/file1.dat", nil)
  145. Expect(err).ToNot(HaveOccurred())
  146. done := make(chan struct{})
  147. go func() {
  148. _, err := client.RoundTrip(request)
  149. Expect(err).To(MatchError(testErr))
  150. close(done)
  151. }()
  152. _, err = client.RoundTrip(request)
  153. Expect(err).To(MatchError(testErr))
  154. Eventually(done).Should(BeClosed())
  155. })
  156. Context("Doing requests", func() {
  157. var request *http.Request
  158. var dataStream *mockStream
  159. getRequest := func(data []byte) *http2.MetaHeadersFrame {
  160. r := bytes.NewReader(data)
  161. decoder := hpack.NewDecoder(4096, func(hf hpack.HeaderField) {})
  162. h2framer := http2.NewFramer(nil, r)
  163. frame, err := h2framer.ReadFrame()
  164. Expect(err).ToNot(HaveOccurred())
  165. mhframe := &http2.MetaHeadersFrame{HeadersFrame: frame.(*http2.HeadersFrame)}
  166. mhframe.Fields, err = decoder.DecodeFull(mhframe.HeadersFrame.HeaderBlockFragment())
  167. Expect(err).ToNot(HaveOccurred())
  168. return mhframe
  169. }
  170. getHeaderFields := func(f *http2.MetaHeadersFrame) map[string]string {
  171. fields := make(map[string]string)
  172. for _, hf := range f.Fields {
  173. fields[hf.Name] = hf.Value
  174. }
  175. return fields
  176. }
  177. BeforeEach(func() {
  178. var err error
  179. dialAddr = func(hostname string, _ *tls.Config, _ *quic.Config) (quic.Session, error) {
  180. return session, nil
  181. }
  182. dataStream = newMockStream(5)
  183. session.streamsToOpen = []quic.Stream{headerStream, dataStream}
  184. request, err = http.NewRequest("https", "https://quic.clemente.io:1337/file1.dat", nil)
  185. Expect(err).ToNot(HaveOccurred())
  186. })
  187. It("does a request", func() {
  188. teapot := &http.Response{
  189. Status: "418 I'm a teapot",
  190. StatusCode: 418,
  191. }
  192. done := make(chan struct{})
  193. go func() {
  194. defer GinkgoRecover()
  195. rsp, err := client.RoundTrip(request)
  196. Expect(err).ToNot(HaveOccurred())
  197. Expect(rsp).To(Equal(teapot))
  198. Expect(rsp.Body).To(Equal(dataStream))
  199. Expect(rsp.ContentLength).To(BeEquivalentTo(-1))
  200. Expect(rsp.Request).To(Equal(request))
  201. close(done)
  202. }()
  203. Eventually(func() []byte { return headerStream.dataWritten.Bytes() }).ShouldNot(BeEmpty())
  204. injectResponse(5, teapot)
  205. Expect(client.headerErrored).ToNot(BeClosed())
  206. Eventually(done).Should(BeClosed())
  207. })
  208. It("errors if a request without a body is canceled", func() {
  209. done := make(chan struct{})
  210. ctx, cancel := context.WithCancel(context.Background())
  211. go func() {
  212. defer GinkgoRecover()
  213. request = request.WithContext(ctx)
  214. rsp, err := client.RoundTrip(request)
  215. Expect(err).To(MatchError(context.Canceled))
  216. Expect(rsp).To(BeNil())
  217. close(done)
  218. }()
  219. cancel()
  220. Eventually(done).Should(BeClosed())
  221. Expect(dataStream.reset).To(BeTrue())
  222. Expect(dataStream.canceledWrite).To(BeTrue())
  223. Expect(client.headerErrored).ToNot(BeClosed())
  224. })
  225. It("errors if a request with a body is canceled after the body is sent", func() {
  226. done := make(chan struct{})
  227. ctx, cancel := context.WithCancel(context.Background())
  228. go func() {
  229. defer GinkgoRecover()
  230. request = request.WithContext(ctx)
  231. request.Body = &mockBody{}
  232. rsp, err := client.RoundTrip(request)
  233. Expect(err).To(MatchError(context.Canceled))
  234. Expect(rsp).To(BeNil())
  235. close(done)
  236. }()
  237. time.Sleep(10 * time.Millisecond)
  238. cancel()
  239. Eventually(done).Should(BeClosed())
  240. Expect(dataStream.reset).To(BeTrue())
  241. Expect(dataStream.canceledWrite).To(BeTrue())
  242. Expect(client.headerErrored).ToNot(BeClosed())
  243. })
  244. It("errors if a request with a body is canceled before the body is sent", func() {
  245. done := make(chan struct{})
  246. ctx, cancel := context.WithCancel(context.Background())
  247. go func() {
  248. defer GinkgoRecover()
  249. request = request.WithContext(ctx)
  250. request.Body = &mockBody{}
  251. cancel()
  252. time.Sleep(10 * time.Millisecond)
  253. rsp, err := client.RoundTrip(request)
  254. Expect(err).To(MatchError(context.Canceled))
  255. Expect(rsp).To(BeNil())
  256. close(done)
  257. }()
  258. Eventually(done).Should(BeClosed())
  259. Expect(dataStream.reset).To(BeTrue())
  260. Expect(dataStream.canceledWrite).To(BeTrue())
  261. Expect(client.headerErrored).ToNot(BeClosed())
  262. })
  263. It("closes the quic client when encountering an error on the header stream", func() {
  264. headerStream.dataToRead.Write(bytes.Repeat([]byte{0}, 100))
  265. done := make(chan struct{})
  266. go func() {
  267. defer GinkgoRecover()
  268. rsp, err := client.RoundTrip(request)
  269. Expect(err).To(MatchError(client.headerErr))
  270. Expect(rsp).To(BeNil())
  271. close(done)
  272. }()
  273. Eventually(done).Should(BeClosed())
  274. Expect(client.headerErr.ErrorCode).To(Equal(qerr.InvalidHeadersStreamData))
  275. Expect(client.session.(*mockSession).closedWithError).To(MatchError(client.headerErr))
  276. })
  277. It("returns subsequent request if there was an error on the header stream before", func() {
  278. session.streamsToOpen = []quic.Stream{headerStream, dataStream, newMockStream(7)}
  279. headerStream.dataToRead.Write(bytes.Repeat([]byte{0}, 100))
  280. _, err := client.RoundTrip(request)
  281. Expect(err).To(BeAssignableToTypeOf(&qerr.QuicError{}))
  282. Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(qerr.InvalidHeadersStreamData))
  283. // now that the first request failed due to an error on the header stream, try another request
  284. _, nextErr := client.RoundTrip(request)
  285. Expect(nextErr).To(MatchError(err))
  286. })
  287. It("blocks if no stream is available", func() {
  288. session.streamsToOpen = []quic.Stream{headerStream, dataStream}
  289. session.blockOpenStreamSync = true
  290. done := make(chan struct{})
  291. go func() {
  292. defer GinkgoRecover()
  293. _, err := client.RoundTrip(request)
  294. Expect(err).ToNot(HaveOccurred())
  295. close(done)
  296. }()
  297. Consistently(done).ShouldNot(BeClosed())
  298. // make the go routine return
  299. client.Close()
  300. injectResponse(5, &http.Response{})
  301. Eventually(done).Should(BeClosed())
  302. })
  303. Context("validating the address", func() {
  304. It("refuses to do requests for the wrong host", func() {
  305. req, err := http.NewRequest("https", "https://quic.clemente.io:1336/foobar.html", nil)
  306. Expect(err).ToNot(HaveOccurred())
  307. _, err = client.RoundTrip(req)
  308. Expect(err).To(MatchError("h2quic Client BUG: RoundTrip called for the wrong client (expected quic.clemente.io:1337, got quic.clemente.io:1336)"))
  309. })
  310. It("refuses to do plain HTTP requests", func() {
  311. req, err := http.NewRequest("https", "http://quic.clemente.io:1337/foobar.html", nil)
  312. Expect(err).ToNot(HaveOccurred())
  313. _, err = client.RoundTrip(req)
  314. Expect(err).To(MatchError("quic http2: unsupported scheme"))
  315. })
  316. It("adds the port for request URLs without one", func() {
  317. client = newClient("quic.clemente.io", nil, &roundTripperOpts{}, nil, nil)
  318. req, err := http.NewRequest("https", "https://quic.clemente.io/foobar.html", nil)
  319. Expect(err).ToNot(HaveOccurred())
  320. done := make(chan struct{})
  321. // the client.RoundTrip will block, because the encryption level is still set to Unencrypted
  322. go func() {
  323. defer GinkgoRecover()
  324. _, err := client.RoundTrip(req)
  325. Expect(err).ToNot(HaveOccurred())
  326. close(done)
  327. }()
  328. Consistently(done).ShouldNot(BeClosed())
  329. // make the go routine return
  330. injectResponse(5, &http.Response{})
  331. Eventually(done).Should(BeClosed())
  332. })
  333. })
  334. It("sets the EndStream header for requests without a body", func() {
  335. done := make(chan struct{})
  336. go func() {
  337. defer GinkgoRecover()
  338. client.RoundTrip(request)
  339. close(done)
  340. }()
  341. Eventually(func() []byte { return headerStream.dataWritten.Bytes() }).ShouldNot(BeNil())
  342. mhf := getRequest(headerStream.dataWritten.Bytes())
  343. Expect(mhf.HeadersFrame.StreamEnded()).To(BeTrue())
  344. // make the go routine return
  345. injectResponse(5, &http.Response{})
  346. Eventually(done).Should(BeClosed())
  347. })
  348. It("sets the EndStream header to false for requests with a body", func() {
  349. request.Body = &mockBody{}
  350. done := make(chan struct{})
  351. go func() {
  352. defer GinkgoRecover()
  353. client.RoundTrip(request)
  354. close(done)
  355. }()
  356. Eventually(func() []byte { return headerStream.dataWritten.Bytes() }).ShouldNot(BeNil())
  357. mhf := getRequest(headerStream.dataWritten.Bytes())
  358. Expect(mhf.HeadersFrame.StreamEnded()).To(BeFalse())
  359. // make the go routine return
  360. injectResponse(5, &http.Response{})
  361. Eventually(done).Should(BeClosed())
  362. })
  363. Context("requests containing a Body", func() {
  364. var requestBody []byte
  365. var response *http.Response
  366. BeforeEach(func() {
  367. requestBody = []byte("request body")
  368. body := &mockBody{}
  369. body.SetData(requestBody)
  370. request.Body = body
  371. response = &http.Response{
  372. StatusCode: 200,
  373. Header: http.Header{"Content-Length": []string{"1000"}},
  374. }
  375. // fake a handshake
  376. client.dialOnce.Do(func() {})
  377. session.streamsToOpen = []quic.Stream{dataStream}
  378. })
  379. It("sends a request", func() {
  380. rspChan := make(chan *http.Response)
  381. go func() {
  382. defer GinkgoRecover()
  383. rsp, err := client.RoundTrip(request)
  384. Expect(err).ToNot(HaveOccurred())
  385. rspChan <- rsp
  386. }()
  387. injectResponse(5, response)
  388. Eventually(rspChan).Should(Receive(Equal(response)))
  389. Expect(dataStream.dataWritten.Bytes()).To(Equal(requestBody))
  390. Expect(dataStream.closed).To(BeTrue())
  391. Expect(request.Body.(*mockBody).closed).To(BeTrue())
  392. })
  393. It("returns the error that occurred when reading the body", func() {
  394. testErr := errors.New("testErr")
  395. request.Body.(*mockBody).readErr = testErr
  396. done := make(chan struct{})
  397. go func() {
  398. defer GinkgoRecover()
  399. rsp, err := client.RoundTrip(request)
  400. Expect(err).To(MatchError(testErr))
  401. Expect(rsp).To(BeNil())
  402. close(done)
  403. }()
  404. Eventually(done).Should(BeClosed())
  405. Expect(request.Body.(*mockBody).closed).To(BeTrue())
  406. })
  407. It("returns the error that occurred when closing the body", func() {
  408. testErr := errors.New("testErr")
  409. request.Body.(*mockBody).closeErr = testErr
  410. done := make(chan struct{})
  411. go func() {
  412. defer GinkgoRecover()
  413. rsp, err := client.RoundTrip(request)
  414. Expect(err).To(MatchError(testErr))
  415. Expect(rsp).To(BeNil())
  416. close(done)
  417. }()
  418. Eventually(done).Should(BeClosed())
  419. Expect(request.Body.(*mockBody).closed).To(BeTrue())
  420. })
  421. })
  422. Context("gzip compression", func() {
  423. var gzippedData []byte // a gzipped foobar
  424. var response *http.Response
  425. BeforeEach(func() {
  426. var b bytes.Buffer
  427. w := gzip.NewWriter(&b)
  428. w.Write([]byte("foobar"))
  429. w.Close()
  430. gzippedData = b.Bytes()
  431. response = &http.Response{
  432. StatusCode: 200,
  433. Header: http.Header{"Content-Length": []string{"1000"}},
  434. }
  435. })
  436. It("adds the gzip header to requests", func() {
  437. done := make(chan struct{})
  438. go func() {
  439. defer GinkgoRecover()
  440. rsp, err := client.RoundTrip(request)
  441. Expect(err).ToNot(HaveOccurred())
  442. Expect(rsp).ToNot(BeNil())
  443. Expect(rsp.ContentLength).To(BeEquivalentTo(-1))
  444. Expect(rsp.Header.Get("Content-Encoding")).To(BeEmpty())
  445. Expect(rsp.Header.Get("Content-Length")).To(BeEmpty())
  446. data := make([]byte, 6)
  447. _, err = io.ReadFull(rsp.Body, data)
  448. Expect(err).ToNot(HaveOccurred())
  449. Expect(data).To(Equal([]byte("foobar")))
  450. close(done)
  451. }()
  452. dataStream.dataToRead.Write(gzippedData)
  453. response.Header.Add("Content-Encoding", "gzip")
  454. injectResponse(5, response)
  455. headers := getHeaderFields(getRequest(headerStream.dataWritten.Bytes()))
  456. Expect(headers).To(HaveKeyWithValue("accept-encoding", "gzip"))
  457. close(dataStream.unblockRead)
  458. Eventually(done).Should(BeClosed())
  459. })
  460. It("doesn't add gzip if the header disable it", func() {
  461. client.opts.DisableCompression = true
  462. done := make(chan struct{})
  463. go func() {
  464. defer GinkgoRecover()
  465. _, err := client.RoundTrip(request)
  466. Expect(err).ToNot(HaveOccurred())
  467. close(done)
  468. }()
  469. Eventually(func() []byte { return headerStream.dataWritten.Bytes() }).ShouldNot(BeEmpty())
  470. headers := getHeaderFields(getRequest(headerStream.dataWritten.Bytes()))
  471. Expect(headers).ToNot(HaveKey("accept-encoding"))
  472. // make the go routine return
  473. injectResponse(5, &http.Response{})
  474. Eventually(done).Should(BeClosed())
  475. })
  476. It("only decompresses the response if the response contains the right content-encoding header", func() {
  477. done := make(chan struct{})
  478. go func() {
  479. defer GinkgoRecover()
  480. rsp, err := client.RoundTrip(request)
  481. Expect(err).ToNot(HaveOccurred())
  482. Expect(rsp).ToNot(BeNil())
  483. data := make([]byte, 11)
  484. rsp.Body.Read(data)
  485. Expect(rsp.ContentLength).ToNot(BeEquivalentTo(-1))
  486. Expect(data).To(Equal([]byte("not gzipped")))
  487. close(done)
  488. }()
  489. dataStream.dataToRead.Write([]byte("not gzipped"))
  490. injectResponse(5, response)
  491. headers := getHeaderFields(getRequest(headerStream.dataWritten.Bytes()))
  492. Expect(headers).To(HaveKeyWithValue("accept-encoding", "gzip"))
  493. Eventually(done).Should(BeClosed())
  494. })
  495. It("doesn't add the gzip header for requests that have the accept-enconding set", func() {
  496. request.Header.Add("accept-encoding", "gzip")
  497. done := make(chan struct{})
  498. go func() {
  499. defer GinkgoRecover()
  500. rsp, err := client.RoundTrip(request)
  501. Expect(err).ToNot(HaveOccurred())
  502. data := make([]byte, 12)
  503. _, err = rsp.Body.Read(data)
  504. Expect(err).ToNot(HaveOccurred())
  505. Expect(rsp.ContentLength).ToNot(BeEquivalentTo(-1))
  506. Expect(data).To(Equal([]byte("gzipped data")))
  507. close(done)
  508. }()
  509. dataStream.dataToRead.Write([]byte("gzipped data"))
  510. injectResponse(5, response)
  511. headers := getHeaderFields(getRequest(headerStream.dataWritten.Bytes()))
  512. Expect(headers).To(HaveKeyWithValue("accept-encoding", "gzip"))
  513. Eventually(done).Should(BeClosed())
  514. })
  515. })
  516. Context("handling the header stream", func() {
  517. var h2framer *http2.Framer
  518. BeforeEach(func() {
  519. h2framer = http2.NewFramer(&headerStream.dataToRead, nil)
  520. client.responses[23] = make(chan *http.Response)
  521. })
  522. It("reads header values from a response", func() {
  523. // Taken from https://http2.github.io/http2-spec/compression.html#request.examples.with.huffman.coding
  524. data := []byte{0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x31, 0x20, 0x47, 0x4d, 0x54, 0x6e, 0x17, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d}
  525. headerStream.dataToRead.Write([]byte{0x0, 0x0, byte(len(data)), 0x1, 0x5, 0x0, 0x0, 0x0, 23})
  526. headerStream.dataToRead.Write(data)
  527. go client.handleHeaderStream()
  528. var rsp *http.Response
  529. Eventually(client.responses[23]).Should(Receive(&rsp))
  530. Expect(rsp).ToNot(BeNil())
  531. Expect(rsp.Proto).To(Equal("HTTP/2.0"))
  532. Expect(rsp.ProtoMajor).To(BeEquivalentTo(2))
  533. Expect(rsp.StatusCode).To(BeEquivalentTo(302))
  534. Expect(rsp.Status).To(Equal("302 Found"))
  535. Expect(rsp.Header).To(HaveKeyWithValue("Location", []string{"https://www.example.com"}))
  536. Expect(rsp.Header).To(HaveKeyWithValue("Cache-Control", []string{"private"}))
  537. })
  538. It("errors if the H2 frame is not a HeadersFrame", func() {
  539. h2framer.WritePing(true, [8]byte{0, 0, 0, 0, 0, 0, 0, 0})
  540. client.handleHeaderStream()
  541. Eventually(client.headerErrored).Should(BeClosed())
  542. Expect(client.headerErr).To(MatchError(qerr.Error(qerr.InvalidHeadersStreamData, "not a headers frame")))
  543. })
  544. It("errors if it can't read the HPACK encoded header fields", func() {
  545. h2framer.WriteHeaders(http2.HeadersFrameParam{
  546. StreamID: 23,
  547. EndHeaders: true,
  548. BlockFragment: []byte("invalid HPACK data"),
  549. })
  550. client.handleHeaderStream()
  551. Eventually(client.headerErrored).Should(BeClosed())
  552. Expect(client.headerErr.ErrorCode).To(Equal(qerr.InvalidHeadersStreamData))
  553. Expect(client.headerErr.ErrorMessage).To(ContainSubstring("cannot read header fields"))
  554. })
  555. It("errors if the stream cannot be found", func() {
  556. var headers bytes.Buffer
  557. enc := hpack.NewEncoder(&headers)
  558. enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
  559. err := h2framer.WriteHeaders(http2.HeadersFrameParam{
  560. StreamID: 1337,
  561. EndHeaders: true,
  562. BlockFragment: headers.Bytes(),
  563. })
  564. Expect(err).ToNot(HaveOccurred())
  565. client.handleHeaderStream()
  566. Eventually(client.headerErrored).Should(BeClosed())
  567. Expect(client.headerErr.ErrorCode).To(Equal(qerr.InvalidHeadersStreamData))
  568. Expect(client.headerErr.ErrorMessage).To(ContainSubstring("response channel for stream 1337 not found"))
  569. })
  570. })
  571. })
  572. })