Browse Source

Create ClientParametersAccessors without heap allocations

Rod Hynes 6 năm trước cách đây
mục cha
commit
5f76e84ce3

+ 102 - 133
psiphon/common/parameters/clientParameters.go

@@ -472,74 +472,6 @@ type ClientParameters struct {
 	snapshot       atomic.Value
 	snapshot       atomic.Value
 }
 }
 
 
-// ClientParametersAccessor defines the interface for accessing client
-// parameter values.
-type ClientParametersAccessor interface {
-
-	// Tag returns the tag associated with these parameters.
-	Tag() string
-
-	// String returns a string parameter value.
-	String(name string) string
-
-	// Strings returns a []string parameter value.
-	Strings(name string) []string
-
-	// Int returns an int parameter value.
-	Int(name string) int
-
-	// Bool returns a bool parameter value.
-	Bool(name string) bool
-
-	// Float returns a float64 parameter value.
-	Float(name string) float64
-
-	// WeightedCoinFlip returns the result of prng.FlipWeightedCoin using the
-	// specified float parameter as the probability input.
-	WeightedCoinFlip(name string) bool
-
-	// Duration returns a time.Duration parameter value. When the duration
-	// parameter has the useNetworkLatencyMultiplier flag, the
-	// NetworkLatencyMultiplier is applied to the returned value.
-	Duration(name string) time.Duration
-
-	// TunnelProtocols returns a protocol.TunnelProtocols parameter value.
-	// If there is a corresponding Probability value, a weighted coin flip
-	// will be performed and, depending on the result, the value or the
-	// parameter default will be returned.
-	TunnelProtocols(name string) protocol.TunnelProtocols
-
-	// TLSProfiles returns a protocol.TLSProfiles parameter value.
-	// If there is a corresponding Probability value, a weighted coin flip
-	// will be performed and, depending on the result, the value or the
-	// parameter default will be returned.
-	TLSProfiles(name string) protocol.TLSProfiles
-
-	// QUICVersions returns a protocol.QUICVersions parameter value.
-	// If there is a corresponding Probability value, a weighted coin flip
-	// will be performed and, depending on the result, the value or the
-	// parameter default will be returned.
-	QUICVersions(name string) protocol.QUICVersions
-
-	// DownloadURLs returns a DownloadURLs parameter value.
-	DownloadURLs(name string) DownloadURLs
-
-	// RateLimits returns a common.RateLimits parameter value.
-	RateLimits(name string) common.RateLimits
-
-	// HTTPHeaders returns an http.Header parameter value.
-	HTTPHeaders(name string) http.Header
-
-	// CustomTLSProfileNames returns the CustomTLSProfile.Name fields for
-	// each profile in the CustomTLSProfiles parameter value.
-	CustomTLSProfileNames() []string
-
-	// CustomTLSProfile returns the CustomTLSProfile fields with the specified
-	// Name field if it exists in the CustomTLSProfiles parameter value.
-	// Returns nil if not found.
-	CustomTLSProfile(name string) *protocol.CustomTLSProfile
-}
-
 // NewClientParameters initializes a new ClientParameters with the default
 // NewClientParameters initializes a new ClientParameters with the default
 // parameter values.
 // parameter values.
 //
 //
@@ -765,19 +697,28 @@ func (p *ClientParameters) Set(
 	return counts, nil
 	return counts, nil
 }
 }
 
 
-// Get returns the current parameters. Values read from the current parameters
-// are not deep copies and must be treated read-only.
+// Get returns the current parameters.
+//
+// Values read from the current parameters are not deep copies and must be
+// treated read-only.
 //
 //
 // The returned ClientParametersAccessor may be used to read multiple related
 // The returned ClientParametersAccessor may be used to read multiple related
 // values atomically and consistently while the current set of values in
 // values atomically and consistently while the current set of values in
 // ClientParameters may change concurrently.
 // ClientParameters may change concurrently.
+//
+// Get does not perform any heap allocations and is intended for repeated,
+// direct, low-overhead invocations.
 func (p *ClientParameters) Get() ClientParametersAccessor {
 func (p *ClientParameters) Get() ClientParametersAccessor {
-	return p.snapshot.Load().(*clientParametersSnapshot)
+	return ClientParametersAccessor{
+		snapshot: p.snapshot.Load().(*clientParametersSnapshot)}
 }
 }
 
 
 // GetCustom returns the current parameters while also setting customizations
 // GetCustom returns the current parameters while also setting customizations
 // for this instance.
 // for this instance.
 //
 //
+// The properties of Get also apply to GetCustom: must be read-only; atomic
+// and consisent view; no heap allocations.
+//
 // Customizations include:
 // Customizations include:
 //
 //
 // - customNetworkLatencyMultiplier, which overrides NetworkLatencyMultiplier
 // - customNetworkLatencyMultiplier, which overrides NetworkLatencyMultiplier
@@ -786,8 +727,8 @@ func (p *ClientParameters) Get() ClientParametersAccessor {
 func (p *ClientParameters) GetCustom(
 func (p *ClientParameters) GetCustom(
 	customNetworkLatencyMultiplier float64) ClientParametersAccessor {
 	customNetworkLatencyMultiplier float64) ClientParametersAccessor {
 
 
-	return &customClientParametersSnapshot{
-		ClientParametersAccessor:       p.Get(),
+	return ClientParametersAccessor{
+		snapshot:                       p.snapshot.Load().(*clientParametersSnapshot),
 		customNetworkLatencyMultiplier: customNetworkLatencyMultiplier,
 		customNetworkLatencyMultiplier: customNetworkLatencyMultiplier,
 	}
 	}
 }
 }
@@ -802,10 +743,6 @@ type clientParametersSnapshot struct {
 	parameters     map[string]interface{}
 	parameters     map[string]interface{}
 }
 }
 
 
-func (p *clientParametersSnapshot) Tag() string {
-	return p.tag
-}
-
 // getValue sets target to the value of the named parameter.
 // getValue sets target to the value of the named parameter.
 //
 //
 // It is an error if the name is not found, target is not a pointer, or the
 // It is an error if the name is not found, target is not a pointer, or the
@@ -853,58 +790,86 @@ func (p *clientParametersSnapshot) getValue(name string, target interface{}) {
 	targetValue.Elem().Set(reflect.ValueOf(value))
 	targetValue.Elem().Set(reflect.ValueOf(value))
 }
 }
 
 
-func (p *clientParametersSnapshot) String(name string) string {
+// ClientParametersAccessor provides consistent, atomic access to client
+// parameter values. Any customizations are applied transparently.
+type ClientParametersAccessor struct {
+	snapshot                       *clientParametersSnapshot
+	customNetworkLatencyMultiplier float64
+}
+
+// Close clears internal references to large memory objects, allowing them to
+// be garbage collected. Call Close when done using a
+// ClientParametersAccessor, where memory footprint is a concern, and where
+// the ClientParametersAccessor is not immediately going out of scope. After
+// Close is called, all other ClientParametersAccessor functions will panic if
+// called.
+func (p ClientParametersAccessor) Close() {
+	p.snapshot = nil
+}
+
+// Tag returns the tag associated with these parameters.
+func (p ClientParametersAccessor) Tag() string {
+	return p.snapshot.tag
+}
+
+// String returns a string parameter value.
+func (p ClientParametersAccessor) String(name string) string {
 	value := ""
 	value := ""
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return value
 	return value
 }
 }
 
 
-func (p *clientParametersSnapshot) Strings(name string) []string {
+func (p ClientParametersAccessor) Strings(name string) []string {
 	value := []string{}
 	value := []string{}
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return value
 	return value
 }
 }
 
 
 // Int returns an int parameter value.
 // Int returns an int parameter value.
-func (p *clientParametersSnapshot) Int(name string) int {
+func (p ClientParametersAccessor) Int(name string) int {
 	value := int(0)
 	value := int(0)
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return value
 	return value
 }
 }
 
 
 // Bool returns a bool parameter value.
 // Bool returns a bool parameter value.
-func (p *clientParametersSnapshot) Bool(name string) bool {
+func (p ClientParametersAccessor) Bool(name string) bool {
 	value := false
 	value := false
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return value
 	return value
 }
 }
 
 
-func (p *clientParametersSnapshot) Float(name string) float64 {
+// Float returns a float64 parameter value.
+func (p ClientParametersAccessor) Float(name string) float64 {
 	value := float64(0.0)
 	value := float64(0.0)
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return value
 	return value
 }
 }
 
 
-func (p *clientParametersSnapshot) WeightedCoinFlip(name string) bool {
+// WeightedCoinFlip returns the result of prng.FlipWeightedCoin using the
+// specified float parameter as the probability input.
+func (p ClientParametersAccessor) WeightedCoinFlip(name string) bool {
 	var value float64
 	var value float64
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return prng.FlipWeightedCoin(value)
 	return prng.FlipWeightedCoin(value)
 }
 }
 
 
-func (p *clientParametersSnapshot) duration(name string, customNetworkLatencyMultiplier float64) time.Duration {
-
+// Duration returns a time.Duration parameter value. When the duration
+// parameter has the useNetworkLatencyMultiplier flag, the
+// NetworkLatencyMultiplier is applied to the returned value.
+func (p ClientParametersAccessor) Duration(name string) time.Duration {
 	value := time.Duration(0)
 	value := time.Duration(0)
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 
 
 	defaultParameter, ok := defaultClientParameters[name]
 	defaultParameter, ok := defaultClientParameters[name]
 	if value > 0 && ok && defaultParameter.flags&useNetworkLatencyMultiplier != 0 {
 	if value > 0 && ok && defaultParameter.flags&useNetworkLatencyMultiplier != 0 {
 
 
 		multiplier := float64(0.0)
 		multiplier := float64(0.0)
 
 
-		if customNetworkLatencyMultiplier != 0.0 {
-			multiplier = customNetworkLatencyMultiplier
+		if p.customNetworkLatencyMultiplier != 0.0 {
+			multiplier = p.customNetworkLatencyMultiplier
 		} else {
 		} else {
-			p.getValue(NetworkLatencyMultiplier, &multiplier)
+			p.snapshot.getValue(NetworkLatencyMultiplier, &multiplier)
 		}
 		}
 
 
 		if multiplier > 0.0 {
 		if multiplier > 0.0 {
@@ -916,17 +881,17 @@ func (p *clientParametersSnapshot) duration(name string, customNetworkLatencyMul
 	return value
 	return value
 }
 }
 
 
-func (p *clientParametersSnapshot) Duration(name string) time.Duration {
-	return p.duration(name, 0.0)
-}
-
-func (p *clientParametersSnapshot) TunnelProtocols(name string) protocol.TunnelProtocols {
+// TunnelProtocols returns a protocol.TunnelProtocols parameter value.
+// If there is a corresponding Probability value, a weighted coin flip
+// will be performed and, depending on the result, the value or the
+// parameter default will be returned.
+func (p ClientParametersAccessor) TunnelProtocols(name string) protocol.TunnelProtocols {
 
 
 	probabilityName := name + "Probability"
 	probabilityName := name + "Probability"
-	_, ok := p.parameters[probabilityName]
+	_, ok := p.snapshot.parameters[probabilityName]
 	if ok {
 	if ok {
 		probabilityValue := float64(1.0)
 		probabilityValue := float64(1.0)
-		p.getValue(probabilityName, &probabilityValue)
+		p.snapshot.getValue(probabilityName, &probabilityValue)
 		if !prng.FlipWeightedCoin(probabilityValue) {
 		if !prng.FlipWeightedCoin(probabilityValue) {
 			defaultParameter, ok := defaultClientParameters[name]
 			defaultParameter, ok := defaultClientParameters[name]
 			if ok {
 			if ok {
@@ -941,17 +906,21 @@ func (p *clientParametersSnapshot) TunnelProtocols(name string) protocol.TunnelP
 	}
 	}
 
 
 	value := protocol.TunnelProtocols{}
 	value := protocol.TunnelProtocols{}
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return value
 	return value
 }
 }
 
 
-func (p *clientParametersSnapshot) TLSProfiles(name string) protocol.TLSProfiles {
+// TLSProfiles returns a protocol.TLSProfiles parameter value.
+// If there is a corresponding Probability value, a weighted coin flip
+// will be performed and, depending on the result, the value or the
+// parameter default will be returned.
+func (p ClientParametersAccessor) TLSProfiles(name string) protocol.TLSProfiles {
 
 
 	probabilityName := name + "Probability"
 	probabilityName := name + "Probability"
-	_, ok := p.parameters[probabilityName]
+	_, ok := p.snapshot.parameters[probabilityName]
 	if ok {
 	if ok {
 		probabilityValue := float64(1.0)
 		probabilityValue := float64(1.0)
-		p.getValue(probabilityName, &probabilityValue)
+		p.snapshot.getValue(probabilityName, &probabilityValue)
 		if !prng.FlipWeightedCoin(probabilityValue) {
 		if !prng.FlipWeightedCoin(probabilityValue) {
 			defaultParameter, ok := defaultClientParameters[name]
 			defaultParameter, ok := defaultClientParameters[name]
 			if ok {
 			if ok {
@@ -966,17 +935,21 @@ func (p *clientParametersSnapshot) TLSProfiles(name string) protocol.TLSProfiles
 	}
 	}
 
 
 	value := protocol.TLSProfiles{}
 	value := protocol.TLSProfiles{}
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return value
 	return value
 }
 }
 
 
-func (p *clientParametersSnapshot) QUICVersions(name string) protocol.QUICVersions {
+// QUICVersions returns a protocol.QUICVersions parameter value.
+// If there is a corresponding Probability value, a weighted coin flip
+// will be performed and, depending on the result, the value or the
+// parameter default will be returned.
+func (p ClientParametersAccessor) QUICVersions(name string) protocol.QUICVersions {
 
 
 	probabilityName := name + "Probability"
 	probabilityName := name + "Probability"
-	_, ok := p.parameters[probabilityName]
+	_, ok := p.snapshot.parameters[probabilityName]
 	if ok {
 	if ok {
 		probabilityValue := float64(1.0)
 		probabilityValue := float64(1.0)
-		p.getValue(probabilityName, &probabilityValue)
+		p.snapshot.getValue(probabilityName, &probabilityValue)
 		if !prng.FlipWeightedCoin(probabilityValue) {
 		if !prng.FlipWeightedCoin(probabilityValue) {
 			defaultParameter, ok := defaultClientParameters[name]
 			defaultParameter, ok := defaultClientParameters[name]
 			if ok {
 			if ok {
@@ -991,31 +964,36 @@ func (p *clientParametersSnapshot) QUICVersions(name string) protocol.QUICVersio
 	}
 	}
 
 
 	value := protocol.QUICVersions{}
 	value := protocol.QUICVersions{}
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return value
 	return value
 }
 }
 
 
-func (p *clientParametersSnapshot) DownloadURLs(name string) DownloadURLs {
+// DownloadURLs returns a DownloadURLs parameter value.
+func (p ClientParametersAccessor) DownloadURLs(name string) DownloadURLs {
 	value := DownloadURLs{}
 	value := DownloadURLs{}
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return value
 	return value
 }
 }
 
 
-func (p *clientParametersSnapshot) RateLimits(name string) common.RateLimits {
+// RateLimits returns a common.RateLimits parameter value.
+func (p ClientParametersAccessor) RateLimits(name string) common.RateLimits {
 	value := common.RateLimits{}
 	value := common.RateLimits{}
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return value
 	return value
 }
 }
 
 
-func (p *clientParametersSnapshot) HTTPHeaders(name string) http.Header {
+// HTTPHeaders returns an http.Header parameter value.
+func (p ClientParametersAccessor) HTTPHeaders(name string) http.Header {
 	value := make(http.Header)
 	value := make(http.Header)
-	p.getValue(name, &value)
+	p.snapshot.getValue(name, &value)
 	return value
 	return value
 }
 }
 
 
-func (p *clientParametersSnapshot) CustomTLSProfileNames() []string {
+// CustomTLSProfileNames returns the CustomTLSProfile.Name fields for
+// each profile in the CustomTLSProfiles parameter value.
+func (p ClientParametersAccessor) CustomTLSProfileNames() []string {
 	value := protocol.CustomTLSProfiles{}
 	value := protocol.CustomTLSProfiles{}
-	p.getValue(CustomTLSProfiles, &value)
+	p.snapshot.getValue(CustomTLSProfiles, &value)
 	names := make([]string, len(value))
 	names := make([]string, len(value))
 	for i := 0; i < len(value); i++ {
 	for i := 0; i < len(value); i++ {
 		names[i] = value[i].Name
 		names[i] = value[i].Name
@@ -1023,9 +1001,12 @@ func (p *clientParametersSnapshot) CustomTLSProfileNames() []string {
 	return names
 	return names
 }
 }
 
 
-func (p *clientParametersSnapshot) CustomTLSProfile(name string) *protocol.CustomTLSProfile {
+// CustomTLSProfile returns the CustomTLSProfile fields with the specified
+// Name field if it exists in the CustomTLSProfiles parameter value.
+// Returns nil if not found.
+func (p ClientParametersAccessor) CustomTLSProfile(name string) *protocol.CustomTLSProfile {
 	value := protocol.CustomTLSProfiles{}
 	value := protocol.CustomTLSProfiles{}
-	p.getValue(CustomTLSProfiles, &value)
+	p.snapshot.getValue(CustomTLSProfiles, &value)
 
 
 	// Note: linear lookup -- assumes a short list
 	// Note: linear lookup -- assumes a short list
 
 
@@ -1036,15 +1017,3 @@ func (p *clientParametersSnapshot) CustomTLSProfile(name string) *protocol.Custo
 	}
 	}
 	return nil
 	return nil
 }
 }
-
-// customClientParametersSnapshot override the behavior of
-// clientParametersSnapshot to apply customNetworkLatencyMultiplier to
-// Duration parameters.
-type customClientParametersSnapshot struct {
-	ClientParametersAccessor
-	customNetworkLatencyMultiplier float64
-}
-
-func (p *customClientParametersSnapshot) Duration(name string) time.Duration {
-	return p.ClientParametersAccessor.(*clientParametersSnapshot).duration(name, p.customNetworkLatencyMultiplier)
-}

+ 6 - 7
psiphon/controller.go

@@ -1277,8 +1277,9 @@ func (controller *Controller) launchEstablishing() {
 		replayCandidateCount:                p.Int(parameters.ReplayCandidateCount),
 		replayCandidateCount:                p.Int(parameters.ReplayCandidateCount),
 	}
 	}
 
 
-	workerPoolSize := controller.config.GetClientParameters().Get().Int(
-		parameters.ConnectionWorkerPoolSize)
+	workerPoolSize := p.Int(parameters.ConnectionWorkerPoolSize)
+
+	p.Close()
 
 
 	// When TargetServerEntry is used, override any worker pool size config or
 	// When TargetServerEntry is used, override any worker pool size config or
 	// tactic parameter and use a pool size of 1. The typical use case for
 	// tactic parameter and use a pool size of 1. The typical use case for
@@ -1289,8 +1290,6 @@ func (controller *Controller) launchEstablishing() {
 		workerPoolSize = 1
 		workerPoolSize = 1
 	}
 	}
 
 
-	p = nil
-
 	// If InitialLimitTunnelProtocols is configured but cannot be satisfied,
 	// If InitialLimitTunnelProtocols is configured but cannot be satisfied,
 	// skip the initial phase in this establishment. This avoids spinning,
 	// skip the initial phase in this establishment. This avoids spinning,
 	// unable to connect, in this case. InitialLimitTunnelProtocols is
 	// unable to connect, in this case. InitialLimitTunnelProtocols is
@@ -1488,7 +1487,7 @@ func (controller *Controller) getTactics(done chan struct{}) {
 			timeout := prng.JitterDuration(
 			timeout := prng.JitterDuration(
 				p.Duration(parameters.TacticsRetryPeriod),
 				p.Duration(parameters.TacticsRetryPeriod),
 				p.Float(parameters.TacticsRetryPeriodJitter))
 				p.Float(parameters.TacticsRetryPeriodJitter))
-			p = nil
+			p.Close()
 
 
 			tacticsRetryDelay := time.NewTimer(timeout)
 			tacticsRetryDelay := time.NewTimer(timeout)
 
 
@@ -1790,7 +1789,7 @@ loop:
 		timeout := prng.JitterDuration(
 		timeout := prng.JitterDuration(
 			p.Duration(parameters.EstablishTunnelPausePeriod),
 			p.Duration(parameters.EstablishTunnelPausePeriod),
 			p.Float(parameters.EstablishTunnelPausePeriodJitter))
 			p.Float(parameters.EstablishTunnelPausePeriodJitter))
-		p = nil
+		p.Close()
 
 
 		timer := time.NewTimer(timeout)
 		timer := time.NewTimer(timeout)
 		select {
 		select {
@@ -1948,7 +1947,7 @@ loop:
 		p := controller.config.GetClientParameters().Get()
 		p := controller.config.GetClientParameters().Get()
 		staggerPeriod := p.Duration(parameters.StaggerConnectionWorkersPeriod)
 		staggerPeriod := p.Duration(parameters.StaggerConnectionWorkersPeriod)
 		staggerJitter := p.Float(parameters.StaggerConnectionWorkersJitter)
 		staggerJitter := p.Float(parameters.StaggerConnectionWorkersJitter)
-		p = nil
+		p.Close()
 
 
 		if establishConnectTunnelCount > 0 && staggerPeriod != 0 {
 		if establishConnectTunnelCount > 0 && staggerPeriod != 0 {
 			controller.staggerMutex.Lock()
 			controller.staggerMutex.Lock()

+ 0 - 1
psiphon/httpProxy.go

@@ -120,7 +120,6 @@ func NewHttpProxy(
 	p := config.GetClientParameters().Get()
 	p := config.GetClientParameters().Get()
 	responseHeaderTimeout := p.Duration(parameters.HTTPProxyOriginServerTimeout)
 	responseHeaderTimeout := p.Duration(parameters.HTTPProxyOriginServerTimeout)
 	maxIdleConnsPerHost := p.Int(parameters.HTTPProxyMaxIdleConnectionsPerHost)
 	maxIdleConnsPerHost := p.Int(parameters.HTTPProxyMaxIdleConnectionsPerHost)
-	p = nil
 
 
 	// TODO: could HTTP proxy share a tunneled transport with URL proxy?
 	// TODO: could HTTP proxy share a tunneled transport with URL proxy?
 	// For now, keeping them distinct just to be conservative.
 	// For now, keeping them distinct just to be conservative.

+ 3 - 4
psiphon/meekConn.go

@@ -521,7 +521,6 @@ func DialMeek(
 			meek.fullReceiveBufferLength = p.Int(parameters.MeekFullReceiveBufferLength)
 			meek.fullReceiveBufferLength = p.Int(parameters.MeekFullReceiveBufferLength)
 			meek.readPayloadChunkLength = p.Int(parameters.MeekReadPayloadChunkLength)
 			meek.readPayloadChunkLength = p.Int(parameters.MeekReadPayloadChunkLength)
 		}
 		}
-		p = nil
 
 
 		meek.emptyReceiveBuffer = make(chan *bytes.Buffer, 1)
 		meek.emptyReceiveBuffer = make(chan *bytes.Buffer, 1)
 		meek.partialReceiveBuffer = make(chan *bytes.Buffer, 1)
 		meek.partialReceiveBuffer = make(chan *bytes.Buffer, 1)
@@ -844,7 +843,7 @@ func (meek *MeekConn) relay() {
 	interval := prng.JitterDuration(
 	interval := prng.JitterDuration(
 		p.Duration(parameters.MeekMinPollInterval),
 		p.Duration(parameters.MeekMinPollInterval),
 		p.Float(parameters.MeekMinPollIntervalJitter))
 		p.Float(parameters.MeekMinPollIntervalJitter))
-	p = nil
+	p.Close()
 
 
 	timeout := time.NewTimer(interval)
 	timeout := time.NewTimer(interval)
 	defer timeout.Stop()
 	defer timeout.Stop()
@@ -944,7 +943,7 @@ func (meek *MeekConn) relay() {
 			}
 			}
 		}
 		}
 
 
-		p = nil
+		p.Close()
 	}
 	}
 }
 }
 
 
@@ -1102,7 +1101,7 @@ func (meek *MeekConn) relayRoundTrip(sendBuffer *bytes.Buffer) (int64, error) {
 	retryDelay := p.Duration(parameters.MeekRoundTripRetryMinDelay)
 	retryDelay := p.Duration(parameters.MeekRoundTripRetryMinDelay)
 	retryMaxDelay := p.Duration(parameters.MeekRoundTripRetryMaxDelay)
 	retryMaxDelay := p.Duration(parameters.MeekRoundTripRetryMaxDelay)
 	retryMultiplier := p.Float(parameters.MeekRoundTripRetryMultiplier)
 	retryMultiplier := p.Float(parameters.MeekRoundTripRetryMultiplier)
-	p = nil
+	p.Close()
 
 
 	serverAcknowledgedRequestPayload := false
 	serverAcknowledgedRequestPayload := false
 
 

+ 2 - 2
psiphon/remoteServerList.go

@@ -57,7 +57,7 @@ func FetchCommonRemoteServerList(
 	publicKey := p.String(parameters.RemoteServerListSignaturePublicKey)
 	publicKey := p.String(parameters.RemoteServerListSignaturePublicKey)
 	urls := p.DownloadURLs(parameters.RemoteServerListURLs)
 	urls := p.DownloadURLs(parameters.RemoteServerListURLs)
 	downloadTimeout := p.Duration(parameters.FetchRemoteServerListTimeout)
 	downloadTimeout := p.Duration(parameters.FetchRemoteServerListTimeout)
-	p = nil
+	p.Close()
 
 
 	downloadURL, canonicalURL, skipVerify := urls.Select(attempt)
 	downloadURL, canonicalURL, skipVerify := urls.Select(attempt)
 
 
@@ -140,7 +140,7 @@ func FetchObfuscatedServerLists(
 	publicKey := p.String(parameters.RemoteServerListSignaturePublicKey)
 	publicKey := p.String(parameters.RemoteServerListSignaturePublicKey)
 	urls := p.DownloadURLs(parameters.ObfuscatedServerListRootURLs)
 	urls := p.DownloadURLs(parameters.ObfuscatedServerListRootURLs)
 	downloadTimeout := p.Duration(parameters.FetchRemoteServerListTimeout)
 	downloadTimeout := p.Duration(parameters.FetchRemoteServerListTimeout)
-	p = nil
+	p.Close()
 
 
 	rootURL, canonicalRootURL, skipVerify := urls.Select(attempt)
 	rootURL, canonicalRootURL, skipVerify := urls.Select(attempt)
 	downloadURL := osl.GetOSLRegistryURL(rootURL)
 	downloadURL := osl.GetOSLRegistryURL(rootURL)

+ 2 - 3
psiphon/splitTunnel.go

@@ -109,7 +109,6 @@ func (classifier *SplitTunnelClassifier) Start(fetchRoutesTunnel *Tunnel) {
 	dnsServerAddress := p.String(parameters.SplitTunnelDNSServer)
 	dnsServerAddress := p.String(parameters.SplitTunnelDNSServer)
 	routesSignaturePublicKey := p.String(parameters.SplitTunnelRoutesSignaturePublicKey)
 	routesSignaturePublicKey := p.String(parameters.SplitTunnelRoutesSignaturePublicKey)
 	fetchRoutesUrlFormat := p.String(parameters.SplitTunnelRoutesURLFormat)
 	fetchRoutesUrlFormat := p.String(parameters.SplitTunnelRoutesURLFormat)
-	p = nil
 
 
 	if dnsServerAddress == "" ||
 	if dnsServerAddress == "" ||
 		routesSignaturePublicKey == "" ||
 		routesSignaturePublicKey == "" ||
@@ -231,7 +230,7 @@ func (classifier *SplitTunnelClassifier) getRoutes(tunnel *Tunnel) (routesData [
 	routesSignaturePublicKey := p.String(parameters.SplitTunnelRoutesSignaturePublicKey)
 	routesSignaturePublicKey := p.String(parameters.SplitTunnelRoutesSignaturePublicKey)
 	fetchRoutesUrlFormat := p.String(parameters.SplitTunnelRoutesURLFormat)
 	fetchRoutesUrlFormat := p.String(parameters.SplitTunnelRoutesURLFormat)
 	fetchTimeout := p.Duration(parameters.FetchSplitTunnelRoutesTimeout)
 	fetchTimeout := p.Duration(parameters.FetchSplitTunnelRoutesTimeout)
-	p = nil
+	p.Close()
 
 
 	url := fmt.Sprintf(fetchRoutesUrlFormat, tunnel.serverContext.clientRegion)
 	url := fmt.Sprintf(fetchRoutesUrlFormat, tunnel.serverContext.clientRegion)
 	request, err := http.NewRequest("GET", url, nil)
 	request, err := http.NewRequest("GET", url, nil)
@@ -253,7 +252,7 @@ func (classifier *SplitTunnelClassifier) getRoutes(tunnel *Tunnel) (routesData [
 		return tunnel.sshClient.Dial("tcp", addr)
 		return tunnel.sshClient.Dial("tcp", addr)
 	}
 	}
 	transport := &http.Transport{
 	transport := &http.Transport{
-		Dial: tunneledDialer,
+		Dial:                  tunneledDialer,
 		ResponseHeaderTimeout: fetchTimeout,
 		ResponseHeaderTimeout: fetchTimeout,
 	}
 	}
 	httpClient := &http.Client{
 	httpClient := &http.Client{

+ 2 - 2
psiphon/tunnel.go

@@ -552,7 +552,7 @@ func dialTunnel(
 	livenessTestMaxUpstreamBytes := p.Int(parameters.LivenessTestMaxUpstreamBytes)
 	livenessTestMaxUpstreamBytes := p.Int(parameters.LivenessTestMaxUpstreamBytes)
 	livenessTestMinDownstreamBytes := p.Int(parameters.LivenessTestMinDownstreamBytes)
 	livenessTestMinDownstreamBytes := p.Int(parameters.LivenessTestMinDownstreamBytes)
 	livenessTestMaxDownstreamBytes := p.Int(parameters.LivenessTestMaxDownstreamBytes)
 	livenessTestMaxDownstreamBytes := p.Int(parameters.LivenessTestMaxDownstreamBytes)
-	p = nil
+	p.Close()
 
 
 	// Ensure that, unless the base context is cancelled, any replayed dial
 	// Ensure that, unless the base context is cancelled, any replayed dial
 	// parameters are cleared, no longer to be retried, if the tunnel fails to
 	// parameters are cleared, no longer to be retried, if the tunnel fails to
@@ -1260,7 +1260,7 @@ func (tunnel *Tunnel) sendSshKeepAlive(isFirstKeepAlive bool, timeout time.Durat
 		request := prng.Padding(
 		request := prng.Padding(
 			p.Int(parameters.SSHKeepAlivePaddingMinBytes),
 			p.Int(parameters.SSHKeepAlivePaddingMinBytes),
 			p.Int(parameters.SSHKeepAlivePaddingMaxBytes))
 			p.Int(parameters.SSHKeepAlivePaddingMaxBytes))
-		p = nil
+		p.Close()
 
 
 		startTime := monotime.Now()
 		startTime := monotime.Now()
 
 

+ 1 - 1
psiphon/upgradeDownload.go

@@ -77,7 +77,7 @@ func DownloadUpgrade(
 	urls := p.DownloadURLs(parameters.UpgradeDownloadURLs)
 	urls := p.DownloadURLs(parameters.UpgradeDownloadURLs)
 	clientVersionHeader := p.String(parameters.UpgradeDownloadClientVersionHeader)
 	clientVersionHeader := p.String(parameters.UpgradeDownloadClientVersionHeader)
 	downloadTimeout := p.Duration(parameters.FetchUpgradeTimeout)
 	downloadTimeout := p.Duration(parameters.FetchUpgradeTimeout)
-	p = nil
+	p.Close()
 
 
 	var cancelFunc context.CancelFunc
 	var cancelFunc context.CancelFunc
 	ctx, cancelFunc = context.WithTimeout(ctx, downloadTimeout)
 	ctx, cancelFunc = context.WithTimeout(ctx, downloadTimeout)