1 package web
2
3 import (
4 "context"
5 "crypto"
6 "crypto/ecdsa"
7 "crypto/rsa"
8 "encoding/json"
9 "fmt"
10 "net"
11 "net/http"
12 "strings"
13 "time"
14
15 blog "github.com/letsencrypt/boulder/log"
16 )
17
18
19
20
21
22 type RequestEvent struct {
23
24
25
26
27 Method string `json:"-"`
28 Endpoint string `json:"-"`
29 Requester int64 `json:"-"`
30 Code int `json:"-"`
31 Latency float64 `json:"-"`
32 RealIP string `json:"-"`
33
34 Slug string `json:",omitempty"`
35 InternalErrors []string `json:",omitempty"`
36 Error string `json:",omitempty"`
37 UserAgent string `json:"ua,omitempty"`
38
39 Origin string `json:",omitempty"`
40 Extra map[string]interface{} `json:",omitempty"`
41
42
43 Created string `json:",omitempty"`
44
45
46
47 Status string `json:",omitempty"`
48
49
50 DNSName string `json:",omitempty"`
51
52
53 DNSNames []string `json:",omitempty"`
54
55
56 ChallengeType string `json:",omitempty"`
57
58
59
60
61 suppressed bool `json:"-"`
62 }
63
64
65
66
67 func (e *RequestEvent) AddError(msg string, args ...interface{}) {
68 e.InternalErrors = append(e.InternalErrors, fmt.Sprintf(msg, args...))
69 e.suppressed = false
70 }
71
72
73
74
75 func (e *RequestEvent) Suppress() {
76 if len(e.InternalErrors) == 0 {
77 e.suppressed = true
78 }
79 }
80
81 type WFEHandlerFunc func(context.Context, *RequestEvent, http.ResponseWriter, *http.Request)
82
83 func (f WFEHandlerFunc) ServeHTTP(e *RequestEvent, w http.ResponseWriter, r *http.Request) {
84 f(r.Context(), e, w, r)
85 }
86
87 type wfeHandler interface {
88 ServeHTTP(e *RequestEvent, w http.ResponseWriter, r *http.Request)
89 }
90
91 type TopHandler struct {
92 wfe wfeHandler
93 log blog.Logger
94 }
95
96 func NewTopHandler(log blog.Logger, wfe wfeHandler) *TopHandler {
97 return &TopHandler{
98 wfe: wfe,
99 log: log,
100 }
101 }
102
103
104
105 type responseWriterWithStatus struct {
106 http.ResponseWriter
107 code int
108 }
109
110
111 func (r *responseWriterWithStatus) WriteHeader(code int) {
112 r.code = code
113 r.ResponseWriter.WriteHeader(code)
114 }
115
116 func (th *TopHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
117
118 realIP := r.Header.Get("X-Real-IP")
119 if net.ParseIP(realIP) == nil {
120 realIP = "0.0.0.0"
121 }
122
123 logEvent := &RequestEvent{
124 RealIP: realIP,
125 Method: r.Method,
126 UserAgent: r.Header.Get("User-Agent"),
127 Origin: r.Header.Get("Origin"),
128 Extra: make(map[string]interface{}),
129 }
130
131
132
133 ctx := context.WithoutCancel(r.Context())
134 r = r.WithContext(ctx)
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149 r.Host = strings.TrimSuffix(r.Host, ":443")
150 r.Host = strings.TrimSuffix(r.Host, ":80")
151
152 begin := time.Now()
153 rwws := &responseWriterWithStatus{w, 0}
154 defer func() {
155 logEvent.Code = rwws.code
156 if logEvent.Code == 0 {
157
158
159 logEvent.Code = http.StatusOK
160 }
161 logEvent.Latency = time.Since(begin).Seconds()
162 th.logEvent(logEvent)
163 }()
164 th.wfe.ServeHTTP(logEvent, rwws, r)
165 }
166
167 func (th *TopHandler) logEvent(logEvent *RequestEvent) {
168 if logEvent.suppressed {
169 return
170 }
171 var msg string
172 jsonEvent, err := json.Marshal(logEvent)
173 if err != nil {
174 th.log.AuditErrf("failed to marshal logEvent - %s - %#v", msg, err)
175 return
176 }
177 th.log.Infof("%s %s %d %d %d %s JSON=%s",
178 logEvent.Method, logEvent.Endpoint, logEvent.Requester, logEvent.Code,
179 int(logEvent.Latency*1000), logEvent.RealIP, jsonEvent)
180 }
181
182
183
184
185 func GetClientAddr(r *http.Request) string {
186 if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
187 return xff + "," + r.RemoteAddr
188 }
189 return r.RemoteAddr
190 }
191
192 func KeyTypeToString(pub crypto.PublicKey) string {
193 switch pk := pub.(type) {
194 case *rsa.PublicKey:
195 return fmt.Sprintf("RSA %d", pk.N.BitLen())
196 case *ecdsa.PublicKey:
197 return fmt.Sprintf("ECDSA %s", pk.Params().Name)
198 }
199 return "unknown"
200 }
201
View as plain text