h2spec/spec/verifier.go

303 строки
5.8 KiB
Go

package spec
import (
"fmt"
"reflect"
"golang.org/x/net/http2"
)
const (
ExpectedConnectionClosed = "Connection closed"
ExpectedStreamClosed = "Stream closed"
ExpectedGoAwayFrame = "GOAWAY Frame (Error Code: %s)"
ExpectedRSTStreamFrame = "RST_STREAM Frame (Error Code: %s)"
)
// VerifyConnectionClose verifies whether the connection was closed.
func VerifyConnectionClose(conn *Conn) error {
var actual Event
passed := false
for !conn.Closed {
event := conn.WaitEvent()
switch ev := event.(type) {
case ConnectionClosedEvent:
passed = true
case TimeoutEvent:
if actual == nil {
actual = ev
}
default:
actual = ev
}
if passed {
break
}
}
if !passed {
return &TestError{
Expected: []string{ExpectedConnectionClosed},
Actual: actual.String(),
}
}
return nil
}
// VerifyConnectionError verifies whether a connection error of HTTP/2
// has occurred.
func VerifyConnectionError(conn *Conn, codes ...http2.ErrCode) error {
var actual Event
passed := false
for !conn.Closed {
ev := conn.WaitEvent()
switch event := ev.(type) {
case ConnectionClosedEvent:
passed = true
case GoAwayFrameEvent:
passed = VerifyErrorCode(codes, event.ErrCode)
case TimeoutEvent:
if actual == nil {
actual = event
}
default:
actual = event
}
if passed {
break
}
}
if !passed {
expected := []string{}
for _, code := range codes {
expected = append(expected, fmt.Sprintf(ExpectedGoAwayFrame, code))
}
expected = append(expected, ExpectedConnectionClosed)
return &TestError{
Expected: expected,
Actual: actual.String(),
}
}
return nil
}
// VerifyStreamError verifies whether a stream error of HTTP/2
// has occurred.
func VerifyStreamError(conn *Conn, codes ...http2.ErrCode) error {
var actual Event
passed := false
for !conn.Closed {
ev := conn.WaitEvent()
switch event := ev.(type) {
case ConnectionClosedEvent:
passed = true
case GoAwayFrameEvent:
passed = VerifyErrorCode(codes, event.ErrCode)
case RSTStreamFrameEvent:
passed = VerifyErrorCode(codes, event.ErrCode)
case TimeoutEvent:
if actual == nil {
actual = event
}
default:
actual = event
}
if passed {
break
}
}
if !passed {
expected := []string{}
for _, code := range codes {
expected = append(expected, fmt.Sprintf(ExpectedGoAwayFrame, code))
expected = append(expected, fmt.Sprintf(ExpectedRSTStreamFrame, code))
}
expected = append(expected, ExpectedConnectionClosed)
return &TestError{
Expected: expected,
Actual: actual.String(),
}
}
return nil
}
// VerifyStreamClose verifies whether a stream close of HTTP/2
// has occurred.
func VerifyStreamClose(conn *Conn) error {
var actual Event
passed := false
for !conn.Closed {
ev := conn.WaitEvent()
switch event := ev.(type) {
case DataFrameEvent:
if event.StreamEnded() {
passed = true
}
case HeadersFrameEvent:
if event.StreamEnded() {
passed = true
}
case RSTStreamFrameEvent:
if event.ErrCode == http2.ErrCodeNo {
passed = true
}
case TimeoutEvent:
if actual == nil {
actual = event
}
default:
actual = event
}
if passed {
break
}
}
if !passed {
return &TestError{
Expected: []string{ExpectedStreamClosed},
Actual: actual.String(),
}
}
return nil
}
// VerifyHeadersFrame verifies whether a HEADERS frame with specified
// stream ID has received.
func VerifyHeadersFrame(conn *Conn, streamID uint32) error {
actual, passed := conn.WaitEventByType(EventHeadersFrame)
switch event := actual.(type) {
case HeadersFrameEvent:
passed = (event.Header().StreamID == streamID)
default:
passed = false
}
if !passed {
expected := []string{
fmt.Sprintf("HEADERS Frame (stream_id:%d)", streamID),
}
return &TestError{
Expected: expected,
Actual: actual.String(),
}
}
return nil
}
// VerifySettingsFrameWithAck verifies whether a SETTINGS frame with
// ACK flag has received.
func VerifySettingsFrameWithAck(conn *Conn) error {
actual, passed := conn.WaitEventByType(EventSettingsFrame)
switch event := actual.(type) {
case SettingsFrameEvent:
passed = event.IsAck()
default:
passed = false
}
if !passed {
expected := []string{
"SETTINGS Frame (length:0, flags:0x01, stream_id:0)",
}
return &TestError{
Expected: expected,
Actual: actual.String(),
}
}
return nil
}
// VerifyPingFrameWithAck verifies whether a PING frame with ACK flag
// has received.
func VerifyPingFrameWithAck(conn *Conn, data [8]byte) error {
actual, passed := conn.WaitEventByType(EventPingFrame)
switch event := actual.(type) {
case PingFrameEvent:
passed = event.IsAck() && reflect.DeepEqual(event.Data, data)
default:
passed = false
}
if !passed {
var actualStr string
expected := []string{
fmt.Sprintf("PING Frame (length:8, flags:0x01, stream_id:0, opaque_data:%s)", data),
}
f, ok := actual.(PingFrameEvent)
if ok {
header := f.Header()
actualStr = fmt.Sprintf(
"PING Frame ((length:%d, flags:0x%02x, stream_id:%d, opaque_data: %s)",
header.Length,
header.Flags,
header.StreamID,
f.Data,
)
} else {
actualStr = actual.String()
}
return &TestError{
Expected: expected,
Actual: actualStr,
}
}
return nil
}
// VerifyEventType verifies whether a frame with specified type
// has received.
func VerifyEventType(conn *Conn, et EventType) error {
var actual Event
actual, passed := conn.WaitEventByType(et)
if !passed {
return &TestError{
Expected: []string{et.String()},
Actual: actual.String(),
}
}
return nil
}
// VerifyErrorCode verifies whether the specified error code is
// the expected error code.
func VerifyErrorCode(codes []http2.ErrCode, code http2.ErrCode) bool {
for _, c := range codes {
if c == code {
return true
}
}
return false
}