Newer
Older
package reportscollector
import (
"errors"
"fmt"
"log"
"mime"
"net/http"
)
var ErrNoMatch = errors.New("no match")
type Handler interface {
Parse(string, *http.Request) ([]Event, error)
}
type Sink interface {
Send(Event)
}
type Collector struct {
handlers []Handler
sink Sink
}
func NewCollector(sink Sink, handlers ...Handler) *Collector {
return &Collector{
handlers: handlers,
sink: sink,
}
}
func (c *Collector) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch req.Method {
case http.MethodPost:
case http.MethodOptions:
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST")
w.Header().Set("Access-Control-Allow-Headers", "content-type")
w.Header().Set("Access-Control-Max-Age", "86400")
w.WriteHeader(http.StatusNoContent)
return
default:
http.Error(w, "Bad method", http.StatusMethodNotAllowed)
return
}
ct, _, err := mime.ParseMediaType(req.Header.Get("Content-Type"))
if err != nil {
http.Error(w, fmt.Sprintf("Bad Content-Type: %v", err.Error()), http.StatusBadRequest)
log.Printf("error parsing content-type: %v", err)
return
}
// Find a handler that can successfully parse the request, and
// get a list of Events.
var events []Event
hloop:
for _, h := range c.handlers {
var err error
events, err = h.Parse(ct, req)
switch err {
case ErrNoMatch:
continue
case nil:
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
if !matched {
log.Printf("no matching handlers for \"%s\"", ct)
http.Error(w, "No matching handlers", http.StatusBadRequest)
return
}
// Augment the Events with additional information obtained
// from the HTTP request, and send them to the forwarder.
for _, e := range events {
c.sink.Send(e)
reportsByType.WithLabelValues(
e.GetString("type"), e.GetString("domain")).Inc()
var (
reportsByType = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "reports_total",
Help: "Number of reports by type.",
},
[]string{"type", "domain"},
)
)