http: return more than data from server app

Server app could return headers in front of body/data buffer.
Offers apis for building and serialization of headers section.
HTTP layer now only add Date, Server and Content-Lengths headers,
rest is up to app. Well known header names are predefined.

Type: improvement

Change-Id: If778bdfc9acf6b0d11a48f0a745a3a56c96c2436
Signed-off-by: Matus Fabian <matfabia@cisco.com>
This commit is contained in:
Matus Fabian
2024-06-20 17:08:26 +02:00
committed by Florin Coras
parent 1f870c9bdc
commit 8ca6ce6fe1
15 changed files with 655 additions and 157 deletions

View File

@ -9,7 +9,6 @@ import (
"time"
. "fd.io/hs-test/infra"
. "github.com/onsi/ginkgo/v2"
)
func init() {
@ -21,7 +20,7 @@ func init() {
HttpContentLengthTest, HttpStaticBuildInUrlGetIfListTest, HttpStaticBuildInUrlGetVersionTest,
HttpStaticMacTimeTest, HttpStaticBuildInUrlGetVersionVerboseTest, HttpVersionNotSupportedTest,
HttpInvalidContentLengthTest, HttpInvalidTargetSyntaxTest, HttpStaticPathTraversalTest, HttpUriDecodeTest,
HttpHeadersTest)
HttpHeadersTest, HttpStaticFileHandler)
RegisterNoTopoSoloTests(HttpStaticPromTest, HttpTpsTest, HttpTpsInterruptModeTest)
}
@ -89,19 +88,81 @@ func HttpCliConnectErrorTest(s *VethsSuite) {
}
func HttpStaticPromTest(s *NoTopoSuite) {
finished := make(chan error, 1)
query := "stats.prom"
vpp := s.GetContainerByName("vpp").VppInstance
serverAddress := s.GetInterfaceByName(TapInterfaceName).Peer.Ip4AddressString()
s.Log(vpp.Vppctl("http static server uri tcp://" + serverAddress + "/80 url-handlers"))
s.Log(vpp.Vppctl("prom enable"))
time.Sleep(time.Second * 5)
go func() {
defer GinkgoRecover()
s.StartWget(finished, serverAddress, "80", query, "")
}()
err := <-finished
s.AssertNil(err)
client := NewHttpClient()
req, err := http.NewRequest("GET", "http://"+serverAddress+":80/"+query, nil)
s.AssertNil(err, fmt.Sprint(err))
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, false))
s.AssertEqual(200, resp.StatusCode)
s.AssertContains(resp.Header.Get("Content-Type"), "text")
s.AssertContains(resp.Header.Get("Content-Type"), "plain")
s.AssertNotEqual(int64(0), resp.ContentLength)
_, err = io.ReadAll(resp.Body)
}
func HttpStaticFileHandler(s *NoTopoSuite) {
content := "<http><body><p>Hello</p></body></http>"
content2 := "<http><body><p>Page</p></body></http>"
vpp := s.GetContainerByName("vpp").VppInstance
vpp.Container.Exec("mkdir -p " + wwwRootPath)
vpp.Container.CreateFile(wwwRootPath+"/index.html", content)
vpp.Container.CreateFile(wwwRootPath+"/page.html", content2)
serverAddress := s.GetInterfaceByName(TapInterfaceName).Peer.Ip4AddressString()
s.Log(vpp.Vppctl("http static server www-root " + wwwRootPath + " uri tcp://" + serverAddress + "/80 debug cache-size 2m"))
client := NewHttpClient()
req, err := http.NewRequest("GET", "http://"+serverAddress+":80/index.html", nil)
s.AssertNil(err, fmt.Sprint(err))
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(200, resp.StatusCode)
s.AssertContains(resp.Header.Get("Content-Type"), "html")
s.AssertContains(resp.Header.Get("Cache-Control"), "max-age=")
s.AssertEqual(int64(len([]rune(content))), resp.ContentLength)
body, err := io.ReadAll(resp.Body)
s.AssertEqual(string(body), content)
o := vpp.Vppctl("show http static server cache verbose")
s.Log(o)
s.AssertContains(o, "index.html")
s.AssertNotContains(o, "page.html")
resp, err = client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(200, resp.StatusCode)
s.AssertContains(resp.Header.Get("Content-Type"), "html")
s.AssertContains(resp.Header.Get("Cache-Control"), "max-age=")
s.AssertEqual(int64(len([]rune(content))), resp.ContentLength)
body, err = io.ReadAll(resp.Body)
s.AssertEqual(string(body), content)
req, err = http.NewRequest("GET", "http://"+serverAddress+":80/page.html", nil)
s.AssertNil(err, fmt.Sprint(err))
resp, err = client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(200, resp.StatusCode)
s.AssertContains(resp.Header.Get("Content-Type"), "html")
s.AssertContains(resp.Header.Get("Cache-Control"), "max-age=")
s.AssertEqual(int64(len([]rune(content2))), resp.ContentLength)
body, err = io.ReadAll(resp.Body)
s.AssertEqual(string(body), content2)
o = vpp.Vppctl("show http static server cache verbose")
s.Log(o)
s.AssertContains(o, "index.html")
s.AssertContains(o, "page.html")
}
func HttpStaticPathTraversalTest(s *NoTopoSuite) {
@ -118,7 +179,11 @@ func HttpStaticPathTraversalTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(404, resp.StatusCode)
s.AssertEmpty(resp.Header.Get("Content-Type"))
s.AssertEmpty(resp.Header.Get("Cache-Control"))
s.AssertEqual(int64(0), resp.ContentLength)
}
func HttpStaticMovedTest(s *NoTopoSuite) {
@ -134,8 +199,12 @@ func HttpStaticMovedTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(301, resp.StatusCode)
s.AssertNotEqual("", resp.Header.Get("Location"))
s.AssertEqual("http://"+serverAddress+"/tmp.aaa/index.html", resp.Header.Get("Location"))
s.AssertEmpty(resp.Header.Get("Content-Type"))
s.AssertEmpty(resp.Header.Get("Cache-Control"))
s.AssertEqual(int64(0), resp.ContentLength)
}
func HttpStaticNotFoundTest(s *NoTopoSuite) {
@ -150,7 +219,11 @@ func HttpStaticNotFoundTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(404, resp.StatusCode)
s.AssertEmpty(resp.Header.Get("Content-Type"))
s.AssertEmpty(resp.Header.Get("Cache-Control"))
s.AssertEqual(int64(0), resp.ContentLength)
}
func HttpCliMethodNotAllowedTest(s *NoTopoSuite) {
@ -164,9 +237,11 @@ func HttpCliMethodNotAllowedTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(405, resp.StatusCode)
// TODO: need to be fixed in http code
//s.AssertNotEqual("", resp.Header.Get("Allow"))
s.AssertNotEqual("", resp.Header.Get("Allow"), "server MUST generate an Allow header")
s.AssertEmpty(resp.Header.Get("Content-Type"))
s.AssertEqual(int64(0), resp.ContentLength)
}
func HttpCliBadRequestTest(s *NoTopoSuite) {
@ -180,7 +255,10 @@ func HttpCliBadRequestTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(400, resp.StatusCode)
s.AssertEmpty(resp.Header.Get("Content-Type"))
s.AssertEqual(int64(0), resp.ContentLength)
}
func HttpStaticBuildInUrlGetVersionTest(s *NoTopoSuite) {
@ -194,6 +272,7 @@ func HttpStaticBuildInUrlGetVersionTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(200, resp.StatusCode)
data, err := io.ReadAll(resp.Body)
s.AssertNil(err, fmt.Sprint(err))
@ -203,6 +282,7 @@ func HttpStaticBuildInUrlGetVersionTest(s *NoTopoSuite) {
s.AssertNotContains(string(data), "build_by")
s.AssertNotContains(string(data), "build_host")
s.AssertNotContains(string(data), "build_dir")
s.AssertContains(resp.Header.Get("Content-Type"), "json")
}
func HttpStaticBuildInUrlGetVersionVerboseTest(s *NoTopoSuite) {
@ -216,6 +296,7 @@ func HttpStaticBuildInUrlGetVersionVerboseTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(200, resp.StatusCode)
data, err := io.ReadAll(resp.Body)
s.AssertNil(err, fmt.Sprint(err))
@ -225,6 +306,7 @@ func HttpStaticBuildInUrlGetVersionVerboseTest(s *NoTopoSuite) {
s.AssertContains(string(data), "build_by")
s.AssertContains(string(data), "build_host")
s.AssertContains(string(data), "build_dir")
s.AssertContains(resp.Header.Get("Content-Type"), "json")
}
func HttpStaticBuildInUrlGetIfListTest(s *NoTopoSuite) {
@ -238,11 +320,13 @@ func HttpStaticBuildInUrlGetIfListTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(200, resp.StatusCode)
data, err := io.ReadAll(resp.Body)
s.AssertNil(err, fmt.Sprint(err))
s.AssertContains(string(data), "interface_list")
s.AssertContains(string(data), s.GetInterfaceByName(TapInterfaceName).Peer.Name())
s.AssertContains(resp.Header.Get("Content-Type"), "json")
}
func HttpStaticBuildInUrlGetIfStatsTest(s *NoTopoSuite) {
@ -256,12 +340,14 @@ func HttpStaticBuildInUrlGetIfStatsTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(200, resp.StatusCode)
data, err := io.ReadAll(resp.Body)
s.AssertNil(err, fmt.Sprint(err))
s.AssertContains(string(data), "interface_stats")
s.AssertContains(string(data), "local0")
s.AssertContains(string(data), s.GetInterfaceByName(TapInterfaceName).Peer.Name())
s.AssertContains(resp.Header.Get("Content-Type"), "json")
}
func validatePostInterfaceStats(s *NoTopoSuite, data string) {
@ -284,10 +370,12 @@ func HttpStaticBuildInUrlPostIfStatsTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(200, resp.StatusCode)
data, err := io.ReadAll(resp.Body)
s.AssertNil(err, fmt.Sprint(err))
validatePostInterfaceStats(s, string(data))
s.AssertContains(resp.Header.Get("Content-Type"), "json")
}
func HttpStaticMacTimeTest(s *NoTopoSuite) {
@ -302,12 +390,14 @@ func HttpStaticMacTimeTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(200, resp.StatusCode)
data, err := io.ReadAll(resp.Body)
s.AssertNil(err, fmt.Sprint(err))
s.AssertContains(string(data), "mactime")
s.AssertContains(string(data), s.GetInterfaceByName(TapInterfaceName).Ip4AddressString())
s.AssertContains(string(data), s.GetInterfaceByName(TapInterfaceName).HwAddress.String())
s.AssertContains(resp.Header.Get("Content-Type"), "json")
}
func HttpInvalidRequestLineTest(s *NoTopoSuite) {
@ -444,7 +534,10 @@ func HttpMethodNotImplementedTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(501, resp.StatusCode)
s.AssertEmpty(resp.Header.Get("Content-Type"))
s.AssertEqual(int64(0), resp.ContentLength)
}
func HttpVersionNotSupportedTest(s *NoTopoSuite) {
@ -468,12 +561,13 @@ func HttpUriDecodeTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(200, resp.StatusCode)
data, err := io.ReadAll(resp.Body)
s.AssertNil(err, fmt.Sprint(err))
s.Log(string(data))
s.AssertNotContains(string(data), "unknown input")
s.AssertContains(string(data), "Compiler")
s.AssertContains(resp.Header.Get("Content-Type"), "html")
}
func HttpHeadersTest(s *NoTopoSuite) {
@ -539,5 +633,8 @@ func HeaderServerTest(s *NoTopoSuite) {
resp, err := client.Do(req)
s.AssertNil(err, fmt.Sprint(err))
defer resp.Body.Close()
s.Log(DumpHttpResp(resp, true))
s.AssertEqual(200, resp.StatusCode)
s.AssertEqual("http_cli_server", resp.Header.Get("Server"))
s.AssertContains(resp.Header.Get("Content-Type"), "html")
}