Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp integration test package #1298

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,16 @@ if let host = proxyAddr?.httpHost {

lanternsdk/TestApp contains a test iOS application demonstrating use of the lanternsdk on iOS.

## A note on iOS and memory usage
The iOS application needs to run as a background process on iOS, meaning that it's severely memory restricted. Because of this, we disable a lot of protocols and extra features using `// go:build !ios` in order to conserve memory.

### Why not use // +build !ios
go-mobile automatically sets the `ios` build tag when building for iOS. In our case, we don't use this because in addition to the iOS app, we also distribute an iOS SDK that's intended for embedding inside of user-interactice apps. This SDK does not have to run in the background and is thus not memory constrained in the same way as our iOS app. Consequently, the sdk can and does include all of the standard lantern protocols and features.

### Architecture

![Overview](https://user-images.githubusercontent.com/1143966/117667942-72c80a80-b173-11eb-8c0d-829f2ccd8cde.png)

## Testing

Unit test are run with `make test`.

Functional tests that run a local Flashlight instance against a local http-proxy-lantern instance live in `./integrationtest`. See the README there for usage instructions.

## Features

We use "features" to enable/disable different characteristics/techniques in Flashlight, usually through the global config.
Expand Down
11 changes: 7 additions & 4 deletions balancer/balancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/getlantern/errors"
"github.com/getlantern/flashlight/ops"
"github.com/getlantern/flashlight/proxyimpl"
"github.com/getlantern/golog"
)

Expand Down Expand Up @@ -129,6 +130,8 @@ type Dialer interface {
Stop()

WriteStats(w io.Writer)

Implementation() proxyimpl.ProxyImpl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like an implementation detail which should not be exposed via the public API. I also don't see this used anywhere?

}

type dialStats struct {
Expand Down Expand Up @@ -231,10 +234,10 @@ func (b *Balancer) ResetFromExisting() {
// Dial dials (network, addr) using one of the currently active configured
// Dialers. The dialer is chosen based on the following ordering:
//
// - succeeding dialers are preferred to failing
// - dialers whose bandwidth is unknown are preferred to those whose bandwidth
// is known (in order to collect data)
// - faster dialers (based on bandwidth / RTT) are preferred to slower ones
// - succeeding dialers are preferred to failing
// - dialers whose bandwidth is unknown are preferred to those whose bandwidth
// is known (in order to collect data)
// - faster dialers (based on bandwidth / RTT) are preferred to slower ones
//
// Only Trusted Dialers are used to dial HTTP hosts.
//
Expand Down
3 changes: 3 additions & 0 deletions balancer/test_dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"sync/atomic"
"time"

"github.com/getlantern/flashlight/proxyimpl"
"github.com/getlantern/mockconn"
)

Expand Down Expand Up @@ -183,3 +184,5 @@ func (d *testDialer) Stop() {
}

func (d *testDialer) WriteStats(w io.Writer) {}

func (d *testDialer) Implementation() proxyimpl.ProxyImpl { return nil }
23 changes: 4 additions & 19 deletions chained/chained_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/getlantern/common/config"
"github.com/getlantern/flashlight/common"
"github.com/getlantern/flashlight/ops"
"github.com/getlantern/flashlight/proxyimpl"
"github.com/getlantern/golog"
)

Expand All @@ -42,15 +43,6 @@ func newTestUserConfig() *common.UserConfigData {
return common.NewUserConfigData(common.DefaultAppName, "device", 1234, "protoken", nil, "en-US")
}

type testImpl struct {
nopCloser
d func(ctx context.Context) (net.Conn, error)
}

func (impl *testImpl) dialServer(op *ops.Op, ctx context.Context) (net.Conn, error) {
return impl.d(ctx)
}

func newDialer(dialServer func(ctx context.Context) (net.Conn, error)) (func(network, addr string) (net.Conn, error), error) {
p, err := newProxy("test", "addr:567", "proto", "netw", &config.ProxyConfig{
AuthToken: "token",
Expand All @@ -64,7 +56,7 @@ func newDialer(dialServer func(ctx context.Context) (net.Conn, error)) (func(net
if err != nil {
return nil, err
}
p.impl = &testImpl{d: dialServer}
p.impl = &proxyimpl.TestImpl{DialServerFunc: dialServer}
return p.dial, nil
}

Expand Down Expand Up @@ -160,7 +152,7 @@ func TestBadAddressToServer(t *testing.T) {
if !assert.NoError(t, err) {
return
}
p.impl = &testImpl{d: func(ctx context.Context) (net.Conn, error) {
p.impl = &proxyimpl.TestImpl{DialServerFunc: func(ctx context.Context) (net.Conn, error) {
return nil, fmt.Errorf("fail intentionally")
}}
l := startServer(t)
Expand Down Expand Up @@ -277,15 +269,8 @@ func test(t *testing.T, dialer func(network, addr string) (net.Conn, error)) {
}

func (p *proxy) dial(network, addr string) (net.Conn, error) {
ctx, cancel := context.WithTimeout(context.Background(), chainedDialTimeout)
ctx, cancel := context.WithTimeout(context.Background(), proxyimpl.ChainedDialTimeout)
defer cancel()
conn, _, err := p.DialContext(ctx, network, addr)
return conn, err
}

func TestCiphersFromNames(t *testing.T) {
assert.Nil(t, ciphersFromNames(nil))
assert.Nil(t, ciphersFromNames([]string{}))
assert.Nil(t, ciphersFromNames([]string{"UNKNOWN"}))
assert.EqualValues(t, []uint16{0x0035, 0x003c}, ciphersFromNames([]string{"TLS_RSA_WITH_AES_256_CBC_SHA", "UNKNOWN", "TLS_RSA_WITH_AES_128_CBC_SHA256"}))
}
166 changes: 0 additions & 166 deletions chained/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@
package chained

import (
"strconv"

"github.com/getlantern/common/config"
"github.com/getlantern/golog"
tls "github.com/refraction-networking/utls"
"google.golang.org/protobuf/proto"
)

Expand All @@ -30,166 +27,3 @@ func CopyConfigs(proxies map[string]*config.ProxyConfig) map[string]*config.Prox
func CopyConfig(pc *config.ProxyConfig) *config.ProxyConfig {
return proto.Clone(pc).(*config.ProxyConfig)
}

func _setting(settings map[string]string, name string) string {
if settings == nil {
return ""
}
return settings[name]
}

func _settingInt(settings map[string]string, name string) int {
_val := _setting(settings, name)
if _val == "" {
return 0
}
val, err := strconv.Atoi(_val)
if err != nil {
log.Errorf("Setting %v: %v is not an int", name, _val)
return 0
}
return val
}

func _settingFloat(settings map[string]string, name string) float64 {
_val := _setting(settings, name)
if _val == "" {
return 0.0
}
val, err := strconv.ParseFloat(_val, 64)
if err != nil {
log.Errorf("Setting %v: %v is not a float", name, _val)
return 0.0
}
return val
}

func _settingBool(settings map[string]string, name string) bool {
_val := _setting(settings, name)
if _val == "" {
return false
}
val, err := strconv.ParseBool(_val)
if err != nil {
log.Errorf("Setting %v: %v is not a boolean", name, _val)
return false
}
return val
}

func ptSetting(pc *config.ProxyConfig, name string) string {
return _setting(pc.PluggableTransportSettings, name)
}

func ptSettingInt(pc *config.ProxyConfig, name string) int {
return _settingInt(pc.PluggableTransportSettings, name)
}

func ptSettingBool(pc *config.ProxyConfig, name string) bool {
return _settingBool(pc.PluggableTransportSettings, name)
}

func ptSettingFloat(pc *config.ProxyConfig, name string) float64 {
return _settingFloat(pc.PluggableTransportSettings, name)
}

func muxSetting(pc *config.ProxyConfig, name string) string {
return _setting(pc.MultiplexedSettings, name)
}

func muxSettingInt(pc *config.ProxyConfig, name string) int {
return _settingInt(pc.MultiplexedSettings, name)
}

func muxSettingBool(pc *config.ProxyConfig, name string) bool {
return _settingBool(pc.MultiplexedSettings, name)
}

func muxSettingFloat(pc *config.ProxyConfig, name string) float64 {
return _settingFloat(pc.MultiplexedSettings, name)
}

func desktopOrderedCipherSuites(pc *config.ProxyConfig) []uint16 {
return ciphersFromNames(pc.TLSDesktopOrderedCipherSuiteNames)
}

func mobileOrderedCipherSuites(pc *config.ProxyConfig) []uint16 {
return ciphersFromNames(pc.TLSMobileOrderedCipherSuiteNames)
}

func ciphersFromNames(cipherNames []string) []uint16 {
var ciphers []uint16

for _, cipherName := range cipherNames {
cipher, found := availableTLSCiphers[cipherName]
if !found {
log.Errorf("Unknown cipher: %v", cipherName)
continue
}
ciphers = append(ciphers, cipher)
}

return ciphers
}

func clientHelloID(pc *config.ProxyConfig) tls.ClientHelloID {
chid := availableClientHelloIDs[pc.TLSClientHelloID]
if chid.Client == "" {
chid = tls.HelloGolang
}
return chid
}

var availableTLSCiphers = map[string]uint16{
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
}

// helloBrowser is a special hello ID denoting that ClientHellos should be based on those used by
// the system default browser. This structure does not actually get passed to utls code. It is
// caught by tlsConfigForProxy and converted to tls.HelloCustom with a proper corresponding
// ClientHelloSpec.
var helloBrowser = tls.ClientHelloID{
Client: "Browser",
Version: "0",
}

var availableClientHelloIDs = map[string]tls.ClientHelloID{
"HelloGolang": tls.HelloGolang,
"HelloRandomized": tls.HelloRandomized,
"HelloRandomizedALPN": tls.HelloRandomizedALPN,
"HelloRandomizedNoALPN": tls.HelloRandomizedNoALPN,
"HelloFirefox_Auto": tls.HelloFirefox_Auto,
"HelloFirefox_55": tls.HelloFirefox_55,
"HelloFirefox_56": tls.HelloFirefox_56,
"HelloFirefox_105": tls.HelloFirefox_105,
"HelloChrome_Auto": tls.HelloChrome_Auto,
"HelloChrome_58": tls.HelloChrome_58,
"HelloChrome_62": tls.HelloChrome_62,
"HelloChrome_106": tls.HelloChrome_106,
"HelloEdge_Auto": tls.HelloEdge_Auto,
"Hello360_Auto": tls.Hello360_Auto,
"HelloQQ_Auto": tls.HelloQQ_Auto,
"HelloQQ_11": tls.HelloQQ_11_1,
"HelloBrowser": helloBrowser,
}
41 changes: 0 additions & 41 deletions chained/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,44 +43,3 @@ func TestCopyConfigs(t *testing.T) {
assert.True(t, proto.Equal(proxies["pc1"], CopyConfigs(proxies)["pc1"]))
assert.True(t, proto.Equal(proxies["pc2"], CopyConfigs(proxies)["pc2"]))
}

func TestPTSettingsNil(t *testing.T) {
s := &config.ProxyConfig{}
assert.False(t, ptSettingBool(s, "bool"))
}

func TestPTSettings(t *testing.T) {
s := &config.ProxyConfig{
PluggableTransportSettings: map[string]string{
"true": "true",
"false": "false",
"empty": "",
"2": "2",
"falsestring": "false",
"truestring": "true",
"2string": "2",

"badint": "notint",
"badbool": "notbool",
},
}
assert.True(t, ptSettingBool(s, "true"))
assert.False(t, ptSettingBool(s, "false"))
assert.False(t, ptSettingBool(s, "empty"))
assert.False(t, ptSettingBool(s, "unknown"))
assert.False(t, ptSettingBool(s, "2"))
assert.Equal(t, 2, ptSettingInt(s, "2"))
assert.Equal(t, 0, ptSettingInt(s, "empty"))
assert.Equal(t, 0, ptSettingInt(s, "unknown"))
assert.Equal(t, 0, ptSettingInt(s, "false"))

assert.False(t, ptSettingBool(s, "falsestring"))
assert.True(t, ptSettingBool(s, "truestring"))
assert.Equal(t, 2, ptSettingInt(s, "2string"))

assert.Equal(t, 0, ptSettingInt(s, "badint"))
assert.False(t, ptSettingBool(s, "badbool"))

assert.Equal(t, "", ptSetting(s, "empty"))
assert.Equal(t, "2", ptSetting(s, "2"))
}
Loading