| // Copyright 2016 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| //go:build !(go1.27 && !http2legacy) | |
| package http2 | |
| import ( | |
| "fmt" | |
| "math" | |
| "reflect" | |
| "testing" | |
| ) | |
| func makeWriteNonStreamRequest() FrameWriteRequest { | |
| return FrameWriteRequest{writeSettingsAck{}, nil, nil} | |
| } | |
| func makeWriteHeadersRequest(streamID uint32) FrameWriteRequest { | |
| st := &stream{id: streamID} | |
| return FrameWriteRequest{&writeResHeaders{streamID: streamID, httpResCode: 200}, st, nil} | |
| } | |
| func makeHandlerPanicRST(streamID uint32) FrameWriteRequest { | |
| st := &stream{id: streamID} | |
| return FrameWriteRequest{&handlerPanicRST{StreamID: streamID}, st, nil} | |
| } | |
| func makeWriteRSTStream(streamID uint32) FrameWriteRequest { | |
| return FrameWriteRequest{write: streamError(streamID, ErrCodeInternal)} | |
| } | |
| func checkConsume(wr FrameWriteRequest, nbytes int32, want []FrameWriteRequest) error { | |
| consumed, rest, n := wr.Consume(nbytes) | |
| var wantConsumed, wantRest FrameWriteRequest | |
| switch len(want) { | |
| case 0: | |
| case 1: | |
| wantConsumed = want[0] | |
| case 2: | |
| wantConsumed = want[0] | |
| wantRest = want[1] | |
| } | |
| if !reflect.DeepEqual(consumed, wantConsumed) || !reflect.DeepEqual(rest, wantRest) || n != len(want) { | |
| return fmt.Errorf("got %v, %v, %v\nwant %v, %v, %v", consumed, rest, n, wantConsumed, wantRest, len(want)) | |
| } | |
| return nil | |
| } | |
| func TestFrameWriteRequestNonData(t *testing.T) { | |
| wr := makeWriteNonStreamRequest() | |
| if got, want := wr.DataSize(), 0; got != want { | |
| t.Errorf("DataSize: got %v, want %v", got, want) | |
| } | |
| // Non-DATA frames are always consumed whole. | |
| if err := checkConsume(wr, 0, []FrameWriteRequest{wr}); err != nil { | |
| t.Errorf("Consume:\n%v", err) | |
| } | |
| wr = makeWriteRSTStream(123) | |
| if got, want := wr.DataSize(), 0; got != want { | |
| t.Errorf("DataSize: got %v, want %v", got, want) | |
| } | |
| // RST_STREAM frames are always consumed whole. | |
| if err := checkConsume(wr, 0, []FrameWriteRequest{wr}); err != nil { | |
| t.Errorf("Consume:\n%v", err) | |
| } | |
| } | |
| // #49741 RST_STREAM and Control frames should have more priority than data | |
| // frames to avoid blocking streams caused by clients not able to drain the | |
| // queue. | |
| func TestFrameWriteRequestWithData(t *testing.T) { | |
| st := &stream{ | |
| id: 1, | |
| sc: &serverConn{maxFrameSize: 16}, | |
| } | |
| const size = 32 | |
| wr := FrameWriteRequest{&writeData{st.id, make([]byte, size), true}, st, make(chan error)} | |
| if got, want := wr.DataSize(), size; got != want { | |
| t.Errorf("DataSize: got %v, want %v", got, want) | |
| } | |
| // No flow-control bytes available: cannot consume anything. | |
| if err := checkConsume(wr, math.MaxInt32, []FrameWriteRequest{}); err != nil { | |
| t.Errorf("Consume(limited by flow control):\n%v", err) | |
| } | |
| wr = makeWriteNonStreamRequest() | |
| if got, want := wr.DataSize(), 0; got != want { | |
| t.Errorf("DataSize: got %v, want %v", got, want) | |
| } | |
| // Non-DATA frames are always consumed whole. | |
| if err := checkConsume(wr, 0, []FrameWriteRequest{wr}); err != nil { | |
| t.Errorf("Consume:\n%v", err) | |
| } | |
| wr = makeWriteRSTStream(1) | |
| if got, want := wr.DataSize(), 0; got != want { | |
| t.Errorf("DataSize: got %v, want %v", got, want) | |
| } | |
| // RST_STREAM frames are always consumed whole. | |
| if err := checkConsume(wr, 0, []FrameWriteRequest{wr}); err != nil { | |
| t.Errorf("Consume:\n%v", err) | |
| } | |
| } | |
| func TestFrameWriteRequestData(t *testing.T) { | |
| st := &stream{ | |
| id: 1, | |
| sc: &serverConn{maxFrameSize: 16}, | |
| } | |
| const size = 32 | |
| wr := FrameWriteRequest{&writeData{st.id, make([]byte, size), true}, st, make(chan error)} | |
| if got, want := wr.DataSize(), size; got != want { | |
| t.Errorf("DataSize: got %v, want %v", got, want) | |
| } | |
| // No flow-control bytes available: cannot consume anything. | |
| if err := checkConsume(wr, math.MaxInt32, []FrameWriteRequest{}); err != nil { | |
| t.Errorf("Consume(limited by flow control):\n%v", err) | |
| } | |
| // Add enough flow-control bytes to consume the entire frame, | |
| // but we're now restricted by st.sc.maxFrameSize. | |
| st.flow.add(size) | |
| want := []FrameWriteRequest{ | |
| { | |
| write: &writeData{st.id, make([]byte, st.sc.maxFrameSize), false}, | |
| stream: st, | |
| done: nil, | |
| }, | |
| { | |
| write: &writeData{st.id, make([]byte, size-st.sc.maxFrameSize), true}, | |
| stream: st, | |
| done: wr.done, | |
| }, | |
| } | |
| if err := checkConsume(wr, math.MaxInt32, want); err != nil { | |
| t.Errorf("Consume(limited by maxFrameSize):\n%v", err) | |
| } | |
| rest := want[1] | |
| // Consume 8 bytes from the remaining frame. | |
| want = []FrameWriteRequest{ | |
| { | |
| write: &writeData{st.id, make([]byte, 8), false}, | |
| stream: st, | |
| done: nil, | |
| }, | |
| { | |
| write: &writeData{st.id, make([]byte, size-st.sc.maxFrameSize-8), true}, | |
| stream: st, | |
| done: wr.done, | |
| }, | |
| } | |
| if err := checkConsume(rest, 8, want); err != nil { | |
| t.Errorf("Consume(8):\n%v", err) | |
| } | |
| rest = want[1] | |
| // Consume all remaining bytes. | |
| want = []FrameWriteRequest{ | |
| { | |
| write: &writeData{st.id, make([]byte, size-st.sc.maxFrameSize-8), true}, | |
| stream: st, | |
| done: wr.done, | |
| }, | |
| } | |
| if err := checkConsume(rest, math.MaxInt32, want); err != nil { | |
| t.Errorf("Consume(remainder):\n%v", err) | |
| } | |
| } | |
| func TestFrameWriteRequest_StreamID(t *testing.T) { | |
| const streamID = 123 | |
| wr := FrameWriteRequest{write: streamError(streamID, ErrCodeNo)} | |
| if got := wr.StreamID(); got != streamID { | |
| t.Errorf("FrameWriteRequest(StreamError) = %v; want %v", got, streamID) | |
| } | |
| } |