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

oxyno-zeta / s3-proxy / 10999784857

23 Sep 2024 05:58PM UTC coverage: 86.012% (+1.8%) from 84.23%
10999784857

push

github

oxyno-zeta
fix(server): Not found not correctly managed inside target router

5325 of 6191 relevant lines covered (86.01%)

44.85 hits per line

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

80.36
/pkg/s3-proxy/server/server.go
1
package server
2

3
import (
4
        "net/http"
5
        "net/url"
6
        "strconv"
7
        "time"
8

9
        "emperror.dev/errors"
10
        "github.com/go-chi/chi/v5"
11
        "github.com/go-chi/chi/v5/middleware"
12
        "github.com/go-chi/cors"
13
        "github.com/go-chi/httptracer"
14
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/authx/authentication"
15
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/authx/authorization"
16
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/bucket"
17
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/config"
18
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/log"
19
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/metrics"
20
        responsehandler "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/response-handler"
21
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/s3client"
22
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/server/middlewares"
23
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/tracing"
24
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/version"
25
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/webhook"
26
        "github.com/thoas/go-funk"
27
)
28

29
type Server struct {
30
        logger          log.Logger
31
        cfgManager      config.Manager
32
        metricsCl       metrics.Client
33
        server          *http.Server
34
        tracingSvc      tracing.Service
35
        s3clientManager s3client.Manager
36
        webhookManager  webhook.Manager
37
}
38

39
func NewServer(
40
        logger log.Logger,
41
        cfgManager config.Manager,
42
        metricsCl metrics.Client,
43
        tracingSvc tracing.Service,
44
        s3clientManager s3client.Manager,
45
        webhookManager webhook.Manager,
46
) *Server {
12✔
47
        return &Server{
12✔
48
                logger:          logger,
12✔
49
                cfgManager:      cfgManager,
12✔
50
                metricsCl:       metricsCl,
12✔
51
                tracingSvc:      tracingSvc,
12✔
52
                s3clientManager: s3clientManager,
12✔
53
                webhookManager:  webhookManager,
12✔
54
        }
12✔
55
}
12✔
56

57
func (svr *Server) Listen() error {
19✔
58
        svr.logger.Infof("Server listening on %s", svr.server.Addr)
19✔
59

19✔
60
        var err error
19✔
61

19✔
62
        // Listen (either HTTPS or HTTP)
19✔
63
        if svr.server.TLSConfig != nil {
27✔
64
                err = svr.server.ListenAndServeTLS("", "")
8✔
65
        } else {
19✔
66
                err = svr.server.ListenAndServe()
11✔
67
        }
11✔
68

69
        // Check error
70
        if err != nil {
38✔
71
                return errors.WithStack(err)
19✔
72
        }
19✔
73

74
        // Default
75
        return nil
×
76
}
77

78
func (svr *Server) GenerateServer() error {
32✔
79
        // Get configuration
32✔
80
        cfg := svr.cfgManager.GetConfig()
32✔
81

32✔
82
        // Generate router
32✔
83
        r, err := svr.generateRouter()
32✔
84
        if err != nil {
33✔
85
                return err
1✔
86
        }
1✔
87

88
        // Create server
89
        addr := cfg.Server.ListenAddr + ":" + strconv.Itoa(cfg.Server.Port)
31✔
90
        server := &http.Server{ //nolint: gosec // Set after
31✔
91
                Addr:    addr,
31✔
92
                Handler: r,
31✔
93
        }
31✔
94

31✔
95
        // Inject timeouts
31✔
96
        err = injectServerTimeout(server, cfg.Server.Timeouts)
31✔
97
        // Check error
31✔
98
        if err != nil {
31✔
99
                return err
×
100
        }
×
101

102
        // Get the TLS configuration (if necessary).
103
        tlsConfig, err := generateTLSConfig(cfg.Server.SSL, svr.logger)
31✔
104
        if err != nil {
43✔
105
                return errors.Wrap(err, "failed to create TLS configuration for server")
12✔
106
        }
12✔
107

108
        server.TLSConfig = tlsConfig
19✔
109

19✔
110
        // Prepare for configuration onChange
19✔
111
        svr.cfgManager.AddOnChangeHook(func() {
19✔
112
                // Generate router
×
113
                r, err2 := svr.generateRouter()
×
114
                if err2 != nil {
×
115
                        svr.logger.Fatal(err2)
×
116
                }
×
117
                // Change server handler
118
                server.Handler = r
×
119

×
120
                svr.logger.Info("Server handler reloaded")
×
121
        })
122

123
        // Store server
124
        svr.server = server
19✔
125

19✔
126
        return nil
19✔
127
}
128

129
func (svr *Server) generateRouter() (http.Handler, error) {
141✔
130
        // Get configuration
141✔
131
        cfg := svr.cfgManager.GetConfig()
141✔
132

141✔
133
        // Create authentication service
141✔
134
        authenticationSvc := authentication.NewAuthenticationService(cfg, svr.cfgManager, svr.metricsCl)
141✔
135

141✔
136
        // Create router
141✔
137
        r := chi.NewRouter()
141✔
138

141✔
139
        // Check if we need to enabled the compress middleware
141✔
140
        if cfg.Server.Compress != nil && *cfg.Server.Compress.Enabled {
260✔
141
                r.Use(middleware.Compress(
119✔
142
                        cfg.Server.Compress.Level,
119✔
143
                        cfg.Server.Compress.Types...,
119✔
144
                ))
119✔
145
        }
119✔
146

147
        // Check if no cache is enabled or not
148
        if cfg.Server.Cache == nil || cfg.Server.Cache.NoCacheEnabled {
280✔
149
                // Apply no cache
139✔
150
                r.Use(middleware.NoCache)
139✔
151
        } else {
141✔
152
                // Apply S3 proxy cache management middleware
2✔
153
                r.Use(middlewares.CacheManagement(cfg.Server.Cache))
2✔
154
        }
2✔
155

156
        r.Use(middleware.RequestID)
141✔
157
        r.Use(middleware.RealIP)
141✔
158
        // Manage tracing
141✔
159
        // Create http tracer configuration
141✔
160
        httptraCfg := httptracer.Config{
141✔
161
                ServiceName:    "s3-proxy",
141✔
162
                ServiceVersion: version.GetVersion().Version,
141✔
163
                SampleRate:     1,
141✔
164
                OperationName:  "http.request",
141✔
165
                Tags:           cfg.Tracing.FixedTags,
141✔
166
        }
141✔
167
        // Put tracing middlewares
141✔
168
        r.Use(httptracer.Tracer(svr.tracingSvc.GetTracer(), httptraCfg))
141✔
169
        r.Use(middlewares.ImproveTracing())
141✔
170
        r.Use(log.NewStructuredLogger(
141✔
171
                svr.logger,
141✔
172
                tracing.GetTraceIDFromRequest,
141✔
173
        ))
141✔
174
        r.Use(log.HTTPAddLoggerToContextMiddleware())
141✔
175
        r.Use(svr.metricsCl.Instrument("business", cfg.Metrics))
141✔
176
        // Recover panic
141✔
177
        r.Use(middleware.Recoverer)
141✔
178

141✔
179
        // Check if cors is enabled
141✔
180
        if cfg.Server != nil && cfg.Server.CORS != nil && cfg.Server.CORS.Enabled {
147✔
181
                // Generate CORS
6✔
182
                cc := generateCors(cfg.Server, svr.logger.GetCorsLogger())
6✔
183
                // Apply CORS handler
6✔
184
                r.Use(cc.Handler)
6✔
185
        }
6✔
186

187
        // Check if auth if enabled and oidc enabled
188
        if cfg.AuthProviders != nil && cfg.AuthProviders.OIDC != nil {
152✔
189
                for k, v := range cfg.AuthProviders.OIDC {
23✔
190
                        // Add oidc endpoints
12✔
191
                        err := authenticationSvc.OIDCEndpoints(k, v, r)
12✔
192
                        // Check error
12✔
193
                        if err != nil {
13✔
194
                                return nil, err
1✔
195
                        }
1✔
196
                }
197
        }
198

199
        notFoundHandler := func(w http.ResponseWriter, r *http.Request) {
154✔
200
                // Answer with general not found handler
14✔
201
                responsehandler.GeneralNotFoundError(r, w, svr.cfgManager)
14✔
202
        }
14✔
203

204
        internalServerHandlerGen := func(err error) http.HandlerFunc {
140✔
205
                return func(w http.ResponseWriter, r *http.Request) {
×
206
                        // Answer with general internal server error handler
×
207
                        responsehandler.GeneralInternalServerError(
×
208
                                r,
×
209
                                w,
×
210
                                svr.cfgManager,
×
211
                                errors.WithStack(err),
×
212
                        )
×
213
                }
×
214
        }
215

216
        // Create host router
217
        hr := NewHostRouter(notFoundHandler, internalServerHandlerGen)
140✔
218

140✔
219
        // Load main route only if main bucket path support option isn't enabled
140✔
220
        if cfg.ListTargets.Enabled {
156✔
221
                // Create new router
16✔
222
                rt := chi.NewRouter()
16✔
223
                // Add middleware in order to add response handler
16✔
224
                rt.Use(responsehandler.HTTPMiddleware(svr.cfgManager, ""))
16✔
225
                // Make list of resources from resource
16✔
226
                resources := make([]*config.Resource, 0)
16✔
227
                if cfg.ListTargets.Resource != nil {
30✔
228
                        resources = append(resources, cfg.ListTargets.Resource)
14✔
229
                }
14✔
230
                // Manage path for list targets feature
231
                // Loop over path list
232
                funk.ForEach(cfg.ListTargets.Mount.Path, func(path string) {
32✔
233
                        rt.Route(path, func(rt2 chi.Router) {
32✔
234
                                // Add authentication middleware to router
16✔
235
                                rt2 = rt2.With(authenticationSvc.Middleware(resources))
16✔
236

16✔
237
                                // Add authorization middleware to router
16✔
238
                                rt2 = rt2.With(authorization.Middleware(svr.cfgManager, svr.metricsCl))
16✔
239

16✔
240
                                rt2.Get("/", func(_ http.ResponseWriter, req *http.Request) {
23✔
241
                                        // Get response handler
7✔
242
                                        resHan := responsehandler.GetResponseHandlerFromContext(req.Context())
7✔
243

7✔
244
                                        // Handle target list
7✔
245
                                        resHan.TargetList()
7✔
246
                                })
7✔
247
                        })
248
                })
249
                // Create domain
250
                domain := cfg.ListTargets.Mount.Host
16✔
251
                if domain == "" {
32✔
252
                        domain = "*"
16✔
253
                }
16✔
254
                // Mount domain from configuration
255
                hr.Map(domain, rt)
16✔
256
        }
257

258
        // Load all targets routes
259
        for targetKey, tgt := range cfg.Targets {
319✔
260
                // Manage domain
179✔
261
                domain := tgt.Mount.Host
179✔
262
                if domain == "" {
354✔
263
                        domain = "*"
175✔
264
                }
175✔
265
                // Get router from hostrouter if exists
266
                rt := hr.Get(domain)
179✔
267
                // Check nil
179✔
268
                if rt == nil {
302✔
269
                        // Create a new router
123✔
270
                        rt = chi.NewRouter()
123✔
271

123✔
272
                        // Add not found
123✔
273
                        rt.NotFound(notFoundHandler)
123✔
274
                }
123✔
275
                // Loop over path list
276
                funk.ForEach(tgt.Mount.Path, func(path string) {
358✔
277
                        rt.Route(path, func(rt2 chi.Router) {
358✔
278
                                // Add middleware in order to add response handler
179✔
279
                                rt2.Use(responsehandler.HTTPMiddleware(svr.cfgManager, targetKey))
179✔
280

179✔
281
                                // Add Bucket request context middleware to initialize it
179✔
282
                                rt2.Use(bucket.HTTPMiddleware(tgt, path, svr.s3clientManager, svr.webhookManager))
179✔
283

179✔
284
                                // Add authentication middleware to router
179✔
285
                                rt2.Use(authenticationSvc.Middleware(tgt.Resources))
179✔
286

179✔
287
                                // Add authorization middleware to router
179✔
288
                                rt2.Use(authorization.Middleware(svr.cfgManager, svr.metricsCl))
179✔
289

179✔
290
                                // Check if HEAD action is enabled
179✔
291
                                if tgt.Actions.HEAD != nil && tgt.Actions.HEAD.Enabled { //nolint:dupl
209✔
292
                                        rt2.Head("/*", func(_ http.ResponseWriter, req *http.Request) {
53✔
293
                                                // Get bucket request context
23✔
294
                                                brctx := bucket.GetBucketRequestContextFromContext(req.Context())
23✔
295
                                                // Get response handler
23✔
296
                                                resHan := responsehandler.GetResponseHandlerFromContext(req.Context())
23✔
297

23✔
298
                                                // Get request path
23✔
299
                                                requestPath := chi.URLParam(req, "*")
23✔
300

23✔
301
                                                // Unescape it
23✔
302
                                                // Found a bug where sometimes the request path isn't unescaped
23✔
303
                                                requestPath, err := url.PathUnescape(requestPath)
23✔
304
                                                // Check error
23✔
305
                                                if err != nil {
23✔
306
                                                        resHan.InternalServerError(brctx.LoadFileContent, errors.WithStack(err))
×
307

×
308
                                                        return
×
309
                                                }
×
310

311
                                                // Get ETag headers
312

313
                                                // Get If-Modified-Since as string
314
                                                ifModifiedSinceStr := req.Header.Get("If-Modified-Since")
23✔
315
                                                // Create result
23✔
316
                                                var ifModifiedSince *time.Time
23✔
317
                                                // Check if content exists
23✔
318
                                                if ifModifiedSinceStr != "" {
23✔
319
                                                        // Parse time
×
320
                                                        ifModifiedSinceTime, err := http.ParseTime(ifModifiedSinceStr)
×
321
                                                        // Check error
×
322
                                                        if err != nil {
×
323
                                                                resHan.BadRequestError(brctx.LoadFileContent, errors.WithStack(err))
×
324

×
325
                                                                return
×
326
                                                        }
×
327
                                                        // Save result
328
                                                        ifModifiedSince = &ifModifiedSinceTime
×
329
                                                }
330

331
                                                // Get Range
332
                                                byteRange := req.Header.Get("Range")
23✔
333

23✔
334
                                                // Get If-Match
23✔
335
                                                ifMatch := req.Header.Get("If-Match")
23✔
336

23✔
337
                                                // Get If-None-Match
23✔
338
                                                ifNoneMatch := req.Header.Get("If-None-Match")
23✔
339

23✔
340
                                                // Get If-Unmodified-Since as string
23✔
341
                                                ifUnmodifiedSinceStr := req.Header.Get("If-Unmodified-Since")
23✔
342
                                                // Create result
23✔
343
                                                var ifUnmodifiedSince *time.Time
23✔
344
                                                // Check if content exists
23✔
345
                                                if ifUnmodifiedSinceStr != "" {
23✔
346
                                                        // Parse time
×
347
                                                        ifUnmodifiedSinceTime, err := http.ParseTime(ifUnmodifiedSinceStr)
×
348
                                                        // Check error
×
349
                                                        if err != nil {
×
350
                                                                resHan.BadRequestError(brctx.LoadFileContent, errors.WithStack(err))
×
351

×
352
                                                                return
×
353
                                                        }
×
354
                                                        // Save result
355
                                                        ifUnmodifiedSince = &ifUnmodifiedSinceTime
×
356
                                                }
357

358
                                                // Proxy GET Request
359
                                                brctx.Head(req.Context(), &bucket.GetInput{
23✔
360
                                                        RequestPath:       requestPath,
23✔
361
                                                        IfModifiedSince:   ifModifiedSince,
23✔
362
                                                        IfMatch:           ifMatch,
23✔
363
                                                        IfNoneMatch:       ifNoneMatch,
23✔
364
                                                        IfUnmodifiedSince: ifUnmodifiedSince,
23✔
365
                                                        Range:             byteRange,
23✔
366
                                                })
23✔
367
                                        })
368
                                }
369

370
                                // Check if GET action is enabled
371
                                if tgt.Actions.GET != nil && tgt.Actions.GET.Enabled { //nolint:dupl
334✔
372
                                        // Add GET method to router
155✔
373
                                        rt2.Get("/*", func(_ http.ResponseWriter, req *http.Request) {
203✔
374
                                                // Get bucket request context
48✔
375
                                                brctx := bucket.GetBucketRequestContextFromContext(req.Context())
48✔
376
                                                // Get response handler
48✔
377
                                                resHan := responsehandler.GetResponseHandlerFromContext(req.Context())
48✔
378

48✔
379
                                                // Get request path
48✔
380
                                                requestPath := chi.URLParam(req, "*")
48✔
381

48✔
382
                                                // Unescape it
48✔
383
                                                // Found a bug where sometimes the request path isn't unescaped
48✔
384
                                                requestPath, err := url.PathUnescape(requestPath)
48✔
385
                                                // Check error
48✔
386
                                                if err != nil {
48✔
387
                                                        resHan.InternalServerError(brctx.LoadFileContent, errors.WithStack(err))
×
388

×
389
                                                        return
×
390
                                                }
×
391

392
                                                // Get ETag headers
393

394
                                                // Get If-Modified-Since as string
395
                                                ifModifiedSinceStr := req.Header.Get("If-Modified-Since")
48✔
396
                                                // Create result
48✔
397
                                                var ifModifiedSince *time.Time
48✔
398
                                                // Check if content exists
48✔
399
                                                if ifModifiedSinceStr != "" {
48✔
400
                                                        // Parse time
×
401
                                                        ifModifiedSinceTime, err := http.ParseTime(ifModifiedSinceStr)
×
402
                                                        // Check error
×
403
                                                        if err != nil {
×
404
                                                                resHan.BadRequestError(brctx.LoadFileContent, errors.WithStack(err))
×
405

×
406
                                                                return
×
407
                                                        }
×
408
                                                        // Save result
409
                                                        ifModifiedSince = &ifModifiedSinceTime
×
410
                                                }
411

412
                                                // Get Range
413
                                                byteRange := req.Header.Get("Range")
48✔
414

48✔
415
                                                // Get If-Match
48✔
416
                                                ifMatch := req.Header.Get("If-Match")
48✔
417

48✔
418
                                                // Get If-None-Match
48✔
419
                                                ifNoneMatch := req.Header.Get("If-None-Match")
48✔
420

48✔
421
                                                // Get If-Unmodified-Since as string
48✔
422
                                                ifUnmodifiedSinceStr := req.Header.Get("If-Unmodified-Since")
48✔
423
                                                // Create result
48✔
424
                                                var ifUnmodifiedSince *time.Time
48✔
425
                                                // Check if content exists
48✔
426
                                                if ifUnmodifiedSinceStr != "" {
48✔
427
                                                        // Parse time
×
428
                                                        ifUnmodifiedSinceTime, err := http.ParseTime(ifUnmodifiedSinceStr)
×
429
                                                        // Check error
×
430
                                                        if err != nil {
×
431
                                                                resHan.BadRequestError(brctx.LoadFileContent, errors.WithStack(err))
×
432

×
433
                                                                return
×
434
                                                        }
×
435
                                                        // Save result
436
                                                        ifUnmodifiedSince = &ifUnmodifiedSinceTime
×
437
                                                }
438

439
                                                // Proxy GET Request
440
                                                brctx.Get(req.Context(), &bucket.GetInput{
48✔
441
                                                        RequestPath:       requestPath,
48✔
442
                                                        IfModifiedSince:   ifModifiedSince,
48✔
443
                                                        IfMatch:           ifMatch,
48✔
444
                                                        IfNoneMatch:       ifNoneMatch,
48✔
445
                                                        IfUnmodifiedSince: ifUnmodifiedSince,
48✔
446
                                                        Range:             byteRange,
48✔
447
                                                })
48✔
448
                                        })
449
                                }
450

451
                                // Check if PUT action is enabled
452
                                if tgt.Actions.PUT != nil && tgt.Actions.PUT.Enabled {
187✔
453
                                        // Add PUT method to router
8✔
454
                                        rt2.Put("/*", func(_ http.ResponseWriter, req *http.Request) {
16✔
455
                                                // Get bucket request context
8✔
456
                                                brctx := bucket.GetBucketRequestContextFromContext(req.Context())
8✔
457
                                                // Get response handler
8✔
458
                                                resHan := responsehandler.GetResponseHandlerFromContext(req.Context())
8✔
459

8✔
460
                                                // Get request path
8✔
461
                                                requestPath := chi.URLParam(req, "*")
8✔
462
                                                // Unescape it
8✔
463
                                                // Found a bug where sometimes the request path isn't unescaped
8✔
464
                                                requestPath, err := url.PathUnescape(requestPath)
8✔
465
                                                // Check error
8✔
466
                                                if err != nil {
8✔
467
                                                        resHan.InternalServerError(brctx.LoadFileContent, errors.WithStack(err))
×
468

×
469
                                                        return
×
470
                                                }
×
471

472
                                                // Parse form
473
                                                err = req.ParseForm()
8✔
474
                                                // Check error
8✔
475
                                                if err != nil {
9✔
476
                                                        resHan.InternalServerError(brctx.LoadFileContent, errors.WithStack(err))
1✔
477

1✔
478
                                                        return
1✔
479
                                                }
1✔
480

481
                                                // Parse multipart form
482
                                                err = req.ParseMultipartForm(0)
7✔
483
                                                if err != nil {
7✔
484
                                                        resHan.InternalServerError(brctx.LoadFileContent, errors.WithStack(err))
×
485

×
486
                                                        return
×
487
                                                }
×
488
                                                // Get file from form
489
                                                file, fileHeader, err := req.FormFile("file")
7✔
490
                                                if err != nil {
8✔
491
                                                        resHan.InternalServerError(brctx.LoadFileContent, errors.WithStack(err))
1✔
492

1✔
493
                                                        return
1✔
494
                                                }
1✔
495
                                                // Defer close file
496
                                                defer file.Close()
6✔
497
                                                // Defer remove all form
6✔
498
                                                defer req.MultipartForm.RemoveAll() //nolint: errcheck // Ignored
6✔
499

6✔
500
                                                // Create input for put request
6✔
501
                                                inp := &bucket.PutInput{
6✔
502
                                                        RequestPath: requestPath,
6✔
503
                                                        Filename:    fileHeader.Filename,
6✔
504
                                                        Body:        file,
6✔
505
                                                        ContentType: fileHeader.Header.Get("Content-Type"),
6✔
506
                                                        ContentSize: fileHeader.Size,
6✔
507
                                                }
6✔
508
                                                // Action
6✔
509
                                                brctx.Put(req.Context(), inp)
6✔
510
                                        })
511
                                }
512

513
                                // Check if DELETE action is enabled
514
                                if tgt.Actions.DELETE != nil && tgt.Actions.DELETE.Enabled {
182✔
515
                                        // Add DELETE method to router
3✔
516
                                        rt2.Delete("/*", func(_ http.ResponseWriter, req *http.Request) {
6✔
517
                                                // Get bucket request context
3✔
518
                                                brctx := bucket.GetBucketRequestContextFromContext(req.Context())
3✔
519
                                                // Get response handler
3✔
520
                                                resHan := responsehandler.GetResponseHandlerFromContext(req.Context())
3✔
521
                                                // Get request path
3✔
522
                                                requestPath := chi.URLParam(req, "*")
3✔
523
                                                // Unescape it
3✔
524
                                                // Found a bug where sometimes the request path isn't unescaped
3✔
525
                                                requestPath, err := url.PathUnescape(requestPath)
3✔
526
                                                // Check error
3✔
527
                                                if err != nil {
3✔
528
                                                        resHan.InternalServerError(brctx.LoadFileContent, errors.WithStack(err))
×
529

×
530
                                                        return
×
531
                                                }
×
532
                                                // Proxy DELETE Request
533
                                                brctx.Delete(req.Context(), requestPath)
3✔
534
                                        })
535
                                }
536
                        })
537
                })
538
                // Mount domain from target
539
                hr.Map(domain, rt)
179✔
540
        }
541

542
        // Mount host router
543
        r.Mount("/", hr)
140✔
544

140✔
545
        return r, nil
140✔
546
}
547

548
// Generate CORS.
549
func generateCors(cfg *config.ServerConfig, logger log.CorsLogger) *cors.Cors {
6✔
550
        // Check if allow all is enabled
6✔
551
        if cfg.CORS.AllowAll {
7✔
552
                cc := cors.AllowAll()
1✔
553
                // Add logger
1✔
554
                cc.Log = logger
1✔
555
                // Return
1✔
556
                return cc
1✔
557
        }
1✔
558

559
        // for more ideas, see: https://developer.github.com/v3/#cross-origin-resource-sharing
560
        corsOpt := cors.Options{}
5✔
561
        // Check if allowed origins exist
5✔
562
        if cfg.CORS.AllowOrigins != nil {
10✔
563
                corsOpt.AllowedOrigins = cfg.CORS.AllowOrigins
5✔
564
        }
5✔
565
        // Check if allowed methods exist
566
        if cfg.CORS.AllowMethods != nil {
10✔
567
                corsOpt.AllowedMethods = cfg.CORS.AllowMethods
5✔
568
        }
5✔
569
        // Check if allowed headers exist
570
        if cfg.CORS.AllowHeaders != nil {
5✔
571
                corsOpt.AllowedHeaders = cfg.CORS.AllowHeaders
×
572
        }
×
573
        // Check if exposed headers exist
574
        if cfg.CORS.ExposeHeaders != nil {
5✔
575
                corsOpt.ExposedHeaders = cfg.CORS.ExposeHeaders
×
576
        }
×
577
        // Check if allow credentials exist
578
        if cfg.CORS.AllowCredentials != nil {
5✔
579
                corsOpt.AllowCredentials = *cfg.CORS.AllowCredentials
×
580
        }
×
581
        // Check if max age exists
582
        // 300 = Maximum value not ignored by any of major browsers
583
        if cfg.CORS.MaxAge != nil {
5✔
584
                corsOpt.MaxAge = *cfg.CORS.MaxAge
×
585
        }
×
586
        // Check if debug option exists
587
        if cfg.CORS.Debug != nil {
5✔
588
                corsOpt.Debug = *cfg.CORS.Debug
×
589
        }
×
590
        // Check if Options Passthrough exists
591
        if cfg.CORS.OptionsPassthrough != nil {
5✔
592
                corsOpt.OptionsPassthrough = *cfg.CORS.OptionsPassthrough
×
593
        }
×
594

595
        cc := cors.New(corsOpt)
5✔
596
        // Add logger
5✔
597
        cc.Log = logger
5✔
598
        // Return
5✔
599
        return cc
5✔
600
}
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