Source file src/crypto/tls/cache_test.go

     1  // Copyright 2025 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  package tls
     5  
     6  import (
     7  	"encoding/pem"
     8  	"runtime"
     9  	"testing"
    10  	"time"
    11  )
    12  
    13  func TestWeakCertCache(t *testing.T) {
    14  	wcc := weakCertCache{}
    15  	p, _ := pem.Decode([]byte(rsaCertPEM))
    16  	if p == nil {
    17  		t.Fatal("Failed to decode certificate")
    18  	}
    19  
    20  	certA, err := wcc.newCert(p.Bytes)
    21  	if err != nil {
    22  		t.Fatalf("newCert failed: %s", err)
    23  	}
    24  	certB, err := wcc.newCert(p.Bytes)
    25  	if err != nil {
    26  		t.Fatalf("newCert failed: %s", err)
    27  	}
    28  	if certA != certB {
    29  		t.Fatal("newCert returned a unique reference for a duplicate certificate")
    30  	}
    31  
    32  	if _, ok := wcc.Load(string(p.Bytes)); !ok {
    33  		t.Fatal("cache does not contain expected entry")
    34  	}
    35  
    36  	timeoutRefCheck := func(t *testing.T, key string, present bool) {
    37  		t.Helper()
    38  		timeout := time.After(4 * time.Second)
    39  		for {
    40  			select {
    41  			case <-timeout:
    42  				t.Fatal("timed out waiting for expected ref count")
    43  			default:
    44  				_, ok := wcc.Load(key)
    45  				if ok == present {
    46  					return
    47  				}
    48  			}
    49  			// Explicitly yield to the scheduler.
    50  			//
    51  			// On single-threaded platforms like js/wasm a busy-loop might
    52  			// never call into the scheduler for the full timeout, meaning
    53  			// that if we arrive here and the cleanup hasn't already run,
    54  			// we'll simply loop until the timeout. Busy-loops put us at the
    55  			// mercy of the Go scheduler, making this test fragile on some
    56  			// platforms.
    57  			runtime.Gosched()
    58  		}
    59  	}
    60  
    61  	// Keep certA alive until at least now, so that we can
    62  	// purposefully nil it and force the finalizer to be
    63  	// called.
    64  	runtime.KeepAlive(certA)
    65  	certA = nil
    66  	runtime.GC()
    67  
    68  	timeoutRefCheck(t, string(p.Bytes), true)
    69  
    70  	// Keep certB alive until at least now, so that we can
    71  	// purposefully nil it and force the finalizer to be
    72  	// called.
    73  	runtime.KeepAlive(certB)
    74  	certB = nil
    75  	runtime.GC()
    76  
    77  	timeoutRefCheck(t, string(p.Bytes), false)
    78  }
    79  

View as plain text