Example #1
0
        /// <summary>
        /// Reads a block of bytes from the current stream and writes the data to a buffer.
        /// </summary>
        /// <param name="buffer">When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
        /// <param name="count">The maximum number of bytes to be read.</param>
        /// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero if the end of the stream has been reached.</returns>
        public override int Read(byte[] buffer, int offset, int count)
        {
            CommonUtility.AssertNotNull("buffer", buffer);
            CommonUtility.AssertInBounds("offset", offset, 0, buffer.Length);
            CommonUtility.AssertInBounds("count", count, 0, buffer.Length - offset);

            return(this.ReadInternal(buffer, offset, count));
        }
        /// <summary>
        /// Adds an operation be submitted as part of the batch.
        /// </summary>
        /// <param name="pageBlob">The <see cref="CloudPageBlob"/> whose tier will be set.</param>
        /// <param name="premiumPageBlobTier">A <see cref="PremiumPageBlobTier"/> representing the tier to set.</param>
        /// <param name="accessCondition">An <see cref="AccessCondition"/> object that represents the condition that must be met in order for the request to proceed. If <c>null</c>, no condition is used.</param>
        /// <param name="blobRequestOptions">A <see cref="BlobRequestOptions"/> object that specifies additional options for the request.</param>
        public void AddSubOperation(CloudPageBlob pageBlob, PremiumPageBlobTier premiumPageBlobTier, AccessCondition accessCondition = default(AccessCondition), BlobRequestOptions blobRequestOptions = default(BlobRequestOptions))
        {
            CommonUtility.AssertInBounds("operationCount", this.Operations.Count, 0, Constants.MaxSubOperationPerBatch - 1);
            CommonUtility.AssertNotNull("pageBlob", pageBlob);
            CommonUtility.AssertNotNull("premiumPageBlobTier", premiumPageBlobTier);

            this.Operations.Add(pageBlob.SetBlobTierImpl(premiumPageBlobTier, blobRequestOptions ?? BlobRequestOptions.BaseDefaultRequestOptions));
        }
        /// <summary>
        /// Adds an operation to be submitted as part of the batch.
        /// </summary>
        /// <param name="blockBlob">The <see cref="CloudBlockBlob"/> whose tier will be set.</param>
        /// <param name="standardBlobTier">A <see cref="StandardBlobTier"/> representing the tier to set.</param>
        /// <param name="accessCondition">An <see cref="AccessCondition"/> object that represents the condition that must be met in order for the request to proceed. If <c>null</c>, no condition is used.</param>
        /// <param name="blobRequestOptions">A <see cref="BlobRequestOptions"/> object that specifies additional options for the request.</param>
        public void AddSubOperation(CloudBlockBlob blockBlob, StandardBlobTier standardBlobTier, AccessCondition accessCondition = default(AccessCondition), BlobRequestOptions blobRequestOptions = default(BlobRequestOptions))
        {
            CommonUtility.AssertInBounds("operationCount", this.Operations.Count, 0, Constants.MaxSubOperationPerBatch - 1);
            CommonUtility.AssertNotNull("blockBlob", blockBlob);
            CommonUtility.AssertNotNull("standardBlobTier", standardBlobTier);

            this.Operations.Add(blockBlob.SetStandardBlobTierImpl(standardBlobTier, default(RehydratePriority?), accessCondition, blobRequestOptions ?? BlobRequestOptions.BaseDefaultRequestOptions));
        }
        /// <summary>
        /// Begins an asynchronous write operation.
        /// </summary>
        /// <param name="buffer">An array of bytes. This method copies count bytes from
        /// buffer to the current stream. </param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin
        /// copying bytes to the current stream.</param>
        /// <param name="count">The number of bytes to be written to the current stream.</param>
        /// <param name="callback">An optional asynchronous callback, to be called when the write is complete.</param>
        /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests.</param>
        /// <returns>An <c>IAsyncResult</c> that represents the asynchronous write, which could still be pending.</returns>
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            CommonUtility.AssertNotNull("buffer", buffer);
            CommonUtility.AssertInBounds("offset", offset, 0, buffer.Length);
            CommonUtility.AssertInBounds("count", count, 0, buffer.Length - offset);

            return(this.cryptoStream.BeginWrite(buffer, offset, count, callback, state));
        }
        /// <summary>
        /// Begins an asynchronous write operation.
        /// </summary>
        /// <param name="buffer">An array of bytes. This method copies count bytes from
        /// buffer to the current stream. </param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin
        /// copying bytes to the current stream.</param>
        /// <param name="count">The number of bytes to be written to the current stream.</param>
        /// <param name="callback">An optional asynchronous callback, to be called when the write is complete.</param>
        /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests.</param>
        /// <returns>An <c>IAsyncResult</c> that represents the asynchronous write, which could still be pending.</returns>
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            CommonUtility.AssertNotNull("buffer", buffer);
            CommonUtility.AssertInBounds("offset", offset, 0, buffer.Length);
            CommonUtility.AssertInBounds("count", count, 0, buffer.Length - offset);

            if (this.committed)
            {
                throw new InvalidOperationException(SR.BlobStreamAlreadyCommitted);
            }

            if (this.blobMD5 != null)
            {
                this.blobMD5.UpdateHash(buffer, offset, count);
            }

            StorageAsyncResult <NullType> storageAsyncResult = new StorageAsyncResult <NullType>(callback, state);
            StorageAsyncResult <NullType> currentAsyncResult = storageAsyncResult;

            this.currentOffset += count;
            bool dispatched = false;

            if (this.lastException == null)
            {
                while (count > 0)
                {
                    int maxBytesToWrite = this.streamWriteSizeInBytes - (int)this.internalBuffer.Length;
                    int bytesToWrite    = Math.Min(count, maxBytesToWrite);

                    this.internalBuffer.Write(buffer, offset, bytesToWrite);
                    if (this.blockMD5 != null)
                    {
                        this.blockMD5.UpdateHash(buffer, offset, bytesToWrite);
                    }

                    count  -= bytesToWrite;
                    offset += bytesToWrite;

                    if (bytesToWrite == maxBytesToWrite)
                    {
                        this.DispatchWrite(currentAsyncResult);
                        dispatched = true;

                        // Do not use the IAsyncResult we are going to return more
                        // than once, as otherwise its callback will be called more
                        // than once.
                        currentAsyncResult = null;
                    }
                }
            }

            if (!dispatched)
            {
                storageAsyncResult.OnComplete(this.lastException);
            }

            return(storageAsyncResult);
        }
Example #6
0
 internal void AssertInBounds(long?offset, long?count, int maxMd5 = int.MaxValue, int maxCrc64 = int.MaxValue)
 {
     if (offset.HasValue && this.HasAny)
     {
         CommonUtility.AssertNotNull("count", count);
         CommonUtility.AssertInBounds("count", count.Value, 1, this.MD5 ? maxMd5 : int.MaxValue);
         CommonUtility.AssertInBounds("count", count.Value, 1, this.CRC64 ? maxCrc64 : int.MaxValue);
     }
 }
Example #7
0
 public override void Write(byte[] buffer, int offset, int count)
 {
     CommonUtility.AssertNotNull("buffer", buffer);
     CommonUtility.AssertInBounds("offset", offset, 0, buffer.Length);
     CommonUtility.AssertInBounds("count", count, 0, buffer.Length - offset);
     if (position + count > capacity)
     {
         Reserve(position + count);
     }
     WriteInternal(buffer, offset, count);
     length = Math.Max(length, position);
 }
Example #8
0
        /// <summary>
        /// Sets the position within the current stream.
        /// </summary>
        /// <param name="offset">A byte offset relative to the origin parameter.</param>
        /// <param name="origin">A value of type <c>SeekOrigin</c> indicating the reference
        /// point used to obtain the new position.</param>
        /// <returns>The new position within the current stream.</returns>
        /// <remarks>Seeking in a FileReadStream disables checksum validation.</remarks>
        public override long Seek(long offset, SeekOrigin origin)
        {
            if (this.lastException != null)
            {
                throw this.lastException;
            }

            long newOffset;

            switch (origin)
            {
            case SeekOrigin.Begin:
                newOffset = offset;
                break;

            case SeekOrigin.Current:
                newOffset = this.currentOffset + offset;
                break;

            case SeekOrigin.End:
                newOffset = this.Length + offset;
                break;

            default:
                CommonUtility.ArgumentOutOfRange("origin", origin);
                throw new ArgumentOutOfRangeException("origin");
            }

            CommonUtility.AssertInBounds("offset", newOffset, 0, this.Length);

            if (newOffset != this.currentOffset)
            {
                long bufferOffset = this.internalBuffer.Position + (newOffset - this.currentOffset);
                if ((bufferOffset >= 0) && (bufferOffset < this.internalBuffer.Length))
                {
                    this.internalBuffer.Position = bufferOffset;
                }
                else
                {
                    this.internalBuffer.SetLength(0);
                }

                this.fileChecksum  = null;
                this.currentOffset = newOffset;
            }

            return(this.currentOffset);
        }
        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            CommonUtility.AssertNotNull("buffer", buffer);
            CommonUtility.AssertInBounds("offset", offset, 0, buffer.Length);
            CommonUtility.AssertInBounds("count", count, 0, buffer.Length - offset);

            if (this.readPending)
            {
                throw new InvalidOperationException(SR.FileStreamReadPending);
            }

            try
            {
                this.readPending = true;
                StorageAsyncResult <int> storageAsyncResult = new StorageAsyncResult <int>(callback, state);

                if (this.lastException != null)
                {
                    storageAsyncResult.OnComplete(this.lastException);
                    return(storageAsyncResult);
                }

                if ((this.currentOffset == this.Length) || (count == 0))
                {
                    storageAsyncResult.Result = 0;
                    storageAsyncResult.OnComplete();
                    return(storageAsyncResult);
                }

                int readCount = this.ConsumeBuffer(buffer, offset, count);
                if (readCount > 0)
                {
                    storageAsyncResult.Result = readCount;
                    storageAsyncResult.OnComplete();
                    return(storageAsyncResult);
                }

                this.DispatchReadAsync(storageAsyncResult, buffer, offset, count);
                return(storageAsyncResult);
            }
            catch (Exception)
            {
                this.readPending = false;
                throw;
            }
        }
Example #10
0
        /// <summary>
        /// Writes a block of bytes to the current stream using data read from a buffer.
        /// </summary>
        /// <param name="buffer">The buffer to write data from.</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
        /// <param name="count">The number of bytes to write. </param>
        public override void Write(byte[] buffer, int offset, int count)
        {
            CommonUtility.AssertNotNull("buffer", buffer);
            CommonUtility.AssertInBounds("offset", offset, 0, buffer.Length);
            CommonUtility.AssertInBounds("count", count, 0, buffer.Length - offset);

            // Grow the buffer if more space is needed
            if (this.position + count > this.capacity)
            {
                this.Reserve(this.position + count);
            }

            this.WriteInternal(buffer, offset, count);

            // Adjust the length to be the max of currently written data.
            this.length = Math.Max(this.length, this.position);
        }
Example #11
0
        /// <summary>
        /// Initializes a new instance of the BlobReadStreamBase class.
        /// </summary>
        /// <param name="blob">Blob reference to read from</param>
        /// <param name="accessCondition">An <see cref="AccessCondition"/> object that represents the condition that must be met in order for the request to proceed. If <c>null</c>, no condition is used.</param>
        /// <param name="options">A <see cref="BlobRequestOptions"/> object that specifies additional options for the request.</param>
        /// <param name="operationContext">An <see cref="OperationContext"/> object that represents the context for the current operation.</param>
        protected BlobReadStreamBase(CloudBlob blob, AccessCondition accessCondition, BlobRequestOptions options, OperationContext operationContext)
        {
            if (options.UseTransactionalMD5.Value)
            {
                CommonUtility.AssertInBounds("StreamMinimumReadSizeInBytes", blob.StreamMinimumReadSizeInBytes, 1, Constants.MaxRangeGetContentMD5Size);
            }

            this.blob           = blob;
            this.blobProperties = new BlobProperties(blob.Properties);
            this.currentOffset  = 0;
            this.streamMinimumReadSizeInBytes = this.blob.StreamMinimumReadSizeInBytes;
            this.internalBuffer   = new MultiBufferMemoryStream(blob.ServiceClient.BufferManager);
            this.accessCondition  = accessCondition;
            this.options          = options;
            this.operationContext = operationContext;
            this.blobMD5          = (this.options.DisableContentMD5Validation.Value || string.IsNullOrEmpty(this.blobProperties.ContentMD5)) ? null : new MD5Wrapper();
            this.lastException    = null;
        }
        /// <summary>
        /// If the rangeSizeInBytes has a value, validate it.
        /// Otherwise set the rangeSizeInBytes to the appropriate default vlaue.
        /// </summary>
        /// <param name="useTransactionalMD5">Indicates if transactional MD5 validation is to be used.</param>
        /// <param name="useTransactionalCRC64">Indicates if transactional CRC64 validation is to be used.</param>
        /// <param name="rangeSizeInBytes">The range size in bytes to be used for each download operation
        /// or null to use the default value.</param>
        /// <returns>The rangeSizeInBytes value that was passed in if not null, otherwise the appropriate default value.</returns>
        private long ValidateOrGetRangeSize(bool useTransactionalMD5, bool useTransactionalCRC64, long?rangeSizeInBytes)
        {
            if (rangeSizeInBytes.HasValue)
            {
                CommonUtility.AssertInBounds("rangeSizeInBytes", rangeSizeInBytes.Value, Constants.MaxRangeGetContentMD5Size);

                //if (useTransactionalMD5)
                //{
                //    CommonUtility.AssertInBounds("rangeSizeInBytes", rangeSizeInBytes.Value, Constants.MaxRangeGetContentMD5Size);
                //}

                //if (useTransactionalCRC64)
                //{
                //    CommonUtility.AssertInBounds("rangeSizeInBytes", rangeSizeInBytes.Value, Constants.MaxRangeGetContentCRC64Size);
                //}

                if (useTransactionalMD5 && rangeSizeInBytes.Value != Constants.MaxRangeGetContentMD5Size)
                {
                    throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, SR.RangeSizeIsInvalidMD5, rangeSizeInBytes, Constants.MaxRangeGetContentMD5Size));
                }
                else if (useTransactionalCRC64 && rangeSizeInBytes.Value != Constants.MaxRangeGetContentCRC64Size)
                {
                    throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, SR.RangeSizeIsInvalidCRC64, rangeSizeInBytes, Constants.MaxRangeGetContentCRC64Size));
                }
                else if (rangeSizeInBytes % (4 * Constants.KB) != 0)
                {
                    throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, SR.RangeSizeIsInvalid, rangeSizeInBytes, Constants.MaxRangeGetContentMD5Size));
                }
            }
            else if (useTransactionalMD5)
            {
                rangeSizeInBytes = Constants.MaxRangeGetContentMD5Size;
            }
            else if (useTransactionalCRC64)
            {
                rangeSizeInBytes = Constants.MaxRangeGetContentCRC64Size;
            }
            else
            {
                rangeSizeInBytes = Constants.DefaultParallelDownloadRangeSizeBytes;
            }

            return(rangeSizeInBytes.Value);
        }
Example #13
0
        /// <summary>
        /// Calculates the new position within the current stream for a Seek operation.
        /// </summary>
        /// <param name="offset">A byte offset relative to the origin parameter.</param>
        /// <param name="origin">A value of type <c>SeekOrigin</c> indicating the reference
        /// point used to obtain the new position.</param>
        /// <returns>The new position within the current stream.</returns>
        protected long GetNewOffset(long offset, SeekOrigin origin)
        {
            if (!this.CanSeek)
            {
                throw new NotSupportedException();
            }

            if (this.lastException != null)
            {
                throw this.lastException;
            }

            long newOffset;

            switch (origin)
            {
            case SeekOrigin.Begin:
                newOffset = offset;
                break;

            case SeekOrigin.Current:
                newOffset = this.currentOffset + offset;
                break;

            case SeekOrigin.End:
                newOffset = this.Length + offset;
                break;

            default:
                CommonUtility.ArgumentOutOfRange("origin", origin);
                throw new ArgumentOutOfRangeException("origin");
            }

            CommonUtility.AssertInBounds("offset", newOffset, 0, this.Length);

            if ((newOffset % Constants.PageSize) != 0)
            {
                CommonUtility.ArgumentOutOfRange("offset", offset);
            }

            return(newOffset);
        }
        /// <summary>
        /// Asynchronously writes a sequence of bytes to the current stream, advances the current
        /// position within this stream by the number of bytes written, and monitors cancellation requests.
        /// </summary>
        /// <param name="buffer">An array of bytes. This method copies count bytes from
        /// buffer to the current stream.</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin
        /// copying bytes to the current stream.</param>
        /// <param name="count">The number of bytes to be written to the current stream.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
        /// <returns>A task that represents the asynchronous write operation.</returns>
        public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            CommonUtility.AssertNotNull("buffer", buffer);
            CommonUtility.AssertInBounds("offset", offset, 0, buffer.Length);
            CommonUtility.AssertInBounds("count", count, 0, buffer.Length - offset);

            if (this.lastException != null)
            {
                throw this.lastException;
            }

            if (this.committed)
            {
                throw new InvalidOperationException(SR.BlobStreamAlreadyCommitted);
            }

            if (this.blobMD5 != null)
            {
                this.blobMD5.UpdateHash(buffer, offset, count);
            }

            this.currentOffset += count;
            while (count > 0)
            {
                int maxBytesToWrite = this.streamWriteSizeInBytes - (int)this.internalBuffer.Length;
                int bytesToWrite    = Math.Min(count, maxBytesToWrite);

                this.internalBuffer.Write(buffer, offset, bytesToWrite);
                if (this.blockMD5 != null)
                {
                    this.blockMD5.UpdateHash(buffer, offset, bytesToWrite);
                }

                count  -= bytesToWrite;
                offset += bytesToWrite;

                if (bytesToWrite == maxBytesToWrite)
                {
                    await this.DispatchWriteAsync();
                }
            }
        }
Example #15
0
        /// <summary>
        /// Begins an asynchronous write operation.
        /// </summary>
        /// <param name="buffer">The buffer to write data from.</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
        /// <param name="count">The number of bytes to write.</param>
        /// <param name="callback">An optional asynchronous callback, to be called when the write is complete.</param>
        /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests.</param>
        /// <returns>An IAsyncResult that represents the asynchronous write, which could still be pending.</returns>
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            CommonUtility.AssertNotNull("buffer", buffer);
            CommonUtility.AssertInBounds("offset", offset, 0, buffer.Length);
            CommonUtility.AssertInBounds("count", count, 0, buffer.Length - offset);

            StorageAsyncResult <NullType> result = new StorageAsyncResult <NullType>(callback, state);

            try
            {
                this.Write(buffer, offset, count);
                result.OnComplete();
            }
            catch (Exception e)
            {
                result.OnComplete(e);
            }

            return(result);
        }
        /// <summary>
        /// Adds the Range Header for Blob Service Operations.
        /// </summary>
        /// <param name="request">Request</param>
        /// <param name="offset">Starting byte of the range</param>
        /// <param name="count">Number of bytes in the range</param>
        private static void AddRange(HttpRequestMessage request, long?offset, long?count)
        {
            if (count.HasValue)
            {
                CommonUtility.AssertNotNull("offset", offset);
                CommonUtility.AssertInBounds("count", count.Value, 1, long.MaxValue);
            }

            if (offset.HasValue)
            {
                string rangeStart = offset.ToString();
                string rangeEnd   = string.Empty;
                if (count.HasValue)
                {
                    rangeEnd = (offset + count.Value - 1).ToString();
                }

                string rangeHeaderValue = string.Format(CultureInfo.InvariantCulture, Constants.HeaderConstants.RangeHeaderFormat, rangeStart, rangeEnd);
                request.Headers.Add(Constants.HeaderConstants.RangeHeader, rangeHeaderValue);
            }
        }
        /// <summary>
        /// Generates a <see cref="RESTCommand{T}" /> for acquiring a lease.
        /// </summary>
        /// <param name="blob">The blob.</param>
        /// <param name="attributes">The attributes.</param>
        /// <param name="leaseTime">A <see cref="TimeSpan" /> representing the span of time for which to acquire the lease,
        /// which will be rounded down to seconds. If null, an infinite lease will be acquired. If not null, this must be
        /// greater than zero.</param>
        /// <param name="proposedLeaseId">A string representing the proposed lease ID for the new lease, or null if no lease ID is proposed.</param>
        /// <param name="accessCondition">An object that represents the access conditions for the blob. If null, no condition is used.</param>
        /// <param name="options">An object that specifies additional options for the request.</param>
        /// <returns>
        /// A <see cref="RESTCommand{T}" /> implementing the acquire lease operation.
        /// </returns>
        internal static RESTCommand <string> AcquireLeaseImpl(ICloudBlob blob, BlobAttributes attributes, TimeSpan?leaseTime, string proposedLeaseId, AccessCondition accessCondition, BlobRequestOptions options)
        {
            int leaseDuration = -1;

            if (leaseTime.HasValue)
            {
                CommonUtility.AssertInBounds("leaseTime", leaseTime.Value, TimeSpan.FromSeconds(1), TimeSpan.MaxValue);
                leaseDuration = (int)leaseTime.Value.TotalSeconds;
            }

            RESTCommand <string> putCmd = new RESTCommand <string>(blob.ServiceClient.Credentials, attributes.Uri);

            putCmd.ApplyRequestOptions(options);
            putCmd.BuildRequestDelegate = (uri, builder, serverTimeout, ctx) => BlobHttpWebRequestFactory.Lease(uri, serverTimeout, LeaseAction.Acquire, proposedLeaseId, leaseDuration, null /* leaseBreakPeriod */, accessCondition, ctx);
            putCmd.SignRequest          = blob.ServiceClient.AuthenticationHandler.SignRequest;
            putCmd.PreProcessResponse   = (cmd, resp, ex, ctx) =>
            {
                HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.Created, resp, null, cmd, ex);
                return(BlobHttpResponseParsers.GetLeaseId(resp));
            };

            return(putCmd);
        }
        /// <summary>
        /// Constructs a web request to return a specified range of the file's content, together with its properties and metadata.
        /// </summary>
        /// <param name="uri">The absolute URI to the file.</param>
        /// <param name="timeout">The server timeout interval, in seconds.</param>
        /// <param name="offset">The byte offset at which to begin returning content.</param>
        /// <param name="count">The number of bytes to return, or null to return all bytes through the end of the file.</param>
        /// <param name="shareSnapshot">A <see cref="DateTimeOffset"/> specifying the share snapshot timestamp, if the share is a snapshot.</param>
        /// <param name="accessCondition">The access condition to apply to the request.</param>
        /// <returns>A web request to use to perform the operation.</returns>
        public static StorageRequestMessage Get(Uri uri, int?timeout, long?offset, long?count, bool rangeContentMD5, DateTimeOffset?shareSnapshot, AccessCondition accessCondition, HttpContent content, OperationContext operationContext, ICanonicalizer canonicalizer, StorageCredentials credentials)
        {
            if (offset.HasValue && offset.Value < 0)
            {
                CommonUtility.ArgumentOutOfRange("offset", offset);
            }

            if (offset.HasValue && rangeContentMD5)
            {
                CommonUtility.AssertNotNull("count", count);
                CommonUtility.AssertInBounds("count", count.Value, 1, Constants.MaxRangeGetContentMD5Size);
            }

            StorageRequestMessage request = Get(uri, timeout, shareSnapshot, accessCondition, content, operationContext, canonicalizer, credentials);

            AddRange(request, offset, count);

            if (offset.HasValue && rangeContentMD5)
            {
                request.Headers.Add(Constants.HeaderConstants.RangeContentMD5Header, Constants.HeaderConstants.TrueHeader);
            }

            return(request);
        }
Example #19
0
        /// <summary>
        /// Asynchronously reads a sequence of bytes from the current stream, advances the
        /// position within the stream by the number of bytes read, and monitors cancellation requests.
        /// </summary>
        /// <remarks>In the returned <see cref="Task{TElement}"/> object, the value of the integer
        /// parameter contains the total number of bytes read into the buffer. The result value can be
        /// less than the number of bytes requested if the number of bytes currently available is less
        /// than the requested number, or it can be 0 (zero) if the end of the stream has been reached.</remarks>
        /// <param name="buffer">The buffer to read the data into.</param>
        /// <param name="offset">The byte offset in buffer at which to begin writing
        /// data read from the stream.</param>
        /// <param name="count">The maximum number of bytes to read.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
        /// <returns>A task that represents the asynchronous read operation.</returns>
        public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            CommonUtility.AssertNotNull("buffer", buffer);
            CommonUtility.AssertInBounds("offset", offset, 0, buffer.Length);
            CommonUtility.AssertInBounds("count", count, 0, buffer.Length - offset);

            if (this.lastException != null)
            {
                throw this.lastException;
            }

            if ((this.currentOffset == this.Length) || (count == 0))
            {
                return 0;
            }

            int readCount = this.ConsumeBuffer(buffer, offset, count);
            if (readCount > 0)
            {
                return readCount;
            }

            return await this.DispatchReadAsync(buffer, offset, count);
        }
        /// <summary>
        /// Constructs a web request to return a specified range of the blob's content, together with its properties and metadata.
        /// </summary>
        /// <param name="uri">The absolute URI to the blob.</param>
        /// <param name="timeout">The server timeout interval, in seconds.</param>
        /// <param name="snapshot">The snapshot version, if the blob is a snapshot.</param>
        /// <param name="offset">The byte offset at which to begin returning content.</param>
        /// <param name="count">The number of bytes to return, or null to return all bytes through the end of the blob.</param>
        /// <param name="accessCondition">The access condition to apply to the request.</param>
        /// <returns>A web request to use to perform the operation.</returns>
        public static HttpRequestMessage Get(Uri uri, int?timeout, DateTimeOffset?snapshot, long?offset, long?count, bool rangeContentMD5, AccessCondition accessCondition, HttpContent content, OperationContext operationContext)
        {
            if (offset.HasValue && offset.Value < 0)
            {
                CommonUtility.ArgumentOutOfRange("offset", offset);
            }

            if (offset.HasValue && rangeContentMD5)
            {
                CommonUtility.AssertNotNull("count", count);
                CommonUtility.AssertInBounds("count", count.Value, 1, Constants.MaxBlockSize);
            }

            HttpRequestMessage request = Get(uri, timeout, snapshot, accessCondition, content, operationContext);

            AddRange(request, offset, count);

            if (offset.HasValue && rangeContentMD5)
            {
                request.Headers.Add(Constants.HeaderConstants.RangeContentMD5Header, Constants.HeaderConstants.TrueHeader);
            }

            return(request);
        }
Example #21
0
        /// <summary>
        /// Constructs a web request to return a specified range of the file's content, together with its properties and metadata.
        /// </summary>
        /// <param name="uri">The absolute URI to the file.</param>
        /// <param name="timeout">The server timeout interval, in seconds.</param>
        /// <param name="offset">The byte offset at which to begin returning content.</param>
        /// <param name="count">The number of bytes to return, or null to return all bytes through the end of the file.</param>
        /// <param name="rangeContentMD5">If set to <c>true</c>, request an MD5 header for the specified range.</param>
        /// <param name="shareSnapshot">A <see cref="DateTimeOffset"/> specifying the share snapshot timestamp, if the share is a snapshot.</param>
        /// <param name="accessCondition">The access condition to apply to the request.</param>
        /// <param name="useVersionHeader">A flag indicating whether to set the x-ms-version HTTP header.</param>
        /// <param name="operationContext">An <see cref="OperationContext" /> object for tracking the current operation.</param>
        /// <returns>A web request to use to perform the operation.</returns>
        internal static HttpWebRequest Get(Uri uri, int?timeout, long?offset, long?count, bool rangeContentMD5, DateTimeOffset?shareSnapshot, AccessCondition accessCondition, bool useVersionHeader, OperationContext operationContext)
        {
            if (offset.HasValue && offset.Value < 0)
            {
                CommonUtility.ArgumentOutOfRange("offset", offset);
            }

            if (offset.HasValue && rangeContentMD5)
            {
                CommonUtility.AssertNotNull("count", count);
                CommonUtility.AssertInBounds("count", count.Value, 1, Constants.MaxRangeGetContentMD5Size);
            }

            HttpWebRequest request = Get(uri, timeout, shareSnapshot, accessCondition, useVersionHeader, operationContext);

            AddRange(request, offset, count);

            if (offset.HasValue && rangeContentMD5)
            {
                request.Headers.Add(Constants.HeaderConstants.RangeContentMD5Header, Constants.HeaderConstants.TrueHeader);
            }

            return(request);
        }
Example #22
0
        /// <summary>
        /// Asynchronously Writes a sequence of bytes to the current stream and advances the current
        /// position within this stream by the number of bytes written.
        /// </summary>
        /// <param name="buffer">An array of bytes. This method copies count bytes from
        /// buffer to the current stream. </param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin
        /// copying bytes to the current stream.</param>
        /// <param name="count">The number of bytes to be written to the current stream.</param>
        /// <param name="token">Cancellation token</param>
        /// <returns>Task</returns>
        public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token)
        {
            CommonUtility.AssertNotNull("buffer", buffer);
            CommonUtility.AssertInBounds("offset", offset, 0, buffer.Length);
            CommonUtility.AssertInBounds("count", count, 0, buffer.Length - offset);

            if (this.committed)
            {
                throw new InvalidOperationException(SR.BlobStreamAlreadyCommitted);
            }

            this.currentOffset += count;
            int initialOffset = offset;
            int initialCount  = count;

            TaskCompletionSource <bool> continueTCS = new TaskCompletionSource <bool>();
            Task <bool> continueTask = continueTCS.Task;

            if (this.lastException == null)
            {
                while (count > 0)
                {
                    int maxBytesToWrite = this.streamWriteSizeInBytes - (int)this.internalBuffer.Length;
                    int bytesToWrite    = Math.Min(count, maxBytesToWrite);

                    await this.internalBuffer.WriteAsync(buffer, offset, bytesToWrite, token).ConfigureAwait(false);

                    if (this.blockChecksum != null)
                    {
                        this.blockChecksum.UpdateHash(buffer, offset, bytesToWrite);
                    }

                    count  -= bytesToWrite;
                    offset += bytesToWrite;

                    if (bytesToWrite == maxBytesToWrite)
                    {
                        // Note that we do not await on temptask, nor do we store it.
                        // We do not await temptask so as to enable parallel reads and writes.
                        // We could store it and await on it later, but that ends up being more complicated
                        // than what we actually do, which is have each write operation manage its own exceptions.
                        Task temptask = this.DispatchWriteAsync(continueTCS, token);

                        // We need to account for the fact that we're not awaiting on DispatchWriteAsync.
                        // DispatchWriteAsync is written in such a manner that any exceptions thrown after
                        // the first await point are handled internally.  This here is to account for
                        // exceptions that could happen inline, before an await point is encountered.
                        if (temptask.IsFaulted)
                        {
                            //We should make sure any exception thrown before the awaiting point in DispatchWriteAsync are stored in this.LastException
                            //We don't want to throw the tempTask.Exception directly since that would result in an aggregate exception
                            ThrowLastExceptionIfExists();
                        }

                        token.ThrowIfCancellationRequested();
                        continueTCS = null;
                    }
                }
            }

            // Update transactional, then update full blob, in that order.
            // This way, if there's any bit corruption that happens in between the two, we detect it at PutBlock on the service,
            // rather than GetBlob + validate on the client
            if (this.blobChecksum != null)
            {
                this.blobChecksum.UpdateHash(buffer, initialOffset, initialCount);
            }

            if (continueTCS == null)
            {
                await continueTask.ConfigureAwait(false);
            }
        }
 public AsyncSemaphore(int initialCount)
 {
     CommonUtility.AssertInBounds("initialCount", initialCount, 0, int.MaxValue);
     this.count = initialCount;
 }
        private async Task StartAsync(FileMode fileMode, int parallelIOCount, long?rangeSizeInBytes)
        {
            CommonUtility.AssertInBounds("parallelIOCount", parallelIOCount, 1);
            bool useTransactionalMD5 = this.blobRequestOptions != null && this.blobRequestOptions.UseTransactionalMD5.HasValue && this.blobRequestOptions.UseTransactionalMD5.Value;

            rangeSizeInBytes = this.ValidateOrGetRangeSize(useTransactionalMD5, rangeSizeInBytes);

            // always do a head request to have an ETag to lock-on to.
            // this code is designed for only large blobs so this request should be neglibile on perf
            await this.Blob.FetchAttributesAsync(this.accessCondition, this.blobRequestOptions, this.operationContext).ConfigureAwait(false);

            if (this.accessCondition == null)
            {
                this.accessCondition = new AccessCondition();
            }

            // it is ok to overwrite the user's IfMatchETag if they had provided one because the HEAD request would have failed
            this.accessCondition.IfMatchETag = this.Blob.Properties.ETag;

            long maxPossibleLength = this.Blob.Properties.Length - this.Offset;

            if (!this.Length.HasValue)
            {
                this.Length = maxPossibleLength;
            }
            else
            {
                this.Length = Math.Min(this.Length.Value, maxPossibleLength);
            }

            // if downloading a zero length blob, just create the file and return
            if (this.Offset == 0 && this.Length.Value == 0)
            {
                File.Create(this.FilePath).Close();
                return;
            }

            // zero size ranges are not allowed except when downloading an empty blob is allowed
            CommonUtility.AssertInBounds("length", this.Length.Value, 1, long.MaxValue);

            int totalIOReadCalls = (int)Math.Ceiling((double)this.Length.Value / (double)rangeSizeInBytes);

            // used to keep track of current ranges being downloaded.
            List <Task> downloadTaskList = new List <Task>();

            // used to dispose of each MemoryMappedViewStream when that range has completed.
            Dictionary <Task, MemoryMappedViewStream> taskToStream = new Dictionary <Task, MemoryMappedViewStream>();

            using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(this.FilePath, fileMode, null, this.Length.Value))
            {
                try
                {
                    for (int i = 0; i < totalIOReadCalls; i++)
                    {
                        if (downloadTaskList.Count >= parallelIOCount)
                        {
                            // The number of on-going I/O operations has reached its maximum, wait until one completes or has an error.
                            Task downloadRangeTask = await Task.WhenAny(downloadTaskList).ConfigureAwait(false);

                            // The await on WhenAny does not await on the download task itself, hence exceptions must be repropagated.
                            await downloadRangeTask.ConfigureAwait(false);

                            taskToStream[downloadRangeTask].Dispose();
                            taskToStream.Remove(downloadRangeTask);
                            downloadTaskList.Remove(downloadRangeTask);
                        }

                        long streamBeginIndex = i * rangeSizeInBytes.Value;
                        long streamReadSize   = rangeSizeInBytes.Value;

                        // last range may be smaller than the range size
                        if (i == totalIOReadCalls - 1)
                        {
                            streamReadSize = this.Length.Value - i * streamReadSize;
                        }

                        MemoryMappedViewStream viewStream = mmf.CreateViewStream(streamBeginIndex, streamReadSize);
                        Task downloadTask = this.DownloadToStreamWrapperAsync(
                            viewStream,
                            this.Offset + streamBeginIndex, streamReadSize,
                            this.accessCondition,
                            this.blobRequestOptions,
                            this.operationContext,
                            cancellationToken);

                        taskToStream.Add(downloadTask, viewStream);
                        downloadTaskList.Add(downloadTask);
                    }

                    while (downloadTaskList.Count > 0)
                    {
                        // All requests to download the blob have gone out, wait until one completes or has an error.
                        Task downloadRangeTask = await Task.WhenAny(downloadTaskList).ConfigureAwait(false);

                        // The await on WhenAny does not await on the download task itself, hence exceptions must be repropagated.
                        await downloadRangeTask.ConfigureAwait(false);

                        taskToStream[downloadRangeTask].Dispose();
                        taskToStream.Remove(downloadRangeTask);
                        downloadTaskList.Remove(downloadRangeTask);
                    }
                }
                finally
                {
                    foreach (KeyValuePair <Task, MemoryMappedViewStream> taskToStreamPair in taskToStream)
                    {
                        taskToStreamPair.Value.Dispose();
                    }
                }
            }
        }