The reconcile package is used for DOM reconcilation in Isomorphic Go web applications.

server_push_test.go 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. // Copyright 2016 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. // +build go1.8
  5. package http2
  6. import (
  7. "errors"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "net/http"
  12. "reflect"
  13. "strconv"
  14. "sync"
  15. "testing"
  16. "time"
  17. )
  18. func TestServer_Push_Success(t *testing.T) {
  19. const (
  20. mainBody = "<html>index page</html>"
  21. pushedBody = "<html>pushed page</html>"
  22. userAgent = "testagent"
  23. cookie = "testcookie"
  24. )
  25. var stURL string
  26. checkPromisedReq := func(r *http.Request, wantMethod string, wantH http.Header) error {
  27. if got, want := r.Method, wantMethod; got != want {
  28. return fmt.Errorf("promised Req.Method=%q, want %q", got, want)
  29. }
  30. if got, want := r.Header, wantH; !reflect.DeepEqual(got, want) {
  31. return fmt.Errorf("promised Req.Header=%q, want %q", got, want)
  32. }
  33. if got, want := "https://"+r.Host, stURL; got != want {
  34. return fmt.Errorf("promised Req.Host=%q, want %q", got, want)
  35. }
  36. if r.Body == nil {
  37. return fmt.Errorf("nil Body")
  38. }
  39. if buf, err := ioutil.ReadAll(r.Body); err != nil || len(buf) != 0 {
  40. return fmt.Errorf("ReadAll(Body)=%q,%v, want '',nil", buf, err)
  41. }
  42. return nil
  43. }
  44. errc := make(chan error, 3)
  45. st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
  46. switch r.URL.RequestURI() {
  47. case "/":
  48. // Push "/pushed?get" as a GET request, using an absolute URL.
  49. opt := &http.PushOptions{
  50. Header: http.Header{
  51. "User-Agent": {userAgent},
  52. },
  53. }
  54. if err := w.(http.Pusher).Push(stURL+"/pushed?get", opt); err != nil {
  55. errc <- fmt.Errorf("error pushing /pushed?get: %v", err)
  56. return
  57. }
  58. // Push "/pushed?head" as a HEAD request, using a path.
  59. opt = &http.PushOptions{
  60. Method: "HEAD",
  61. Header: http.Header{
  62. "User-Agent": {userAgent},
  63. "Cookie": {cookie},
  64. },
  65. }
  66. if err := w.(http.Pusher).Push("/pushed?head", opt); err != nil {
  67. errc <- fmt.Errorf("error pushing /pushed?head: %v", err)
  68. return
  69. }
  70. w.Header().Set("Content-Type", "text/html")
  71. w.Header().Set("Content-Length", strconv.Itoa(len(mainBody)))
  72. w.WriteHeader(200)
  73. io.WriteString(w, mainBody)
  74. errc <- nil
  75. case "/pushed?get":
  76. wantH := http.Header{}
  77. wantH.Set("User-Agent", userAgent)
  78. if err := checkPromisedReq(r, "GET", wantH); err != nil {
  79. errc <- fmt.Errorf("/pushed?get: %v", err)
  80. return
  81. }
  82. w.Header().Set("Content-Type", "text/html")
  83. w.Header().Set("Content-Length", strconv.Itoa(len(pushedBody)))
  84. w.WriteHeader(200)
  85. io.WriteString(w, pushedBody)
  86. errc <- nil
  87. case "/pushed?head":
  88. wantH := http.Header{}
  89. wantH.Set("User-Agent", userAgent)
  90. wantH.Set("Cookie", cookie)
  91. if err := checkPromisedReq(r, "HEAD", wantH); err != nil {
  92. errc <- fmt.Errorf("/pushed?head: %v", err)
  93. return
  94. }
  95. w.WriteHeader(204)
  96. errc <- nil
  97. default:
  98. errc <- fmt.Errorf("unknown RequestURL %q", r.URL.RequestURI())
  99. }
  100. })
  101. stURL = st.ts.URL
  102. // Send one request, which should push two responses.
  103. st.greet()
  104. getSlash(st)
  105. for k := 0; k < 3; k++ {
  106. select {
  107. case <-time.After(2 * time.Second):
  108. t.Errorf("timeout waiting for handler %d to finish", k)
  109. case err := <-errc:
  110. if err != nil {
  111. t.Fatal(err)
  112. }
  113. }
  114. }
  115. checkPushPromise := func(f Frame, promiseID uint32, wantH [][2]string) error {
  116. pp, ok := f.(*PushPromiseFrame)
  117. if !ok {
  118. return fmt.Errorf("got a %T; want *PushPromiseFrame", f)
  119. }
  120. if !pp.HeadersEnded() {
  121. return fmt.Errorf("want END_HEADERS flag in PushPromiseFrame")
  122. }
  123. if got, want := pp.PromiseID, promiseID; got != want {
  124. return fmt.Errorf("got PromiseID %v; want %v", got, want)
  125. }
  126. gotH := st.decodeHeader(pp.HeaderBlockFragment())
  127. if !reflect.DeepEqual(gotH, wantH) {
  128. return fmt.Errorf("got promised headers %v; want %v", gotH, wantH)
  129. }
  130. return nil
  131. }
  132. checkHeaders := func(f Frame, wantH [][2]string) error {
  133. hf, ok := f.(*HeadersFrame)
  134. if !ok {
  135. return fmt.Errorf("got a %T; want *HeadersFrame", f)
  136. }
  137. gotH := st.decodeHeader(hf.HeaderBlockFragment())
  138. if !reflect.DeepEqual(gotH, wantH) {
  139. return fmt.Errorf("got response headers %v; want %v", gotH, wantH)
  140. }
  141. return nil
  142. }
  143. checkData := func(f Frame, wantData string) error {
  144. df, ok := f.(*DataFrame)
  145. if !ok {
  146. return fmt.Errorf("got a %T; want *DataFrame", f)
  147. }
  148. if gotData := string(df.Data()); gotData != wantData {
  149. return fmt.Errorf("got response data %q; want %q", gotData, wantData)
  150. }
  151. return nil
  152. }
  153. // Stream 1 has 2 PUSH_PROMISE + HEADERS + DATA
  154. // Stream 2 has HEADERS + DATA
  155. // Stream 4 has HEADERS
  156. expected := map[uint32][]func(Frame) error{
  157. 1: {
  158. func(f Frame) error {
  159. return checkPushPromise(f, 2, [][2]string{
  160. {":method", "GET"},
  161. {":scheme", "https"},
  162. {":authority", st.ts.Listener.Addr().String()},
  163. {":path", "/pushed?get"},
  164. {"user-agent", userAgent},
  165. })
  166. },
  167. func(f Frame) error {
  168. return checkPushPromise(f, 4, [][2]string{
  169. {":method", "HEAD"},
  170. {":scheme", "https"},
  171. {":authority", st.ts.Listener.Addr().String()},
  172. {":path", "/pushed?head"},
  173. {"cookie", cookie},
  174. {"user-agent", userAgent},
  175. })
  176. },
  177. func(f Frame) error {
  178. return checkHeaders(f, [][2]string{
  179. {":status", "200"},
  180. {"content-type", "text/html"},
  181. {"content-length", strconv.Itoa(len(mainBody))},
  182. })
  183. },
  184. func(f Frame) error {
  185. return checkData(f, mainBody)
  186. },
  187. },
  188. 2: {
  189. func(f Frame) error {
  190. return checkHeaders(f, [][2]string{
  191. {":status", "200"},
  192. {"content-type", "text/html"},
  193. {"content-length", strconv.Itoa(len(pushedBody))},
  194. })
  195. },
  196. func(f Frame) error {
  197. return checkData(f, pushedBody)
  198. },
  199. },
  200. 4: {
  201. func(f Frame) error {
  202. return checkHeaders(f, [][2]string{
  203. {":status", "204"},
  204. })
  205. },
  206. },
  207. }
  208. consumed := map[uint32]int{}
  209. for k := 0; len(expected) > 0; k++ {
  210. f, err := st.readFrame()
  211. if err != nil {
  212. for id, left := range expected {
  213. t.Errorf("stream %d: missing %d frames", id, len(left))
  214. }
  215. t.Fatalf("readFrame %d: %v", k, err)
  216. }
  217. id := f.Header().StreamID
  218. label := fmt.Sprintf("stream %d, frame %d", id, consumed[id])
  219. if len(expected[id]) == 0 {
  220. t.Fatalf("%s: unexpected frame %#+v", label, f)
  221. }
  222. check := expected[id][0]
  223. expected[id] = expected[id][1:]
  224. if len(expected[id]) == 0 {
  225. delete(expected, id)
  226. }
  227. if err := check(f); err != nil {
  228. t.Fatalf("%s: %v", label, err)
  229. }
  230. consumed[id]++
  231. }
  232. }
  233. func TestServer_Push_SuccessNoRace(t *testing.T) {
  234. // Regression test for issue #18326. Ensure the request handler can mutate
  235. // pushed request headers without racing with the PUSH_PROMISE write.
  236. errc := make(chan error, 2)
  237. st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
  238. switch r.URL.RequestURI() {
  239. case "/":
  240. opt := &http.PushOptions{
  241. Header: http.Header{"User-Agent": {"testagent"}},
  242. }
  243. if err := w.(http.Pusher).Push("/pushed", opt); err != nil {
  244. errc <- fmt.Errorf("error pushing: %v", err)
  245. return
  246. }
  247. w.WriteHeader(200)
  248. errc <- nil
  249. case "/pushed":
  250. // Update request header, ensure there is no race.
  251. r.Header.Set("User-Agent", "newagent")
  252. r.Header.Set("Cookie", "cookie")
  253. w.WriteHeader(200)
  254. errc <- nil
  255. default:
  256. errc <- fmt.Errorf("unknown RequestURL %q", r.URL.RequestURI())
  257. }
  258. })
  259. // Send one request, which should push one response.
  260. st.greet()
  261. getSlash(st)
  262. for k := 0; k < 2; k++ {
  263. select {
  264. case <-time.After(2 * time.Second):
  265. t.Errorf("timeout waiting for handler %d to finish", k)
  266. case err := <-errc:
  267. if err != nil {
  268. t.Fatal(err)
  269. }
  270. }
  271. }
  272. }
  273. func TestServer_Push_RejectRecursivePush(t *testing.T) {
  274. // Expect two requests, but might get three if there's a bug and the second push succeeds.
  275. errc := make(chan error, 3)
  276. handler := func(w http.ResponseWriter, r *http.Request) error {
  277. baseURL := "https://" + r.Host
  278. switch r.URL.Path {
  279. case "/":
  280. if err := w.(http.Pusher).Push(baseURL+"/push1", nil); err != nil {
  281. return fmt.Errorf("first Push()=%v, want nil", err)
  282. }
  283. return nil
  284. case "/push1":
  285. if got, want := w.(http.Pusher).Push(baseURL+"/push2", nil), ErrRecursivePush; got != want {
  286. return fmt.Errorf("Push()=%v, want %v", got, want)
  287. }
  288. return nil
  289. default:
  290. return fmt.Errorf("unexpected path: %q", r.URL.Path)
  291. }
  292. }
  293. st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
  294. errc <- handler(w, r)
  295. })
  296. defer st.Close()
  297. st.greet()
  298. getSlash(st)
  299. if err := <-errc; err != nil {
  300. t.Errorf("First request failed: %v", err)
  301. }
  302. if err := <-errc; err != nil {
  303. t.Errorf("Second request failed: %v", err)
  304. }
  305. }
  306. func testServer_Push_RejectSingleRequest(t *testing.T, doPush func(http.Pusher, *http.Request) error, settings ...Setting) {
  307. // Expect one request, but might get two if there's a bug and the push succeeds.
  308. errc := make(chan error, 2)
  309. st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
  310. errc <- doPush(w.(http.Pusher), r)
  311. })
  312. defer st.Close()
  313. st.greet()
  314. if err := st.fr.WriteSettings(settings...); err != nil {
  315. st.t.Fatalf("WriteSettings: %v", err)
  316. }
  317. st.wantSettingsAck()
  318. getSlash(st)
  319. if err := <-errc; err != nil {
  320. t.Error(err)
  321. }
  322. // Should not get a PUSH_PROMISE frame.
  323. hf := st.wantHeaders()
  324. if !hf.StreamEnded() {
  325. t.Error("stream should end after headers")
  326. }
  327. }
  328. func TestServer_Push_RejectIfDisabled(t *testing.T) {
  329. testServer_Push_RejectSingleRequest(t,
  330. func(p http.Pusher, r *http.Request) error {
  331. if got, want := p.Push("https://"+r.Host+"/pushed", nil), http.ErrNotSupported; got != want {
  332. return fmt.Errorf("Push()=%v, want %v", got, want)
  333. }
  334. return nil
  335. },
  336. Setting{SettingEnablePush, 0})
  337. }
  338. func TestServer_Push_RejectWhenNoConcurrentStreams(t *testing.T) {
  339. testServer_Push_RejectSingleRequest(t,
  340. func(p http.Pusher, r *http.Request) error {
  341. if got, want := p.Push("https://"+r.Host+"/pushed", nil), ErrPushLimitReached; got != want {
  342. return fmt.Errorf("Push()=%v, want %v", got, want)
  343. }
  344. return nil
  345. },
  346. Setting{SettingMaxConcurrentStreams, 0})
  347. }
  348. func TestServer_Push_RejectWrongScheme(t *testing.T) {
  349. testServer_Push_RejectSingleRequest(t,
  350. func(p http.Pusher, r *http.Request) error {
  351. if err := p.Push("http://"+r.Host+"/pushed", nil); err == nil {
  352. return errors.New("Push() should have failed (push target URL is http)")
  353. }
  354. return nil
  355. })
  356. }
  357. func TestServer_Push_RejectMissingHost(t *testing.T) {
  358. testServer_Push_RejectSingleRequest(t,
  359. func(p http.Pusher, r *http.Request) error {
  360. if err := p.Push("https:pushed", nil); err == nil {
  361. return errors.New("Push() should have failed (push target URL missing host)")
  362. }
  363. return nil
  364. })
  365. }
  366. func TestServer_Push_RejectRelativePath(t *testing.T) {
  367. testServer_Push_RejectSingleRequest(t,
  368. func(p http.Pusher, r *http.Request) error {
  369. if err := p.Push("../test", nil); err == nil {
  370. return errors.New("Push() should have failed (push target is a relative path)")
  371. }
  372. return nil
  373. })
  374. }
  375. func TestServer_Push_RejectForbiddenMethod(t *testing.T) {
  376. testServer_Push_RejectSingleRequest(t,
  377. func(p http.Pusher, r *http.Request) error {
  378. if err := p.Push("https://"+r.Host+"/pushed", &http.PushOptions{Method: "POST"}); err == nil {
  379. return errors.New("Push() should have failed (cannot promise a POST)")
  380. }
  381. return nil
  382. })
  383. }
  384. func TestServer_Push_RejectForbiddenHeader(t *testing.T) {
  385. testServer_Push_RejectSingleRequest(t,
  386. func(p http.Pusher, r *http.Request) error {
  387. header := http.Header{
  388. "Content-Length": {"10"},
  389. "Content-Encoding": {"gzip"},
  390. "Trailer": {"Foo"},
  391. "Te": {"trailers"},
  392. "Host": {"test.com"},
  393. ":authority": {"test.com"},
  394. }
  395. if err := p.Push("https://"+r.Host+"/pushed", &http.PushOptions{Header: header}); err == nil {
  396. return errors.New("Push() should have failed (forbidden headers)")
  397. }
  398. return nil
  399. })
  400. }
  401. func TestServer_Push_StateTransitions(t *testing.T) {
  402. const body = "foo"
  403. gotPromise := make(chan bool)
  404. finishedPush := make(chan bool)
  405. st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
  406. switch r.URL.RequestURI() {
  407. case "/":
  408. if err := w.(http.Pusher).Push("/pushed", nil); err != nil {
  409. t.Errorf("Push error: %v", err)
  410. }
  411. // Don't finish this request until the push finishes so we don't
  412. // nondeterministically interleave output frames with the push.
  413. <-finishedPush
  414. case "/pushed":
  415. <-gotPromise
  416. }
  417. w.Header().Set("Content-Type", "text/html")
  418. w.Header().Set("Content-Length", strconv.Itoa(len(body)))
  419. w.WriteHeader(200)
  420. io.WriteString(w, body)
  421. })
  422. defer st.Close()
  423. st.greet()
  424. if st.stream(2) != nil {
  425. t.Fatal("stream 2 should be empty")
  426. }
  427. if got, want := st.streamState(2), stateIdle; got != want {
  428. t.Fatalf("streamState(2)=%v, want %v", got, want)
  429. }
  430. getSlash(st)
  431. // After the PUSH_PROMISE is sent, the stream should be stateHalfClosedRemote.
  432. st.wantPushPromise()
  433. if got, want := st.streamState(2), stateHalfClosedRemote; got != want {
  434. t.Fatalf("streamState(2)=%v, want %v", got, want)
  435. }
  436. // We stall the HTTP handler for "/pushed" until the above check. If we don't
  437. // stall the handler, then the handler might write HEADERS and DATA and finish
  438. // the stream before we check st.streamState(2) -- should that happen, we'll
  439. // see stateClosed and fail the above check.
  440. close(gotPromise)
  441. st.wantHeaders()
  442. if df := st.wantData(); !df.StreamEnded() {
  443. t.Fatal("expected END_STREAM flag on DATA")
  444. }
  445. if got, want := st.streamState(2), stateClosed; got != want {
  446. t.Fatalf("streamState(2)=%v, want %v", got, want)
  447. }
  448. close(finishedPush)
  449. }
  450. func TestServer_Push_RejectAfterGoAway(t *testing.T) {
  451. var readyOnce sync.Once
  452. ready := make(chan struct{})
  453. errc := make(chan error, 2)
  454. st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
  455. select {
  456. case <-ready:
  457. case <-time.After(5 * time.Second):
  458. errc <- fmt.Errorf("timeout waiting for GOAWAY to be processed")
  459. }
  460. if got, want := w.(http.Pusher).Push("https://"+r.Host+"/pushed", nil), http.ErrNotSupported; got != want {
  461. errc <- fmt.Errorf("Push()=%v, want %v", got, want)
  462. }
  463. errc <- nil
  464. })
  465. defer st.Close()
  466. st.greet()
  467. getSlash(st)
  468. // Send GOAWAY and wait for it to be processed.
  469. st.fr.WriteGoAway(1, ErrCodeNo, nil)
  470. go func() {
  471. for {
  472. select {
  473. case <-ready:
  474. return
  475. default:
  476. }
  477. st.sc.serveMsgCh <- func(loopNum int) {
  478. if !st.sc.pushEnabled {
  479. readyOnce.Do(func() { close(ready) })
  480. }
  481. }
  482. }
  483. }()
  484. if err := <-errc; err != nil {
  485. t.Error(err)
  486. }
  487. }