-
Notifications
You must be signed in to change notification settings - Fork 14
/
uid.go
92 lines (76 loc) · 2.29 KB
/
uid.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package middleware
import (
"context"
"net/http"
"regexp"
"github.com/google/uuid"
"github.com/rs/xid"
"github.com/avenga/couper/config"
"github.com/avenga/couper/config/request"
"github.com/avenga/couper/errors"
)
var regexUID = regexp.MustCompile(`^[a-zA-Z0-9@=/+-]{12,64}$`)
type UID struct {
conf *config.Settings
devProxyHeader string
generate UIDFunc
handler http.Handler
}
func NewUIDHandler(conf *config.Settings, devProxy string) Next {
return func(handler http.Handler) *NextHandler {
return NewHandler(&UID{
conf: conf,
devProxyHeader: devProxy,
generate: NewUIDFunc(conf.RequestIDFormat),
handler: handler,
}, handler)
}
}
// ServeHTTP generates a unique request-id and add this id to the request context and
// at least the response header even on error case.
func (u *UID) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
uid, err := u.newUID(req.Header)
*req = *req.WithContext(context.WithValue(req.Context(), request.UID, uid))
if u.conf.RequestIDClientHeader != "" {
rw.Header().Set(u.conf.RequestIDClientHeader, uid)
}
if err != nil {
errors.DefaultHTML.WithError(errors.ClientRequest.With(err)).ServeHTTP(rw, req)
return
}
if u.conf.RequestIDBackendHeader != "" {
req.Header.Set(u.conf.RequestIDBackendHeader, uid)
}
u.handler.ServeHTTP(rw, req)
}
func (u *UID) newUID(header http.Header) (string, error) {
if u.conf.RequestIDAcceptFromHeader != "" {
if v := header.Get(u.conf.RequestIDAcceptFromHeader); v != "" {
if !regexUID.MatchString(v) {
return u.generate(), errors.ClientRequest.
Messagef("invalid request-id header value: %s: %s", u.conf.RequestIDAcceptFromHeader, v)
}
return v, nil
}
} else if httpsDevProxyID := header.Get(u.devProxyHeader); httpsDevProxyID != "" {
header.Del(u.devProxyHeader)
return httpsDevProxyID, nil
}
return u.generate(), nil
}
// UIDFunc wraps different unique id implementations.
type UIDFunc func() string
func NewUIDFunc(requestIDFormat string) UIDFunc {
var fn UIDFunc
if requestIDFormat == "uuid4" {
uuid.EnableRandPool() // Enabling the pool may improve the UUID generation throughput significantly.
fn = func() string {
return uuid.NewString()
}
} else {
fn = func() string {
return xid.New().String()
}
}
return fn
}