Skip to content
This repository has been archived by the owner on Feb 15, 2019. It is now read-only.

Fix proxying of unannounced trailers #15

Merged
merged 8 commits into from
Jun 28, 2017
Merged

Conversation

tcolgate
Copy link

No description provided.

@tcolgate
Copy link
Author

tcolgate commented May 20, 2017

Fixes traefik/traefik#1419

@ldez ldez self-requested a review May 23, 2017 11:09
Copy link
Member

@timoreimann timoreimann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments but mostly questions. :-)

return
}

if written != 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason we're removing this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a set of a header after this initial WriteStatus, which isn't allowed. The only way to do this would be to buffer the entire response, write the content-length, then write the body out. You have to assume that the original handler has already set the content-length. It's unrelated to the actual fix to trailers, but it's definitely not a valid thing to do.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, interesting, didn't know. For the sake of completeness, is this forbidden by Go (and possibly documented somewhere) or rather an underlying HTTP requirement?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did the same a year ago in 1832129
But I had to rollback it to fix an empty response issue in traefik traefik/traefik#476
Any idea on this @tcolgate ?

forward/fwd.go Outdated

announcedTrailerKeyCount := len(response.Trailer)
if announcedTrailerKeyCount > 0 {
trailerKeys := make([]string, 0, len(response.Trailer))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it for performance reasons that we don't just append to a nil slice?

forward/fwd.go Outdated
written, err := io.Copy(newResponseFlusher(w, stream), response.Body)
_, err = io.Copy(newResponseFlusher(w, stream), response.Body)
if err != nil {
ctx.log.Errorf("Error copying upstream response Body: %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Body -> body

forward/fwd.go Outdated
return
}

response.Body.Close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the defer might be preferable in order to allow future reads of the body inside this method without having to think about adjusting this line. WDYT?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't defer the Close() this is the crux of the problem. You must write trailers after the body is closed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's leave a comment then so future developers know.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can use ioutils.NewReadCloserWrapper

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops it's a docker package...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for comment here

@timoreimann
Copy link
Member

I forgot: would it be possible to add a test as well? Thanks!

@timoreimann
Copy link
Member

@tcolgate any updates on this one? do you think it would be possible to add a test and address the nit-picky comments? :)

@timoreimann
Copy link
Member

For the sake of completion: vulcand/oxy also has an open PR on this issue: vulcand#27

Unfortunately, it never received maintainer attention.

@tcolgate
Copy link
Author

tcolgate commented May 31, 2017 via email

@timoreimann
Copy link
Member

@tcolgate thanks for the heads up! whenever you have time.

btw, any chance you could assess if you fix might have a positive impact on traefik/traefik#1686?

@tcolgate
Copy link
Author

tcolgate commented Jun 1, 2017 via email

@emilevauge emilevauge added the WIP label Jun 15, 2017
@emilevauge
Copy link
Member

@tcolgate any news on this one ?

@tcolgate
Copy link
Author

Sorry, not yet, tied up with other stuff at the moment.

forward/fwd.go Outdated
return
}

response.Body.Close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for comment here

forward/fwd.go Outdated
}

//No defer on body close in order to force announce trailer after body close
response.Body.Close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, we don't need the response to be closed, we just need that the body was read until EOF

https://github.com/golang/go/blob/master/src/net/http/response.go#L96

@juliens
Copy link
Member

juliens commented Jun 28, 2017

@tcolgate WDYT about this changes ?

@tcolgate
Copy link
Author

The writeheader after data has been written is invalid unfortunately (headers are , by definition, only valid at the start of the response). The empty response is likely cause by no write ever having been done on a response before a close in some other path (so you never actually write a status).

@ldez ldez added enhancement and removed WIP labels Jun 28, 2017
Copy link

@ldez ldez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Big improvement!

LGTM

Copy link
Member

@timoreimann timoreimann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One nit-pick left, but I'll already give my LGTM. :-)

forward/fwd.go Outdated
@@ -178,6 +190,20 @@ func (f *httpForwarder) serveHTTP(w http.ResponseWriter, req *http.Request, ctx
}
}
written, err := io.Copy(newResponseFlusher(w, stream), response.Body)
if err != nil {
ctx.log.Errorf("Error copying upstream response Body: %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still nit-pick, but should still be decapitalized to body. :-)

Copy link
Member

@emilevauge emilevauge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @tcolgate & @juliens 👏
@tcolgate we will manage the ContentLength issue separately #19 :)
LGTM

@ldez ldez merged commit 7da864c into containous:master Jun 28, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants