I am using this Go API client on my app https://github.com/heroku/docker-registry-client to interact with a docker registry using Go. The case is that internally that is having some issue when does a PUT request using the package "net/http".
When I run the following code I am getting this as error Put url: http: ContentLength=2821 with Body length 0
. So it seems that net/http Client.Do() function is not getting the body I set at some point of the function. But as you can se right on the code below at some point I still have the JSON content that I want to send in a []byte
.
body, err := manifest.MarshalJSON()
if err != nil {
return err
}
log.Println(string(body)) // I get the JSON data back here
req, err := http.NewRequest("PUT", url, bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", manifestV2.MediaTypeManifest)
resp, err := registry.Client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
return err
As far as I have digged into it, the error comes from the net/http Client.do()
function (golang.org/src/net/http/client.go line 514), and I'd say the error is triggered from Request.GetBody()
function (from line 591 on Client).
So still trying to go deeper and do some tests to find out what is going on here.
Any clue?
In case the error is given by the server I'll have to get something like this, but in the response body and no errors on the net/http Client.Do()
call.
Content-Length: <length>
Content-Type: application/json; charset=utf-8
{
"errors:" [
{
"code": <error code>,
"message": "<error message>",
"detail": ...
},
...
]
}
Thank you so much!
Cheers
The issue is modified RoundTripper in https://github.com/heroku/docker-registry-client makes a redirect call to registry after getting authentication token.when it tries to redirect call to registry ,body is empty because http.request body is a buffer and once it is read in first call,buffer becomes empty and there is no body content to send in redirect call.
To fix it:
if req.Method == "PUT" || req.Method == "POST" {
if req.Body != nil {
reUseBody, _ = req.GetBody()
}
}
add this in https://github.com/heroku/docker-registry-client/blob/master/registry/tokentransport.go#L17 and
if req.Method == "PUT" || req.Method == "POST" {
if reUseBody != nil {
req.Body = reUseBody
}
}
in here https://github.com/heroku/docker-registry-client/blob/master/registry/tokentransport.go#L72
and change this function signature https://github.com/heroku/docker-registry-client/blob/master/registry/tokentransport.go#L71 to accept extra argument.
func (t *TokenTransport) retry(req *http.Request, token string, reUseBody io.ReadCloser)