Factor CloseNotifier use into a new function

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2015-07-29 18:18:50 -07:00
parent 8fbc1de081
commit b0d133045d
3 changed files with 48 additions and 75 deletions

View file

@ -1,8 +1,12 @@
package handlers
import (
"errors"
"io"
"net/http"
ctxu "github.com/docker/distribution/context"
"github.com/docker/distribution/registry/api/errcode"
)
// closeResources closes all the provided resources after running the target
@ -15,3 +19,38 @@ func closeResources(handler http.Handler, closers ...io.Closer) http.Handler {
handler.ServeHTTP(w, r)
})
}
// copyFullPayload copies the payload of a HTTP request to destWriter. If it
// receives less content than expected, and the client disconnected during the
// upload, it avoids sending a 400 error to keep the logs cleaner.
func copyFullPayload(responseWriter http.ResponseWriter, r *http.Request, destWriter io.Writer, context ctxu.Context, action string, errSlice *errcode.Errors) error {
// Get a channel that tells us if the client disconnects
var clientClosed <-chan bool
if notifier, ok := responseWriter.(http.CloseNotifier); ok {
clientClosed = notifier.CloseNotify()
} else {
panic("the ResponseWriter does not implement CloseNotifier")
}
// Read in the data, if any.
copied, err := io.Copy(destWriter, r.Body)
if clientClosed != nil && (err != nil || (r.ContentLength > 0 && copied < r.ContentLength)) {
// Didn't recieve as much content as expected. Did the client
// disconnect during the request? If so, avoid returning a 400
// error to keep the logs cleaner.
select {
case <-clientClosed:
ctxu.GetLogger(context).Error("client disconnected during " + action)
return errors.New("client disconnected")
default:
}
}
if err != nil {
ctxu.GetLogger(context).Errorf("unknown error reading request payload: %v", err)
*errSlice = append(*errSlice, errcode.ErrorCodeUnknown.WithDetail(err))
return err
}
return nil
}