...

Source file src/github.com/docker/distribution/registry/handlers/helpers.go

Documentation: github.com/docker/distribution/registry/handlers

     1  package handlers
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"net/http"
     8  
     9  	dcontext "github.com/docker/distribution/context"
    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 an 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  //
    27  // The copy will be limited to `limit` bytes, if limit is greater than zero.
    28  func copyFullPayload(ctx context.Context, responseWriter http.ResponseWriter, r *http.Request, destWriter io.Writer, limit int64, action string) error {
    29  	// Get a channel that tells us if the client disconnects
    30  	clientClosed := r.Context().Done()
    31  	var body = r.Body
    32  	if limit > 0 {
    33  		body = http.MaxBytesReader(responseWriter, body, limit)
    34  	}
    35  
    36  	// Read in the data, if any.
    37  	copied, err := io.Copy(destWriter, body)
    38  	if clientClosed != nil && (err != nil || (r.ContentLength > 0 && copied < r.ContentLength)) {
    39  		// Didn't receive as much content as expected. Did the client
    40  		// disconnect during the request? If so, avoid returning a 400
    41  		// error to keep the logs cleaner.
    42  		select {
    43  		case <-clientClosed:
    44  			// Set the response code to "499 Client Closed Request"
    45  			// Even though the connection has already been closed,
    46  			// this causes the logger to pick up a 499 error
    47  			// instead of showing 0 for the HTTP status.
    48  			responseWriter.WriteHeader(499)
    49  
    50  			dcontext.GetLoggerWithFields(ctx, map[interface{}]interface{}{
    51  				"error":         err,
    52  				"copied":        copied,
    53  				"contentLength": r.ContentLength,
    54  			}, "error", "copied", "contentLength").Error("client disconnected during " + action)
    55  			return errors.New("client disconnected")
    56  		default:
    57  		}
    58  	}
    59  
    60  	if err != nil {
    61  		dcontext.GetLogger(ctx).Errorf("unknown error reading request payload: %v", err)
    62  		return err
    63  	}
    64  
    65  	return nil
    66  }
    67  

View as plain text