...

Source file src/github.com/google/go-containerregistry/internal/gzip/zip.go

Documentation: github.com/google/go-containerregistry/internal/gzip

     1  // Copyright 2020 Google LLC All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package gzip provides helper functions for interacting with gzipped streams.
    16  package gzip
    17  
    18  import (
    19  	"bufio"
    20  	"bytes"
    21  	"compress/gzip"
    22  	"io"
    23  
    24  	"github.com/google/go-containerregistry/internal/and"
    25  )
    26  
    27  // MagicHeader is the start of gzip files.
    28  var MagicHeader = []byte{'\x1f', '\x8b'}
    29  
    30  // ReadCloser reads uncompressed input data from the io.ReadCloser and
    31  // returns an io.ReadCloser from which compressed data may be read.
    32  // This uses gzip.BestSpeed for the compression level.
    33  func ReadCloser(r io.ReadCloser) io.ReadCloser {
    34  	return ReadCloserLevel(r, gzip.BestSpeed)
    35  }
    36  
    37  // ReadCloserLevel reads uncompressed input data from the io.ReadCloser and
    38  // returns an io.ReadCloser from which compressed data may be read.
    39  // Refer to compress/gzip for the level:
    40  // https://golang.org/pkg/compress/gzip/#pkg-constants
    41  func ReadCloserLevel(r io.ReadCloser, level int) io.ReadCloser {
    42  	pr, pw := io.Pipe()
    43  
    44  	// For highly compressible layers, gzip.Writer will output a very small
    45  	// number of bytes per Write(). This is normally fine, but when pushing
    46  	// to a registry, we want to ensure that we're taking full advantage of
    47  	// the available bandwidth instead of sending tons of tiny writes over
    48  	// the wire.
    49  	// 64K ought to be small enough for anybody.
    50  	bw := bufio.NewWriterSize(pw, 2<<16)
    51  
    52  	// Returns err so we can pw.CloseWithError(err)
    53  	go func() error {
    54  		// TODO(go1.14): Just defer {pw,gw,r}.Close like you'd expect.
    55  		// Context: https://golang.org/issue/24283
    56  		gw, err := gzip.NewWriterLevel(bw, level)
    57  		if err != nil {
    58  			return pw.CloseWithError(err)
    59  		}
    60  
    61  		if _, err := io.Copy(gw, r); err != nil {
    62  			defer r.Close()
    63  			defer gw.Close()
    64  			return pw.CloseWithError(err)
    65  		}
    66  
    67  		// Close gzip writer to Flush it and write gzip trailers.
    68  		if err := gw.Close(); err != nil {
    69  			return pw.CloseWithError(err)
    70  		}
    71  
    72  		// Flush bufio writer to ensure we write out everything.
    73  		if err := bw.Flush(); err != nil {
    74  			return pw.CloseWithError(err)
    75  		}
    76  
    77  		// We don't really care if these fail.
    78  		defer pw.Close()
    79  		defer r.Close()
    80  
    81  		return nil
    82  	}()
    83  
    84  	return pr
    85  }
    86  
    87  // UnzipReadCloser reads compressed input data from the io.ReadCloser and
    88  // returns an io.ReadCloser from which uncompressed data may be read.
    89  func UnzipReadCloser(r io.ReadCloser) (io.ReadCloser, error) {
    90  	gr, err := gzip.NewReader(r)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	return &and.ReadCloser{
    95  		Reader: gr,
    96  		CloseFunc: func() error {
    97  			// If the unzip fails, then this seems to return the same
    98  			// error as the read.  We don't want this to interfere with
    99  			// us closing the main ReadCloser, since this could leave
   100  			// an open file descriptor (fails on Windows).
   101  			gr.Close()
   102  			return r.Close()
   103  		},
   104  	}, nil
   105  }
   106  
   107  // Is detects whether the input stream is compressed.
   108  func Is(r io.Reader) (bool, error) {
   109  	magicHeader := make([]byte, 2)
   110  	n, err := r.Read(magicHeader)
   111  	if n == 0 && err == io.EOF {
   112  		return false, nil
   113  	}
   114  	if err != nil {
   115  		return false, err
   116  	}
   117  	return bytes.Equal(magicHeader, MagicHeader), nil
   118  }
   119  

View as plain text