Files
httprunner/server/main.go

155 lines
3.4 KiB
Go

package server
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/httprunner/httprunner/v5/mcphost"
"github.com/httprunner/httprunner/v5/uixt"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"
)
func NewRouter() *Router {
router := &Router{
Engine: gin.Default(),
}
router.Init()
return router
}
type Router struct {
*gin.Engine
mcpHost *mcphost.MCPHost
}
func (r *Router) InitMCPHost(configPath string) error {
mcpHost, err := mcphost.NewMCPHost(configPath, true)
if err != nil {
log.Error().Err(err).Msg("init MCP host failed")
return err
}
r.mcpHost = mcpHost
return nil
}
func (r *Router) Init() {
r.Engine.Use(r.teardown())
r.Engine.GET("/ping", r.pingHandler)
r.Engine.GET("/", r.pingHandler)
r.Engine.POST("/", r.pingHandler)
apiV1PlatformSerial := r.Group("/api/v1").Group("/:platform").Group("/:serial")
// tool operations
apiV1PlatformSerial.POST("/tool/invoke", r.invokeToolHandler)
// uixt operations
apiV1PlatformSerial.POST("/uixt/action", r.uixtActionHandler)
apiV1PlatformSerial.POST("/uixt/actions", r.uixtActionsHandler)
}
func (r *Router) Run(port int) error {
// Create HTTP server
server := &http.Server{
Addr: fmt.Sprintf("localhost:%d", port),
Handler: r.Engine,
}
// Channel to listen for interrupt signal
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
// Start server in a goroutine
go func() {
log.Info().Int("port", port).Msg("Starting hrp server")
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Error().Err(err).Msg("HTTP server failed to start")
}
}()
// Wait for interrupt signal
<-quit
log.Info().Msg("Shutting down hrp server...")
// Create a context with timeout for graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Shutdown MCP host first if it exists
if r.mcpHost != nil {
log.Info().Msg("Shutting down MCP host...")
r.mcpHost.Shutdown()
}
// Shutdown HTTP server
if err := server.Shutdown(ctx); err != nil {
log.Error().Err(err).Msg("hrp server forced to shutdown")
return err
}
log.Info().Msg("hrp server exited")
return nil
}
func (r *Router) pingHandler(c *gin.Context) {
RenderSuccess(c, true)
}
func (r *Router) teardown() gin.HandlerFunc {
return func(c *gin.Context) {
logID := c.Request.Header.Get("x-tt-logid")
startTime := time.Now()
// 结束处理后打印日志
fmt.Printf("[GIN] %s | %s | %-7s \"%s\"\n",
startTime.Format("2006/01/02 - 15:04:05"),
logID,
c.Request.Method,
c.Request.URL.Path,
)
// 执行请求处理器
c.Next()
driverObj, exists := c.Get("driver")
if exists {
if driver, ok := driverObj.(*uixt.XTDriver); ok {
_ = driver.TearDown()
}
}
deviceObj, exists := c.Get("device")
if exists {
if device, ok := deviceObj.(uixt.IDevice); ok {
err := device.Teardown()
if err != nil {
log.Error().Err(err)
}
}
}
// 处理请求后获取结束时间
endTime := time.Now()
latency := endTime.Sub(startTime)
// 获取请求的状态码、客户端IP等信息
statusCode := c.Writer.Status()
// 结束处理后打印日志
fmt.Printf("[GIN] %s | %d | %v | %s | %-7s \"%s\"\n",
endTime.Format("2006/01/02 - 15:04:05"),
statusCode,
latency,
logID,
c.Request.Method,
c.Request.URL.Path,
)
c.Writer.Flush()
}
}