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

dunglas / mercure / 18716847916

22 Oct 2025 12:54PM UTC coverage: 84.764% (-0.02%) from 84.779%
18716847916

push

github

dunglas
refactor: allow building without deprecated code

232 of 263 new or added lines in 3 files covered. (88.21%)

2 existing lines in 1 file now uncovered.

1669 of 1969 relevant lines covered (84.76%)

61.83 hits per line

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

86.15
/handler_deprecated.go
1
//go:build deprecated_server
2

3
package mercure
4

5
import (
6
        "context"
7
        "errors"
8
        "fmt"
9
        "io/fs"
10
        "net/http"
11
        "os"
12
        "os/signal"
13

14
        "github.com/gorilla/handlers"
15
        "github.com/gorilla/mux"
16
        "github.com/unrolled/secure"
17
        "go.uber.org/zap"
18
        "golang.org/x/crypto/acme/autocert"
19
)
20

21
// Serve starts the HTTP server.
22
//
23
// Deprecated: use the Caddy server module or the standalone library instead.
24
func (h *Hub) Serve() { //nolint:funlen
9✔
25
        addr := h.config.GetString("addr")
9✔
26

9✔
27
        h.server = &http.Server{
9✔
28
                Addr:              addr,
9✔
29
                Handler:           h.baseHandler(),
9✔
30
                ReadTimeout:       h.config.GetDuration("read_timeout"),
9✔
31
                ReadHeaderTimeout: h.config.GetDuration("read_header_timeout"),
9✔
32
                WriteTimeout:      h.config.GetDuration("write_timeout"),
9✔
33
        }
9✔
34

9✔
35
        if _, ok := h.metrics.(*PrometheusMetrics); ok {
12✔
36
                addr := h.config.GetString("metrics_addr")
3✔
37

3✔
38
                h.metricsServer = &http.Server{
3✔
39
                        Addr:              addr,
3✔
40
                        Handler:           h.metricsHandler(),
3✔
41
                        ReadTimeout:       h.config.GetDuration("read_timeout"),
3✔
42
                        ReadHeaderTimeout: h.config.GetDuration("read_header_timeout"),
3✔
43
                        WriteTimeout:      h.config.GetDuration("write_timeout"),
3✔
44
                }
3✔
45

3✔
46
                if c := h.logger.Check(zap.InfoLevel, "Mercure metrics started"); c != nil {
3✔
NEW
47
                        c.Write(zap.String("addr", addr))
×
NEW
48
                }
×
49

50
                go h.metricsServer.ListenAndServe()
3✔
51
        }
52

53
        acme := len(h.allowedHosts) > 0
9✔
54

9✔
55
        certFile := h.config.GetString("cert_file")
9✔
56
        keyFile := h.config.GetString("key_file")
9✔
57

9✔
58
        done := h.listenShutdown()
9✔
59

9✔
60
        var err error
9✔
61

9✔
62
        if !acme && certFile == "" && keyFile == "" { //nolint:nestif
15✔
63
                if c := h.logger.Check(zap.InfoLevel, "Mercure started"); c != nil {
7✔
64
                        c.Write(zap.String("protocol", "http"), zap.String("addr", addr))
1✔
65
                }
1✔
66

67
                err = h.server.ListenAndServe()
6✔
68
        } else {
3✔
69
                // TLS
3✔
70
                if acme {
4✔
71
                        certManager := &autocert.Manager{
1✔
72
                                Prompt:     autocert.AcceptTOS,
1✔
73
                                HostPolicy: autocert.HostWhitelist(h.allowedHosts...),
1✔
74
                        }
1✔
75

1✔
76
                        acmeCertDir := h.config.GetString("acme_cert_dir")
1✔
77
                        if acmeCertDir != "" {
2✔
78
                                certManager.Cache = autocert.DirCache(acmeCertDir)
1✔
79
                        }
1✔
80

81
                        h.server.TLSConfig = certManager.TLSConfig()
1✔
82

1✔
83
                        // Mandatory for Let's Encrypt http-01 challenge
1✔
84
                        go http.ListenAndServe(h.config.GetString("acme_http01_addr"), certManager.HTTPHandler(nil)) //nolint:gosec
1✔
85
                }
86

87
                if c := h.logger.Check(zap.InfoLevel, "Mercure started"); c != nil {
3✔
NEW
88
                        c.Write(zap.String("protocol", "https"), zap.String("addr", addr))
×
NEW
89
                }
×
90

91
                err = h.server.ListenAndServeTLS(certFile, keyFile)
3✔
92
        }
93

94
        if !errors.Is(err, http.ErrServerClosed) {
9✔
NEW
95
                if c := h.logger.Check(zap.ErrorLevel, "Unexpected error"); c != nil {
×
NEW
96
                        c.Write(zap.Error(err))
×
NEW
97
                }
×
98
        }
99

100
        <-done
9✔
101
}
102

103
// Deprecated: use the Caddy server module or the standalone library instead.
104
func (h *Hub) listenShutdown() <-chan struct{} {
9✔
105
        idleConnsClosed := make(chan struct{})
9✔
106

9✔
107
        h.server.RegisterOnShutdown(func() {
18✔
108
                select {
9✔
NEW
109
                case <-idleConnsClosed:
×
110
                default:
9✔
111
                        close(idleConnsClosed)
9✔
112
                }
113
        })
114

115
        go func() {
18✔
116
                sigint := make(chan os.Signal, 1)
9✔
117
                signal.Notify(sigint, os.Interrupt)
9✔
118
                <-sigint
9✔
119

9✔
120
                if err := h.server.Shutdown(context.Background()); err != nil {
9✔
NEW
121
                        if c := h.logger.Check(zap.ErrorLevel, "Unexpected error during server shutdown"); c != nil {
×
NEW
122
                                c.Write(zap.Error(err))
×
NEW
123
                        }
×
124
                }
125

NEW
126
                if h.metricsServer != nil {
×
NEW
127
                        if err := h.metricsServer.Shutdown(context.Background()); err != nil {
×
NEW
128
                                if c := h.logger.Check(zap.ErrorLevel, "Unexpected error during metrics server shutdown"); c != nil {
×
NEW
129
                                        c.Write(zap.Error(err))
×
NEW
130
                                }
×
131
                        }
132
                }
133

NEW
134
                if c := h.logger.Check(zap.InfoLevel, "My Baby Shot Me Down"); c != nil {
×
NEW
135
                        c.Write()
×
NEW
136
                }
×
137

NEW
138
                select {
×
NEW
139
                case <-idleConnsClosed:
×
NEW
140
                default:
×
NEW
141
                        close(idleConnsClosed)
×
142
                }
143
        }()
144

145
        return idleConnsClosed
9✔
146
}
147

148
// chainHandlers configures and chains handlers.
149
// Deprecated: use the Caddy server module or the standalone library instead.
150
func (h *Hub) chainHandlers() http.Handler { //nolint:funlen
9✔
151
        r := mux.NewRouter()
9✔
152
        h.registerSubscriptionHandlers(r)
9✔
153

9✔
154
        r.HandleFunc(defaultHubURL, h.SubscribeHandler).Methods(http.MethodGet, http.MethodHead)
9✔
155
        r.HandleFunc(defaultHubURL, h.PublishHandler).Methods(http.MethodPost)
9✔
156

9✔
157
        csp := "default-src 'self'"
9✔
158

9✔
159
        if h.demo {
10✔
160
                r.PathPrefix("/demo").HandlerFunc(h.Demo).Methods(http.MethodGet, http.MethodHead)
1✔
161
        }
1✔
162

163
        if h.ui {
10✔
164
                public, err := fs.Sub(uiContent, "public")
1✔
165
                if err != nil {
1✔
NEW
166
                        panic(err)
×
167
                }
168

169
                r.PathPrefix("/").Handler(http.FileServer(http.FS(public)))
1✔
170

1✔
171
                csp += " mercure.rocks cdn.jsdelivr.net"
1✔
172
        } else {
8✔
173
                r.HandleFunc("/", welcomeHandler).Methods(http.MethodGet, http.MethodHead)
8✔
174
        }
8✔
175

176
        secureMiddleware := secure.New(secure.Options{
9✔
177
                IsDevelopment:         h.debug,
9✔
178
                AllowedHosts:          h.allowedHosts,
9✔
179
                FrameDeny:             true,
9✔
180
                ContentTypeNosniff:    true,
9✔
181
                BrowserXssFilter:      true,
9✔
182
                ContentSecurityPolicy: csp,
9✔
183
        })
9✔
184

9✔
185
        var corsHandler http.Handler
9✔
186

9✔
187
        if len(h.corsOrigins) > 0 {
11✔
188
                allowedOrigins := handlers.AllowedOrigins(h.corsOrigins)
2✔
189
                allowedHeaders := handlers.AllowedHeaders([]string{"authorization", "cache-control", "last-event-id"})
2✔
190

2✔
191
                corsHandler = handlers.CORS(handlers.AllowCredentials(), allowedOrigins, allowedHeaders)(r)
2✔
192
        } else {
9✔
193
                corsHandler = r
7✔
194
        }
7✔
195

196
        var compressHandler http.Handler
9✔
197
        if h.config.GetBool("compress") {
11✔
198
                compressHandler = handlers.CompressHandler(corsHandler)
2✔
199
        } else {
9✔
200
                compressHandler = corsHandler
7✔
201
        }
7✔
202

203
        var useForwardedHeadersHandlers http.Handler
9✔
204
        if h.config.GetBool("use_forwarded_headers") {
10✔
205
                useForwardedHeadersHandlers = handlers.ProxyHeaders(compressHandler)
1✔
206
        } else {
9✔
207
                useForwardedHeadersHandlers = compressHandler
8✔
208
        }
8✔
209

210
        secureHandler := secureMiddleware.Handler(useForwardedHeadersHandlers)
9✔
211

9✔
212
        var loggingHandler http.Handler
9✔
213
        if h.logger != nil && h.logger.Level().Enabled(zap.FatalLevel) {
10✔
214
                loggingHandler = handlers.CombinedLoggingHandler(os.Stderr, secureHandler)
1✔
215
        } else {
9✔
216
                loggingHandler = secureHandler
8✔
217
        }
8✔
218

219
        recoveryHandler := handlers.RecoveryHandler(
9✔
220
                handlers.RecoveryLogger(zapRecoveryHandlerLogger{h.logger}),
9✔
221
                handlers.PrintRecoveryStack(h.debug),
9✔
222
        )(loggingHandler)
9✔
223

9✔
224
        return recoveryHandler
9✔
225
}
226

227
// Deprecated: use the Caddy server module or the standalone library instead.
228
func (h *Hub) baseHandler() http.Handler {
9✔
229
        mainRouter := mux.NewRouter()
9✔
230
        mainRouter.UseEncodedPath()
9✔
231
        mainRouter.SkipClean(true)
9✔
232

9✔
233
        // Register /healthz (if enabled, in a way that doesn't pollute the HTTP logs).
9✔
234
        registerHealthz(mainRouter)
9✔
235

9✔
236
        handler := h.chainHandlers()
9✔
237
        mainRouter.PathPrefix("/").Handler(handler)
9✔
238

9✔
239
        return mainRouter
9✔
240
}
9✔
241

242
// Deprecated: use the Caddy server module or the standalone library instead.
243
func (h *Hub) metricsHandler() http.Handler {
3✔
244
        router := mux.NewRouter()
3✔
245

3✔
246
        registerHealthz(router)
3✔
247
        h.metrics.(*PrometheusMetrics).Register(router.PathPrefix("/").Subrouter())
3✔
248

3✔
249
        return router
3✔
250
}
3✔
251

252
// Deprecated: use the Caddy server module or the standalone library instead.
253
func registerHealthz(router *mux.Router) {
12✔
254
        router.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
14✔
255
                fmt.Fprint(w, "ok")
2✔
256
        }).Methods(http.MethodGet, http.MethodHead)
2✔
257
}
258

259
// Deprecated: use the Caddy server module or the standalone library instead.
260
func welcomeHandler(w http.ResponseWriter, _ *http.Request) {
5✔
261
        fmt.Fprint(w, `<!DOCTYPE html>
5✔
262
<title>Mercure Hub</title>
5✔
263
<h1>Welcome to <a href="https://mercure.rocks">Mercure</a>!</h1>`)
5✔
264
}
5✔
265

266
// Deprecated: use the Caddy server module or the standalone library instead.
267
type zapRecoveryHandlerLogger struct {
268
        logger Logger
269
}
270

NEW
271
func (z zapRecoveryHandlerLogger) Println(args ...any) {
×
NEW
272
        z.logger.Error(fmt.Sprint(args...))
×
NEW
273
}
×
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