| /* |
| * |
| * Copyright 2020 gRPC authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package certprovider |
| |
| import ( |
| "context" |
| "sync" |
| |
| "google.golang.org/grpc/internal/grpcsync" |
| ) |
| |
| // Distributor makes it easy for provider implementations to furnish new key |
| // materials by handling synchronization between the producer and consumers of |
| // the key material. |
| // |
| // Provider implementations which choose to use a Distributor should do the |
| // following: |
| // - create a new Distributor using the NewDistributor() function. |
| // - invoke the Set() method whenever they have new key material or errors to |
| // report. |
| // - delegate to the distributor when handing calls to KeyMaterial(). |
| // - invoke the Stop() method when they are done using the distributor. |
| type Distributor struct { |
| // mu protects the underlying key material. |
| mu sync.Mutex |
| km *KeyMaterial |
| pErr error |
| |
| ready *grpcsync.Event |
| closed *grpcsync.Event |
| } |
| |
| // NewDistributor returns a new Distributor. |
| func NewDistributor() *Distributor { |
| return &Distributor{ |
| ready: grpcsync.NewEvent(), |
| closed: grpcsync.NewEvent(), |
| } |
| } |
| |
| // Set updates the key material in the distributor with km. |
| // |
| // Provider implementations which use the distributor must not modify the |
| // contents of the KeyMaterial struct pointed to by km. |
| // |
| // A non-nil err value indicates the error that the provider implementation ran |
| // into when trying to fetch key material, and makes it possible to surface the |
| // error to the user. A non-nil error value passed here causes distributor's |
| // KeyMaterial() method to return nil key material. |
| func (d *Distributor) Set(km *KeyMaterial, err error) { |
| d.mu.Lock() |
| d.km = km |
| d.pErr = err |
| if err != nil { |
| // If a non-nil err is passed, we ignore the key material being passed. |
| d.km = nil |
| } |
| d.ready.Fire() |
| d.mu.Unlock() |
| } |
| |
| // KeyMaterial returns the most recent key material provided to the distributor. |
| // If no key material was provided at the time of this call, it will block until |
| // the deadline on the context expires or fresh key material arrives. |
| func (d *Distributor) KeyMaterial(ctx context.Context) (*KeyMaterial, error) { |
| if d.closed.HasFired() { |
| return nil, errProviderClosed |
| } |
| |
| if d.ready.HasFired() { |
| return d.keyMaterial() |
| } |
| |
| select { |
| case <-ctx.Done(): |
| return nil, ctx.Err() |
| case <-d.closed.Done(): |
| return nil, errProviderClosed |
| case <-d.ready.Done(): |
| return d.keyMaterial() |
| } |
| } |
| |
| func (d *Distributor) keyMaterial() (*KeyMaterial, error) { |
| d.mu.Lock() |
| defer d.mu.Unlock() |
| return d.km, d.pErr |
| } |
| |
| // Stop turns down the distributor, releases allocated resources and fails any |
| // active KeyMaterial() call waiting for new key material. |
| func (d *Distributor) Stop() { |
| d.closed.Fire() |
| } |