github.com/npaton/distribution@v2.3.1-rc.0+incompatible/registry/handlers/helpers.go (about) 1 package handlers 2 3 import ( 4 "errors" 5 "io" 6 "net/http" 7 8 ctxu "github.com/docker/distribution/context" 9 "github.com/docker/distribution/registry/api/errcode" 10 ) 11 12 // closeResources closes all the provided resources after running the target 13 // handler. 14 func closeResources(handler http.Handler, closers ...io.Closer) http.Handler { 15 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 16 for _, closer := range closers { 17 defer closer.Close() 18 } 19 handler.ServeHTTP(w, r) 20 }) 21 } 22 23 // copyFullPayload copies the payload of a HTTP request to destWriter. If it 24 // receives less content than expected, and the client disconnected during the 25 // upload, it avoids sending a 400 error to keep the logs cleaner. 26 func copyFullPayload(responseWriter http.ResponseWriter, r *http.Request, destWriter io.Writer, context ctxu.Context, action string, errSlice *errcode.Errors) error { 27 // Get a channel that tells us if the client disconnects 28 var clientClosed <-chan bool 29 if notifier, ok := responseWriter.(http.CloseNotifier); ok { 30 clientClosed = notifier.CloseNotify() 31 } else { 32 ctxu.GetLogger(context).Warnf("the ResponseWriter does not implement CloseNotifier (type: %T)", responseWriter) 33 } 34 35 // Read in the data, if any. 36 copied, err := io.Copy(destWriter, r.Body) 37 if clientClosed != nil && (err != nil || (r.ContentLength > 0 && copied < r.ContentLength)) { 38 // Didn't recieve as much content as expected. Did the client 39 // disconnect during the request? If so, avoid returning a 400 40 // error to keep the logs cleaner. 41 select { 42 case <-clientClosed: 43 // Set the response code to "499 Client Closed Request" 44 // Even though the connection has already been closed, 45 // this causes the logger to pick up a 499 error 46 // instead of showing 0 for the HTTP status. 47 responseWriter.WriteHeader(499) 48 49 ctxu.GetLogger(context).Error("client disconnected during " + action) 50 return errors.New("client disconnected") 51 default: 52 } 53 } 54 55 if err != nil { 56 ctxu.GetLogger(context).Errorf("unknown error reading request payload: %v", err) 57 *errSlice = append(*errSlice, errcode.ErrorCodeUnknown.WithDetail(err)) 58 return err 59 } 60 61 return nil 62 }