Skip to content

Commit

Permalink
Merge pull request #170 from getlantern/country-specific-configs
Browse files Browse the repository at this point in the history
add capability for country-specific cloud configs
  • Loading branch information
myleshorton committed Apr 20, 2015
2 parents 0a17866 + 31825ba commit d53ba74
Showing 1 changed file with 48 additions and 18 deletions.
66 changes: 48 additions & 18 deletions src/github.com/getlantern/flashlight/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"path/filepath"
"runtime"
"sort"
"strings"
"time"

"github.com/getlantern/appdir"
Expand All @@ -31,15 +32,19 @@ import (
const (
CloudConfigPollInterval = 1 * time.Minute

cloudflare = "cloudflare"
etag = "ETag"
ifNoneMatch = "If-None-Match"
// In seconds
waitForLocationTimeout = 20

cloudflare = "cloudflare"
etag = "ETag"
ifNoneMatch = "If-None-Match"
countryPlaceholder = "${COUNTRY}"
)

var (
log = golog.LoggerFor("flashlight.config")
m *yamlconf.Manager
lastCloudConfigETag = ""
lastCloudConfigETag = map[string]string{}
)

type Config struct {
Expand Down Expand Up @@ -222,7 +227,7 @@ func (cfg *Config) ApplyDefaults() {
}

if cfg.CloudConfig == "" {
cfg.CloudConfig = "https://s3.amazonaws.com/lantern_config/cloud.2.0.0-nl.yaml.gz"
cfg.CloudConfig = fmt.Sprintf("https://s3.amazonaws.com/lantern_config/cloud.%v.yaml.gz", countryPlaceholder)
}

// Default country
Expand Down Expand Up @@ -341,27 +346,27 @@ func (cfg Config) cloudPollSleepTime() time.Duration {
}

func (cfg Config) fetchCloudConfig() (bytes []byte, err error) {
log.Debugf("Fetching cloud config from: %s", cfg.CloudConfig)
log.Debugf("Fetching cloud config...")

if cfg.IsDownstream() {
// Clients must always proxy the request
if cfg.Addr == "" {
err = fmt.Errorf("No proxyAddr")
} else {
bytes, err = cfg.doFetchCloudConfig(cfg.Addr)
bytes, err = cfg.fetchCloudConfigForAddr(cfg.Addr)
}
} else {
bytes, err = cfg.doFetchCloudConfig("")
bytes, err = cfg.fetchCloudConfigForAddr("")
}
if err != nil {
bytes = nil
err = fmt.Errorf("Unable to read yaml from %s: %s", cfg.CloudConfig, err)
err = fmt.Errorf("Unable to read yaml from cloud config: %s", err)
}
return
}

func (cfg Config) doFetchCloudConfig(proxyAddr string) ([]byte, error) {
log.Tracef("doFetchCloudConfig via '%s'", proxyAddr)
func (cfg Config) fetchCloudConfigForAddr(proxyAddr string) ([]byte, error) {
log.Tracef("fetchCloudConfigForAddr '%s'", proxyAddr)

if proxyAddr != "" {
// Wait for proxy to become available
Expand All @@ -375,19 +380,44 @@ func (cfg Config) doFetchCloudConfig(proxyAddr string) ([]byte, error) {
if err != nil {
return nil, fmt.Errorf("Unable to initialize HTTP client: %s", err)
}
// Try and wait to get geolocated, up to a point. Waiting indefinitely
// might prevent us from ever getting geolocated if our current
// configuration is preventing domain fronting from working.
for i := 0; i < waitForLocationTimeout; i++ {
country := strings.ToLower(globals.GetCountry())
if country != "" && country != "xx" {
ret, err := cfg.fetchCloudConfigForCountry(client, country)
// We could check specifically for a 404, but S3 actually returns a 403 when
// a resource is not available. I thought we'd better lean on the side of
// robustness by avoiding hardcoding an S3-ism here, than to try and save an
// extra request every now and then.
if err == nil {
return ret, err
} else {
log.Debugf("Couldn't fetch cloud config for country '%s'; trying the default one", country)
}
break
}
log.Debugf("Waiting for location...")
time.Sleep(1 * time.Second)
}
return cfg.fetchCloudConfigForCountry(client, "default")
}

log.Debugf("Checking for cloud configuration at: %s", cfg.CloudConfig)
req, err := http.NewRequest("GET", cfg.CloudConfig, nil)
func (cfg Config) fetchCloudConfigForCountry(client *http.Client, country string) ([]byte, error) {
url := strings.Replace(cfg.CloudConfig, countryPlaceholder, country, 1)
log.Debugf("Checking for cloud configuration at: %s", url)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("Unable to construct request for cloud config at %s: %s", cfg.CloudConfig, err)
return nil, fmt.Errorf("Unable to construct request for cloud config at %s: %s", url, err)
}
if lastCloudConfigETag != "" {
if lastCloudConfigETag[url] != "" {
// Don't bother fetching if unchanged
req.Header.Set(ifNoneMatch, lastCloudConfigETag)
req.Header.Set(ifNoneMatch, lastCloudConfigETag[url])
}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("Unable to fetch cloud config at %s: %s", cfg.CloudConfig, err)
return nil, fmt.Errorf("Unable to fetch cloud config at %s: %s", url, err)
}
defer resp.Body.Close()

Expand All @@ -398,7 +428,7 @@ func (cfg Config) doFetchCloudConfig(proxyAddr string) ([]byte, error) {
return nil, fmt.Errorf("Unexpected response status: %d", resp.StatusCode)
}

lastCloudConfigETag = resp.Header.Get(etag)
lastCloudConfigETag[url] = resp.Header.Get(etag)
gzReader, err := gzip.NewReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("Unable to open gzip reader: %s", err)
Expand Down

0 comments on commit d53ba74

Please sign in to comment.