• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

murex / TCR / 19010290325

02 Nov 2025 09:22AM UTC coverage: 89.66% (-0.03%) from 89.694%
19010290325

push

github

mengdaming
Update node dependencies

5272 of 5880 relevant lines covered (89.66%)

1054.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.6
/src/http/web_ui_server.go
1
/*
2
Copyright (c) 2024 Murex
3

4
Permission is hereby granted, free of charge, to any person obtaining a copy
5
of this software and associated documentation files (the "Software"), to deal
6
in the Software without restriction, including without limitation the rights
7
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
copies of the Software, and to permit persons to whom the Software is
9
furnished to do so, subject to the following conditions:
10

11
The above copyright notice and this permission notice shall be included in all
12
copies or substantial portions of the Software.
13

14
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
SOFTWARE.
21
*/
22

23
package http
24

25
import (
26
        "context"
27
        "fmt"
28
        "net/http"
29
        "time"
30

31
        "github.com/gin-contrib/cors"
32
        "github.com/gin-contrib/static"
33
        "github.com/gin-gonic/gin"
34
        "github.com/murex/tcr/engine"
35
        "github.com/murex/tcr/http/api"
36
        "github.com/murex/tcr/http/ws"
37
        "github.com/murex/tcr/params"
38
        "github.com/murex/tcr/report"
39
        "github.com/murex/tcr/runmode"
40
)
41

42
// WebUIServer provides a TCR interface implementation over HTTP. It acts
43
// as a proxy between the TCR engine and HTTP clients
44
type WebUIServer struct {
45
        tcr              engine.TCRInterface
46
        params           params.Params
47
        host             string
48
        devMode          bool
49
        router           *gin.Engine
50
        httpServer       *http.Server
51
        websocketTimeout time.Duration
52
}
53

54
// New creates a new instance of WebUIServer
55
func New(p params.Params, tcr engine.TCRInterface) *WebUIServer {
21✔
56
        webUIServer := WebUIServer{
21✔
57
                tcr: tcr,
21✔
58
                // host: "0.0.0.0", // To enable connections from a remote host
21✔
59
                host:             "127.0.0.1", // To restrict connections to local host only
21✔
60
                devMode:          p.Trace == "http",
21✔
61
                router:           nil,
21✔
62
                httpServer:       nil,
21✔
63
                websocketTimeout: 2 * time.Hour, // default timeout value
21✔
64
                params:           p,
21✔
65
        }
21✔
66
        tcr.AttachUI(&webUIServer, false)
21✔
67
        return &webUIServer
21✔
68
}
21✔
69

70
// Start starts TCR HTTP server
71
func (webUIServer *WebUIServer) Start() {
3✔
72
        report.PostInfo("Starting HTTP server on port ", webUIServer.params.PortNumber)
3✔
73
        webUIServer.initGinEngine()
3✔
74
        webUIServer.addStaticRoutes()
3✔
75
        webUIServer.addAPIRoutes()
3✔
76
        webUIServer.addWebsocketRoutes()
3✔
77
        webUIServer.startGinEngine()
3✔
78
}
3✔
79

80
func (webUIServer *WebUIServer) initGinEngine() {
18✔
81
        gin.SetMode(gin.ReleaseMode)
18✔
82

18✔
83
        // gin.Default() uses gin.Logger() which should be turned off in TCR production version
18✔
84
        webUIServer.router = gin.New()
18✔
85
        webUIServer.router.Use(gin.Recovery())
18✔
86

18✔
87
        if webUIServer.InDevMode() {
21✔
88
                gin.SetMode(gin.DebugMode)
3✔
89
                // In development mode we want to see incoming HTTP requests
3✔
90
                webUIServer.router.Use(gin.Logger())
3✔
91
                // Add CORS Middleware in development mode to allow running
3✔
92
                // backend and frontend on separate ports
3✔
93
                webUIServer.router.Use(corsMiddleware())
3✔
94
        }
3✔
95
}
96

97
func (webUIServer *WebUIServer) startGinEngine() {
3✔
98
        // Create HTTP server instance
3✔
99
        webUIServer.httpServer = &http.Server{ //nolint:gosec
3✔
100
                Addr:    webUIServer.GetServerAddress(),
3✔
101
                Handler: webUIServer.router,
3✔
102
        }
3✔
103

3✔
104
        // Start HTTP server on its own goroutine
3✔
105
        go func() {
5✔
106
                err := webUIServer.httpServer.ListenAndServe()
2✔
107
                if err != nil {
4✔
108
                        report.PostError("could not start HTTP server: ", err.Error())
2✔
109
                }
2✔
110
        }()
111
}
112

113
// stopGinEngine is provided for testing purpose, so that we can shutdown
114
// the HTTP server when needed
115
func (webUIServer *WebUIServer) stopGinEngine() {
3✔
116
        report.PostInfo("Stopping HTTP server")
3✔
117
        if err := webUIServer.httpServer.Shutdown(context.Background()); err != nil {
3✔
118
                report.PostError("could not stop HTTP server: ", err.Error())
×
119
        }
×
120
}
121

122
func (webUIServer *WebUIServer) addStaticRoutes() {
6✔
123
        // Serve frontend static files from embedded filesystem
6✔
124
        webUIServer.router.Use(static.Serve("/", embedFolder(staticFS, "static/webapp/browser")))
6✔
125
        webUIServer.router.NoRoute(func(c *gin.Context) {
12✔
126
                report.PostInfo(c.Request.URL.Path, " doesn't exists, redirecting to /")
6✔
127
                c.Redirect(http.StatusMovedPermanently, "/")
6✔
128
        })
6✔
129
}
130

131
func (webUIServer *WebUIServer) addAPIRoutes() {
6✔
132
        // Add TCR engine to gin context so that it can be accessed by API handlers
6✔
133
        webUIServer.router.Use(api.TCREngineMiddleware(webUIServer.tcr))
6✔
134
        // Setup route group for the API
6✔
135
        apiRoutes := webUIServer.router.Group("/api")
6✔
136
        {
12✔
137
                apiRoutes.GET("/build-info", api.BuildInfoGetHandler)
6✔
138
                apiRoutes.GET("/session-info", api.SessionInfoGetHandler)
6✔
139
                apiRoutes.GET("/roles", api.RolesGetHandler)
6✔
140
                apiRoutes.GET("/roles/:name", api.RoleGetHandler)
6✔
141
                apiRoutes.POST("/roles/:name/:action", api.RolesPostHandler)
6✔
142
                apiRoutes.GET("/timer", api.TimerGetHandler)
6✔
143
                apiRoutes.POST("/controls/:name", api.ControlsPostHandler)
6✔
144
        }
6✔
145
}
146

147
func (webUIServer *WebUIServer) addWebsocketRoutes() {
6✔
148
        // Add self to gin context so that it can be accessed by web socket handlers
6✔
149
        webUIServer.router.Use(ws.HTTPServerMiddleware(webUIServer))
6✔
150
        // Setup websocket route
6✔
151
        webUIServer.router.GET("/ws", ws.WebsocketHandler)
6✔
152
}
6✔
153

154
// InDevMode indicates if the server is running in dev (development) mode
155
func (webUIServer *WebUIServer) InDevMode() bool {
18✔
156
        return webUIServer.devMode
18✔
157
}
18✔
158

159
// GetServerAddress returns the TCP server address that the server is listening to.
160
func (webUIServer *WebUIServer) GetServerAddress() string {
3✔
161
        return fmt.Sprintf("%s:%d", webUIServer.host, webUIServer.params.PortNumber)
3✔
162
}
3✔
163

164
// GetWebsocketTimeout returns the timeout after which inactive websocket connections
165
// should be closed
166
func (webUIServer *WebUIServer) GetWebsocketTimeout() time.Duration {
3✔
167
        return webUIServer.websocketTimeout
3✔
168
}
3✔
169

170
// ShowRunningMode shows the current running mode
171
func (*WebUIServer) ShowRunningMode(_ runmode.RunMode) {
×
172
        // Not needed: Runmode query will be handled by the client through a GET request
×
173
}
×
174

175
// ShowSessionInfo shows main information related to the current TCR session
176
func (*WebUIServer) ShowSessionInfo() {
×
177
        // With HTTP server this operation is triggered by the client through
×
178
        // a GET request. There is nothing to do here
×
179
}
×
180

181
// Confirm asks the user for confirmation
182
func (*WebUIServer) Confirm(_ string, _ bool) bool {
×
183
        // Always return true until there is a need for this function
×
184
        return true
×
185
}
×
186

187
// StartReporting tells HTTP server to start reporting information
188
func (*WebUIServer) StartReporting() {
×
189
        // Not needed: subscription is managed by each websocket handler instance
×
190
}
×
191

192
// StopReporting tells HTTP server to stop reporting information
193
func (*WebUIServer) StopReporting() {
×
194
        // Not needed: subscription is managed by each websocket handler instance
×
195
}
×
196

197
// MuteDesktopNotifications allows preventing desktop Notification popups from being displayed.
198
// Used for test automation at the moment. Could be turned into a feature later if there is need for it.
199
func (*WebUIServer) MuteDesktopNotifications(_ bool) {
×
200
        // With HTTP server this operation should be triggered by the client though
×
201
        // a GET request. There is nothing to do here
×
202
}
×
203

204
// corsMiddleware opens CORS connections to HTTP server instance.
205
// So far this is required only during development (mainly during frontend development
206
// where frontend application is running on its own HTTP server instance)
207
// Depending on future evolutions there could be a need to open CORS in production
208
// too (may require finer tuning in this case to limit CORS to what is needed only)
209
func corsMiddleware() gin.HandlerFunc {
9✔
210
        report.PostInfo("Using CORS middleware")
9✔
211
        return cors.New(cors.Config{
9✔
212
                AllowWildcard:    true,
9✔
213
                AllowAllOrigins:  true,
9✔
214
                AllowMethods:     []string{"*"},
9✔
215
                AllowHeaders:     []string{"Content-Type", "Origin"},
9✔
216
                ExposeHeaders:    []string{"Content-Length", "Content-Type"},
9✔
217
                AllowCredentials: false,
9✔
218
                AllowWebSockets:  true,
9✔
219
                MaxAge:           12 * time.Hour,
9✔
220
        })
9✔
221
}
9✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc