diff --git a/core/challenges.go b/core/challenges.go index 75a20373fb0..104d754a879 100644 --- a/core/challenges.go +++ b/core/challenges.go @@ -1,7 +1,5 @@ package core -import "fmt" - func newChallenge(challengeType AcmeChallenge, token string) Challenge { return Challenge{ Type: challengeType, @@ -29,20 +27,3 @@ func TLSALPNChallenge01(token string) Challenge { func DNSAccountChallenge01(token string) Challenge { return newChallenge(ChallengeTypeDNSAccount01, token) } - -// NewChallenge constructs a challenge of the given kind. It returns an -// error if the challenge type is unrecognized. -func NewChallenge(kind AcmeChallenge, token string) (Challenge, error) { - switch kind { - case ChallengeTypeHTTP01: - return HTTPChallenge01(token), nil - case ChallengeTypeDNS01: - return DNSChallenge01(token), nil - case ChallengeTypeTLSALPN01: - return TLSALPNChallenge01(token), nil - case ChallengeTypeDNSAccount01: - return DNSAccountChallenge01(token), nil - default: - return Challenge{}, fmt.Errorf("unrecognized challenge type %q", kind) - } -} diff --git a/core/objects.go b/core/objects.go index ac7a6f96b1b..8f9ed49bc1f 100644 --- a/core/objects.go +++ b/core/objects.go @@ -33,21 +33,6 @@ const ( StatusDeactivated = AcmeStatus("deactivated") // Object has been deactivated ) -// AcmeResource values identify different types of ACME resources -type AcmeResource string - -// The types of ACME resources -const ( - ResourceNewReg = AcmeResource("new-reg") - ResourceNewAuthz = AcmeResource("new-authz") - ResourceNewCert = AcmeResource("new-cert") - ResourceRevokeCert = AcmeResource("revoke-cert") - ResourceRegistration = AcmeResource("reg") - ResourceChallenge = AcmeResource("challenge") - ResourceAuthz = AcmeResource("authz") - ResourceKeyChange = AcmeResource("key-change") -) - // AcmeChallenge values identify different types of ACME challenges type AcmeChallenge string diff --git a/go.mod b/go.mod index 23cc218cb7a..c874036d20d 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,6 @@ require ( go.opentelemetry.io/otel/trace v1.38.0 golang.org/x/crypto v0.44.0 golang.org/x/net v0.47.0 - golang.org/x/sync v0.18.0 golang.org/x/term v0.37.0 golang.org/x/text v0.31.0 golang.org/x/time v0.11.0 @@ -86,6 +85,7 @@ require ( go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/proto/otlp v1.7.1 // indirect golang.org/x/mod v0.29.0 // indirect + golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/tools v0.38.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect diff --git a/grpc/internal/resolver/dns/dns_resolver_test.go b/grpc/internal/resolver/dns/dns_resolver_test.go index 3ec58417900..51cb2ddffac 100644 --- a/grpc/internal/resolver/dns/dns_resolver_test.go +++ b/grpc/internal/resolver/dns/dns_resolver_test.go @@ -48,7 +48,6 @@ func TestMain(m *testing.M) { } const ( - txtBytesLimit = 255 defaultTestTimeout = 10 * time.Second defaultTestShortTimeout = 10 * time.Millisecond ) diff --git a/log/mock.go b/log/mock.go index 0fd3d0cd72a..289c09ec1af 100644 --- a/log/mock.go +++ b/log/mock.go @@ -5,7 +5,6 @@ import ( "log/syslog" "regexp" "strings" - "time" ) // UseMock sets a mock logger as the default logger, and returns it. @@ -20,13 +19,6 @@ func NewMock() *Mock { return &Mock{impl{newMockWriter()}} } -// NewWaitingMock creates a mock logger implementing the writer interface. -// It stores all logged messages in a buffer for inspection by test -// functions. -func NewWaitingMock() *WaitingMock { - return &WaitingMock{impl{newWaitingMockWriter()}} -} - // Mock is a logger that stores all log messages in memory to be examined by a // test. type Mock struct { @@ -130,39 +122,3 @@ func (m *Mock) Clear() { w := m.w.(*mockWriter) w.clearChan <- struct{}{} } - -type waitingMockWriter struct { - logChan chan string -} - -// newWaitingMockWriter returns a new waitingMockWriter -func newWaitingMockWriter() *waitingMockWriter { - logChan := make(chan string, 1000) - return &waitingMockWriter{ - logChan, - } -} - -func (m *waitingMockWriter) logAtLevel(p syslog.Priority, msg string, a ...any) { - m.logChan <- fmt.Sprintf("%s: %s", levelName[p&7], fmt.Sprintf(msg, a...)) -} - -// WaitForMatch returns the first log line matching a regex. It accepts a -// regexp string and timeout. If the timeout value is met before the -// matching pattern is read from the channel, an error is returned. -func (m *WaitingMock) WaitForMatch(reString string, timeout time.Duration) (string, error) { - w := m.w.(*waitingMockWriter) - deadline := time.After(timeout) - re := regexp.MustCompile(reString) - for { - select { - case logLine := <-w.logChan: - if re.MatchString(logLine) { - close(w.logChan) - return logLine, nil - } - case <-deadline: - return "", fmt.Errorf("timeout waiting for match: %q", reString) - } - } -} diff --git a/mocks/sa.go b/mocks/sa.go index 15135ff8412..915a2ad2df3 100644 --- a/mocks/sa.go +++ b/mocks/sa.go @@ -5,7 +5,6 @@ import ( "context" "crypto/x509" "errors" - "math/rand/v2" "os" "time" @@ -34,17 +33,6 @@ func NewStorageAuthorityReadOnly(clk clock.Clock) *StorageAuthorityReadOnly { return &StorageAuthorityReadOnly{clk} } -// StorageAuthority is a mock of sapb.StorageAuthorityClient -type StorageAuthority struct { - StorageAuthorityReadOnly -} - -// NewStorageAuthority creates a new mock storage authority -// with the given clock. -func NewStorageAuthority(clk clock.Clock) *StorageAuthority { - return &StorageAuthority{StorageAuthorityReadOnly{clk}} -} - const ( test1KeyPublicJSON = `{"kty":"RSA","n":"yNWVhtYEKJR21y9xsHV-PD_bYwbXSeNuFal46xYxVfRL5mqha7vttvjB_vc7Xg2RvgCxHPCqoxgMPTzHrZT75LjCwIW2K_klBYN8oYvTwwmeSkAz6ut7ZxPv-nZaT5TJhGk0NT2kh_zSpdriEJ_3vW-mqxYbbBmpvHqsa1_zx9fSuHYctAZJWzxzUZXykbWMWQZpEiE0J4ajj51fInEzVn7VxV-mzfMyboQjujPh7aNJxAWSq4oQEJJDgWwSh9leyoJoPpONHxh5nEE5AjE01FkGICSxjpZsF-w8hOTI3XXohUdu29Se26k2B0PolDSuj0GIQU6-W9TdLXSjBb2SpQ","e":"AQAB"}` test2KeyPublicJSON = `{"kty":"RSA","n":"qnARLrT7Xz4gRcKyLdydmCr-ey9OuPImX4X40thk3on26FkMznR3fRjs66eLK7mmPcBZ6uOJseURU6wAaZNmemoYx1dMvqvWWIyiQleHSD7Q8vBrhR6uIoO4jAzJZR-ChzZuSDt7iHN-3xUVspu5XGwXU_MVJZshTwp4TaFx5elHIT_ObnTvTOU3Xhish07AbgZKmWsVbXh5s-CrIicU4OexJPgunWZ_YJJueOKmTvnLlTV4MzKR2oZlBKZ27S0-SfdV_QDx_ydle5oMAyKVtlAV35cyPMIsYNwgUGBCdY_2Uzi5eX0lTc7MPRwz6qR1kip-i59VcGcUQgqHV6Fyqw","e":"AQAB"}` @@ -221,51 +209,21 @@ func (sa *StorageAuthorityReadOnly) SerialsForIncident(ctx context.Context, _ *s return &ServerStreamClient[sapb.IncidentSerial]{}, nil } -// SerialsForIncident is a mock -func (sa *StorageAuthority) SerialsForIncident(ctx context.Context, _ *sapb.SerialsForIncidentRequest, _ ...grpc.CallOption) (sapb.StorageAuthority_SerialsForIncidentClient, error) { - return &ServerStreamClient[sapb.IncidentSerial]{}, nil -} - // CheckIdentifiersPaused is a mock func (sa *StorageAuthorityReadOnly) CheckIdentifiersPaused(_ context.Context, _ *sapb.PauseRequest, _ ...grpc.CallOption) (*sapb.Identifiers, error) { return nil, nil } -// CheckIdentifiersPaused is a mock -func (sa *StorageAuthority) CheckIdentifiersPaused(_ context.Context, _ *sapb.PauseRequest, _ ...grpc.CallOption) (*sapb.Identifiers, error) { - return nil, nil -} - // GetPausedIdentifiers is a mock func (sa *StorageAuthorityReadOnly) GetPausedIdentifiers(_ context.Context, _ *sapb.RegistrationID, _ ...grpc.CallOption) (*sapb.Identifiers, error) { return nil, nil } -// GetPausedIdentifiers is a mock -func (sa *StorageAuthority) GetPausedIdentifiers(_ context.Context, _ *sapb.RegistrationID, _ ...grpc.CallOption) (*sapb.Identifiers, error) { - return nil, nil -} - // GetRevokedCertsByShard is a mock func (sa *StorageAuthorityReadOnly) GetRevokedCertsByShard(ctx context.Context, _ *sapb.GetRevokedCertsByShardRequest, _ ...grpc.CallOption) (grpc.ServerStreamingClient[corepb.CRLEntry], error) { return &ServerStreamClient[corepb.CRLEntry]{}, nil } -// AddRateLimitOverride is a mock -func (sa *StorageAuthority) AddRateLimitOverride(_ context.Context, req *sapb.AddRateLimitOverrideRequest, _ ...grpc.CallOption) (*sapb.AddRateLimitOverrideResponse, error) { - return nil, nil -} - -// DisableRateLimitOverride is a mock -func (sa *StorageAuthority) DisableRateLimitOverride(ctx context.Context, req *sapb.DisableRateLimitOverrideRequest) (*emptypb.Empty, error) { - return nil, nil -} - -// EnableRateLimitOverride is a mock -func (sa *StorageAuthority) EnableRateLimitOverride(ctx context.Context, req *sapb.EnableRateLimitOverrideRequest) (*emptypb.Empty, error) { - return nil, nil -} - // GetRateLimitOverride is a mock func (sa *StorageAuthorityReadOnly) GetRateLimitOverride(_ context.Context, req *sapb.GetRateLimitOverrideRequest, _ ...grpc.CallOption) (*sapb.RateLimitOverrideResponse, error) { return nil, nil @@ -276,31 +234,6 @@ func (sa *StorageAuthorityReadOnly) GetEnabledRateLimitOverrides(_ context.Conte return nil, nil } -// AddPrecertificate is a mock -func (sa *StorageAuthority) AddPrecertificate(ctx context.Context, req *sapb.AddCertificateRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return nil, nil -} - -// AddSerial is a mock -func (sa *StorageAuthority) AddSerial(ctx context.Context, req *sapb.AddSerialRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return nil, nil -} - -// AddCertificate is a mock -func (sa *StorageAuthority) AddCertificate(_ context.Context, _ *sapb.AddCertificateRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return nil, nil -} - -// NewRegistration is a mock -func (sa *StorageAuthority) NewRegistration(_ context.Context, _ *corepb.Registration, _ ...grpc.CallOption) (*corepb.Registration, error) { - return &corepb.Registration{}, nil -} - -// UpdateRegistration is a mock -func (sa *StorageAuthority) UpdateRegistration(_ context.Context, _ *corepb.Registration, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return &emptypb.Empty{}, nil -} - // FQDNSetTimestampsForWindow is a mock func (sa *StorageAuthorityReadOnly) FQDNSetTimestampsForWindow(_ context.Context, _ *sapb.CountFQDNSetsRequest, _ ...grpc.CallOption) (*sapb.Timestamps, error) { return &sapb.Timestamps{}, nil @@ -311,45 +244,6 @@ func (sa *StorageAuthorityReadOnly) FQDNSetExists(_ context.Context, _ *sapb.FQD return &sapb.Exists{Exists: false}, nil } -// DeactivateRegistration is a mock -func (sa *StorageAuthority) DeactivateRegistration(_ context.Context, _ *sapb.RegistrationID, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return &emptypb.Empty{}, nil -} - -// NewOrderAndAuthzs is a mock -func (sa *StorageAuthority) NewOrderAndAuthzs(_ context.Context, req *sapb.NewOrderAndAuthzsRequest, _ ...grpc.CallOption) (*corepb.Order, error) { - response := &corepb.Order{ - // Fields from the input new order request. - RegistrationID: req.NewOrder.RegistrationID, - Expires: req.NewOrder.Expires, - Identifiers: req.NewOrder.Identifiers, - V2Authorizations: req.NewOrder.V2Authorizations, - // Mock new fields generated by the database transaction. - Id: rand.Int64(), - Created: timestamppb.Now(), - // A new order is never processing because it can't have been finalized yet. - BeganProcessing: false, - Status: string(core.StatusPending), - CertificateProfileName: req.NewOrder.CertificateProfileName, - } - return response, nil -} - -// SetOrderProcessing is a mock -func (sa *StorageAuthority) SetOrderProcessing(_ context.Context, req *sapb.OrderRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return &emptypb.Empty{}, nil -} - -// SetOrderError is a mock -func (sa *StorageAuthority) SetOrderError(_ context.Context, req *sapb.SetOrderErrorRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return &emptypb.Empty{}, nil -} - -// FinalizeOrder is a mock -func (sa *StorageAuthority) FinalizeOrder(_ context.Context, req *sapb.FinalizeOrderRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return &emptypb.Empty{}, nil -} - // GetOrder is a mock func (sa *StorageAuthorityReadOnly) GetOrder(_ context.Context, req *sapb.OrderRequest, _ ...grpc.CallOption) (*corepb.Order, error) { if req.Id == 2 { @@ -416,14 +310,6 @@ func (sa *StorageAuthorityReadOnly) GetOrderForNames(_ context.Context, _ *sapb. return nil, nil } -func (sa *StorageAuthority) FinalizeAuthorization2(ctx context.Context, req *sapb.FinalizeAuthorizationRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return &emptypb.Empty{}, nil -} - -func (sa *StorageAuthority) DeactivateAuthorization2(ctx context.Context, req *sapb.AuthorizationID2, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return nil, nil -} - func (sa *StorageAuthorityReadOnly) CountPendingAuthorizations2(ctx context.Context, req *sapb.RegistrationID, _ ...grpc.CallOption) (*sapb.Count, error) { return &sapb.Count{}, nil } @@ -476,36 +362,11 @@ func (sa *StorageAuthorityReadOnly) GetSerialsByKey(ctx context.Context, _ *sapb return &ServerStreamClient[sapb.Serial]{}, nil } -// GetSerialsByKey is a mock -func (sa *StorageAuthority) GetSerialsByKey(ctx context.Context, _ *sapb.SPKIHash, _ ...grpc.CallOption) (sapb.StorageAuthority_GetSerialsByKeyClient, error) { - return &ServerStreamClient[sapb.Serial]{}, nil -} - // GetSerialsByAccount is a mock func (sa *StorageAuthorityReadOnly) GetSerialsByAccount(ctx context.Context, _ *sapb.RegistrationID, _ ...grpc.CallOption) (sapb.StorageAuthorityReadOnly_GetSerialsByAccountClient, error) { return &ServerStreamClient[sapb.Serial]{}, nil } -// GetSerialsByAccount is a mock -func (sa *StorageAuthority) GetSerialsByAccount(ctx context.Context, _ *sapb.RegistrationID, _ ...grpc.CallOption) (sapb.StorageAuthority_GetSerialsByAccountClient, error) { - return &ServerStreamClient[sapb.Serial]{}, nil -} - -// RevokeCertificate is a mock -func (sa *StorageAuthority) RevokeCertificate(ctx context.Context, req *sapb.RevokeCertificateRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return nil, nil -} - -// UpdateRevokedCertificate is a mock -func (sa *StorageAuthority) UpdateRevokedCertificate(ctx context.Context, req *sapb.RevokeCertificateRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return nil, nil -} - -// AddBlockedKey is a mock -func (sa *StorageAuthority) AddBlockedKey(ctx context.Context, req *sapb.AddBlockedKeyRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return &emptypb.Empty{}, nil -} - // KeyBlocked is a mock func (sa *StorageAuthorityReadOnly) KeyBlocked(ctx context.Context, req *sapb.SPKIHash, _ ...grpc.CallOption) (*sapb.Exists, error) { return &sapb.Exists{Exists: false}, nil @@ -516,16 +377,6 @@ func (sa *StorageAuthorityReadOnly) IncidentsForSerial(ctx context.Context, req return &sapb.Incidents{}, nil } -// LeaseCRLShard is a mock. -func (sa *StorageAuthority) LeaseCRLShard(ctx context.Context, req *sapb.LeaseCRLShardRequest, _ ...grpc.CallOption) (*sapb.LeaseCRLShardResponse, error) { - return nil, errors.New("unimplemented") -} - -// UpdateCRLShard is a mock. -func (sa *StorageAuthority) UpdateCRLShard(ctx context.Context, req *sapb.UpdateCRLShardRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { - return nil, errors.New("unimplemented") -} - // ReplacementOrderExists is a mock. func (sa *StorageAuthorityReadOnly) ReplacementOrderExists(ctx context.Context, req *sapb.Serial, _ ...grpc.CallOption) (*sapb.Exists, error) { return nil, nil diff --git a/probs/probs.go b/probs/probs.go index 7ff35ca61f1..fc8ba057656 100644 --- a/probs/probs.go +++ b/probs/probs.go @@ -312,19 +312,6 @@ func UnsupportedIdentifier(detail string, a ...any) *ProblemDetails { // Additional helper functions that return variations on MalformedProblem with // different HTTP status codes set. -// Canceled returns a ProblemDetails with a MalformedProblem and a 408 Request -// Timeout status code. -func Canceled(detail string, a ...any) *ProblemDetails { - if len(a) > 0 { - detail = fmt.Sprintf(detail, a...) - } - return &ProblemDetails{ - Type: MalformedProblem, - Detail: detail, - HTTPStatus: http.StatusRequestTimeout, - } -} - // Conflict returns a ProblemDetails with a ConflictProblem and a 409 Conflict // status code. func Conflict(detail string) *ProblemDetails { diff --git a/semaphore/semaphore.go b/semaphore/semaphore.go deleted file mode 100644 index 305966898c8..00000000000 --- a/semaphore/semaphore.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// Modified by Boulder to provide a load-shedding mechanism. - -// Package semaphore provides a weighted semaphore implementation. -package semaphore // import "golang.org/x/sync/semaphore" - -import ( - "container/list" - "context" - "errors" - "sync" -) - -type waiter struct { - n int64 - ready chan<- struct{} // Closed when semaphore acquired. -} - -// ErrMaxWaiters is returned when Acquire is called, but there are more than -// maxWaiters waiters. -var ErrMaxWaiters = errors.New("too many waiters") - -// NewWeighted creates a new weighted semaphore with the given -// maximum combined weight for concurrent access. -// maxWaiters provides a limit such that calls to Acquire -// will immediately error if the number of waiters is that high. -// A maxWaiters of zero means no limit. -func NewWeighted(n int64, maxWaiters int) *Weighted { - w := &Weighted{size: n, maxWaiters: maxWaiters} - return w -} - -// Weighted provides a way to bound concurrent access to a resource. -// The callers can request access with a given weight. -type Weighted struct { - size int64 - cur int64 - mu sync.Mutex - waiters list.List - maxWaiters int -} - -// Acquire acquires the semaphore with a weight of n, blocking until resources -// are available or ctx is done. On success, returns nil. On failure, returns -// ctx.Err() and leaves the semaphore unchanged. -// -// If ctx is already done, Acquire may still succeed without blocking. -// -// If there are maxWaiters waiters, Acquire will return an error immediately. -func (s *Weighted) Acquire(ctx context.Context, n int64) error { - s.mu.Lock() - if s.size-s.cur >= n && s.waiters.Len() == 0 { - s.cur += n - s.mu.Unlock() - return nil - } - - if n > s.size { - // Don't make other Acquire calls block on one that's doomed to fail. - s.mu.Unlock() - <-ctx.Done() - return ctx.Err() - } - - if s.maxWaiters > 0 && s.waiters.Len() >= s.maxWaiters { - s.mu.Unlock() - return ErrMaxWaiters - } - - ready := make(chan struct{}) - w := waiter{n: n, ready: ready} - elem := s.waiters.PushBack(w) - s.mu.Unlock() - - select { - case <-ctx.Done(): - err := ctx.Err() - s.mu.Lock() - select { - case <-ready: - // Acquired the semaphore after we were canceled. Rather than trying to - // fix up the queue, just pretend we didn't notice the cancellation. - err = nil - default: - isFront := s.waiters.Front() == elem - s.waiters.Remove(elem) - // If we're at the front and there're extra tokens left, notify other waiters. - if isFront && s.size > s.cur { - s.notifyWaiters() - } - } - s.mu.Unlock() - return err - - case <-ready: - return nil - } -} - -// TryAcquire acquires the semaphore with a weight of n without blocking. -// On success, returns true. On failure, returns false and leaves the semaphore unchanged. -func (s *Weighted) TryAcquire(n int64) bool { - s.mu.Lock() - success := s.size-s.cur >= n && s.waiters.Len() == 0 - if success { - s.cur += n - } - s.mu.Unlock() - return success -} - -// Release releases the semaphore with a weight of n. -func (s *Weighted) Release(n int64) { - s.mu.Lock() - s.cur -= n - if s.cur < 0 { - s.mu.Unlock() - panic("semaphore: released more than held") - } - s.notifyWaiters() - s.mu.Unlock() -} - -func (s *Weighted) NumWaiters() int { - s.mu.Lock() - defer s.mu.Unlock() - return s.waiters.Len() -} - -func (s *Weighted) notifyWaiters() { - for { - next := s.waiters.Front() - if next == nil { - break // No more waiters blocked. - } - - w := next.Value.(waiter) - if s.size-s.cur < w.n { - // Not enough tokens for the next waiter. We could keep going (to try to - // find a waiter with a smaller request), but under load that could cause - // starvation for large requests; instead, we leave all remaining waiters - // blocked. - // - // Consider a semaphore used as a read-write lock, with N tokens, N - // readers, and one writer. Each reader can Acquire(1) to obtain a read - // lock. The writer can Acquire(N) to obtain a write lock, excluding all - // of the readers. If we allow the readers to jump ahead in the queue, - // the writer will starve — there is always one token available for every - // reader. - break - } - - s.cur += w.n - s.waiters.Remove(next) - close(w.ready) - } -} diff --git a/semaphore/semaphore_bench_test.go b/semaphore/semaphore_bench_test.go deleted file mode 100644 index 991dd6fdcc0..00000000000 --- a/semaphore/semaphore_bench_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.7 -// +build go1.7 - -package semaphore_test - -import ( - "context" - "fmt" - "testing" - - "github.com/letsencrypt/boulder/semaphore" -) - -// weighted is an interface matching a subset of *Weighted. It allows -// alternate implementations for testing and benchmarking. -type weighted interface { - Acquire(context.Context, int64) error - TryAcquire(int64) bool - Release(int64) -} - -// semChan implements Weighted using a channel for -// comparing against the condition variable-based implementation. -type semChan chan struct{} - -func newSemChan(n int64) semChan { - return semChan(make(chan struct{}, n)) -} - -func (s semChan) Acquire(_ context.Context, n int64) error { - for i := int64(0); i < n; i++ { - s <- struct{}{} - } - return nil -} - -func (s semChan) TryAcquire(n int64) bool { - if int64(len(s))+n > int64(cap(s)) { - return false - } - - for i := int64(0); i < n; i++ { - s <- struct{}{} - } - return true -} - -func (s semChan) Release(n int64) { - for i := int64(0); i < n; i++ { - <-s - } -} - -// acquireN calls Acquire(size) on sem N times and then calls Release(size) N times. -func acquireN(b *testing.B, sem weighted, size int64, N int) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - for j := 0; j < N; j++ { - _ = sem.Acquire(context.Background(), size) - } - for j := 0; j < N; j++ { - sem.Release(size) - } - } -} - -// tryAcquireN calls TryAcquire(size) on sem N times and then calls Release(size) N times. -func tryAcquireN(b *testing.B, sem weighted, size int64, N int) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - for j := 0; j < N; j++ { - if !sem.TryAcquire(size) { - b.Fatalf("TryAcquire(%v) = false, want true", size) - } - } - for j := 0; j < N; j++ { - sem.Release(size) - } - } -} - -func BenchmarkNewSeq(b *testing.B) { - for _, cap := range []int64{1, 128} { - b.Run(fmt.Sprintf("Weighted-%d", cap), func(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = semaphore.NewWeighted(cap, 0) - } - }) - b.Run(fmt.Sprintf("semChan-%d", cap), func(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = newSemChan(cap) - } - }) - } -} - -func BenchmarkAcquireSeq(b *testing.B) { - for _, c := range []struct { - cap, size int64 - N int - }{ - {1, 1, 1}, - {2, 1, 1}, - {16, 1, 1}, - {128, 1, 1}, - {2, 2, 1}, - {16, 2, 8}, - {128, 2, 64}, - {2, 1, 2}, - {16, 8, 2}, - {128, 64, 2}, - } { - for _, w := range []struct { - name string - w weighted - }{ - {"Weighted", semaphore.NewWeighted(c.cap, 0)}, - {"semChan", newSemChan(c.cap)}, - } { - b.Run(fmt.Sprintf("%s-acquire-%d-%d-%d", w.name, c.cap, c.size, c.N), func(b *testing.B) { - acquireN(b, w.w, c.size, c.N) - }) - b.Run(fmt.Sprintf("%s-tryAcquire-%d-%d-%d", w.name, c.cap, c.size, c.N), func(b *testing.B) { - tryAcquireN(b, w.w, c.size, c.N) - }) - } - } -} diff --git a/semaphore/semaphore_example_test.go b/semaphore/semaphore_example_test.go deleted file mode 100644 index e75cd79f5bc..00000000000 --- a/semaphore/semaphore_example_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package semaphore_test - -import ( - "context" - "fmt" - "log" - "runtime" - - "golang.org/x/sync/semaphore" -) - -// Example_workerPool demonstrates how to use a semaphore to limit the number of -// goroutines working on parallel tasks. -// -// This use of a semaphore mimics a typical “worker pool” pattern, but without -// the need to explicitly shut down idle workers when the work is done. -func Example_workerPool() { - ctx := context.TODO() - - var ( - maxWorkers = runtime.GOMAXPROCS(0) - sem = semaphore.NewWeighted(int64(maxWorkers)) - out = make([]int, 32) - ) - - // Compute the output using up to maxWorkers goroutines at a time. - for i := range out { - // When maxWorkers goroutines are in flight, Acquire blocks until one of the - // workers finishes. - if err := sem.Acquire(ctx, 1); err != nil { - log.Printf("Failed to acquire semaphore: %v", err) - break - } - - go func(i int) { - defer sem.Release(1) - out[i] = collatzSteps(i + 1) - }(i) - } - - // Acquire all of the tokens to wait for any remaining workers to finish. - // - // If you are already waiting for the workers by some other means (such as an - // errgroup.Group), you can omit this final Acquire call. - if err := sem.Acquire(ctx, int64(maxWorkers)); err != nil { - log.Printf("Failed to acquire semaphore: %v", err) - } - - fmt.Println(out) - - // Output: - // [0 1 7 2 5 8 16 3 19 6 14 9 9 17 17 4 12 20 20 7 7 15 15 10 23 10 111 18 18 18 106 5] -} - -// collatzSteps computes the number of steps to reach 1 under the Collatz -// conjecture. (See https://en.wikipedia.org/wiki/Collatz_conjecture.) -func collatzSteps(n int) (steps int) { - if n <= 0 { - panic("nonpositive input") - } - - for ; n > 1; steps++ { - if steps < 0 { - panic("too many steps") - } - - if n%2 == 0 { - n /= 2 - continue - } - - const maxInt = int(^uint(0) >> 1) - if n > (maxInt-1)/3 { - panic("overflow") - } - n = 3*n + 1 - } - - return steps -} diff --git a/semaphore/semaphore_test.go b/semaphore/semaphore_test.go deleted file mode 100644 index 287ed5aa47f..00000000000 --- a/semaphore/semaphore_test.go +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package semaphore_test - -import ( - "context" - "math/rand/v2" - "runtime" - "sync" - "testing" - "time" - - "golang.org/x/sync/errgroup" - - "github.com/letsencrypt/boulder/semaphore" -) - -const maxSleep = 1 * time.Millisecond - -func HammerWeighted(sem *semaphore.Weighted, n int64, loops int) { - for range loops { - _ = sem.Acquire(context.Background(), n) - time.Sleep(time.Duration(rand.Int64N(int64(maxSleep/time.Nanosecond))) * time.Nanosecond) - sem.Release(n) - } -} - -func TestWeighted(t *testing.T) { - t.Parallel() - - n := runtime.GOMAXPROCS(0) - loops := 10000 / n - sem := semaphore.NewWeighted(int64(n), 0) - var wg sync.WaitGroup - wg.Add(n) - for i := range n { - i := i - go func() { - defer wg.Done() - HammerWeighted(sem, int64(i), loops) - }() - } - wg.Wait() -} - -func TestWeightedPanic(t *testing.T) { - t.Parallel() - - defer func() { - if recover() == nil { - t.Fatal("release of an unacquired weighted semaphore did not panic") - } - }() - w := semaphore.NewWeighted(1, 0) - w.Release(1) -} - -func TestWeightedTryAcquire(t *testing.T) { - t.Parallel() - - ctx := context.Background() - sem := semaphore.NewWeighted(2, 0) - tries := []bool{} - _ = sem.Acquire(ctx, 1) - tries = append(tries, sem.TryAcquire(1)) - tries = append(tries, sem.TryAcquire(1)) - - sem.Release(2) - - tries = append(tries, sem.TryAcquire(1)) - _ = sem.Acquire(ctx, 1) - tries = append(tries, sem.TryAcquire(1)) - - want := []bool{true, false, true, false} - for i := range tries { - if tries[i] != want[i] { - t.Errorf("tries[%d]: got %t, want %t", i, tries[i], want[i]) - } - } -} - -func TestWeightedAcquire(t *testing.T) { - t.Parallel() - - ctx := context.Background() - sem := semaphore.NewWeighted(2, 0) - tryAcquire := func(n int64) bool { - ctx, cancel := context.WithTimeout(ctx, 10*time.Millisecond) - defer cancel() - return sem.Acquire(ctx, n) == nil - } - - tries := []bool{} - _ = sem.Acquire(ctx, 1) - tries = append(tries, tryAcquire(1)) - tries = append(tries, tryAcquire(1)) - - sem.Release(2) - - tries = append(tries, tryAcquire(1)) - _ = sem.Acquire(ctx, 1) - tries = append(tries, tryAcquire(1)) - - want := []bool{true, false, true, false} - for i := range tries { - if tries[i] != want[i] { - t.Errorf("tries[%d]: got %t, want %t", i, tries[i], want[i]) - } - } -} - -func TestWeightedDoesntBlockIfTooBig(t *testing.T) { - t.Parallel() - - const n = 2 - sem := semaphore.NewWeighted(n, 0) - { - go func() { - _ = sem.Acquire(t.Context(), n+1) - }() - } - - g, ctx := errgroup.WithContext(context.Background()) - for i := n * 3; i > 0; i-- { - g.Go(func() error { - err := sem.Acquire(ctx, 1) - if err == nil { - time.Sleep(1 * time.Millisecond) - sem.Release(1) - } - return err - }) - } - if err := g.Wait(); err != nil { - t.Errorf("semaphore.NewWeighted(%v, 0) failed to AcquireCtx(_, 1) with AcquireCtx(_, %v) pending", n, n+1) - } -} - -// TestLargeAcquireDoesntStarve times out if a large call to Acquire starves. -// Merely returning from the test function indicates success. -func TestLargeAcquireDoesntStarve(t *testing.T) { - t.Parallel() - - ctx := context.Background() - n := int64(runtime.GOMAXPROCS(0)) - sem := semaphore.NewWeighted(n, 0) - running := true - - var wg sync.WaitGroup - wg.Add(int(n)) - for i := n; i > 0; i-- { - _ = sem.Acquire(ctx, 1) - go func() { - defer func() { - sem.Release(1) - wg.Done() - }() - for running { - time.Sleep(1 * time.Millisecond) - sem.Release(1) - _ = sem.Acquire(ctx, 1) - } - }() - } - - _ = sem.Acquire(ctx, n) - running = false - sem.Release(n) - wg.Wait() -} - -// translated from https://github.com/zhiqiangxu/util/blob/master/mutex/crwmutex_test.go#L43 -func TestAllocCancelDoesntStarve(t *testing.T) { - sem := semaphore.NewWeighted(10, 0) - - // Block off a portion of the semaphore so that Acquire(_, 10) can eventually succeed. - _ = sem.Acquire(context.Background(), 1) - - // In the background, Acquire(_, 10). - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - go func() { - _ = sem.Acquire(ctx, 10) - }() - - // Wait until the Acquire(_, 10) call blocks. - for sem.TryAcquire(1) { - sem.Release(1) - runtime.Gosched() - } - - // Now try to grab a read lock, and simultaneously unblock the Acquire(_, 10) call. - // Both Acquire calls should unblock and return, in either order. - go cancel() - - err := sem.Acquire(context.Background(), 1) - if err != nil { - t.Fatalf("Acquire(_, 1) failed unexpectedly: %v", err) - } - sem.Release(1) -} - -func TestMaxWaiters(t *testing.T) { - ctx := t.Context() - sem := semaphore.NewWeighted(1, 10) - _ = sem.Acquire(ctx, 1) - - for range 10 { - go func() { - _ = sem.Acquire(ctx, 1) - <-ctx.Done() - }() - } - - // Since the goroutines that act as waiters are intended to block in - // sem.Acquire, there's no principled wait to trigger here once they're - // blocked. Instead, loop until we reach the expected number of waiters. - for sem.NumWaiters() < 10 { - time.Sleep(10 * time.Millisecond) - } - err := sem.Acquire(ctx, 1) - if err != semaphore.ErrMaxWaiters { - t.Errorf("expected error when maxWaiters was reached, but got %#v", err) - } -} diff --git a/test/certs.go b/test/certs.go index 5ad0e282cf8..add38e4d1b3 100644 --- a/test/certs.go +++ b/test/certs.go @@ -2,67 +2,20 @@ package test import ( "bytes" - "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" - "crypto/rsa" "crypto/x509" "encoding/hex" - "encoding/pem" - "errors" "fmt" "math/big" "net" - "os" "testing" "time" "github.com/jmhodges/clock" ) -// LoadSigner loads a PEM private key specified by filename or returns an error. -// Can be paired with issuance.LoadCertificate to get both a CA cert and its -// associated private key for use in signing throwaway test certs. -func LoadSigner(filename string) (crypto.Signer, error) { - keyBytes, err := os.ReadFile(filename) - if err != nil { - return nil, err - } - - // pem.Decode does not return an error as its 2nd arg, but instead the "rest" - // that was leftover from parsing the PEM block. We only care if the decoded - // PEM block was empty for this test function. - block, _ := pem.Decode(keyBytes) - if block == nil { - return nil, errors.New("Unable to decode private key PEM bytes") - } - - // Try decoding as an RSA private key - if rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil { - return rsaKey, nil - } - - // Try decoding as a PKCS8 private key - if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil { - // Determine the key's true type and return it as a crypto.Signer - switch k := key.(type) { - case *rsa.PrivateKey: - return k, nil - case *ecdsa.PrivateKey: - return k, nil - } - } - - // Try as an ECDSA private key - if ecdsaKey, err := x509.ParseECPrivateKey(block.Bytes); err == nil { - return ecdsaKey, nil - } - - // Nothing worked! Fail hard. - return nil, errors.New("Unable to decode private key PEM bytes") -} - // ThrowAwayCert is a small test helper function that creates a self-signed // certificate with one SAN. It returns the parsed certificate and its serial // in string form for convenience. diff --git a/va/va.go b/va/va.go index bd128abf761..fd766262267 100644 --- a/va/va.go +++ b/va/va.go @@ -40,9 +40,8 @@ const ( PrimaryPerspective = "Primary" allPerspectives = "all" - opDCVAndCAA = "dcv+caa" - opDCV = "dcv" - opCAA = "caa" + opDCV = "dcv" + opCAA = "caa" pass = "pass" fail = "fail" diff --git a/vendor/golang.org/x/sync/semaphore/semaphore.go b/vendor/golang.org/x/sync/semaphore/semaphore.go deleted file mode 100644 index b618162aab6..00000000000 --- a/vendor/golang.org/x/sync/semaphore/semaphore.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package semaphore provides a weighted semaphore implementation. -package semaphore // import "golang.org/x/sync/semaphore" - -import ( - "container/list" - "context" - "sync" -) - -type waiter struct { - n int64 - ready chan<- struct{} // Closed when semaphore acquired. -} - -// NewWeighted creates a new weighted semaphore with the given -// maximum combined weight for concurrent access. -func NewWeighted(n int64) *Weighted { - w := &Weighted{size: n} - return w -} - -// Weighted provides a way to bound concurrent access to a resource. -// The callers can request access with a given weight. -type Weighted struct { - size int64 - cur int64 - mu sync.Mutex - waiters list.List -} - -// Acquire acquires the semaphore with a weight of n, blocking until resources -// are available or ctx is done. On success, returns nil. On failure, returns -// ctx.Err() and leaves the semaphore unchanged. -func (s *Weighted) Acquire(ctx context.Context, n int64) error { - done := ctx.Done() - - s.mu.Lock() - select { - case <-done: - // ctx becoming done has "happened before" acquiring the semaphore, - // whether it became done before the call began or while we were - // waiting for the mutex. We prefer to fail even if we could acquire - // the mutex without blocking. - s.mu.Unlock() - return ctx.Err() - default: - } - if s.size-s.cur >= n && s.waiters.Len() == 0 { - // Since we hold s.mu and haven't synchronized since checking done, if - // ctx becomes done before we return here, it becoming done must have - // "happened concurrently" with this call - it cannot "happen before" - // we return in this branch. So, we're ok to always acquire here. - s.cur += n - s.mu.Unlock() - return nil - } - - if n > s.size { - // Don't make other Acquire calls block on one that's doomed to fail. - s.mu.Unlock() - <-done - return ctx.Err() - } - - ready := make(chan struct{}) - w := waiter{n: n, ready: ready} - elem := s.waiters.PushBack(w) - s.mu.Unlock() - - select { - case <-done: - s.mu.Lock() - select { - case <-ready: - // Acquired the semaphore after we were canceled. - // Pretend we didn't and put the tokens back. - s.cur -= n - s.notifyWaiters() - default: - isFront := s.waiters.Front() == elem - s.waiters.Remove(elem) - // If we're at the front and there're extra tokens left, notify other waiters. - if isFront && s.size > s.cur { - s.notifyWaiters() - } - } - s.mu.Unlock() - return ctx.Err() - - case <-ready: - // Acquired the semaphore. Check that ctx isn't already done. - // We check the done channel instead of calling ctx.Err because we - // already have the channel, and ctx.Err is O(n) with the nesting - // depth of ctx. - select { - case <-done: - s.Release(n) - return ctx.Err() - default: - } - return nil - } -} - -// TryAcquire acquires the semaphore with a weight of n without blocking. -// On success, returns true. On failure, returns false and leaves the semaphore unchanged. -func (s *Weighted) TryAcquire(n int64) bool { - s.mu.Lock() - success := s.size-s.cur >= n && s.waiters.Len() == 0 - if success { - s.cur += n - } - s.mu.Unlock() - return success -} - -// Release releases the semaphore with a weight of n. -func (s *Weighted) Release(n int64) { - s.mu.Lock() - s.cur -= n - if s.cur < 0 { - s.mu.Unlock() - panic("semaphore: released more than held") - } - s.notifyWaiters() - s.mu.Unlock() -} - -func (s *Weighted) notifyWaiters() { - for { - next := s.waiters.Front() - if next == nil { - break // No more waiters blocked. - } - - w := next.Value.(waiter) - if s.size-s.cur < w.n { - // Not enough tokens for the next waiter. We could keep going (to try to - // find a waiter with a smaller request), but under load that could cause - // starvation for large requests; instead, we leave all remaining waiters - // blocked. - // - // Consider a semaphore used as a read-write lock, with N tokens, N - // readers, and one writer. Each reader can Acquire(1) to obtain a read - // lock. The writer can Acquire(N) to obtain a write lock, excluding all - // of the readers. If we allow the readers to jump ahead in the queue, - // the writer will starve — there is always one token available for every - // reader. - break - } - - s.cur += w.n - s.waiters.Remove(next) - close(w.ready) - } -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 4fc3cdb6a56..5a8663a671e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -406,7 +406,6 @@ golang.org/x/net/trace # golang.org/x/sync v0.18.0 ## explicit; go 1.24.0 golang.org/x/sync/errgroup -golang.org/x/sync/semaphore # golang.org/x/sys v0.38.0 ## explicit; go 1.24.0 golang.org/x/sys/plan9 diff --git a/web/context.go b/web/context.go index b343f8899a9..927f837ac56 100644 --- a/web/context.go +++ b/web/context.go @@ -208,16 +208,6 @@ func (th *TopHandler) logEvent(logEvent *RequestEvent) { int(logEvent.Latency*1000), logEvent.RealIP, jsonEvent) } -// GetClientAddr returns a comma-separated list of HTTP clients involved in -// making this request, starting with the original requester and ending with the -// remote end of our TCP connection (which is typically our own proxy). -func GetClientAddr(r *http.Request) string { - if xff := r.Header.Get("X-Forwarded-For"); xff != "" { - return xff + "," + r.RemoteAddr - } - return r.RemoteAddr -} - func KeyTypeToString(pub crypto.PublicKey) string { switch pk := pub.(type) { case *rsa.PublicKey: diff --git a/web/jwk.go b/web/jwk.go deleted file mode 100644 index 6a842c85028..00000000000 --- a/web/jwk.go +++ /dev/null @@ -1,19 +0,0 @@ -package web - -import ( - "encoding/json" - "os" - - "github.com/go-jose/go-jose/v4" -) - -// LoadJWK loads a JSON encoded JWK specified by filename or returns an error -func LoadJWK(filename string) (*jose.JSONWebKey, error) { - var jwk jose.JSONWebKey - if jsonBytes, err := os.ReadFile(filename); err != nil { - return nil, err - } else if err = json.Unmarshal(jsonBytes, &jwk); err != nil { - return nil, err - } - return &jwk, nil -}