Source file src/net/http/internal/httpcommon/gzip_test.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package httpcommon
     6  
     7  import (
     8  	"bytes"
     9  	"compress/gzip"
    10  	"fmt"
    11  	"io"
    12  	"io/fs"
    13  	"strings"
    14  	"testing"
    15  	"testing/synctest"
    16  )
    17  
    18  // Tests that GzipReader doesn't crash on a second Read call following
    19  // the first Read call's gzip.NewReader returning an error.
    20  func TestGzipReaderDoubleReadCrash(t *testing.T) {
    21  	gz := &GzipReader{Body: io.NopCloser(strings.NewReader("0123456789"))}
    22  	var buf [1]byte
    23  	n, err1 := gz.Read(buf[:])
    24  	if n != 0 || !strings.Contains(fmt.Sprint(err1), "invalid header") {
    25  		t.Fatalf("Read = %v, %v; want 0, invalid header", n, err1)
    26  	}
    27  	n, err2 := gz.Read(buf[:])
    28  	if n != 0 || err2 != err1 {
    29  		t.Fatalf("second Read = %v, %v; want 0, %v", n, err2, err1)
    30  	}
    31  }
    32  
    33  func TestGzipReaderReadAfterClose(t *testing.T) {
    34  	var body bytes.Buffer
    35  	w := gzip.NewWriter(&body)
    36  	w.Write([]byte("012345679"))
    37  	w.Close()
    38  	gz := &GzipReader{Body: io.NopCloser(&body)}
    39  	var buf [1]byte
    40  	n, err := gz.Read(buf[:])
    41  	if n != 1 || err != nil {
    42  		t.Fatalf("first Read = %v, %v; want 1, nil", n, err)
    43  	}
    44  	if err := gz.Close(); err != nil {
    45  		t.Fatalf("gz Close error: %v", err)
    46  	}
    47  	n, err = gz.Read(buf[:])
    48  	if n != 0 || err != fs.ErrClosed {
    49  		t.Fatalf("Read after close = %v, %v; want 0, fs.ErrClosed", n, err)
    50  	}
    51  }
    52  
    53  // blockingReadCloser blocks in Read until it is closed.
    54  type blockingReadCloser struct {
    55  	closed chan struct{}
    56  }
    57  
    58  func (r *blockingReadCloser) Read([]byte) (int, error) {
    59  	<-r.closed
    60  	return 0, fs.ErrClosed
    61  }
    62  
    63  func (r *blockingReadCloser) Close() error {
    64  	close(r.closed)
    65  	return nil
    66  }
    67  
    68  // Tests that closing a GzipReader unblocks a Read that is waiting for the
    69  // gzip header, rather than deadlocking on the GzipReader's mutex.
    70  func TestGzipReaderConcurrentCloseAndRead(t *testing.T) {
    71  	synctest.Test(t, func(t *testing.T) {
    72  		gz := &GzipReader{Body: &blockingReadCloser{closed: make(chan struct{})}}
    73  
    74  		readErr := make(chan error, 1)
    75  		go func() {
    76  			var buf [1]byte
    77  			_, err := gz.Read(buf[:])
    78  			readErr <- err
    79  		}()
    80  		synctest.Wait()
    81  
    82  		if err := gz.Close(); err != nil {
    83  			t.Fatalf("Close = %v, want nil", err)
    84  		}
    85  		synctest.Wait()
    86  
    87  		select {
    88  		case err := <-readErr:
    89  			if err == nil {
    90  				t.Error("Read returned nil error, want error")
    91  			}
    92  		default:
    93  			t.Fatal("Read did not unblock on Close")
    94  		}
    95  	})
    96  }
    97  

View as plain text