-
Notifications
You must be signed in to change notification settings - Fork 6
/
context.go
132 lines (112 loc) · 4.15 KB
/
context.go
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package fronted
import (
"crypto/x509"
"fmt"
"net/http"
"time"
tls "github.com/refraction-networking/utls"
"github.com/getlantern/eventual"
)
var (
DefaultContext = NewFrontingContext("default")
)
// Configure sets the masquerades to use, the trusted root CAs, and the
// cache file for caching masquerades to set up direct domain fronting
// in the default context.
//
// defaultProviderID is used when a masquerade without a provider is
// encountered (eg in a cache file)
func Configure(pool *x509.CertPool, providers map[string]*Provider, defaultProviderID string, cacheFile string) {
if err := DefaultContext.Configure(pool, providers, defaultProviderID, cacheFile); err != nil {
log.Errorf("Error configuring fronting %s context: %s!!", DefaultContext.name, err)
}
}
// NewDirect creates a new http.RoundTripper that does direct domain fronting
// using the default context. If the default context isn't configured within
// the given timeout, this method returns nil, false.
func NewDirect(timeout time.Duration) (http.RoundTripper, bool) {
return DefaultContext.NewDirect(timeout)
}
// Close closes any existing cache file in the default context
func Close() {
DefaultContext.Close()
}
func NewFrontingContext(name string) *FrontingContext {
return &FrontingContext{
name: name,
instance: eventual.NewValue(),
}
}
type FrontingContext struct {
name string
instance eventual.Value
}
// Configure sets the masquerades to use, the trusted root CAs, and the
// cache file for caching masquerades to set up direct domain fronting.
// defaultProviderID is used when a masquerade without a provider is
// encountered (eg in a cache file)
func (fctx *FrontingContext) Configure(pool *x509.CertPool, providers map[string]*Provider, defaultProviderID string, cacheFile string) error {
return fctx.ConfigureWithHello(pool, providers, defaultProviderID, cacheFile, tls.ClientHelloID{})
}
func (fctx *FrontingContext) ConfigureWithHello(pool *x509.CertPool, providers map[string]*Provider, defaultProviderID string, cacheFile string, clientHelloID tls.ClientHelloID) error {
log.Debugf("Configuring fronted %s context", fctx.name)
if len(providers) == 0 {
return fmt.Errorf("no fronted providers for %s context", fctx.name)
}
_existing, ok := fctx.instance.Get(0)
if ok && _existing != nil {
existing := _existing.(*direct)
log.Debugf("Closing cache from existing instance for %s context", fctx.name)
existing.closeCache()
}
size := 0
for _, p := range providers {
size += len(p.Masquerades)
}
if size == 0 {
return fmt.Errorf("no masquerades for %s context", fctx.name)
}
d := &direct{
certPool: pool,
masquerades: make(sortedMasquerades, 0, size),
maxAllowedCachedAge: defaultMaxAllowedCachedAge,
maxCacheSize: defaultMaxCacheSize,
cacheSaveInterval: defaultCacheSaveInterval,
cacheDirty: make(chan interface{}, 1),
cacheClosed: make(chan interface{}),
defaultProviderID: defaultProviderID,
providers: make(map[string]*Provider),
clientHelloID: clientHelloID,
}
// copy providers
for k, p := range providers {
d.providers[k] = NewProvider(p.HostAliases, p.TestURL, p.Masquerades, p.Validator, p.PassthroughPatterns, p.SNIConfig, p.VerifyHostname)
}
d.loadCandidates(d.providers)
if cacheFile != "" {
d.initCaching(cacheFile)
}
go d.vet(numberToVetInitially)
fctx.instance.Set(d)
return nil
}
// NewDirect creates a new http.RoundTripper that does direct domain fronting.
// If the context isn't configured within the given timeout, this method
// returns nil, false.
func (fctx *FrontingContext) NewDirect(timeout time.Duration) (http.RoundTripper, bool) {
instance, ok := fctx.instance.Get(timeout)
if !ok {
log.Errorf("No DirectHttpClient available within %v for context %s", timeout, fctx.name)
return nil, false
}
return instance.(http.RoundTripper), true
}
// Close closes any existing cache file in the default contexxt.
func (fctx *FrontingContext) Close() {
_existing, ok := fctx.instance.Get(0)
if ok && _existing != nil {
existing := _existing.(*direct)
log.Debugf("Closing cache from existing instance in %s context", fctx.name)
existing.closeCache()
}
}