1package raven
2
3import (
4	"errors"
5	"fmt"
6	"net"
7	"net/http"
8	"net/url"
9	"runtime/debug"
10	"strings"
11)
12
13func NewHttp(req *http.Request) *Http {
14	proto := "http"
15	if req.TLS != nil || req.Header.Get("X-Forwarded-Proto") == "https" {
16		proto = "https"
17	}
18	h := &Http{
19		Method:  req.Method,
20		Cookies: req.Header.Get("Cookie"),
21		Query:   sanitizeQuery(req.URL.Query()).Encode(),
22		URL:     proto + "://" + req.Host + req.URL.Path,
23		Headers: make(map[string]string, len(req.Header)),
24	}
25	if addr, port, err := net.SplitHostPort(req.RemoteAddr); err == nil {
26		h.Env = map[string]string{"REMOTE_ADDR": addr, "REMOTE_PORT": port}
27	}
28	for k, v := range req.Header {
29		h.Headers[k] = strings.Join(v, ",")
30	}
31	h.Headers["Host"] = req.Host
32	return h
33}
34
35var querySecretFields = []string{"password", "passphrase", "passwd", "secret"}
36
37func sanitizeQuery(query url.Values) url.Values {
38	for _, keyword := range querySecretFields {
39		for field := range query {
40			if strings.Contains(field, keyword) {
41				query[field] = []string{"********"}
42			}
43		}
44	}
45	return query
46}
47
48// https://docs.getsentry.com/hosted/clientdev/interfaces/#context-interfaces
49type Http struct {
50	// Required
51	URL    string `json:"url"`
52	Method string `json:"method"`
53	Query  string `json:"query_string,omitempty"`
54
55	// Optional
56	Cookies string            `json:"cookies,omitempty"`
57	Headers map[string]string `json:"headers,omitempty"`
58	Env     map[string]string `json:"env,omitempty"`
59
60	// Must be either a string or map[string]string
61	Data interface{} `json:"data,omitempty"`
62}
63
64func (h *Http) Class() string { return "request" }
65
66// Recovery handler to wrap the stdlib net/http Mux.
67// Example:
68//	http.HandleFunc("/", raven.RecoveryHandler(func(w http.ResponseWriter, r *http.Request) {
69//		...
70//	}))
71func RecoveryHandler(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
72	return Recoverer(http.HandlerFunc(handler)).ServeHTTP
73}
74
75// Recovery handler to wrap the stdlib net/http Mux.
76// Example:
77//  mux := http.NewServeMux
78//  ...
79//	http.Handle("/", raven.Recoverer(mux))
80func Recoverer(handler http.Handler) http.Handler {
81	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
82		defer func() {
83			if rval := recover(); rval != nil {
84				debug.PrintStack()
85				rvalStr := fmt.Sprint(rval)
86				var packet *Packet
87				if err, ok := rval.(error); ok {
88					packet = NewPacket(rvalStr, NewException(errors.New(rvalStr), GetOrNewStacktrace(err, 2, 3, nil)), NewHttp(r))
89				} else {
90					packet = NewPacket(rvalStr, NewException(errors.New(rvalStr), NewStacktrace(2, 3, nil)), NewHttp(r))
91				}
92				Capture(packet, nil)
93				w.WriteHeader(http.StatusInternalServerError)
94			}
95		}()
96
97		handler.ServeHTTP(w, r)
98	})
99}
100