This repository has been archived by the owner on Apr 12, 2024. It is now read-only.
/
http.go
124 lines (108 loc) · 3.03 KB
/
http.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package http
import (
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"strings"
"time"
"github.com/tylerb/graceful"
"gopkg.in/tomb.v2"
"github.com/skbkontur/frontreport"
)
// Handler processes incoming reports
type Handler struct {
ReportStorage frontreport.ReportStorage
Port string
Logger frontreport.Logger
MetricStorage frontreport.MetricStorage
tomb tomb.Tomb
metrics struct {
total map[string]frontreport.MetricCounter
errors map[string]frontreport.MetricCounter
}
}
// Start initializes HTTP request handling
func (h *Handler) Start() error {
h.metrics.total = make(map[string]frontreport.MetricCounter)
h.metrics.errors = make(map[string]frontreport.MetricCounter)
for _, reportType := range []string{"csp", "pkp", "stacktracejs"} {
h.metrics.total[reportType] = h.MetricStorage.RegisterCounter(fmt.Sprintf("http.report_decoding.%s.total", reportType))
h.metrics.errors[reportType] = h.MetricStorage.RegisterCounter(fmt.Sprintf("http.report_decoding.%s.errors", reportType))
}
server := &graceful.Server{
Timeout: 10 * time.Second,
NoSignalHandling: true,
Server: &http.Server{
Addr: fmt.Sprintf(":%s", h.Port),
Handler: http.HandlerFunc(h.handleReport),
},
}
listener, err := net.Listen("tcp", server.Addr)
if err != nil {
return err
}
h.tomb.Go(func() error {
err := server.Serve(listener)
select {
case <-h.tomb.Dying():
return nil
default:
return err
}
})
h.tomb.Go(func() error {
<-h.tomb.Dying()
return listener.Close()
})
return nil
}
// Stop finishes listening to HTTP
func (h *Handler) Stop() error {
h.tomb.Kill(nil)
return h.tomb.Wait()
}
func (h *Handler) handleReport(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
switch {
case strings.Contains(r.URL.Path, "csp"):
report := &frontreport.CSPReport{}
if err := h.processReport(r.Body, report, r.Host); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
case strings.Contains(r.URL.Path, "pkp"):
report := &frontreport.PKPReport{}
if err := h.processReport(r.Body, report, r.Host); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
case strings.Contains(r.URL.Path, "stacktracejs"):
report := &frontreport.StacktraceJSReport{}
if err := h.processReport(r.Body, report, r.Host); err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
default:
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusNoContent)
}
func (h *Handler) processReport(body io.Reader, report frontreport.Reportable, host string) error {
h.metrics.total[report.GetType()].Inc(1)
dec := json.NewDecoder(body)
if err := dec.Decode(report); err != nil {
h.Logger.Log("msg", "cannot process JSON body", "report_type", report.GetType(), "error", err)
h.metrics.errors[report.GetType()].Inc(1)
return err
}
report.SetTimestamp(time.Now().UTC().Format("2006-01-02T15:04:05.999Z"))
report.SetHost(host)
h.ReportStorage.AddReport(report)
return nil
}