diff --git a/internal/context/context.go b/internal/context/context.go index 7b8416a..a30a7a3 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -32,7 +32,8 @@ type NFContext struct { TaskMutex sync.RWMutex NextTaskID uint64 - Messages []Message + Messages []Message + DragonBallData map[string]int32 } type Message struct { @@ -86,6 +87,16 @@ func InitNfContext() { nfContext.NextTaskID = 0 nfContext.Messages = make([]Message, 0) + + nfContext.DragonBallData = map[string]int32{ + "Goku": 7, + "Vegeta": 6, + "Gohan": 5, + "Trunks": 4, + "Piccolo": 3, + "Krillin": 2, + "Yamcha": 1, + } } func GetSelf() *NFContext { diff --git a/internal/sbi/api_dragonball.go b/internal/sbi/api_dragonball.go new file mode 100644 index 0000000..c40f4a9 --- /dev/null +++ b/internal/sbi/api_dragonball.go @@ -0,0 +1,141 @@ +package sbi + +import ( + "net/http" + + "github.com/Alonza0314/nf-example/internal/logger" + "github.com/gin-gonic/gin" +) + +func (s *Server) getDragonBallRoute() []Route { + return []Route{ + { + Name: "Hello Dragon Ball!", + Method: http.MethodGet, + Pattern: "/", + APIFunc: func(c *gin.Context) { + c.JSON(http.StatusOK, "Hello Dragon Ball!") + }, + // Use + // curl -X GET http://127.0.0.163:8000/dragonball/ + }, + { + Name: "Dragon Ball Search Character", + Method: http.MethodGet, + Pattern: "/character/:name", + APIFunc: s.HTTPSearchDragonBallCharacter, + // Use + // curl -X GET http://127.0.0.163:8000/dragonball/character/Goku + }, + { + Name: "Dragon Ball Fight", + Method: http.MethodPost, + Pattern: "/battle", + APIFunc: s.HTTPDragonBallFight, + // Use + // curl -X POST "http://127.0.0.163:8000/dragonball/battle" -d '{"name1": "Goku", "name2": "Vegeta"}' + }, + { + Name: "Add Dragon Ball Character", + Method: http.MethodPost, + Pattern: "/character", + APIFunc: s.HTTPAddDragonBallCharacter, + // Use + // curl -X POST "http://127.0.0.163:8000/dragonball/character" -d '{"Name": "Saitama", "Powerlevel": 10000}' + }, + { + Name: "Update Dragon Ball Character's Powerlevel", + Method: http.MethodPut, + Pattern: "/character/:name", + APIFunc: s.HTTPUpdateDragonBallCharacter, + // Use + // curl -X PUT "http://127.0.0.163:8000/dragonball/character/Goku" -d '{"Powerlevel": 500}' + }, + } +} + +func (s *Server) HTTPSearchDragonBallCharacter(c *gin.Context) { + logger.SBILog.Infof("In HTTPSearchDragonBallCharacter") + targetName := c.Param("name") + + if targetName == "" { + c.String(http.StatusBadRequest, "No name provided") + return + } + + s.Processor().SearchDragonBallCharacter(c, targetName) +} + +func (s *Server) HTTPDragonBallFight(c *gin.Context) { + logger.SBILog.Infof("In HTTPDragonBallFight") + + type RequestBody struct { + TargetName1 string `json:"name1"` + TargetName2 string `json:"name2"` + } + var requestbody RequestBody + if err := c.ShouldBindBodyWithJSON(&requestbody); err != nil { + c.String(http.StatusBadRequest, "error") + return + } + if requestbody.TargetName1 == "" { + c.String(http.StatusBadRequest, "No name1 provided") + return + } + if requestbody.TargetName2 == "" { + c.String(http.StatusBadRequest, "No name2 provided") + return + } + + s.Processor().FightDragonBall(c, requestbody.TargetName1, requestbody.TargetName2) +} + +func (s *Server) HTTPAddDragonBallCharacter(c *gin.Context) { + logger.SBILog.Infof("In HTTPAddDragonBallCharacter") + + type RequestBody struct { + Name string `json:"name"` + PowerLevel *int32 `json:"powerLevel"` + } + var requestbody RequestBody + if err := c.ShouldBindBodyWithJSON(&requestbody); err != nil { + c.String(http.StatusBadRequest, "error") + return + } + if requestbody.Name == "" { + c.String(http.StatusBadRequest, "No name provided") + return + } + if requestbody.PowerLevel == nil { + c.String(http.StatusBadRequest, "No Powerlevel provided") + return + } + + s.Processor().AddDragonBallCharacter(c, requestbody.Name, *requestbody.PowerLevel) +} + +func (s *Server) HTTPUpdateDragonBallCharacter(c *gin.Context) { + logger.SBILog.Infof("In HTTPUpdateDragonBallCharacter") + targetName := c.Param("name") + + if targetName == "" { + c.String(http.StatusBadRequest, "No name provided") + return + } + + type RequestBody struct { + PowerLevel *int32 `json:"powerLevel"` + } + var requestbody RequestBody + if err := c.ShouldBindBodyWithJSON(&requestbody); err != nil { + c.String(http.StatusBadRequest, "error") + return + } + + if requestbody.PowerLevel == nil { + c.String(http.StatusBadRequest, "No Powerlevel provided") + return + } + + s.Processor().UpdateDragonBallCharacter(c, targetName, *requestbody.PowerLevel) +} diff --git a/internal/sbi/api_dragonball_test.go b/internal/sbi/api_dragonball_test.go new file mode 100644 index 0000000..905e267 --- /dev/null +++ b/internal/sbi/api_dragonball_test.go @@ -0,0 +1,242 @@ +package sbi_test + +import ( + "bytes" + "net/http" + "net/http/httptest" + "testing" + + "github.com/Alonza0314/nf-example/internal/sbi" + "github.com/Alonza0314/nf-example/pkg/factory" + "github.com/gin-gonic/gin" + "go.uber.org/mock/gomock" +) + +// go test will automatically run any function with a name beginning with "Test" + +func setupTestServer() *sbi.Server { + gin.SetMode(gin.TestMode) + mockCtrl := gomock.NewController(nil) + nfApp := sbi.NewMocknfApp(mockCtrl) + nfApp.EXPECT().Config().Return(&factory.Config{ + Configuration: &factory.Configuration{ + Sbi: &factory.Sbi{Port: 8000}, + }, + }).AnyTimes() + server := sbi.NewServer(nfApp, "") + return server +} + +func Test_HTTPSearchDragonBallCharacter(t *testing.T) { + server := setupTestServer() + + t.Run("No name provided", func(t *testing.T) { + const EXPECTED_STATUS = http.StatusBadRequest + const EXPECTED_BODY = "No name provided" + + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + + var err error + ginCtx.Request, err = http.NewRequest("GET", "/dragonball", nil) + if err != nil { + t.Errorf("Failed to create request: %s", err) + return + } + + server.HTTPSearchDragonBallCharacter(ginCtx) + + if httpRecorder.Code != EXPECTED_STATUS { + t.Errorf("Expected status code %d, got %d", EXPECTED_STATUS, httpRecorder.Code) + } + + if httpRecorder.Body.String() != EXPECTED_BODY { + t.Errorf("Expected body %s, got %s", EXPECTED_BODY, httpRecorder.Body.String()) + } + }) +} + +func Test_HTTPDragonBallFight(t *testing.T) { + server := setupTestServer() + + // define test cases + tests := []struct { + name string + jsonBody string + expectedStatus int + expectedBody string + }{ + { + name: "error", + jsonBody: `{`, + expectedStatus: http.StatusBadRequest, + expectedBody: "error", + }, + { + name: "No name1 provided", + jsonBody: `{"name2": "Vegeta"}`, + expectedStatus: http.StatusBadRequest, + expectedBody: "No name1 provided", + }, + { + name: "No name2 provided", + jsonBody: `{"name1":"Goku"}`, + expectedStatus: http.StatusBadRequest, + expectedBody: "No name2 provided", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + + req, err := http.NewRequest("POST", "/dragonball/battle", bytes.NewBufferString(tc.jsonBody)) + if err != nil { + t.Errorf("Failed to create request: %s", err) + return + } + ginCtx.Request = req + + server.HTTPDragonBallFight(ginCtx) + + if httpRecorder.Code != tc.expectedStatus { + t.Errorf("expected status %d, got %d body=%s", + tc.expectedStatus, httpRecorder.Code, httpRecorder.Body.String()) + } + + if httpRecorder.Body.String() != tc.expectedBody { + t.Errorf("expected body %q, got %q", + tc.expectedBody, httpRecorder.Body.String()) + } + }) + } +} + +func Test_HTTPAddDragonBallCharacter(t *testing.T) { + gin.SetMode(gin.TestMode) + + mockCtrl := gomock.NewController(t) + nfApp := sbi.NewMocknfApp(mockCtrl) + nfApp.EXPECT().Config().Return(&factory.Config{ + Configuration: &factory.Configuration{ + Sbi: &factory.Sbi{Port: 8000}, + }, + }).AnyTimes() + server := sbi.NewServer(nfApp, "") + + // define test cases + tests := []struct { + name string + jsonBody string + expectedStatus int + expectedBody string + }{ + { + name: "error", + jsonBody: `{`, + expectedStatus: http.StatusBadRequest, + expectedBody: "error", + }, + { + name: "No name provided", + jsonBody: `{"powerLevel":100}`, + expectedStatus: http.StatusBadRequest, + expectedBody: "No name provided", + }, + { + name: "No Powerlevel provided", + jsonBody: `{"name":"Goku"}`, + expectedStatus: http.StatusBadRequest, + expectedBody: "No Powerlevel provided", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + + req, err := http.NewRequest("POST", "/dragonball", bytes.NewBufferString(tc.jsonBody)) + if err != nil { + t.Errorf("Failed to create request: %s", err) + return + } + ginCtx.Request = req + + server.HTTPAddDragonBallCharacter(ginCtx) + + if httpRecorder.Code != tc.expectedStatus { + t.Errorf("expected status %d, got %d body=%s", + tc.expectedStatus, httpRecorder.Code, httpRecorder.Body.String()) + } + + if httpRecorder.Body.String() != tc.expectedBody { + t.Errorf("expected body %q, got %q", + tc.expectedBody, httpRecorder.Body.String()) + } + }) + } +} + +func Test_HTTPUpdateDragonBallCharacter(t *testing.T) { + server := setupTestServer() + + // define test cases + tests := []struct { + name string + jsonBody string + expectedStatus int + expectedBody string + url_param string + }{ + { + name: "No name provided", + expectedStatus: http.StatusBadRequest, + expectedBody: "No name provided", + url_param: "", + }, + { + name: "error", + jsonBody: `{`, + expectedStatus: http.StatusBadRequest, + expectedBody: "error", + url_param: "/Character", + }, + { + name: "No Powerlevel provided", + jsonBody: `{}`, + expectedStatus: http.StatusBadRequest, + expectedBody: "No Powerlevel provided", + url_param: "/Character", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + // check if :name is provided + ginCtx.Params = gin.Params{gin.Param{Key: "name", Value: tc.url_param}} + + req, err := http.NewRequest("PUT", "/dragonball", bytes.NewBufferString(tc.jsonBody)) + if err != nil { + t.Errorf("Failed to create request: %s", err) + return + } + ginCtx.Request = req + + server.HTTPUpdateDragonBallCharacter(ginCtx) + + if httpRecorder.Code != tc.expectedStatus { + t.Errorf("expected status %d, got %d body=%s", + tc.expectedStatus, httpRecorder.Code, httpRecorder.Body.String()) + } + + if httpRecorder.Body.String() != tc.expectedBody { + t.Errorf("expected body %q, got %q", + tc.expectedBody, httpRecorder.Body.String()) + } + }) + } +} diff --git a/internal/sbi/processor/dragon_ball.go b/internal/sbi/processor/dragon_ball.go new file mode 100644 index 0000000..45c72a0 --- /dev/null +++ b/internal/sbi/processor/dragon_ball.go @@ -0,0 +1,59 @@ +package processor + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" +) + +func (p *Processor) SearchDragonBallCharacter(c *gin.Context, targetName string) { + pl, ok := p.Context().DragonBallData[targetName] + if !ok { + c.String(http.StatusNotFound, fmt.Sprintf("[%s] not found in Dragon Ball\n", targetName)) + return + } + + c.String(http.StatusOK, fmt.Sprintf("Character: %s, Powerlevel: %d\n", targetName, pl)) +} + +func (p *Processor) FightDragonBall(c *gin.Context, targetName1 string, targetName2 string) { + pl1, ok1 := p.Context().DragonBallData[targetName1] + pl2, ok2 := p.Context().DragonBallData[targetName2] + + if !ok1 { + c.String(http.StatusNotFound, fmt.Sprintf("[%s] not found in Dragon Ball\n", targetName1)) + return + } + if !ok2 { + c.String(http.StatusNotFound, fmt.Sprintf("[%s] not found in Dragon Ball\n", targetName2)) + return + } + + if pl1 > pl2 { + c.String(http.StatusOK, fmt.Sprintf("%s defeats %s\n", targetName1, targetName2)) + } else if pl1 < pl2 { + c.String(http.StatusOK, fmt.Sprintf("%s defeats %s\n", targetName2, targetName1)) + } else { + c.String(http.StatusOK, fmt.Sprintf("%s ties with %s\n", targetName1, targetName2)) + } +} + +func (p *Processor) AddDragonBallCharacter(c *gin.Context, targetName string, powerlevel int32) { + pl, ok := p.Context().DragonBallData[targetName] + if ok { + c.String(http.StatusConflict, fmt.Sprintf("Character %s already exists with Powerlevel %d\n", targetName, pl)) + return + } + p.Context().DragonBallData[targetName] = powerlevel + c.String(http.StatusCreated, fmt.Sprintf("Add Character %s with Powerlevel %d\n", targetName, powerlevel)) +} + +func (p *Processor) UpdateDragonBallCharacter(c *gin.Context, targetName string, powerlevel int32) { + if _, ok := p.Context().DragonBallData[targetName]; !ok { + c.String(http.StatusNotFound, fmt.Sprintf("Character %s not found\n", targetName)) + return + } + p.Context().DragonBallData[targetName] = powerlevel + c.String(http.StatusOK, fmt.Sprintf("Update Character %s with Powerlevel %d\n", targetName, powerlevel)) +} diff --git a/internal/sbi/processor/dragon_ball_test.go b/internal/sbi/processor/dragon_ball_test.go new file mode 100644 index 0000000..98daae5 --- /dev/null +++ b/internal/sbi/processor/dragon_ball_test.go @@ -0,0 +1,244 @@ +package processor_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + nf_context "github.com/Alonza0314/nf-example/internal/context" + "github.com/Alonza0314/nf-example/internal/sbi/processor" + "github.com/gin-gonic/gin" + gomock "go.uber.org/mock/gomock" +) + +func Test_SearchDragonBallCharacter(t *testing.T) { + gin.SetMode(gin.TestMode) + + mockCtrl := gomock.NewController(t) + processorNf := processor.NewMockProcessorNf(mockCtrl) + processor, err := processor.NewProcessor(processorNf) + if err != nil { + t.Errorf("Failed to create processor: %s", err) + return + } + + t.Run("Find Character That Exists", func(t *testing.T) { + const INPUT_NAME = "Goku" + const EXPECTED_STATUS = http.StatusOK + const EXPECTED_BODY = "Character: " + INPUT_NAME + ", Powerlevel: 7\n" + processorNf.EXPECT().Context().Return(&nf_context.NFContext{ + DragonBallData: map[string]int32{ + "Goku": 7, + }, + }) + + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + processor.SearchDragonBallCharacter(ginCtx, INPUT_NAME) + + if httpRecorder.Code != EXPECTED_STATUS { + t.Errorf("Expected status code %d, got %d", EXPECTED_STATUS, httpRecorder.Code) + } + + if httpRecorder.Body.String() != EXPECTED_BODY { + t.Errorf("Expected body %s, got %s", EXPECTED_BODY, httpRecorder.Body.String()) + } + }) + + t.Run("Find Character That Does Not Exist", func(t *testing.T) { + const INPUT_NAME = "Andy" + const EXPECTED_STATUS = http.StatusNotFound + const EXPECTED_BODY = "[" + INPUT_NAME + "] not found in Dragon Ball\n" + + processorNf.EXPECT().Context().Return(&nf_context.NFContext{ + DragonBallData: map[string]int32{ + "Goku": 7, + }, + }) + + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + processor.SearchDragonBallCharacter(ginCtx, INPUT_NAME) + + if httpRecorder.Code != EXPECTED_STATUS { + t.Errorf("Expected status code %d, got %d", EXPECTED_STATUS, httpRecorder.Code) + } + + if httpRecorder.Body.String() != EXPECTED_BODY { + t.Errorf("Expected body %s, got %s", EXPECTED_BODY, httpRecorder.Body.String()) + } + }) +} + +func Test_FightDragonBall(t *testing.T) { + gin.SetMode(gin.TestMode) + + mockCtrl := gomock.NewController(t) + processorNf := processor.NewMockProcessorNf(mockCtrl) + processor, err := processor.NewProcessor(processorNf) + if err != nil { + t.Errorf("Failed to create processor: %s", err) + return + } + + processorNf.EXPECT().Context().Return(&nf_context.NFContext{ + DragonBallData: map[string]int32{ + "Goku": 7, + "Vegeta": 6, + "Krillin": 7, + }, + }).AnyTimes() + + tests := []struct { + name string + targetName1 string + targetName2 string + expectedStatus int + expectedBody string + }{ + { + name: "targetName1 not found", + targetName1: "Andy", + targetName2: "Vegeta", + expectedStatus: http.StatusNotFound, + expectedBody: "[Andy] not found in Dragon Ball\n", + }, + { + name: "targetName2 not found", + targetName1: "Goku", + targetName2: "Andy", + expectedStatus: http.StatusNotFound, + expectedBody: "[Andy] not found in Dragon Ball\n", + }, + { + name: "Goku defeats Vegeta", + targetName1: "Goku", + targetName2: "Vegeta", + expectedStatus: http.StatusOK, + expectedBody: "Goku defeats Vegeta\n", + }, + { + name: "Vegeta defeats Krillin", + targetName1: "Vegeta", + targetName2: "Krillin", + expectedStatus: http.StatusOK, + expectedBody: "Krillin defeats Vegeta\n", + }, + { + name: "Tie", + targetName1: "Goku", + targetName2: "Krillin", + expectedStatus: http.StatusOK, + expectedBody: "Goku ties with Krillin\n", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + processor.FightDragonBall(ginCtx, tc.targetName1, tc.targetName2) + + if httpRecorder.Code != tc.expectedStatus { + t.Errorf("Expected status code %d, got %d", tc.expectedStatus, httpRecorder.Code) + } + if httpRecorder.Body.String() != tc.expectedBody { + t.Errorf("Expected body %q, got %q", tc.expectedBody, httpRecorder.Body.String()) + } + }) + } +} + +//nolint:dupl +func Test_AddDragonBallCharacter(t *testing.T) { + gin.SetMode(gin.TestMode) + + mockCtrl := gomock.NewController(t) + processorNf := processor.NewMockProcessorNf(mockCtrl) + + processorNf.EXPECT().Context().Return(&nf_context.NFContext{ + DragonBallData: map[string]int32{ + "Goku": 7, + }, + }).AnyTimes() + processor, err := processor.NewProcessor(processorNf) + if err != nil { + t.Errorf("Failed to create processor: %s", err) + return + } + + t.Run("Add existing character", func(t *testing.T) { + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + processor.AddDragonBallCharacter(ginCtx, "Goku", 7) + + if httpRecorder.Code != http.StatusConflict { + t.Errorf("Expected status code %d, got %d", http.StatusConflict, httpRecorder.Code) + } + expectedBody := "Character Goku already exists with Powerlevel 7\n" + if httpRecorder.Body.String() != expectedBody { + t.Errorf("Expected body %q, got %q", expectedBody, httpRecorder.Body.String()) + } + }) + + t.Run("Add new character", func(t *testing.T) { + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + processor.AddDragonBallCharacter(ginCtx, "Vegeta", 6) + + if httpRecorder.Code != http.StatusCreated { + t.Errorf("Expected status code %d, got %d", http.StatusCreated, httpRecorder.Code) + } + expectedBody := "Add Character Vegeta with Powerlevel 6\n" + if httpRecorder.Body.String() != expectedBody { + t.Errorf("Expected body %q, got %q", expectedBody, httpRecorder.Body.String()) + } + }) +} + +//nolint:dupl +func Test_UpdateDragonBallCharacter(t *testing.T) { + gin.SetMode(gin.TestMode) + + mockCtrl := gomock.NewController(t) + processorNf := processor.NewMockProcessorNf(mockCtrl) + + processorNf.EXPECT().Context().Return(&nf_context.NFContext{ + DragonBallData: map[string]int32{ + "Goku": 7, + }, + }).AnyTimes() + processor, err := processor.NewProcessor(processorNf) + if err != nil { + t.Errorf("Failed to create processor: %s", err) + return + } + + t.Run("Update non-existing character", func(t *testing.T) { + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + processor.UpdateDragonBallCharacter(ginCtx, "Vegeta", 6) + + if httpRecorder.Code != http.StatusNotFound { + t.Errorf("Expected status code %d, got %d", http.StatusNotFound, httpRecorder.Code) + } + expectedBody := "Character Vegeta not found\n" + if httpRecorder.Body.String() != expectedBody { + t.Errorf("Expected body %q, got %q", expectedBody, httpRecorder.Body.String()) + } + }) + + t.Run("Update existing character", func(t *testing.T) { + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + processor.UpdateDragonBallCharacter(ginCtx, "Goku", 10) + + if httpRecorder.Code != http.StatusOK { + t.Errorf("Expected status code %d, got %d", http.StatusOK, httpRecorder.Code) + } + expectedBody := "Update Character Goku with Powerlevel 10\n" + if httpRecorder.Body.String() != expectedBody { + t.Errorf("Expected body %q, got %q", expectedBody, httpRecorder.Body.String()) + } + }) +} diff --git a/internal/sbi/router.go b/internal/sbi/router.go index 5ee955b..83a9b7b 100644 --- a/internal/sbi/router.go +++ b/internal/sbi/router.go @@ -39,6 +39,7 @@ func applyRoutes(group *gin.RouterGroup, routes []Route) { func newRouter(s *Server) *gin.Engine { router := logger_util.NewGinWithLogrus(logger.GinLog) + // Add routes to each api group defaultGroup := router.Group("/default") applyRoutes(defaultGroup, s.getDefaultRoute()) @@ -57,11 +58,15 @@ func newRouter(s *Server) *gin.Engine { messageGroup := router.Group("/msg") // add for lab6 applyRoutes(messageGroup, s.getMessageRoute()) + dragonBallGroup := router.Group("/dragonball") + applyRoutes(dragonBallGroup, s.getDragonBallRoute()) + return router } func bindRouter(nf app.App, router *gin.Engine, tlsKeyLogPath string) (*http.Server, error) { sbiConfig := nf.Config().Configuration.Sbi bindAddr := fmt.Sprintf("%s:%d", sbiConfig.BindingIPv4, sbiConfig.Port) + // Use http2 for all SBI communication return httpwrapper.NewHttp2Server(bindAddr, tlsKeyLogPath, router) }