Skip to content

Commit

Permalink
measure and pickup external http dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
Vincent Landgraf committed Dec 15, 2020
1 parent 8dd0b4c commit 68414d9
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 0 deletions.
16 changes: 16 additions & 0 deletions http/transport/default_transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ package transport
// If not explicitly finalized via `Final` it uses `http.DefaultTransport` as finalizer.
func NewDefaultTransportChain() *RoundTripperChain {
return Chain(
&ExternalDependencyRoundTripper{},
NewDefaultRetryRoundTripper(),
&JaegerRoundTripper{},
NewDumpRoundTripperEnv(),
&LoggingRoundTripper{},
&LocaleRoundTripper{},
&RequestIDRoundTripper{},
)
}

// NewDefaultTransportChain returns a transport chain with retry, jaeger and logging support.
// If not explicitly finalized via `Final` it uses `http.DefaultTransport` as finalizer.
// The passed name is recorded as external dependency
func NewDefaultTransportChainWithExternalName(name string) *RoundTripperChain {
return Chain(
&ExternalDependencyRoundTripper{name: name},
NewDefaultRetryRoundTripper(),
&JaegerRoundTripper{},
NewDumpRoundTripperEnv(),
Expand Down
51 changes: 51 additions & 0 deletions http/transport/external_dependency_round_tripper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright © 2020 by PACE Telematics GmbH. All rights reserved.
// Created at 2020/12/14 by Vincent Landgraf

package transport

import (
"net/http"
"time"

"github.com/pace/bricks/http/middleware"
)

// ExternalDependencyRoundTripper greps external dependency headers and
// attach them to the currect context
type ExternalDependencyRoundTripper struct {
name string
transport http.RoundTripper
}

// Transport returns the RoundTripper to make HTTP requests
func (l *ExternalDependencyRoundTripper) Transport() http.RoundTripper {
return l.transport
}

// SetTransport sets the RoundTripper to make HTTP requests
func (l *ExternalDependencyRoundTripper) SetTransport(rt http.RoundTripper) {
l.transport = rt
}

// RoundTrip executes a single HTTP transaction via Transport()
func (l *ExternalDependencyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
start := time.Now()
resp, err := l.Transport().RoundTrip(req)
elapsed := time.Since(start)

ec := middleware.ExternalDependencyContextFromContext(req.Context())
if ec != nil {
if l.name != "" {
ec.AddDependency(l.name, elapsed)
}

if resp != nil {
header := resp.Header.Get(middleware.ExternalDependencyHeaderName)
if header != "" {
ec.Parse(header)
}
}
}

return resp, err
}
68 changes: 68 additions & 0 deletions http/transport/external_dependency_round_tripper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright © 2020 by PACE Telematics GmbH. All rights reserved.
// Created at 2020/12/15 by Vincent Landgraf

package transport

import (
"context"
"net/http"
"net/http/httptest"
"testing"

"github.com/pace/bricks/http/middleware"
"github.com/stretchr/testify/assert"
)

type edRoundTripperMock struct {
req *http.Request
resp *http.Response
}

func (m *edRoundTripperMock) RoundTrip(req *http.Request) (*http.Response, error) {
m.req = req
return m.resp, nil
}

func TestExternalDependencyRoundTripper(t *testing.T) {
var edc middleware.ExternalDependencyContext
ctx := middleware.ContextWithExternalDependency(context.Background(), &edc)

r := httptest.NewRequest("GET", "http://example.com/test", nil)
r = r.WithContext(ctx)

mock := &edRoundTripperMock{
resp: &http.Response{
Header: http.Header{
middleware.ExternalDependencyHeaderName: []string{"test1:123,test2:53"},
},
},
}
lrt := &ExternalDependencyRoundTripper{transport: mock}

_, err := lrt.RoundTrip(r)
assert.NoError(t, err)

assert.EqualValues(t, "test1:123,test2:53", edc.String())
}

func TestExternalDependencyRoundTripperWithName(t *testing.T) {
var edc middleware.ExternalDependencyContext
ctx := middleware.ContextWithExternalDependency(context.Background(), &edc)

r := httptest.NewRequest("GET", "http://example.com/test", nil)
r = r.WithContext(ctx)

mock := &edRoundTripperMock{
resp: &http.Response{
Header: http.Header{
middleware.ExternalDependencyHeaderName: []string{"test1:123,test2:53"},
},
},
}
lrt := &ExternalDependencyRoundTripper{name: "ext", transport: mock}

_, err := lrt.RoundTrip(r)
assert.NoError(t, err)

assert.EqualValues(t, "ext:0,test1:123,test2:53", edc.String())
}

0 comments on commit 68414d9

Please sign in to comment.