-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathreporting.go
More file actions
112 lines (96 loc) · 3.82 KB
/
Copy pathreporting.go
File metadata and controls
112 lines (96 loc) · 3.82 KB
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
package proxy
import (
"context"
"net"
"strings"
"time"
rclient "github.com/go-redis/redis/v8"
"github.com/getlantern/geo"
"github.com/getlantern/http-proxy-lantern/v2/common"
"github.com/getlantern/http-proxy-lantern/v2/listeners"
"github.com/getlantern/measured"
"github.com/getlantern/http-proxy-lantern/v2/instrument"
"github.com/getlantern/http-proxy-lantern/v2/redis"
"github.com/getlantern/http-proxy-lantern/v2/throttle"
)
var (
measuredReportingInterval = 1 * time.Minute
)
type reportingConfig struct {
enabled bool
wrapper func(ls net.Listener) net.Listener
}
func newReportingConfig(countryLookup geo.CountryLookup, rc *rclient.Client, instrument instrument.Instrument, throttleConfig throttle.Config) *reportingConfig {
proxiedBytesReporter := func(ctx map[string]interface{}, stats *measured.Stats, deltaStats *measured.Stats, final bool) {
noDelta := deltaStats.SentTotal == 0 && deltaStats.RecvTotal == 0
if noDelta && !final {
// nothing to report on an idle, non-final interval; return before
// any client IP parsing to keep periodic reporting cheap.
return
}
var client_ip net.IP
if s, ok := ctx[common.ClientIP].(string); ok {
client_ip = net.ParseIP(s)
}
if final {
// Record per-session download goodput once at connection close,
// using the connection's cumulative received bytes and open time.
// Done before the zero-delta early return below so a session that
// was idle during its final reporting interval is still counted.
instrument.SessionGoodput(context.Background(), stats.RecvTotal, stats.Duration, client_ip)
}
if noDelta {
// nothing more to report (final call with no new bytes this interval)
return
}
// Note - sometimes we're missing the platform and version
platform := fromContext(ctx, common.Platform)
platformVersion := fromContext(ctx, common.PlatformVersion)
appVersion := fromContext(ctx, common.AppVersion)
libraryVersion := fromContext(ctx, common.LibraryVersion)
app := lowerFromContext(ctx, common.App)
locale := lowerFromContext(ctx, common.Locale)
deviceID := fromContext(ctx, common.DeviceID)
originHost := fromContext(ctx, common.OriginHost)
probingError := fromContext(ctx, common.ProbingError)
arch := fromContext(ctx, common.KernelArch)
dataCapCohort := ""
throttleSettings, hasThrottleSettings := ctx[common.ThrottleSettings]
if hasThrottleSettings {
dataCapCohort = throttleSettings.(*throttle.Settings).Label
}
instrument.ProxiedBytes(context.Background(), deltaStats.SentTotal, deltaStats.RecvTotal, platform, platformVersion, libraryVersion, appVersion, app, locale, dataCapCohort, probingError, client_ip, deviceID, originHost, arch)
}
var reporter listeners.MeasuredReportFN
if throttleConfig == nil {
log.Debug("No throttling configured, don't bother reporting bandwidth usage to Redis")
reporter = func(ctx map[string]interface{}, stats *measured.Stats, deltaStats *measured.Stats,
final bool) {
// noop
}
} else if rc != nil {
reporter = redis.NewMeasuredReporter(countryLookup, rc, measuredReportingInterval, throttleConfig)
}
reporter = combineReporter(reporter, proxiedBytesReporter)
wrapper := func(ls net.Listener) net.Listener {
return listeners.NewMeasuredListener(ls, measuredReportingInterval, reporter)
}
return &reportingConfig{true, wrapper}
}
func fromContext(ctx map[string]interface{}, key string) string {
value := ctx[key]
if value != nil {
return value.(string)
}
return ""
}
func lowerFromContext(ctx map[string]interface{}, key string) string {
return strings.ToLower(fromContext(ctx, key))
}
func combineReporter(reporters ...listeners.MeasuredReportFN) listeners.MeasuredReportFN {
return func(ctx map[string]interface{}, stats *measured.Stats, deltaStats *measured.Stats, final bool) {
for _, r := range reporters {
r(ctx, stats, deltaStats, final)
}
}
}