sources/Google.Solutions.Iap/Net/OneTimeUseStream.cs (87 lines of code) (raw):

// // Copyright 2019 Google LLC // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you 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. // using System; using System.Threading; using System.Threading.Tasks; namespace Google.Solutions.Iap.Net { /// <summary> /// Base class protecting against usage after /// the stream has been closed or has failed. /// </summary> public abstract class OneTimeUseStream : INetworkStream { private volatile bool closed; private void VerifyStreamNotClosed() { if (this.closed) { throw new NetworkStreamClosedException("Stream is closed"); } } //--------------------------------------------------------------------- // Methods to be overridden //--------------------------------------------------------------------- protected abstract Task CloseAsyncWithCloseProtection(CancellationToken cancellationToken); protected abstract Task<int> ReadAsyncWithCloseProtection( byte[] buffer, int offset, int count, CancellationToken cancellationToken); protected abstract Task WriteAsyncWithCloseProtection( byte[] buffer, int offset, int count, CancellationToken cancellationToken); //--------------------------------------------------------------------- // Publics //--------------------------------------------------------------------- public async Task CloseAsync(CancellationToken cancellationToken) { // // Calling CloseAsync on a closed connection is acceptable // since the server might have closed first. // await CloseAsyncWithCloseProtection(cancellationToken).ConfigureAwait(false); this.closed = true; } public async Task<int> ReadAsync( byte[] buffer, int offset, int count, CancellationToken cancellationToken) { VerifyStreamNotClosed(); try { var bytesRead = await ReadAsyncWithCloseProtection( buffer, offset, count, cancellationToken).ConfigureAwait(false); if (bytesRead == 0) { this.closed = true; } return bytesRead; } catch (NetworkStreamClosedException) { this.closed = true; throw; } } public async Task WriteAsync( byte[] buffer, int offset, int count, CancellationToken cancellationToken) { VerifyStreamNotClosed(); try { await WriteAsyncWithCloseProtection( buffer, offset, count, cancellationToken).ConfigureAwait(false); } catch (NetworkStreamClosedException) { this.closed = true; throw; } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected abstract void Dispose(bool disposing); } }