Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Headers duplicated when sending multiple events and using WithCustomHeader context decorator #941

Open
dan-j opened this issue Sep 29, 2023 · 2 comments

Comments

@dan-j
Copy link
Contributor

dan-j commented Sep 29, 2023

These functions enable users to customise the base set of headers to send to a CE consumer:

func HeaderFrom(ctx context.Context) http.Header {
return binding.GetOrDefaultFromCtx(ctx, headerKey, make(http.Header)).(http.Header)
}
func WithCustomHeader(ctx context.Context, header http.Header) context.Context {
return context.WithValue(ctx, headerKey, header)
}

Then it's used in Protocol.makeRequest(context.Context) to create our new HTTP request:

req := &http.Request{
Method: http.MethodPost,
Header: HeaderFrom(ctx),
}

Then when writing the event to the request, httpRequestWriter.SetAttribute(Attribute, interface{}) appends the attribute to the request.Header:

b.Header[mapping] = append(b.Header[mapping], s)

Sending multiple events with the same underlying ctx is causing these headers to be duplicated on each invocation.

Can we change newRequest to clone the headers? i.e.

req := &http.Request{
	Method: http.MethodPost,
	Header: HeaderFrom(ctx).Clone(),
}
@embano1
Copy link
Member

embano1 commented Oct 1, 2023

Thx for flagging this @dan-j !

I briefly skimmed the code, and I'm not yet 100% following. Sending multiple events should invoke Send, i.e., makeRequest every single time (per event), thus creating a new http.Request for each call:

// Request implements binding.Requester
func (p *Protocol) Request(ctx context.Context, m binding.Message, transformers ...binding.Transformer) (binding.Message, error) {
	if ctx == nil {
		return nil, fmt.Errorf("nil Context")
	} else if m == nil {
		return nil, fmt.Errorf("nil Message")
	}

	var err error
	defer func() { _ = m.Finish(err) }()

	req := p.makeRequest(ctx)

	if p.Client == nil || req == nil || req.URL == nil {
		return nil, fmt.Errorf("not initialized: %#v", p)
	}

	if err = WriteRequest(ctx, m, req, transformers...); err != nil {
		return nil, err
	}

	return p.do(ctx, req)
}

Can you elaborate more on what "sending multiple events" means in this issue? Do we also have an issue with retrying the same event btw?

@dan-j
Copy link
Contributor Author

dan-j commented Oct 1, 2023

Hey @embano1, yeah a new request is made, but if you look at the second snippet, is initialises Request.Header with a call to HeaderFrom(ctx). This passes the same value on each invocation. Since http.Header is just a map[string][]string, it's passing the same map each time, and modifications to that map are retained.

I believe cloning the headers each time should fix this issue. I don't mind providing a PR, just it was late Friday evening when I finally spotted the bug (it's affecting us in production) so we just made a local fix on our own codebase for now.

FYI, a temporary workaround is to override the context value with a cloned copy before calling Send(). i.e:

a.sender.Send(cehttp.WithCustomHeader(ctx, cehttp.HeaderFrom(ctx).Clone()), event)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants