-
Notifications
You must be signed in to change notification settings - Fork 294
Fixes #1324: Properly closes connections in tribe #1327
Conversation
if resp.StatusCode == 200 { | ||
if resp.Header.Get("Content-Type") != "application/x-gzip" { | ||
logger.WithField("content-type", resp.Header.Get("Content-Type")).Error("Expected application/x-gzip") | ||
} | ||
dir, err := ioutil.TempDir("", "") | ||
if err != nil { | ||
logger.Error(err) | ||
resp.Body.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this just be a defer
right after the c.TribeRequest()
call? It would get rid of the Body.Close()
calls below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good suggestion, thanks!
edit:
Actually if you look at context, this is running in a for loop and we need to close this after every run, not just at the end of the function call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
too many resp.Body.Close() in every error. Should it be added in the source?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So maybe move this part to separate function (and make this one less blobby), or just wrap it by ad hoc function
func() {
...
resp := Do()
iferr...
defer resp.Body.Close()
...
}()
I just want to explore a different option. Don't you think by doing in (c _Client) TribeRequest() (_http.Response, error) method is better than doing resp.Close() in every error of the loop? |
if resp.StatusCode == 200 { | ||
if resp.Header.Get("Content-Type") != "application/x-gzip" { | ||
logger.WithField("content-type", resp.Header.Get("Content-Type")).Error("Expected application/x-gzip") | ||
} | ||
dir, err := ioutil.TempDir("", "") | ||
if err != nil { | ||
logger.Error(err) | ||
resp.Body.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
too many resp.Body.Close() in every error. Should it be added in the source?
Go Documentation says
So it's good idea to read contents even if not used before closing. |
Cannot add line comment to unchanged line, but here:
response body still has to be closed. |
@lmroz, @candysmurf: I'm sure the mutliple rsp.Body.Close() calls can in all cases be reduced. When I was doing this fix I was focused on making it work. I can work on a version that is "cleaner" and refactors some of the ways we do things around http.Requests. @lmroz: The code your linking already has the response body closed (I believe) because of the defer rsp.Body.Close() calls that happen prior to going into that function. |
@IRCody Right, looks like body is closed just after this function is called. However this function is calling |
@lmroz: I agree there is room for improvement. This code started out as debugging a live issue and iterating on fixing the issue. I need to go back and refactor so it's a cleaner fix. You're right that multiple calls to Close() are implementation defined. |
@lmroz, @candysmurf, @thomastaylor312: I added two commits that I think Improve this PR. Before merging I'd like to merge the first two but I wanted to leave it as a separate commit for the moment so it's easy to see what changes have been made from the initial review. The first commit addresses the comments in this PR around duplicate calls to Body.Close(). I refactored the downloading plugin part into another function that handles this so there is just one defer call to body.Close(). The second commit fixes an issue I noticed in the client where I think we are leaking http.Transports on client creation. The fix makes use of something that was added in go 1.7 (IdleConnTimeout in http.Transport). Since this was added in 1.7 the 1.6.3 tests don't work. I talked with @nanliu about it we were wondering if it makes sense to peg Snap to more than the latest version of Go. Since the released binaries will always be the latest version. I was hoping to get some opinions on that here since it's relevant. If we decide to continue doing latest-1 support I can update this PR to not have IdleConnTimeout and resubmit that change once go 1.8 is released. /cc @intelsdi-x/snap-maintainers |
We only build binaries with go 1.7.1. Since this is an application and not a library, we can safely drop 1.6.3. This will also reduce the test matrix significantly. |
This makes the assumption that everyone using snap is using a release binary and is not building off of master. |
Or that they are comfortable running the latest version of Go. We already require relatively newer releases of go by only testing/supporting the 2 most recent releases. |
"_block": "download-plugin", | ||
}) | ||
resp, err := c.TribeRequest() | ||
defer resp.Body.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nil pointer dereference possible here (on error TribeRequest
will return nil response).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. I moved the defer below the err handling for tribeRequest.
logger.Error(err) | ||
return nil, err | ||
} | ||
f, err := os.Create(path.Join(dir, fmt.Sprintf("%s-%s-%d", plugin.TypeName(), plugin.Name(), plugin.Version()))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not related to PR but Create
is just return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
, so chmod can be done in more compact way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified this so it's just one call to OpenFile.
if resp.Header.Get("Content-Type") != "application/x-gzip" { | ||
logger.WithField("content-type", resp.Header.Get("Content-Type")).Error("Expected application/x-gzip") | ||
} | ||
dir, err := ioutil.TempDir("", "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not related to PR but maybe this code should delete this dir (why dir?) and file in case of error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It won't be created if there is an error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But error during Create/Chmod (these are rather unlike) will leave that directory and function just eats the path.
@@ -101,6 +102,17 @@ func Username(u string) metaOp { | |||
} | |||
} | |||
|
|||
var ( | |||
secureTransport = &http.Transport{ | |||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InsecureSkipVerify: true
shouldn't this be set to false
for secure transport?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch @andrzej-k. I updated with the correct settings.
IdleConnTimeout: time.Second, | ||
} | ||
insecureTransport = &http.Transport{ | ||
TLSClientConfig: &tls.Config{InsecureSkipVerify: false}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
false -> true ?
I factored out the IdleConnTimeout for the transports so that this fix can be merged. I'll submit a separate PR with the ConnTimeout (that requires go1.7+) so discussion on that can happen separately. cc: @candysmurf |
return nil, err | ||
} | ||
io.Copy(f, resp.Body) | ||
f.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if using defer f.Close(), it'll take care of the error case and force close it anyway. will it?
logger.Error(err) | ||
return err | ||
} | ||
if w.isPluginLoaded(plugin.Name(), plugin.TypeName(), plugin.Version()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a comment. Making sure the plugin is loaded. Otherwise, I thought it should check this in the beginning before loading a plugin. Then I saw there is a similar check at the beginning. If no error is not good enough when loading a plugin?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what you're saying.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant to say:
method: w.pluginManager.Load(rp) should give enough info of if a plugin is loaded or not. Why we need to check it again in line 343. If line 343 is needed, please add a comment to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand now. I'm not sure I can give the original reasoning behind this as this section was just refactored in this PR, not modified. I think it's better to leave it as-is.
Properly closes some http.Respons's that were not previously closed.
Avoids leaking http.Transports on client creation by re-using transports. Removes duplicate close of response body in httpRespToAPIResp which resulted in undefined behavior.
LGTM |
Fixes #1324
Summary of changes:
Testing done:
@intelsdi-x/snap-maintainers