Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/openvsx-proxy/pkg/handler.go
2498 views
1
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package pkg
6
7
import (
8
"context"
9
"fmt"
10
"math/rand"
11
"net/http"
12
"net/http/httputil"
13
"strconv"
14
"time"
15
16
"github.com/gitpod-io/gitpod/common-go/log"
17
"github.com/google/uuid"
18
"github.com/sirupsen/logrus"
19
)
20
21
func (o *OpenVSXProxy) Handler(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
22
return func(rw http.ResponseWriter, r *http.Request) {
23
start := time.Now()
24
25
var (
26
hitCacheRegular = false
27
hitCacheBackup = false
28
)
29
30
reqid := ""
31
uuid, err := uuid.NewRandom()
32
if err != nil {
33
log.WithError(err).Warn("cannot generate a UUID")
34
reqid = fmt.Sprintf("req%d", rand.Intn(999999))
35
} else {
36
reqid = uuid.String()
37
}
38
39
logFields := logrus.Fields{
40
LOG_FIELD_FUNC: "request_handler",
41
LOG_FIELD_REQUEST_ID: reqid,
42
LOG_FIELD_REQUEST: fmt.Sprintf("%s %s", r.Method, r.URL),
43
"request_content_length": strconv.FormatInt(r.ContentLength, 10),
44
}
45
46
log.WithFields(logFields).Debug("handling request")
47
r = r.WithContext(context.WithValue(r.Context(), REQUEST_ID_CTX, reqid))
48
49
upstream := o.GetUpstreamUrl(r)
50
r = r.WithContext(context.WithValue(r.Context(), UPSTREAM_CTX, upstream))
51
52
if o.IsDisabledCache(upstream) {
53
log.WithFields(logFields).WithField("upstream", upstream.String()).Debug("go without cache")
54
p.ServeHTTP(rw, r)
55
o.finishLog(logFields, start, false, false)
56
o.metrics.DurationRequestProcessingHistogram.Observe(time.Since(start).Seconds())
57
return
58
}
59
60
key, err := o.key(r)
61
if err != nil {
62
log.WithFields(logFields).WithError(err).Error("cannot create cache key")
63
p.ServeHTTP(rw, r)
64
o.finishLog(logFields, start, hitCacheRegular, hitCacheBackup)
65
o.metrics.DurationRequestProcessingHistogram.Observe(time.Since(start).Seconds())
66
return
67
}
68
r = r.WithContext(context.WithValue(r.Context(), REQUEST_CACHE_KEY_CTX, key))
69
logFields[LOG_FIELD_REQUEST] = key
70
71
if o.Config.CacheDurationRegular > 0 {
72
cached, ok, err := o.ReadCache(key)
73
if err != nil {
74
log.WithFields(logFields).WithError(err).Error("cannot read from cache")
75
} else if !ok {
76
log.WithFields(logFields).Debug("cache has no entry for key")
77
} else {
78
hitCacheBackup = true
79
dateHeader := cached.Header.Get("Date")
80
log.WithFields(logFields).Debugf("there is a cached value with date: %s", dateHeader)
81
t, err := time.Parse("Mon, _2 Jan 2006 15:04:05 MST", dateHeader)
82
if err != nil {
83
log.WithFields(logFields).WithError(err).Warn("cannot parse date header of cached value")
84
} else {
85
minDate := time.Now().Add(-time.Duration(o.Config.CacheDurationRegular))
86
if t.After(minDate) {
87
hitCacheRegular = true
88
log.WithFields(logFields).Debugf("cached value is younger than %s - using cached value", o.Config.CacheDurationRegular)
89
for k, v := range cached.Header {
90
for i, val := range v {
91
if i == 0 {
92
rw.Header().Set(k, val)
93
} else {
94
rw.Header().Add(k, val)
95
}
96
}
97
}
98
if v := rw.Header().Get("Access-Control-Allow-Origin"); v != "" && v != "*" {
99
rw.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
100
}
101
rw.Header().Set("X-Cache", "HIT")
102
rw.WriteHeader(cached.StatusCode)
103
rw.Write(cached.Body)
104
o.finishLog(logFields, start, hitCacheRegular, hitCacheBackup)
105
o.metrics.DurationRequestProcessingHistogram.Observe(time.Since(start).Seconds())
106
return
107
} else {
108
log.WithFields(logFields).Debugf("cached value is older than %s - ignoring cached value", o.Config.CacheDurationRegular)
109
}
110
}
111
}
112
}
113
114
duration := time.Since(start)
115
log.WithFields(logFields).WithFields(o.DurationLogFields(duration)).Debug("processing request finished")
116
o.metrics.DurationRequestProcessingHistogram.Observe(duration.Seconds())
117
118
p.ServeHTTP(rw, r)
119
o.finishLog(logFields, start, hitCacheRegular, hitCacheBackup)
120
}
121
}
122
123
func (o *OpenVSXProxy) finishLog(logFields logrus.Fields, start time.Time, hitCacheRegular, hitCacheBackup bool) {
124
duration := time.Since(start)
125
o.metrics.DurationOverallHistogram.Observe(duration.Seconds())
126
if hitCacheBackup {
127
o.metrics.BackupCacheHitCounter.Inc()
128
} else {
129
o.metrics.BackupCacheMissCounter.Inc()
130
}
131
if hitCacheRegular {
132
o.metrics.RegularCacheHitServeCounter.Inc()
133
} else {
134
o.metrics.RegularCacheMissCounter.Inc()
135
}
136
log.
137
WithFields(logFields).
138
WithFields(o.DurationLogFields(duration)).
139
WithField("hit_cache_regular", hitCacheRegular).
140
WithField("hit_cache_backup", hitCacheBackup).
141
Info("request finished")
142
}
143
144