Commit 49ba77a4 authored by Sebastian Stammler's avatar Sebastian Stammler Committed by GitHub

op-service/client: Add http header option to BasicHTTPClient (#9601)

parent a69fa9a1
...@@ -23,16 +23,40 @@ type HTTP interface { ...@@ -23,16 +23,40 @@ type HTTP interface {
type BasicHTTPClient struct { type BasicHTTPClient struct {
endpoint string endpoint string
header http.Header // optional header to use in every request
log log.Logger log log.Logger
client *http.Client client *http.Client
} }
func NewBasicHTTPClient(endpoint string, log log.Logger) *BasicHTTPClient { func NewBasicHTTPClient(endpoint string, log log.Logger, opts ...BasicHTTPClientOption) *BasicHTTPClient {
return &BasicHTTPClient{ c := &BasicHTTPClient{
endpoint: endpoint, endpoint: endpoint,
log: log, log: log,
client: &http.Client{Timeout: DefaultTimeoutSeconds * time.Second}, client: &http.Client{Timeout: DefaultTimeoutSeconds * time.Second},
} }
for _, opt := range opts {
opt.Apply(c)
}
return c
}
type BasicHTTPClientOption interface {
Apply(c *BasicHTTPClient)
}
type BasicHTTPClientOptionFn func(*BasicHTTPClient)
func (fn BasicHTTPClientOptionFn) Apply(c *BasicHTTPClient) {
fn(c)
}
func WithHeader(h http.Header) BasicHTTPClientOption {
return BasicHTTPClientOptionFn(func(c *BasicHTTPClient) {
c.header = h
})
} }
var ErrNoEndpoint = errors.New("no endpoint is configured") var ErrNoEndpoint = errors.New("no endpoint is configured")
...@@ -54,10 +78,16 @@ func (cl *BasicHTTPClient) Get(ctx context.Context, p string, query url.Values, ...@@ -54,10 +78,16 @@ func (cl *BasicHTTPClient) Get(ctx context.Context, p string, query url.Values,
if err != nil { if err != nil {
return nil, fmt.Errorf("%w: failed to construct request", err) return nil, fmt.Errorf("%w: failed to construct request", err)
} }
for k, values := range headers { addHTTPHeaders(req.Header, cl.header, headers)
for _, v := range values { return cl.client.Do(req)
req.Header.Add(k, v) }
func addHTTPHeaders(header http.Header, hs ...http.Header) {
for _, h := range hs {
for key, values := range h {
for _, value := range values {
header.Add(key, value)
}
} }
} }
return cl.client.Do(req)
} }
package client
import (
"context"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"golang.org/x/exp/slog"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/stretchr/testify/require"
)
func TestBasicHTTPClient(t *testing.T) {
called := make(chan *http.Request, 1)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
called <- r
}))
defer ts.Close()
// generate deep clones
mkhdr := func() http.Header {
return http.Header{
"Foo": []string{"bar", "baz"},
"Superchain": []string{"op"},
}
}
opt := WithHeader(mkhdr())
c := NewBasicHTTPClient(ts.URL, testlog.Logger(t, slog.LevelInfo), opt)
const ep = "/api/version"
query := url.Values{
"key": []string{"123"},
}
getheader := http.Header{
"Fruits": []string{"apple"},
"Superchain": []string{"base"},
}
resp, err := c.Get(context.Background(), ep, query, getheader)
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusOK, resp.StatusCode)
req := <-called
require.Equal(t, ep, req.URL.Path)
require.Equal(t, query, req.URL.Query())
require.ElementsMatch(t, req.Header.Values("Foo"), mkhdr()["Foo"])
require.ElementsMatch(t, req.Header.Values("Fruits"), getheader["Fruits"])
require.ElementsMatch(t, req.Header.Values("Superchain"), []string{"op", "base"})
}
func TestAddHTTPHeaders(t *testing.T) {
for _, test := range []struct {
desc string
expheader http.Header
headers []http.Header
}{
{
desc: "all-empty",
expheader: http.Header{},
headers: nil,
},
{
desc: "1-header-and-nils",
expheader: http.Header{"Foo": []string{"bar"}},
headers: []http.Header{
nil,
{"Foo": []string{"bar"}},
nil,
},
},
{
desc: "2-headers",
expheader: http.Header{
"Foo": []string{"bar", "baz"},
"Super": []string{"chain"},
"Fruit": []string{"apple"},
},
headers: []http.Header{
{
"Foo": []string{"bar"},
"Super": []string{"chain"},
},
{
"Foo": []string{"baz"},
"Fruit": []string{"apple"},
},
},
},
} {
t.Run(test.desc, func(t *testing.T) {
h := make(http.Header)
addHTTPHeaders(h, test.headers...)
require.Equal(t, test.expheader, h)
})
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment