sourcegraph/internal/httpcli/external.go

124 lines
3.6 KiB
Go
Raw Permalink Normal View History

package httpcli
import (
"context"
"crypto/tls"
"crypto/x509"
"net/http"
"reflect"
"sync"
"sync/atomic"
"go.opentelemetry.io/otel/attribute"
"github.com/sourcegraph/sourcegraph/internal/trace"
"github.com/sourcegraph/sourcegraph/schema"
)
type externalTransport struct {
base *http.Transport
mu sync.RWMutex
config *schema.TlsExternal
effective *http.Transport
}
var tlsExternalConfigStore struct {
sync.RWMutex
*schema.TlsExternal
}
var outboundRequestLogLimitStore atomic.Int32
var redactOutboundRequestHeadersStore atomic.Bool
// setTLSExternalConfig is called by the conf package whenever TLSExternalConfig changes.
// This is needed to avoid circular imports.
func setTLSExternalConfig(c *schema.TlsExternal) {
tlsExternalConfigStore.Lock()
tlsExternalConfigStore.TlsExternal = c
tlsExternalConfigStore.Unlock()
}
// tlsExternalConfig returns the current value of the global TLS external config.
func tlsExternalConfig() *schema.TlsExternal {
tlsExternalConfigStore.RLock()
defer tlsExternalConfigStore.RUnlock()
return tlsExternalConfigStore.TlsExternal
}
// setOutboundRequestLogLimit is called by the conf package whenever OutboundRequestLogLimit changes.
// This is needed to avoid circular imports.
func setOutboundRequestLogLimit(i int32) {
outboundRequestLogLimitStore.Store(i)
}
// outboundRequestLogLimit returns the current value of the global OutboundRequestLogLimit value.
func outboundRequestLogLimit() int32 {
return outboundRequestLogLimitStore.Load()
}
// setRedactOutboundRequestHeaders is called by the conf package whenever the RedactOutboundRequestHeaders setting changes.
// This is needed to avoid circular imports.
func setRedactOutboundRequestHeaders(b bool) {
redactOutboundRequestHeadersStore.Store(b)
}
// redactOutboundRequestHeaders returns the current value of the global redactOutboundRequestHeaders setting.
func redactOutboundRequestHeaders() bool {
return redactOutboundRequestHeadersStore.Load()
}
func (t *externalTransport) RoundTrip(r *http.Request) (*http.Response, error) {
t.mu.RLock()
config, effective := t.config, t.effective
t.mu.RUnlock()
if current := tlsExternalConfig(); current == nil {
return t.base.RoundTrip(r)
} else if !reflect.DeepEqual(config, current) {
effective = t.update(r.Context(), current)
}
return effective.RoundTrip(r)
}
func (t *externalTransport) update(ctx context.Context, config *schema.TlsExternal) *http.Transport {
// No function calls here use the context further
tr, _ := trace.New(ctx, "externalTransport.update")
Tracing: final cleanups (#54694) This will be my last PR related to the backend tracing work I've been doing. This is a set of small cleanups to the `trace` package that I've collecting as I have worked on tracing and used tracing. Following this PR, the `trace` package is just a very lightweight wrapper around the standard OpenTelemetry APIs. I think it's best to keep the package around rather than using opentelemetry directly because it easy to add convenience methods (which I would be sad to lose). Each commit is self-contained and has a descriptive message. If anyone wants to pick up where I'm leaving off, here are a few things left undone: - Convert Zoekt to use OpenTelemetry rather than OpenTracing - Add OpenTelemetry support to other services like syntect-server - Merge the `internal/trace` and `internal/tracer` packages - Consider adding a type that conforms to the OpenTelemetry `Span` interface but also writes to `x/net/trace` that can be enabled when tracing is not available. - Remove unrelated code from the `trace` and `tracer` package (see [here](https://sourcegraph.com/github.com/sourcegraph/sourcegraph@a6759b95dbd8e5e3a604f7fd452b0b85f37091d9/-/blob/internal/tracer/tracer.go?L75-83) and [here](https://sourcegraph.com/github.com/sourcegraph/sourcegraph@769fbbf5008e8decc63967dbff53f26333620265/-/blob/internal/trace/buckets.go?L3-7)) - Noodle on a `Traceable` interface (one that impls `Attr()` or `Attrs()`) so types can be easily added with `SetAttributes()` - Experiment with sampling - Experiment with replacing `policy.ShouldTrace` with native opentelemetry tools ## Test plan Tested manually that tracing still looks good locally. Will test on other instances when it rolls out. <!-- All pull requests REQUIRE a test plan: https://docs.sourcegraph.com/dev/background-information/testing_principles -->
2023-07-13 08:16:11 +00:00
defer tr.End()
t.mu.Lock()
defer t.mu.Unlock()
effective := t.base.Clone()
if effective.TLSClientConfig == nil {
effective.TLSClientConfig = new(tls.Config)
}
effective.TLSClientConfig.InsecureSkipVerify = config.InsecureSkipVerify
for _, cert := range config.Certificates {
// There is no exposed Clone function for CertPools. So if a certificate
// is removed it will continue to be accepted since we are mutating base's
// RootCAs. This is an acceptable tradeoff since it would be quite tricky
// to avoid this.
if effective.TLSClientConfig.RootCAs == nil {
pool, err := x509.SystemCertPool() // safe to mutate, a clone is returned
if err != nil {
tr.AddEvent("failed to load SystemCertPool",
trace.Error(err),
attribute.String("warning", "communication with external HTTPS APIs may fail"))
pool = x509.NewCertPool()
}
effective.TLSClientConfig.RootCAs = pool
}
// TODO(keegancsmith) ensure we validate these certs somewhere
effective.TLSClientConfig.RootCAs.AppendCertsFromPEM([]byte(cert))
}
t.config = config
t.effective = effective
return effective
}