aboutsummaryrefslogtreecommitdiffstats
path: root/swarm/api/http/middleware.go
blob: f7f819eab51adcb621bdf1fee0fb2bc45ee84c4b (plain) (blame)
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
package http

import (
    "fmt"
    "net/http"
    "runtime/debug"
    "strings"
    "time"

    "github.com/ethereum/go-ethereum/metrics"
    "github.com/ethereum/go-ethereum/swarm/api"
    "github.com/ethereum/go-ethereum/swarm/log"
    "github.com/ethereum/go-ethereum/swarm/sctx"
    "github.com/ethereum/go-ethereum/swarm/spancontext"
    "github.com/pborman/uuid"
)

// Adapt chains h (main request handler) main handler to adapters (middleware handlers)
// Please note that the order of execution for `adapters` is FIFO (adapters[0] will be executed first)
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
    for i := range adapters {
        adapter := adapters[len(adapters)-1-i]
        h = adapter(h)
    }
    return h
}

type Adapter func(http.Handler) http.Handler

func SetRequestID(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        r = r.WithContext(SetRUID(r.Context(), uuid.New()[:8]))
        metrics.GetOrRegisterCounter(fmt.Sprintf("http.request.%s", r.Method), nil).Inc(1)
        log.Info("created ruid for request", "ruid", GetRUID(r.Context()), "method", r.Method, "url", r.RequestURI)

        h.ServeHTTP(w, r)
    })
}

func SetRequestHost(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        r = r.WithContext(sctx.SetHost(r.Context(), r.Host))
        log.Info("setting request host", "ruid", GetRUID(r.Context()), "host", sctx.GetHost(r.Context()))

        h.ServeHTTP(w, r)
    })
}

func ParseURI(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        uri, err := api.Parse(strings.TrimLeft(r.URL.Path, "/"))
        if err != nil {
            w.WriteHeader(http.StatusBadRequest)
            respondError(w, r, fmt.Sprintf("invalid URI %q", r.URL.Path), http.StatusBadRequest)
            return
        }
        if uri.Addr != "" && strings.HasPrefix(uri.Addr, "0x") {
            uri.Addr = strings.TrimPrefix(uri.Addr, "0x")

            msg := fmt.Sprintf(`The requested hash seems to be prefixed with '0x'. You will be redirected to the correct URL within 5 seconds.<br/>
            Please click <a href='%[1]s'>here</a> if your browser does not redirect you within 5 seconds.<script>setTimeout("location.href='%[1]s';",5000);</script>`, "/"+uri.String())
            w.WriteHeader(http.StatusNotFound)
            w.Write([]byte(msg))
            return
        }

        ctx := r.Context()
        r = r.WithContext(SetURI(ctx, uri))
        log.Debug("parsed request path", "ruid", GetRUID(r.Context()), "method", r.Method, "uri.Addr", uri.Addr, "uri.Path", uri.Path, "uri.Scheme", uri.Scheme)

        h.ServeHTTP(w, r)
    })
}

func InitLoggingResponseWriter(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        tn := time.Now()

        writer := newLoggingResponseWriter(w)
        h.ServeHTTP(writer, r)

        ts := time.Since(tn)
        log.Info("request served", "ruid", GetRUID(r.Context()), "code", writer.statusCode, "time", ts*time.Millisecond)
        metrics.GetOrRegisterResettingTimer(fmt.Sprintf("http.request.%s.time", r.Method), nil).Update(ts)
        metrics.GetOrRegisterResettingTimer(fmt.Sprintf("http.request.%s.%d.time", r.Method, writer.statusCode), nil).Update(ts)
    })
}

func InstrumentOpenTracing(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        uri := GetURI(r.Context())
        if uri == nil || r.Method == "" || (uri != nil && uri.Scheme == "") {
            h.ServeHTTP(w, r) // soft fail
            return
        }
        spanName := fmt.Sprintf("http.%s.%s", r.Method, uri.Scheme)
        ctx, sp := spancontext.StartSpan(r.Context(), spanName)

        defer sp.Finish()
        h.ServeHTTP(w, r.WithContext(ctx))
    })
}

func RecoverPanic(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                log.Error("panic recovery!", "stack trace", string(debug.Stack()), "url", r.URL.String(), "headers", r.Header)
            }
        }()
        h.ServeHTTP(w, r)
    })
}