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

Copy the request ContentLength and TransferEncoding field in proxy #16400

Merged

Conversation

caesarxuchao
Copy link
Member

Thanks to @tiran for identifying the problem and proposing the fix.

@lavalamp @liggitt

fix #16093

@k8s-github-robot
Copy link

Labelling this PR as size/XS

@k8s-github-robot k8s-github-robot added the size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. label Oct 28, 2015
@k8s-bot
Copy link

k8s-bot commented Oct 28, 2015

GCE e2e test build/test passed for commit 3289397.

@@ -151,6 +151,8 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
httpCode = http.StatusOK
newReq.Header = req.Header
newReq.ContentLength = req.ContentLength
newReq.TransferEncoding = req.TransferEncoding
Copy link
Member

Choose a reason for hiding this comment

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

whatever we do here we also need to do in generic/rest/proxy.go (a refactor to make apiserver/proxy.go delegate to the generic proxy is overdue, but should be a separate PR)

Copy link
Member

Choose a reason for hiding this comment

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

Looks like the reverse proxy strips the transfer encoding header.... isn't it a problem to send content length to the backend without transfer encoding?

Copy link
Member

Choose a reason for hiding this comment

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

in any case, I'd want tests through the proxy to a backend using no transport encoding, gzip, and chunked, with and without content-length, to make sure the backend gets a correct content length in all cases

Copy link
Member

Choose a reason for hiding this comment

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

hmm... this also gets set prior to the upgrade attempt... not sure if spdy or websockets would ever set a content length or transfer encoding, but we should at least check on that

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks @liggitt. I'll take a look at the issues you mentioned tomorrow or Friday.

Copy link
Member

Choose a reason for hiding this comment

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

After digging more, I'm pretty sure we should only be setting ContentLength. TransferEncoding is interpreted, and the request body wrapped in an interpreting reader before we get it (that's why a request sent as "chunked" comes in with no transfer encoding, and reading the body returns unchunked data).

Copy link
Member Author

Choose a reason for hiding this comment

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

But we need to keep the new request being "chunked" when it hits wire, right? If so, I think we need to copy the Request.TransferEncoding

The transfer writer will set the transfer-encoding header field to "chunked" if Request.TransferEncoding contains "chunked:
https://golang.org/src/net/http/transfer.go#L177
Same for the Body, the body will be chunked if Request.TransferEncoding contains "chunked":
https://golang.org/src/net/http/transfer.go#L213

Copy link
Member

Choose a reason for hiding this comment

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

hmm, I suppose so... I'm pretty sure the http package will always unwrap any transfer-encodings that are present, but in case they don't, it doesn't hurt to copy what remains to the new request

Copy link
Member Author

Choose a reason for hiding this comment

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

tl;dr: we can keep this line, though it may not be useful.

After more digging, I found actually the http package is powerful enough deduce the outbound request should be "chunked" based on the fact that the content-length is left unset && there is content:
https://golang.org/src/net/http/transfer.go#L88

Well, it doesn't hurt to keep the copy in the PR, and perhaps it will save a broken request where "transfer-encoding:chunked" and "content-length: non-zero-value" are set at the same time.

@k8s-github-robot
Copy link

Labelling this PR as size/M

@k8s-github-robot k8s-github-robot added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. and removed size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. labels Nov 3, 2015
@k8s-bot
Copy link

k8s-bot commented Nov 3, 2015

GCE e2e test build/test passed for commit 0940741481341fff406e39dae5865b332d1933e0.

@k8s-github-robot
Copy link

Labelling this PR as size/L

@k8s-github-robot k8s-github-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Nov 3, 2015
}
}

// Help wanted: why is the transfer-encoding header field always empty?
Copy link
Member Author

Choose a reason for hiding this comment

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

@liggitt @lavalamp, the backend server always receives request with empty transfer-encoding header. Is this expected?

Copy link
Member

Choose a reason for hiding this comment

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

Perhaps look at the header instead of the variable?

Copy link
Member

Choose a reason for hiding this comment

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

Oh, you are... hm

Copy link
Member Author

Choose a reason for hiding this comment

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

For the record, in the received request, the struct field req.TranferEncoding = []string{"chunked"} for the last 2 test cases. I think go library deduced this somehow.

Copy link
Member Author

Choose a reason for hiding this comment

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

I prints the content in the buffer in the conn struct and persistConn struct to understand what's written/read by the go standard library to/from the wire. The golang library only supports chuncked as a transfer-encoding, more specifically,

Copy link
Member Author

Choose a reason for hiding this comment

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

Asked the question in golang-nuts group:
https://groups.google.com/forum/#!topic/golang-nuts/g7xJk09xBxU

@k8s-bot
Copy link

k8s-bot commented Nov 3, 2015

GCE e2e build/test failed for commit 34a863e65765a37768b1233f58accef2ecf84657.

@k8s-bot
Copy link

k8s-bot commented Nov 3, 2015

GCE e2e test build/test passed for commit 135b0b39c486081af26e9a00dbe049450fb10edb.

}

for _, item := range table {
proxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggest naming this "downstreamServer"-- it's not the proxy, it's the thing the proxy dials, right?

@lavalamp
Copy link
Member

I think I found a couple bugs in the test, but I'm not sure if they'll actually fix your problem or not. Let me know.

@caesarxuchao caesarxuchao force-pushed the fix-proxy-content-length branch from d3e3562 to 9690029 Compare November 17, 2015 21:58
}
}

// The http library will strip the "Transfer-Encoding" field from request.Header.
Copy link
Member Author

Choose a reason for hiding this comment

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

As the comment reads, Go library strips the "Transfer-Encoding" field. It will stores the value in request.TransferEncodings after some processing, the only possible value will be either "" or "chunked".

People say usually "gzip" will be a "Content-Encoding", so hopefully we won't get bit for this.

The discussion in go-nuts group: https://groups.google.com/forum/#!topic/golang-nuts/g7xJk09xBxU

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the investigation!

Copy link
Member

Choose a reason for hiding this comment

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

yes, thanks... we should test with content-encoding: gzip, transfer-encoding:'' as well

@k8s-bot
Copy link

k8s-bot commented Nov 17, 2015

GCE e2e test build/test passed for commit 9690029.

@k8s-bot
Copy link

k8s-bot commented Nov 17, 2015

GCE e2e build/test failed for commit 1fe49b6.

@liggitt
Copy link
Member

liggitt commented Nov 18, 2015

After digging into the http DefaultClient/DefaultTransport more, it looks like there is no way to make it faithfully echo transfer-encoding/content-length headers to the server... it really wants to own the transfer. For low-level tests like this, I think we need to write headers and request bodies ourselves. Opened caesarxuchao#2 to do so

@caesarxuchao
Copy link
Member Author

it looks like there is no way to make it faithfully echo transfer-encoding/content-length headers to the server

That's true. I'll take a look at the PR. Thanks.

@caesarxuchao caesarxuchao force-pushed the fix-proxy-content-length branch from 597492b to 32f2ec7 Compare November 20, 2015 17:57
@googlebot
Copy link

CLAs look good, thanks!

@caesarxuchao
Copy link
Member Author

Squashed. I kept liggitt in the commit message as an acknowledgement to his contribution.

@caesarxuchao
Copy link
Member Author

@lavalamp PTAL. Thanks.

@k8s-bot
Copy link

k8s-bot commented Nov 20, 2015

GCE e2e test build/test passed for commit e1e393ec1287fe5ca5f72e9a48c7b6c135f82bc1.

@k8s-bot
Copy link

k8s-bot commented Nov 20, 2015

GCE e2e build/test failed for commit 597492b845fb4dde54f5c82350391fc1a5debfc9.

@k8s-bot
Copy link

k8s-bot commented Nov 20, 2015

GCE e2e build/test failed for commit 32f2ec7.

@caesarxuchao
Copy link
Member Author

@k8s-bot test this please

@k8s-bot
Copy link

k8s-bot commented Nov 20, 2015

GCE e2e test build/test passed for commit 32f2ec7.

@lavalamp
Copy link
Member

LGTM, but the code duplication is unfortunate. I guess we already had that problem. :)

@lavalamp lavalamp added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Nov 30, 2015
@k8s-github-robot
Copy link

@k8s-bot test this

Tests are more than 48 hours old. Re-running tests.

@k8s-bot
Copy link

k8s-bot commented Dec 1, 2015

GCE e2e test build/test passed for commit 32f2ec7.

@wojtek-t
Copy link
Member

wojtek-t commented Dec 3, 2015

@k8s-bot unit test this please

1 similar comment
@caesarxuchao
Copy link
Member Author

@k8s-bot unit test this please

@k8s-github-robot
Copy link

@k8s-bot test this

Tests are more than 48 hours old. Re-running tests.

@k8s-bot
Copy link

k8s-bot commented Dec 3, 2015

GCE e2e test build/test passed for commit 32f2ec7.

@k8s-github-robot
Copy link

@k8s-bot test this [submit-queue is verifying that this PR is safe to merge]

@k8s-bot
Copy link

k8s-bot commented Dec 3, 2015

GCE e2e build/test failed for commit 32f2ec7.

@caesarxuchao
Copy link
Member Author

Unrelated test failure:

+++ [1203 19:55:59] Testing kubectl(:multiple resources)
Testing with file hack/testdata/multi-resource-yaml.yaml and replace with file hack/testdata/multi-resource-yaml-modify.yaml
Successful get services {{range.items}}{{.metadata.name}}:{{end}}: kubernetes:
�
FAIL!
Get rc {{range.items}}{{.metadata.name}}:{{end}}
  Expected: 
  Got:      nginx-deployment-p889s:
�
1058 ./hack/test-cmd.sh
�
!!! Error in ./hack/test-cmd.sh:50

@caesarxuchao
Copy link
Member Author

@k8s-bot test this please

@k8s-bot
Copy link

k8s-bot commented Dec 3, 2015

GCE e2e build/test failed for commit 32f2ec7.

@caesarxuchao
Copy link
Member Author

unrelated e2e test failure:

Kubernetes e2e suite.Kubectl client Kubectl describe should check if kubectl describe prints relevant information for rc and pods 

@caesarxuchao
Copy link
Member Author

@k8s-bot e2e test this please

@k8s-bot
Copy link

k8s-bot commented Dec 3, 2015

GCE e2e test build/test passed for commit 32f2ec7.

@k8s-github-robot
Copy link

@k8s-bot test this [submit-queue is verifying that this PR is safe to merge]

@k8s-bot
Copy link

k8s-bot commented Dec 3, 2015

GCE e2e test build/test passed for commit 32f2ec7.

@k8s-github-robot
Copy link

Automatic merge from submit-queue

k8s-github-robot pushed a commit that referenced this pull request Dec 3, 2015
@k8s-github-robot k8s-github-robot merged commit c849825 into kubernetes:master Dec 3, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lgtm "Looks good to me", indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

kube-apiserver HTTP proxy removes Content-Length header
9 participants