MCPcopy Create free account
hub / github.com/containers/image / GetBlobAt

Method GetBlobAt

docker/docker_image_src.go:390–451  ·  view source on GitHub ↗

GetBlobAt returns a sequential channel of readers that contain data for the requested blob chunks, and a channel that might get a single error value. The specified chunks must be not overlapping and sorted by their offset. The readers must be fully consumed, in the order they are returned, before bl

(ctx context.Context, info types.BlobInfo, chunks []private.ImageSourceChunk)

Source from the content-addressed store, hash-verified

388// If the Length for the last chunk is set to math.MaxUint64, then it
389// fully fetches the remaining data from the offset to the end of the blob.
390func (s *dockerImageSource) GetBlobAt(ctx context.Context, info types.BlobInfo, chunks []private.ImageSourceChunk) (chan io.ReadCloser, chan error, error) {
391 headers := make(map[string][]string)
392
393 rangeVals := make([]string, 0, len(chunks))
394 lastFound := false
395 for _, c := range chunks {
396 if lastFound {
397 return nil, nil, fmt.Errorf("internal error: another chunk requested after an util-EOF chunk")
398 }
399 // If the Length is set to -1, then request anything after the specified offset.
400 if c.Length == math.MaxUint64 {
401 lastFound = true
402 rangeVals = append(rangeVals, fmt.Sprintf("%d-", c.Offset))
403 } else {
404 rangeVals = append(rangeVals, fmt.Sprintf("%d-%d", c.Offset, c.Offset+c.Length-1))
405 }
406 }
407
408 headers["Range"] = []string{fmt.Sprintf("bytes=%s", strings.Join(rangeVals, ","))}
409
410 if len(info.URLs) != 0 {
411 return nil, nil, fmt.Errorf("external URLs not supported with GetBlobAt")
412 }
413
414 if err := info.Digest.Validate(); err != nil { // Make sure info.Digest.String() does not contain any unexpected characters
415 return nil, nil, err
416 }
417 path := fmt.Sprintf(blobsPath, reference.Path(s.physicalRef.ref), info.Digest.String())
418 logrus.Debugf("Downloading %s", path)
419 res, err := s.c.makeRequest(ctx, http.MethodGet, path, headers, nil, v2Auth, nil)
420 if err != nil {
421 return nil, nil, err
422 }
423
424 switch res.StatusCode {
425 case http.StatusOK:
426 // if the server replied with a 200 status code, convert the full body response to a series of
427 // streams as it would have been done with 206.
428 streams := make(chan io.ReadCloser)
429 errs := make(chan error)
430 go splitHTTP200ResponseToPartial(streams, errs, res.Body, chunks)
431 return streams, errs, nil
432 case http.StatusPartialContent:
433 mediaType, params, err := parseMediaType(res.Header.Get("Content-Type"))
434 if err != nil {
435 return nil, nil, err
436 }
437
438 streams := make(chan io.ReadCloser)
439 errs := make(chan error)
440
441 go handle206Response(streams, errs, res.Body, chunks, mediaType, params)
442 return streams, errs, nil
443 case http.StatusBadRequest:
444 res.Body.Close()
445 return nil, nil, private.BadPartialRequestError{Status: res.Status}
446 default:
447 err := registryHTTPResponseToError(res)

Callers

nothing calls this directly

Calls 9

PathFunction · 0.92
parseMediaTypeFunction · 0.85
handle206ResponseFunction · 0.85
GetMethod · 0.80
StringMethod · 0.65
CloseMethod · 0.65
makeRequestMethod · 0.45

Tested by

no test coverage detected