Skip to content

Commit

Permalink
Support customising the http.url tag (client side) (#39)
Browse files Browse the repository at this point in the history
This may be useful where the url contains sensitive information.

This allows url tags such as:

 http.url: https://Aladdin:OpenSesame@www.example.com/index.html
 http.url: https://www.example.com?foo=bar&token=123

to be redacted:

 http.url: https://xxx:xxx@www.example.com/index.html
 http.url: https://www.example.com?foo=bar&token=***

This completes:

  #33

Co-authored-by: Stuart McLaren <[email protected]>
Co-authored-by: Yuri Shkuro <[email protected]>
  • Loading branch information
3 people authored Jun 8, 2020
1 parent 2976f70 commit 8a6ff1a
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
16 changes: 15 additions & 1 deletion nethttp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"net/http"
"net/http/httptrace"
"net/url"

"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
Expand All @@ -33,6 +34,7 @@ type Transport struct {
type clientOptions struct {
operationName string
componentName string
urlTagFunc func(u *url.URL) string
disableClientTrace bool
disableInjectSpanContext bool
spanObserver func(span opentracing.Span, r *http.Request)
Expand All @@ -49,6 +51,15 @@ func OperationName(operationName string) ClientOption {
}
}

// URLTagFunc returns a ClientOption that uses given function f
// to set the span's http.url tag. Can be used to change the default
// http.url tag, eg to redact sensitive information.
func URLTagFunc(f func(u *url.URL) string) ClientOption {
return func(options *clientOptions) {
options.urlTagFunc = f
}
}

// ComponentName returns a ClientOption that sets the component
// name for the client-side span.
func ComponentName(componentName string) ClientOption {
Expand Down Expand Up @@ -109,6 +120,9 @@ func ClientSpanObserver(f func(span opentracing.Span, r *http.Request)) ClientOp
// }
func TraceRequest(tr opentracing.Tracer, req *http.Request, options ...ClientOption) (*http.Request, *Tracer) {
opts := &clientOptions{
urlTagFunc: func(u *url.URL) string {
return u.String()
},
spanObserver: func(_ opentracing.Span, _ *http.Request) {},
}
for _, opt := range options {
Expand Down Expand Up @@ -159,7 +173,7 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
tracer.start(req)

ext.HTTPMethod.Set(tracer.sp, req.Method)
ext.HTTPUrl.Set(tracer.sp, req.URL.String())
ext.HTTPUrl.Set(tracer.sp, tracer.opts.urlTagFunc(req.URL))
tracer.opts.spanObserver(tracer.sp, req)

if !tracer.opts.disableInjectSpanContext {
Expand Down
45 changes: 45 additions & 0 deletions nethttp/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package nethttp
import (
"net/http"
"net/http/httptest"
"net/url"
"testing"

opentracing "github.com/opentracing/opentracing-go"
Expand Down Expand Up @@ -208,3 +209,47 @@ func makeTags(keyVals ...interface{}) map[string]interface{} {
}
return result
}

func TestClientCustomURL(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/ok", func(w http.ResponseWriter, r *http.Request) {})
srv := httptest.NewServer(mux)
defer srv.Close()

fn := func(u *url.URL) string {
// Simulate redacting token
return srv.URL + u.Path + "?token=*"
}

tests := []struct {
opts []ClientOption
url string
tag string
}{
// These first cases fail early
{[]ClientOption{}, "/ok?token=a", srv.Listener.Addr().String()},
{[]ClientOption{URLTagFunc(fn)}, "/ok?token=c", srv.Listener.Addr().String()},
// Disable ClientTrace to fire RoundTrip
{[]ClientOption{ClientTrace(false)}, "/ok?token=b", srv.URL + "/ok?token=b"},
{[]ClientOption{ClientTrace(false), URLTagFunc(fn)}, "/ok?token=c", srv.URL + "/ok?token=*"},
}

for _, tt := range tests {
var clientSpan *mocktracer.MockSpan

spans := makeRequest(t, srv.URL+tt.url, tt.opts...)
for _, span := range spans {
if span.OperationName == "HTTP GET" {
clientSpan = span
break
}
}
if clientSpan == nil {
t.Fatal("cannot find client span")
}
tag := clientSpan.Tags()["http.url"]
if got, want := tag, tt.tag; got != want {
t.Fatalf("got %s tag name, expected %s", got, want)
}
}
}

0 comments on commit 8a6ff1a

Please sign in to comment.