From: Matus Fabian Date: Thu, 17 Jul 2025 12:58:08 +0000 (-0400) Subject: hs-test: h2 client testing with h2spec X-Git-Tag: v26.02-rc0~165 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F45%2F43445%2F4;p=vpp.git hs-test: h2 client testing with h2spec Type: test Change-Id: If588fa070adebb631bf71fc8c3e82a00ba628a69 Signed-off-by: Matus Fabian --- diff --git a/extras/hs-test/infra/hst_suite.go b/extras/hs-test/infra/hst_suite.go index b610b66b614..d7d9eb89f71 100644 --- a/extras/hs-test/infra/hst_suite.go +++ b/extras/hs-test/infra/hst_suite.go @@ -93,6 +93,65 @@ var reservedPorts = []string{ "6633", "6081", "53053", + // reserved for h2specd + "30000", + "30001", + "30002", + "30003", + "30004", + "30005", + "30006", + "30007", + "30008", + "30009", + "30010", + "30011", + "30012", + "30013", + "30014", + "30015", + "30016", + "30017", + "30018", + "30019", + "30020", + "30021", + "30022", + "30023", + "30024", + "30025", + "30026", + "30027", + "30028", + "30029", + "30030", + "30031", + "30032", + "30033", + "30034", + "30035", + "30036", + "30037", + "30038", + "30039", + "30040", + "30041", + "30042", + "30043", + "30044", + "30045", + "30046", + "30047", + "30048", + "30049", + "30050", + "30051", + "30052", + "30053", + "30054", + "30055", + "30056", + "30080", } // used for colorful ReportEntry @@ -610,6 +669,7 @@ func (s *HstSuite) GeneratePort() string { } reservedPorts = append(reservedPorts, port) s.numOfNewPorts++ + s.Log("generated port " + port) return port } diff --git a/extras/hs-test/infra/suite_http2.go b/extras/hs-test/infra/suite_http2.go index d67399b577c..b26ffd9e978 100644 --- a/extras/hs-test/infra/suite_http2.go +++ b/extras/hs-test/infra/suite_http2.go @@ -2,7 +2,9 @@ package hst import ( "bytes" + "fmt" "io" + "net/http" "os" "reflect" "runtime" @@ -13,6 +15,7 @@ import ( "fd.io/hs-test/h2spec_extras" . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" + "github.com/summerwind/h2spec" "github.com/summerwind/h2spec/config" "github.com/summerwind/h2spec/generic" "github.com/summerwind/h2spec/hpack" @@ -24,6 +27,11 @@ var h2Tests = map[string][]func(s *Http2Suite){} var h2SoloTests = map[string][]func(s *Http2Suite){} var h2MWTests = map[string][]func(s *Http2Suite){} +const ( + h2specdFromPort int = 30000 + h2specdReportPort int = 30080 +) + type Http2Suite struct { HstSuite Interfaces struct { @@ -36,9 +44,10 @@ type Http2Suite struct { NginxServer *Container } Ports struct { - Port1 string - Port1AsInt int - Port2 string + Port1 string + Port1AsInt int + Port2 string + H2specdAsInt int } } @@ -396,7 +405,6 @@ var specs = []struct { {ExtrasTestGroup, extrasTests}, } -// Marked as pending since http plugin is not build with http/2 enabled by default var _ = Describe("H2SpecSuite", Ordered, ContinueOnFailure, func() { var s Http2Suite BeforeAll(func() { @@ -472,3 +480,163 @@ var _ = Describe("H2SpecSuite", Ordered, ContinueOnFailure, func() { } } }) + +func h2specdVerifyResult(s Http2Suite, nExecuted int) bool { + client := NewHttpClient(time.Second*5, false) + uri := fmt.Sprintf("http://%s:%d/report", s.HostAddr(), h2specdReportPort) + req, err := http.NewRequest("GET", uri, nil) + s.AssertNil(err, fmt.Sprint(err)) + resp, err := client.Do(req) + s.AssertNil(err, fmt.Sprint(err)) + defer resp.Body.Close() + report, err := io.ReadAll(resp.Body) + s.AssertContains(string(report), "0 failed") + expected := fmt.Sprintf("
%d tests, %d passed", nExecuted, nExecuted) + if !strings.Contains(string(report), expected) { + return false + } + return true +} + +var _ = Describe("H2SpecClientSuite", Ordered, Serial, func() { + var s Http2Suite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TeardownSuite() + }) + AfterEach(func() { + s.TeardownTest() + }) + + testCases := []struct { + desc string + portOffset int + }{ + {desc: "client/1/1", portOffset: 0}, + {desc: "client/4.1/1", portOffset: 1}, + {desc: "client/4.1/2", portOffset: 2}, + {desc: "client/4.1/3", portOffset: 3}, + // TODO: message framing without content length using END_STREAM flag + //{desc: "client/4.2/1", portOffset: 4}, + //{desc: "client/4.2/2", portOffset: 5}, + {desc: "client/4.2/3", portOffset: 6}, + {desc: "client/4.3/1", portOffset: 7}, + {desc: "client/5.1/1", portOffset: 8}, + {desc: "client/5.1/2", portOffset: 9}, + {desc: "client/5.1/3", portOffset: 10}, + {desc: "client/5.1/4", portOffset: 11}, + // TODO: message framing without content length using END_STREAM flag + //{desc: "client/5.1/5", portOffset: 12}, + //{desc: "client/5.1/6", portOffset: 13}, + //{desc: "client/5.1/7", portOffset: 14}, + {desc: "client/5.1/8", portOffset: 15}, + {desc: "client/5.1/9", portOffset: 16}, + {desc: "client/5.1/10", portOffset: 17}, + {desc: "client/5.1.1/1", portOffset: 18}, + {desc: "client/5.4.1/1", portOffset: 19}, + {desc: "client/5.4.1/2", portOffset: 20}, + {desc: "client/5.5/1", portOffset: 21}, + {desc: "client/6.1/1", portOffset: 22}, + {desc: "client/6.1/2", portOffset: 23}, + {desc: "client/6.1/3", portOffset: 24}, + {desc: "client/6.2/1", portOffset: 25}, + {desc: "client/6.2/2", portOffset: 26}, + {desc: "client/6.2/3", portOffset: 27}, + // PRIORITY is deprecated + //{desc: "client/6.3/1", portOffset: 28}, + //{desc: "client/6.3/2", portOffset: 29}, + {desc: "client/6.4/1", portOffset: 30}, + {desc: "client/6.4/2", portOffset: 31}, + {desc: "client/6.4/3", portOffset: 32}, + {desc: "client/6.5/1", portOffset: 33}, + {desc: "client/6.5/2", portOffset: 34}, + {desc: "client/6.5/3", portOffset: 35}, + {desc: "client/6.5.2/1", portOffset: 36}, + {desc: "client/6.5.2/2", portOffset: 37}, + {desc: "client/6.5.2/3", portOffset: 38}, + {desc: "client/6.5.2/4", portOffset: 39}, + {desc: "client/6.5.3/1", portOffset: 40}, + {desc: "client/6.7/1", portOffset: 41}, + {desc: "client/6.7/2", portOffset: 42}, + {desc: "client/6.7/3", portOffset: 43}, + {desc: "client/6.7/4", portOffset: 44}, + {desc: "client/6.8/1", portOffset: 45}, + {desc: "client/6.9/1", portOffset: 46}, + // TODO: message framing without content length using END_STREAM flag + //{desc: "client/6.9/2", portOffset: 47}, + {desc: "client/6.9/3", portOffset: 48}, + {desc: "client/6.9.1/1", portOffset: 49}, + // TODO: message framing without content length using END_STREAM flag + //{desc: "client/6.9.1/2", portOffset: 50}, + {desc: "client/6.10/1", portOffset: 51}, + {desc: "client/6.10/2", portOffset: 52}, + {desc: "client/6.10/3", portOffset: 53}, + {desc: "client/6.10/4", portOffset: 54}, + {desc: "client/6.10/5", portOffset: 55}, + {desc: "client/6.10/6", portOffset: 56}, + } + + nExecuted := 0 + for _, test := range testCases { + test := test + testName := "http2_test.go/h2spec_" + strings.ReplaceAll(test.desc, "/", "_") + It(testName, func(ctx SpecContext) { + s.Log(testName + ": BEGIN") + nExecuted++ + serverAddress := s.HostAddr() + wd, _ := os.Getwd() + conf := &config.Config{ + Host: serverAddress, + Port: h2specdReportPort, + Timeout: 20 * time.Second, + MaxHeaderLen: 4096, + TLS: true, + CertFile: wd + "/resources/cert/localhost.crt", + CertKeyFile: wd + "/resources/cert/localhost.key", + Verbose: true, + DryRun: false, + Exec: "", + FromPort: h2specdFromPort, + Sections: []string{}, + } + //capture h2spec output so it will be in log + oldStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + go h2spec.RunClientSpec(conf) + + cmd := fmt.Sprintf("http client timeout 5 verbose uri https://%s:%d/", serverAddress, h2specdFromPort+test.portOffset) + res := s.Containers.Vpp.VppInstance.Vppctl(cmd) + s.Log(res) + s.AssertNotContains(res, "error: timeout") + + oChan := make(chan string) + go func() { + var buf bytes.Buffer + io.Copy(&buf, r) + oChan <- buf.String() + }() + + //restore to normal state + w.Close() + os.Stdout = oldStdout + o := <-oChan + s.Log(o) + + //read report + for nTries := 0; nTries < 30; nTries++ { + if h2specdVerifyResult(s, nExecuted) { + break + } + time.Sleep(1 * time.Second) + } + + }, SpecTimeout(TestTimeout)) + } +})