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  }