/// <summary> /// Implements the FetchAttributes method. The attributes are updated immediately. /// </summary> /// <param name="blobUri">The URI of the blob.</param> /// <param name="accessCondition">An <see cref="AccessCondition"/> object that represents the access conditions for the blob. If <c>null</c>, no condition is used.</param> /// <param name="options">A <see cref="BlobRequestOptions"/> object that specifies any additional options for the request.</param> /// <returns>A <see cref="RESTCommand"/> that fetches the attributes.</returns> private RESTCommand <ICloudBlob> GetBlobReferenceImpl(Uri blobUri, AccessCondition accessCondition, BlobRequestOptions options) { // If the blob Uri contains SAS credentials, we need to use those // credentials instead of this service client's stored credentials. StorageCredentials parsedCredentials; DateTimeOffset? parsedSnapshot; blobUri = NavigationHelper.ParseBlobQueryAndVerify(blobUri, out parsedCredentials, out parsedSnapshot); CloudBlobClient client = parsedCredentials != null ? new CloudBlobClient(this.BaseUri, parsedCredentials) : this; RESTCommand <ICloudBlob> getCmd = new RESTCommand <ICloudBlob>(client.Credentials, blobUri); getCmd.ApplyRequestOptions(options); getCmd.Handler = client.AuthenticationHandler; getCmd.BuildClient = HttpClientFactory.BuildHttpClient; getCmd.BuildRequest = (cmd, cnt, ctx) => BlobHttpRequestMessageFactory.GetProperties(cmd.Uri, cmd.ServerTimeoutInSeconds, parsedSnapshot, accessCondition, cnt, ctx); getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, null /* retVal */, cmd, ex, ctx); BlobAttributes attributes = new BlobAttributes() { Uri = blobUri, SnapshotTime = parsedSnapshot, }; CloudBlobSharedImpl.UpdateAfterFetchAttributes(attributes, resp, false); switch (attributes.Properties.BlobType) { case BlobType.BlockBlob: return(new CloudBlockBlob(attributes, client)); case BlobType.PageBlob: return(new CloudPageBlob(attributes, client)); default: throw new InvalidOperationException(); } }; return(getCmd); }
/// <summary> /// Updates this blob with the given attributes a the end of a fetch attributes operation. /// </summary> /// <param name="attributes">The new attributes.</param> /// <param name="response">The response.</param> /// <param name="ignoreMD5">if set to <c>true</c>, blob's MD5 will not be updated.</param> /// <exception cref="System.InvalidOperationException"></exception> internal static void UpdateAfterFetchAttributes(BlobAttributes attributes, HttpWebResponse response, bool ignoreMD5) { BlobProperties properties = BlobHttpResponseParsers.GetProperties(response); // If BlobType is specified and the value returned from cloud is different, // then it's a client error and we need to throw. if (attributes.Properties.BlobType != BlobType.Unspecified && attributes.Properties.BlobType != properties.BlobType) { throw new InvalidOperationException(SR.BlobTypeMismatchExceptionMessage); } if (ignoreMD5) { properties.ContentMD5 = attributes.Properties.ContentMD5; } attributes.Properties = properties; attributes.Metadata = BlobHttpResponseParsers.GetMetadata(response); attributes.CopyState = BlobHttpResponseParsers.GetCopyAttributes(response); }
/// <summary> /// Implementation for the Exists method. /// </summary> /// <param name="blob">The blob.</param> /// <param name="attributes">The attributes.</param> /// <param name="options">An object that specifies additional options for the request.</param> /// <returns> /// A <see cref="RESTCommand{T}" /> that checks existence. /// </returns> internal static RESTCommand <bool> ExistsImpl(ICloudBlob blob, BlobAttributes attributes, BlobRequestOptions options) { RESTCommand <bool> getCmd = new RESTCommand <bool>(blob.ServiceClient.Credentials, attributes.Uri); getCmd.ApplyRequestOptions(options); getCmd.BuildRequestDelegate = (uri, builder, serverTimeout, ctx) => BlobHttpWebRequestFactory.GetProperties(uri, serverTimeout, attributes.SnapshotTime, null /* accessCondition */, ctx); getCmd.SignRequest = blob.ServiceClient.AuthenticationHandler.SignRequest; getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { if (resp.StatusCode == HttpStatusCode.NotFound) { return(false); } HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, true, cmd, ex); CloudBlobSharedImpl.UpdateAfterFetchAttributes(attributes, resp, false); return(true); }; return(getCmd); }
/// <summary> /// Implementation for the SetProperties method. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</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"/> that sets the metadata.</returns> internal static RESTCommand <NullType> SetPropertiesImpl(ICloudBlob blob, BlobAttributes attributes, AccessCondition accessCondition, BlobRequestOptions options) { RESTCommand <NullType> putCmd = new RESTCommand <NullType>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.Handler = blob.ServiceClient.AuthenticationHandler; putCmd.BuildClient = HttpClientFactory.BuildHttpClient; putCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => { HttpRequestMessage msg = BlobHttpRequestMessageFactory.SetProperties(uri, serverTimeout, attributes.Properties, accessCondition, cnt, ctx); BlobHttpRequestMessageFactory.AddMetadata(msg, attributes.Metadata); return(msg); }; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, NullType.Value, cmd, ex); CloudBlobSharedImpl.UpdateETagLMTAndSequenceNumber(attributes, resp); return(NullType.Value); }; return(putCmd); }
/// <summary> /// Generates a <see cref="RESTCommand{T}"/> for releasing a lease. /// </summary> /// <param name="blob">The blob.</param> /// <param name="attributes">The attributes.</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> /// <returns> /// A <see cref="RESTCommand{T}"/> implementing the release lease operation. /// </returns> /// <exception cref="System.ArgumentException">accessCondition</exception> internal static RESTCommand <NullType> ReleaseLeaseImpl(ICloudBlob blob, BlobAttributes attributes, AccessCondition accessCondition, BlobRequestOptions options) { CommonUtility.AssertNotNull("accessCondition", accessCondition); if (accessCondition.LeaseId == null) { throw new ArgumentException(SR.MissingLeaseIDReleasing, "accessCondition"); } RESTCommand <NullType> putCmd = new RESTCommand <NullType>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, ctx) => BlobHttpWebRequestFactory.Lease(uri, serverTimeout, LeaseAction.Release, null /* proposedLeaseId */, null /* leaseDuration */, null /* leaseBreakPeriod */, accessCondition, useVersionHeader, ctx); putCmd.SignRequest = blob.ServiceClient.AuthenticationHandler.SignRequest; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, NullType.Value, cmd, ex); CloudBlobSharedImpl.UpdateETagLMTLengthAndSequenceNumber(attributes, resp, false); return(NullType.Value); }; return(putCmd); }
/// <summary> /// Implementation for the SetProperties method. /// </summary> /// <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 any additional options for the request.</param> /// <returns>A <see cref="RESTCommand"/> that sets the metadata.</returns> internal static RESTCommand <NullType> SetPropertiesImpl(ICloudBlob blob, BlobAttributes attributes, AccessCondition accessCondition, BlobRequestOptions options) { RESTCommand <NullType> putCmd = new RESTCommand <NullType>(blob.ServiceClient.Credentials, attributes.Uri); putCmd.ApplyRequestOptions(options); putCmd.Handler = blob.ServiceClient.AuthenticationHandler; putCmd.BuildClient = HttpClientFactory.BuildHttpClient; putCmd.BuildRequest = (cmd, cnt, ctx) => { HttpRequestMessage msg = BlobHttpRequestMessageFactory.SetProperties(cmd.Uri, cmd.ServerTimeoutInSeconds, attributes.Properties, accessCondition, cnt, ctx); BlobHttpRequestMessageFactory.AddMetadata(msg, attributes.Metadata); return(msg); }; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, NullType.Value, cmd, ex, ctx); CloudBlobSharedImpl.ParseSizeAndLastModified(attributes, resp); return(NullType.Value); }; return(putCmd); }
/// <summary> /// Generates a <see cref="RESTCommand{T}" /> for changing a lease ID. /// </summary> /// <param name="blob">The blob.</param> /// <param name="attributes">The attributes.</param> /// <param name="proposedLeaseId">The proposed new lease ID.</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 any additional options for the request.</param> /// <returns> /// A <see cref="RESTCommand{T}" /> implementing the change lease ID operation. /// </returns> /// <exception cref="System.ArgumentException">accessCondition</exception> internal static RESTCommand <string> ChangeLeaseImpl(ICloudBlob blob, BlobAttributes attributes, string proposedLeaseId, AccessCondition accessCondition, BlobRequestOptions options) { CommonUtils.AssertNotNull("accessCondition", accessCondition); CommonUtils.AssertNotNull("proposedLeaseId", proposedLeaseId); if (accessCondition.LeaseId == null) { throw new ArgumentException(SR.MissingLeaseIDChanging, "accessCondition"); } 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.Change, proposedLeaseId, null /* leaseDuration */, null /* leaseBreakPeriod */, accessCondition, ctx); putCmd.SignRequest = blob.ServiceClient.AuthenticationHandler.SignRequest; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, null /* retVal */, cmd, ex, ctx); return(BlobHttpResponseParsers.GetLeaseId(resp)); }; return(putCmd); }
/// <summary> /// Implements the Exists method. The attributes are updated immediately. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</param> /// <param name="options">An object that specifies additional options for the request.</param> /// <param name="primaryOnly">If <c>true</c>, the command will be executed against the primary location.</param> /// <returns>A <see cref="RESTCommand"/> that checks existence.</returns> internal static RESTCommand <bool> ExistsImpl(ICloudBlob blob, BlobAttributes attributes, BlobRequestOptions options, bool primaryOnly) { RESTCommand <bool> getCmd = new RESTCommand <bool>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(getCmd); getCmd.CommandLocationMode = primaryOnly ? CommandLocationMode.PrimaryOnly : CommandLocationMode.PrimaryOrSecondary; getCmd.Handler = blob.ServiceClient.AuthenticationHandler; getCmd.BuildClient = HttpClientFactory.BuildHttpClient; getCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => BlobHttpRequestMessageFactory.GetProperties(uri, serverTimeout, attributes.SnapshotTime, null /* accessCondition */, cnt, ctx); getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { if (resp.StatusCode == HttpStatusCode.NotFound) { return(false); } HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, true, cmd, ex); CloudBlobSharedImpl.UpdateAfterFetchAttributes(attributes, resp, false); return(true); }; return(getCmd); }
/// <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 any 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) { CommonUtils.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, ctx); return(BlobHttpResponseParsers.GetLeaseId(resp)); }; return(putCmd); }
/// <summary> /// Generates a <see cref="RESTCommand"/> for changing a lease ID. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</param> /// <param name="proposedLeaseId">The proposed new lease ID.</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"/> implementing the change lease ID operation.</returns> internal static RESTCommand <string> ChangeLeaseImpl(ICloudBlob blob, BlobAttributes attributes, string proposedLeaseId, AccessCondition accessCondition, BlobRequestOptions options) { CommonUtility.AssertNotNull("accessCondition", accessCondition); CommonUtility.AssertNotNull("proposedLeaseId", proposedLeaseId); if (accessCondition.LeaseId == null) { throw new ArgumentException(SR.MissingLeaseIDChanging, "accessCondition"); } RESTCommand <string> putCmd = new RESTCommand <string>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.Handler = blob.ServiceClient.AuthenticationHandler; putCmd.BuildClient = HttpClientFactory.BuildHttpClient; putCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => BlobHttpRequestMessageFactory.Lease(uri, serverTimeout, LeaseAction.Change, proposedLeaseId, null /* leaseDuration */, null /* leaseBreakPeriod */, accessCondition, cnt, ctx); putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, null /* retVal */, cmd, ex); CloudBlobSharedImpl.UpdateETagLMTAndSequenceNumber(attributes, resp); return(BlobHttpResponseParsers.GetLeaseId(resp)); }; return(putCmd); }
/// <summary> /// Generates a <see cref="RESTCommand{T}"/> for acquiring a lease. /// </summary> /// <param name="blobAttributes">The attributes.</param> /// <param name="leaseTime">A <see cref="System.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 <c>null</c> if no lease ID is proposed.</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> /// <returns> /// A <see cref="RESTCommand{T}"/> implementing the acquire lease operation. /// </returns> private RESTCommand<string> AcquireLeaseImpl(BlobAttributes blobAttributes, 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>(this.ServiceClient.Credentials, blobAttributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, ctx) => BlobHttpWebRequestFactory.Lease(uri, serverTimeout, LeaseAction.Acquire, proposedLeaseId, leaseDuration, null /* leaseBreakPeriod */, accessCondition, useVersionHeader, ctx); putCmd.SignRequest = this.ServiceClient.AuthenticationHandler.SignRequest; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.Created, resp, null, cmd, ex); CloudBlob.UpdateETagLMTLengthAndSequenceNumber(blobAttributes, resp, false); return BlobHttpResponseParsers.GetLeaseId(resp); }; return putCmd; }
/// <summary> /// Implementation for the SetProperties method. /// </summary> /// <param name="blobAttributes">The attributes.</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> /// <returns> /// A <see cref="RESTCommand{T}"/> that sets the properties. /// </returns> private RESTCommand<NullType> SetPropertiesImpl(BlobAttributes blobAttributes, AccessCondition accessCondition, BlobRequestOptions options) { RESTCommand<NullType> putCmd = new RESTCommand<NullType>(this.ServiceClient.Credentials, blobAttributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, ctx) => BlobHttpWebRequestFactory.SetProperties(uri, serverTimeout, blobAttributes.Properties, accessCondition, useVersionHeader, ctx); putCmd.SetHeaders = (r, ctx) => BlobHttpWebRequestFactory.AddMetadata(r, blobAttributes.Metadata); putCmd.SignRequest = this.ServiceClient.AuthenticationHandler.SignRequest; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, NullType.Value, cmd, ex); CloudBlob.UpdateETagLMTLengthAndSequenceNumber(blobAttributes, resp, false); return NullType.Value; }; return putCmd; }
/// <summary> /// Implements the FetchAttributes method. The attributes are updated immediately. /// </summary> /// <param name="blobAttributes">The attributes.</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> /// <returns> /// A <see cref="RESTCommand{T}"/> that fetches the attributes. /// </returns> private RESTCommand<NullType> FetchAttributesImpl(BlobAttributes blobAttributes, AccessCondition accessCondition, BlobRequestOptions options) { RESTCommand<NullType> getCmd = new RESTCommand<NullType>(this.ServiceClient.Credentials, blobAttributes.StorageUri); options.ApplyToStorageCommand(getCmd); getCmd.CommandLocationMode = CommandLocationMode.PrimaryOrSecondary; getCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, ctx) => BlobHttpWebRequestFactory.GetProperties(uri, serverTimeout, blobAttributes.SnapshotTime, accessCondition, useVersionHeader, ctx); getCmd.SignRequest = this.ServiceClient.AuthenticationHandler.SignRequest; getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, NullType.Value, cmd, ex); CloudBlob.UpdateAfterFetchAttributes(blobAttributes, resp, false); return NullType.Value; }; return getCmd; }
/// <summary> /// Retrieve ETag, LMT, Length and Sequence-Number from response. /// </summary> /// <param name="attributes">The blob attributes to update.</param> /// <param name="response">The response to parse.</param> /// <param name="updateLength">If set to <c>true</c>, update the blob length.</param> internal static void UpdateETagLMTLengthAndSequenceNumber(BlobAttributes attributes, HttpResponseMessage response, bool updateLength) { BlobProperties parsedProperties = BlobHttpResponseParsers.GetProperties(response); attributes.Properties.ETag = parsedProperties.ETag ?? attributes.Properties.ETag; attributes.Properties.LastModified = parsedProperties.LastModified ?? attributes.Properties.LastModified; attributes.Properties.PageBlobSequenceNumber = parsedProperties.PageBlobSequenceNumber ?? attributes.Properties.PageBlobSequenceNumber; if (updateLength) { attributes.Properties.Length = parsedProperties.Length; } }
/// <summary> /// Internal helper method to wrap a user provided stream with the appropriate crypto stream. /// </summary> internal static Stream WrapUserStreamWithDecryptStream(CloudBlob blob, Stream userProvidedStream, BlobRequestOptions options, BlobAttributes attributes, bool rangeRead, out ICryptoTransform transform, long?endOffset = null, long?userSpecifiedLength = null, int discardFirst = 0, bool bufferIV = false) { if (!rangeRead) { // The user provided stream should be wrapped in a NonCloseableStream in order to // avoid closing the user stream when the crypto stream is closed to flush the final decrypted // block of data. Stream decryptStream = options.EncryptionPolicy.DecryptBlob(new NonCloseableStream(userProvidedStream), attributes.Metadata, out transform, options.RequireEncryption, null, blob.BlobType == BlobType.PageBlob); return(decryptStream); } else { // Check if end offset lies in the last AES block and send this information over to set the correct padding mode. bool noPadding = blob.BlobType == BlobType.PageBlob || (endOffset.HasValue && endOffset.Value < attributes.Properties.Length - 16); transform = null; return(new BlobDecryptStream(userProvidedStream, attributes.Metadata, userSpecifiedLength, discardFirst, bufferIV, noPadding, options.EncryptionPolicy, options.RequireEncryption)); } }
/// <summary> /// Retrieve ETag, LMT, Length and Sequence-Number from response. /// </summary> /// <param name="blobAttributes">The attributes.</param> /// <param name="response">The response to parse.</param> /// <param name="updateLength">If set to <c>true</c>, update the blob length.</param> internal static void UpdateETagLMTLengthAndSequenceNumber(BlobAttributes blobAttributes, HttpWebResponse response, bool updateLength) { BlobProperties parsedProperties = BlobHttpResponseParsers.GetProperties(response); blobAttributes.Properties.ETag = parsedProperties.ETag ?? blobAttributes.Properties.ETag; blobAttributes.Properties.LastModified = parsedProperties.LastModified ?? blobAttributes.Properties.LastModified; blobAttributes.Properties.PageBlobSequenceNumber = parsedProperties.PageBlobSequenceNumber ?? blobAttributes.Properties.PageBlobSequenceNumber; blobAttributes.Properties.AppendBlobCommittedBlockCount = parsedProperties.AppendBlobCommittedBlockCount ?? blobAttributes.Properties.AppendBlobCommittedBlockCount; if (updateLength) { blobAttributes.Properties.Length = parsedProperties.Length; } }
/// <summary> /// Implements the FetchAttributes method. The attributes are updated immediately. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</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"/> that fetches the attributes.</returns> internal static RESTCommand<NullType> FetchAttributesImpl(ICloudBlob blob, BlobAttributes attributes, AccessCondition accessCondition, BlobRequestOptions options) { RESTCommand<NullType> getCmd = new RESTCommand<NullType>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(getCmd); getCmd.CommandLocationMode = CommandLocationMode.PrimaryOrSecondary; getCmd.Handler = blob.ServiceClient.AuthenticationHandler; getCmd.BuildClient = HttpClientFactory.BuildHttpClient; getCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => BlobHttpRequestMessageFactory.GetProperties(uri, serverTimeout, attributes.SnapshotTime, accessCondition, cnt, ctx); getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, NullType.Value, cmd, ex); CloudBlobSharedImpl.UpdateAfterFetchAttributes(attributes, resp, false); return NullType.Value; }; return getCmd; }
internal CloudBlob(BlobAttributes attributes, CloudBlobClient serviceClient) { throw new System.NotImplementedException(); }
/// <summary> /// Implements getting the stream without specifying a range. /// </summary> /// <param name="blob">The blob.</param> /// <param name="attributes">The attributes.</param> /// <param name="destStream">The destination stream.</param> /// <param name="offset">The offset.</param> /// <param name="length">The length.</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 any additional options for the request.</param> /// <returns> /// A <see cref="RESTCommand{T}" /> that gets the stream. /// </returns> internal static RESTCommand <NullType> GetBlobImpl(ICloudBlob blob, BlobAttributes attributes, Stream destStream, long?offset, long?length, AccessCondition accessCondition, BlobRequestOptions options) { string lockedETag = null; AccessCondition lockedAccessCondition = null; bool isRangeGet = offset.HasValue; bool arePropertiesPopulated = false; string storedMD5 = null; long startingOffset = offset.HasValue ? offset.Value : 0; long?startingLength = length; RESTCommand <NullType> getCmd = new RESTCommand <NullType>(blob.ServiceClient.Credentials, attributes.Uri); getCmd.ApplyRequestOptions(options); getCmd.RetrieveResponseStream = true; getCmd.DestinationStream = destStream; getCmd.CalculateMd5ForResponseStream = !options.DisableContentMD5Validation.Value; getCmd.BuildRequestDelegate = (uri, builder, serverTimeout, ctx) => BlobHttpWebRequestFactory.Get(uri, serverTimeout, attributes.SnapshotTime, offset, length, options.UseTransactionalMD5.Value, accessCondition, ctx); getCmd.SignRequest = blob.ServiceClient.AuthenticationHandler.SignRequest; getCmd.RecoveryAction = (cmd, ex, ctx) => { if ((lockedAccessCondition == null) && !string.IsNullOrEmpty(lockedETag)) { lockedAccessCondition = AccessCondition.GenerateIfMatchCondition(lockedETag); if (accessCondition != null) { lockedAccessCondition.LeaseId = accessCondition.LeaseId; } } if (cmd.StreamCopyState != null) { offset = startingOffset + cmd.StreamCopyState.Length; if (startingLength.HasValue) { length = startingLength.Value - cmd.StreamCopyState.Length; } } getCmd.BuildRequestDelegate = (uri, builder, serverTimeout, context) => BlobHttpWebRequestFactory.Get(uri, serverTimeout, attributes.SnapshotTime, offset, length, options.UseTransactionalMD5.Value && !arePropertiesPopulated, lockedAccessCondition ?? accessCondition, context); }; getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(offset.HasValue ? HttpStatusCode.PartialContent : HttpStatusCode.OK, resp, NullType.Value, cmd, ex, ctx); if (!arePropertiesPopulated) { CloudBlobSharedImpl.UpdateAfterFetchAttributes(attributes, resp, isRangeGet); storedMD5 = resp.Headers[HttpResponseHeader.ContentMd5]; if (!options.DisableContentMD5Validation.Value && options.UseTransactionalMD5.Value && string.IsNullOrEmpty(storedMD5)) { throw new StorageException( cmd.CurrentResult, SR.MD5NotPresentError, null) { IsRetryable = false }; } lockedETag = attributes.Properties.ETag; arePropertiesPopulated = true; } return(NullType.Value); }; getCmd.PostProcessResponse = (cmd, resp, ex, ctx) => { long validateLength = startingLength.HasValue ? startingLength.Value : (attributes.Properties.Length - startingOffset); HttpResponseParsers.ValidateResponseStreamMd5AndLength(validateLength, storedMD5, cmd, cmd.StreamCopyState); return(NullType.Value); }; return(getCmd); }
/// <summary> /// Generates a <see cref="RESTCommand{T}"/> for breaking a lease. /// </summary> /// <param name="blobAttributes">The attributes.</param> /// <param name="breakPeriod">The amount of time to allow the lease to remain, rounded down to seconds. /// If null, the break period is the remainder of the current lease, or zero for infinite leases.</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> /// <returns> /// A <see cref="RESTCommand{T}"/> implementing the break lease operation. /// </returns> private RESTCommand<TimeSpan> BreakLeaseImpl(BlobAttributes blobAttributes, TimeSpan? breakPeriod, AccessCondition accessCondition, BlobRequestOptions options) { int? breakSeconds = null; if (breakPeriod.HasValue) { CommonUtility.AssertInBounds("breakPeriod", breakPeriod.Value, TimeSpan.Zero, TimeSpan.MaxValue); breakSeconds = (int)breakPeriod.Value.TotalSeconds; } RESTCommand<TimeSpan> putCmd = new RESTCommand<TimeSpan>(this.ServiceClient.Credentials, blobAttributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, ctx) => BlobHttpWebRequestFactory.Lease(uri, serverTimeout, LeaseAction.Break, null /* proposedLeaseId */, null /* leaseDuration */, breakSeconds, accessCondition, useVersionHeader, ctx); putCmd.SignRequest = this.ServiceClient.AuthenticationHandler.SignRequest; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.Accepted, resp, TimeSpan.Zero, cmd, ex); CloudBlob.UpdateETagLMTLengthAndSequenceNumber(blobAttributes, resp, false); int? remainingLeaseTime = BlobHttpResponseParsers.GetRemainingLeaseTime(resp); if (!remainingLeaseTime.HasValue) { // Unexpected result from service. throw new StorageException(cmd.CurrentResult, SR.LeaseTimeNotReceived, null /* inner */); } return TimeSpan.FromSeconds(remainingLeaseTime.Value); }; return putCmd; }
/// <summary> /// Implementation for the SetProperties method. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</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"/> that sets the metadata.</returns> internal static RESTCommand<NullType> SetPropertiesImpl(ICloudBlob blob, BlobAttributes attributes, AccessCondition accessCondition, BlobRequestOptions options) { RESTCommand<NullType> putCmd = new RESTCommand<NullType>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.Handler = blob.ServiceClient.AuthenticationHandler; putCmd.BuildClient = HttpClientFactory.BuildHttpClient; putCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => { HttpRequestMessage msg = BlobHttpRequestMessageFactory.SetProperties(uri, serverTimeout, attributes.Properties, accessCondition, cnt, ctx); BlobHttpRequestMessageFactory.AddMetadata(msg, attributes.Metadata); return msg; }; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, NullType.Value, cmd, ex); CloudBlobSharedImpl.UpdateETagLMTLengthAndSequenceNumber(attributes, resp, false); return NullType.Value; }; return putCmd; }
/// <summary> /// Implementation of the AbortCopy method. No result is produced. /// </summary> /// <param name="blobAttributes">The attributes.</param> /// <param name="copyId">The copy ID of the copy operation to abort.</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> /// <returns> /// A <see cref="RESTCommand{T}"/> that aborts the copy. /// </returns> private RESTCommand<NullType> AbortCopyImpl(BlobAttributes blobAttributes, string copyId, AccessCondition accessCondition, BlobRequestOptions options) { CommonUtility.AssertNotNull("copyId", copyId); RESTCommand<NullType> putCmd = new RESTCommand<NullType>(this.ServiceClient.Credentials, blobAttributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, ctx) => BlobHttpWebRequestFactory.AbortCopy(uri, serverTimeout, copyId, accessCondition, useVersionHeader, ctx); putCmd.SignRequest = this.ServiceClient.AuthenticationHandler.SignRequest; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.NoContent, resp, NullType.Value, cmd, ex); return putCmd; }
/// <summary> /// Generates a <see cref="RESTCommand"/> for changing a lease ID. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</param> /// <param name="proposedLeaseId">The proposed new lease ID.</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"/> implementing the change lease ID operation.</returns> internal static RESTCommand<string> ChangeLeaseImpl(ICloudBlob blob, BlobAttributes attributes, string proposedLeaseId, AccessCondition accessCondition, BlobRequestOptions options) { CommonUtility.AssertNotNull("accessCondition", accessCondition); CommonUtility.AssertNotNull("proposedLeaseId", proposedLeaseId); if (accessCondition.LeaseId == null) { throw new ArgumentException(SR.MissingLeaseIDChanging, "accessCondition"); } RESTCommand<string> putCmd = new RESTCommand<string>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.Handler = blob.ServiceClient.AuthenticationHandler; putCmd.BuildClient = HttpClientFactory.BuildHttpClient; putCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => BlobHttpRequestMessageFactory.Lease(uri, serverTimeout, LeaseAction.Change, proposedLeaseId, null /* leaseDuration */, null /* leaseBreakPeriod */, accessCondition, cnt, ctx); putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, null /* retVal */, cmd, ex); CloudBlobSharedImpl.UpdateETagLMTLengthAndSequenceNumber(attributes, resp, false); return BlobHttpResponseParsers.GetLeaseId(resp); }; return putCmd; }
/// <summary> /// Implements the FetchAttributes method. The attributes are updated immediately. /// </summary> /// <param name="blobUri">The URI of the blob.</param> /// <param name="accessCondition">An <see cref="AccessCondition"/> object that represents the access conditions for the blob. 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> /// <returns>A <see cref="RESTCommand"/> that fetches the attributes.</returns> private RESTCommand<CloudBlob> GetBlobReferenceFromServerImpl(StorageUri blobUri, AccessCondition accessCondition, BlobRequestOptions options) { // If the blob Uri contains SAS credentials, we need to use those // credentials instead of this service client's stored credentials. StorageCredentials parsedCredentials; DateTimeOffset? parsedSnapshot; blobUri = NavigationHelper.ParseBlobQueryAndVerify(blobUri, out parsedCredentials, out parsedSnapshot); CloudBlobClient client = parsedCredentials != null ? new CloudBlobClient(this.StorageUri, parsedCredentials) : this; RESTCommand<CloudBlob> getCmd = new RESTCommand<CloudBlob>(client.Credentials, blobUri); options.ApplyToStorageCommand(getCmd); getCmd.CommandLocationMode = CommandLocationMode.PrimaryOrSecondary; getCmd.Handler = client.AuthenticationHandler; getCmd.BuildClient = HttpClientFactory.BuildHttpClient; getCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => BlobHttpRequestMessageFactory.GetProperties(uri, serverTimeout, parsedSnapshot, accessCondition, cnt, ctx); getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, null /* retVal */, cmd, ex); BlobAttributes attributes = new BlobAttributes() { StorageUri = blobUri, SnapshotTime = parsedSnapshot, }; CloudBlob.UpdateAfterFetchAttributes(attributes, resp, false); switch (attributes.Properties.BlobType) { case BlobType.BlockBlob: return new CloudBlockBlob(attributes, client); case BlobType.PageBlob: return new CloudPageBlob(attributes, client); case BlobType.AppendBlob: return new CloudAppendBlob(attributes, client); default: throw new InvalidOperationException(); } }; return getCmd; }
/// <summary> /// Implementation of the StartCopyFromBlob method. Result is a BlobAttributes object derived from the response headers. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</param> /// <param name="source">The URI of the source blob.</param> /// <param name="sourceAccessCondition">An object that represents the access conditions for the source blob. If null, no condition is used.</param> /// <param name="destAccessCondition">An object that represents the access conditions for the destination blob. If null, no condition is used.</param> /// <param name="options">An object that specifies additional options for the request.</param> /// <param name="setResult">A delegate for setting the BlobAttributes result.</param> /// <returns>A <see cref="RESTCommand"/> that starts to copy the blob.</returns> internal static RESTCommand<string> StartCopyFromBlobImpl(ICloudBlob blob, BlobAttributes attributes, Uri source, AccessCondition sourceAccessCondition, AccessCondition destAccessCondition, BlobRequestOptions options) { if (sourceAccessCondition != null && !string.IsNullOrEmpty(sourceAccessCondition.LeaseId)) { throw new ArgumentException(SR.LeaseConditionOnSource, "sourceAccessCondition"); } RESTCommand<string> putCmd = new RESTCommand<string>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.Handler = blob.ServiceClient.AuthenticationHandler; putCmd.BuildClient = HttpClientFactory.BuildHttpClient; putCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => { HttpRequestMessage msg = BlobHttpRequestMessageFactory.CopyFrom(uri, serverTimeout, source, sourceAccessCondition, destAccessCondition, cnt, ctx); BlobHttpRequestMessageFactory.AddMetadata(msg, attributes.Metadata); return msg; }; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.Accepted, resp, null /* retVal */, cmd, ex); CopyState state = BlobHttpResponseParsers.GetCopyAttributes(resp); attributes.Properties = BlobHttpResponseParsers.GetProperties(resp); attributes.Metadata = BlobHttpResponseParsers.GetMetadata(resp); attributes.CopyState = state; return state.CopyId; }; return putCmd; }
private RESTCommand <NullType> GetBlobImpl(BlobAttributes attributes, Stream destStream, long?offset, long?length, AccessCondition accessCondition, BlobRequestOptions options) { throw new System.NotImplementedException(); }
/// <summary> /// Implements getting the blob. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</param> /// <param name="destStream">The target stream.</param> /// <param name="offset">The offset at which to begin downloading the blob, in bytes.</param> /// <param name="length">The length of the data to download from the blob, in bytes.</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="SynchronousTask"/> that gets the stream.</returns> internal static RESTCommand <NullType> GetBlobImpl(ICloudBlob blob, BlobAttributes attributes, Stream destStream, long?offset, long?length, AccessCondition accessCondition, BlobRequestOptions options) { string lockedETag = null; AccessCondition lockedAccessCondition = null; bool isRangeGet = offset.HasValue; bool arePropertiesPopulated = false; string storedMD5 = null; long startingOffset = offset.HasValue ? offset.Value : 0; long?startingLength = length; long?validateLength = null; RESTCommand <NullType> getCmd = new RESTCommand <NullType>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(getCmd); getCmd.CommandLocationMode = CommandLocationMode.PrimaryOrSecondary; getCmd.RetrieveResponseStream = true; getCmd.DestinationStream = destStream; getCmd.CalculateMd5ForResponseStream = !options.DisableContentMD5Validation.Value; getCmd.Handler = blob.ServiceClient.AuthenticationHandler; getCmd.BuildClient = HttpClientFactory.BuildHttpClient; getCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => BlobHttpRequestMessageFactory.Get(uri, serverTimeout, attributes.SnapshotTime, offset, length, options.UseTransactionalMD5.Value, accessCondition, cnt, ctx); getCmd.RecoveryAction = (cmd, ex, ctx) => { if ((lockedAccessCondition == null) && !string.IsNullOrEmpty(lockedETag)) { lockedAccessCondition = AccessCondition.GenerateIfMatchCondition(lockedETag); if (accessCondition != null) { lockedAccessCondition.LeaseId = accessCondition.LeaseId; } } if (cmd.StreamCopyState != null) { offset = startingOffset + cmd.StreamCopyState.Length; if (startingLength.HasValue) { length = startingLength.Value - cmd.StreamCopyState.Length; } } getCmd.BuildRequest = (command, uri, builder, cnt, serverTimeout, context) => BlobHttpRequestMessageFactory.Get(uri, serverTimeout, attributes.SnapshotTime, offset, length, options.UseTransactionalMD5.Value && !arePropertiesPopulated, lockedAccessCondition ?? accessCondition, cnt, context); }; getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(offset.HasValue ? HttpStatusCode.PartialContent : HttpStatusCode.OK, resp, NullType.Value, cmd, ex); if (!arePropertiesPopulated) { CloudBlobSharedImpl.UpdateAfterFetchAttributes(attributes, resp, isRangeGet); if (resp.Content.Headers.ContentMD5 != null) { storedMD5 = Convert.ToBase64String(resp.Content.Headers.ContentMD5); } if (!options.DisableContentMD5Validation.Value && options.UseTransactionalMD5.Value && string.IsNullOrEmpty(storedMD5)) { throw new StorageException( cmd.CurrentResult, SR.MD5NotPresentError, null) { IsRetryable = false }; } // If the download fails and Get Blob needs to resume the download, going to the // same storage location is important to prevent a possible ETag mismatch. getCmd.CommandLocationMode = cmd.CurrentResult.TargetLocation == StorageLocation.Primary ? CommandLocationMode.PrimaryOnly : CommandLocationMode.SecondaryOnly; lockedETag = attributes.Properties.ETag; validateLength = resp.Content.Headers.ContentLength; arePropertiesPopulated = true; } return(NullType.Value); }; getCmd.PostProcessResponse = (cmd, resp, ctx) => { HttpResponseParsers.ValidateResponseStreamMd5AndLength(validateLength, storedMD5, cmd); return(Task.FromResult(NullType.Value)); }; return(getCmd); }
/// <summary> /// Implements the Exists method. The attributes are updated immediately. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</param> /// <param name="options">An object that specifies additional options for the request.</param> /// <param name="primaryOnly">If <c>true</c>, the command will be executed against the primary location.</param> /// <returns>A <see cref="RESTCommand"/> that checks existence.</returns> internal static RESTCommand<bool> ExistsImpl(ICloudBlob blob, BlobAttributes attributes, BlobRequestOptions options, bool primaryOnly) { RESTCommand<bool> getCmd = new RESTCommand<bool>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(getCmd); getCmd.CommandLocationMode = primaryOnly ? CommandLocationMode.PrimaryOnly : CommandLocationMode.PrimaryOrSecondary; getCmd.Handler = blob.ServiceClient.AuthenticationHandler; getCmd.BuildClient = HttpClientFactory.BuildHttpClient; getCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => BlobHttpRequestMessageFactory.GetProperties(uri, serverTimeout, attributes.SnapshotTime, null /* accessCondition */, cnt, ctx); getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { if (resp.StatusCode == HttpStatusCode.NotFound) { return false; } HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, true, cmd, ex); CloudBlobSharedImpl.UpdateAfterFetchAttributes(attributes, resp, false); return true; }; return getCmd; }
private RESTCommand <bool> ExistsImpl(BlobAttributes attributes, BlobRequestOptions options, bool primaryOnly) { throw new System.NotImplementedException(); }
/// <summary> /// Implements the DeleteBlob method. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</param> /// <param name="deleteSnapshotsOption">Whether to only delete the blob, to delete the blob and all snapshots, or to only delete the snapshots.</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"/> that deletes the blob.</returns> internal static RESTCommand<NullType> DeleteBlobImpl(ICloudBlob blob, BlobAttributes attributes, DeleteSnapshotsOption deleteSnapshotsOption, AccessCondition accessCondition, BlobRequestOptions options) { RESTCommand<NullType> deleteCmd = new RESTCommand<NullType>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(deleteCmd); deleteCmd.Handler = blob.ServiceClient.AuthenticationHandler; deleteCmd.BuildClient = HttpClientFactory.BuildHttpClient; deleteCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => BlobHttpRequestMessageFactory.Delete(uri, serverTimeout, attributes.SnapshotTime, deleteSnapshotsOption, accessCondition, cnt, ctx); deleteCmd.PreProcessResponse = (cmd, resp, ex, ctx) => HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.Accepted, resp, NullType.Value, cmd, ex); return deleteCmd; }
private RESTCommand <NullType> DeleteBlobImpl(BlobAttributes attributes, DeleteSnapshotsOption deleteSnapshotsOption, AccessCondition accessCondition, BlobRequestOptions options) { throw new System.NotImplementedException(); }
/// <summary> /// Implements getting the blob. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</param> /// <param name="destStream">The target stream.</param> /// <param name="offset">The offset at which to begin downloading the blob, in bytes.</param> /// <param name="length">The length of the data to download from the blob, in bytes.</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="SynchronousTask"/> that gets the stream.</returns> internal static RESTCommand<NullType> GetBlobImpl(ICloudBlob blob, BlobAttributes attributes, Stream destStream, long? offset, long? length, AccessCondition accessCondition, BlobRequestOptions options) { string lockedETag = null; AccessCondition lockedAccessCondition = null; bool isRangeGet = offset.HasValue; bool arePropertiesPopulated = false; string storedMD5 = null; long startingOffset = offset.HasValue ? offset.Value : 0; long? startingLength = length; long? validateLength = null; RESTCommand<NullType> getCmd = new RESTCommand<NullType>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(getCmd); getCmd.CommandLocationMode = CommandLocationMode.PrimaryOrSecondary; getCmd.RetrieveResponseStream = true; getCmd.DestinationStream = destStream; getCmd.CalculateMd5ForResponseStream = !options.DisableContentMD5Validation.Value; getCmd.Handler = blob.ServiceClient.AuthenticationHandler; getCmd.BuildClient = HttpClientFactory.BuildHttpClient; getCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => BlobHttpRequestMessageFactory.Get(uri, serverTimeout, attributes.SnapshotTime, offset, length, options.UseTransactionalMD5.Value, accessCondition, cnt, ctx); getCmd.RecoveryAction = (cmd, ex, ctx) => { if ((lockedAccessCondition == null) && !string.IsNullOrEmpty(lockedETag)) { lockedAccessCondition = AccessCondition.GenerateIfMatchCondition(lockedETag); if (accessCondition != null) { lockedAccessCondition.LeaseId = accessCondition.LeaseId; } } if (cmd.StreamCopyState != null) { offset = startingOffset + cmd.StreamCopyState.Length; if (startingLength.HasValue) { length = startingLength.Value - cmd.StreamCopyState.Length; } } getCmd.BuildRequest = (command, uri, builder, cnt, serverTimeout, context) => BlobHttpRequestMessageFactory.Get(uri, serverTimeout, attributes.SnapshotTime, offset, length, options.UseTransactionalMD5.Value && !arePropertiesPopulated, lockedAccessCondition ?? accessCondition, cnt, context); }; getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(offset.HasValue ? HttpStatusCode.PartialContent : HttpStatusCode.OK, resp, NullType.Value, cmd, ex); if (!arePropertiesPopulated) { CloudBlobSharedImpl.UpdateAfterFetchAttributes(attributes, resp, isRangeGet); if (resp.Content.Headers.ContentMD5 != null) { storedMD5 = Convert.ToBase64String(resp.Content.Headers.ContentMD5); } if (!options.DisableContentMD5Validation.Value && options.UseTransactionalMD5.Value && string.IsNullOrEmpty(storedMD5)) { throw new StorageException( cmd.CurrentResult, SR.MD5NotPresentError, null) { IsRetryable = false }; } // If the download fails and Get Blob needs to resume the download, going to the // same storage location is important to prevent a possible ETag mismatch. getCmd.CommandLocationMode = cmd.CurrentResult.TargetLocation == StorageLocation.Primary ? CommandLocationMode.PrimaryOnly : CommandLocationMode.SecondaryOnly; lockedETag = attributes.Properties.ETag; validateLength = resp.Content.Headers.ContentLength; arePropertiesPopulated = true; } return NullType.Value; }; getCmd.PostProcessResponse = (cmd, resp, ctx) => { HttpResponseParsers.ValidateResponseStreamMd5AndLength(validateLength, storedMD5, cmd); return Task.FromResult(NullType.Value); }; return getCmd; }
private RESTCommand <string> ChangeLeaseImpl(BlobAttributes attributes, string proposedLeaseId, AccessCondition accessCondition, BlobRequestOptions options) { throw new System.NotImplementedException(); }
/// <summary> /// Implementation of the AbortCopy method. No result is produced. /// </summary> /// <param name="blob">The blob object that is calling this method.</param> /// <param name="attributes">The blob's attributes.</param> /// <param name="copyId">The copy ID of the copy operation to abort.</param> /// <param name="accessCondition">An object that represents the access conditions for the operation. If null, no condition is used.</param> /// <param name="options">An object that specifies additional options for the request.</param> /// <returns>A <see cref="TaskSequence"/> that copies the blob.</returns> internal static RESTCommand<NullType> AbortCopyImpl(ICloudBlob blob, BlobAttributes attributes, string copyId, AccessCondition accessCondition, BlobRequestOptions options) { CommonUtility.AssertNotNull("copyId", copyId); RESTCommand<NullType> putCmd = new RESTCommand<NullType>(blob.ServiceClient.Credentials, attributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.Handler = blob.ServiceClient.AuthenticationHandler; putCmd.BuildClient = HttpClientFactory.BuildHttpClient; putCmd.BuildRequest = (cmd, uri, builder, cnt, serverTimeout, ctx) => BlobHttpRequestMessageFactory.AbortCopy(uri, serverTimeout, copyId, accessCondition, cnt, ctx); putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.NoContent, resp, NullType.Value, cmd, ex); return putCmd; }
private RESTCommand <NullType> ReleaseLeaseImpl(BlobAttributes attributes, AccessCondition accessCondition, BlobRequestOptions options) { throw new System.NotImplementedException(); }
/// <summary> /// Implements getting the stream without specifying a range. /// </summary> /// <param name="blobAttributes">The attributes.</param> /// <param name="destStream">The destination stream.</param> /// <param name="offset">The offset.</param> /// <param name="length">The length.</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> /// <returns> /// A <see cref="RESTCommand{T}"/> that gets the stream. /// </returns> private RESTCommand<NullType> GetBlobImpl(BlobAttributes blobAttributes, Stream destStream, long? offset, long? length, AccessCondition accessCondition, BlobRequestOptions options) { string lockedETag = null; AccessCondition lockedAccessCondition = null; bool isRangeGet = offset.HasValue; // Adjust the range if the encryption policy is set and it is a range download. For now, we can do it this way. // Once we have multiple versions/algorithms, we will not be able to do this adjustment deterministically without fetching // properties from the service. Since it is an extra call, we will add it once we support multiple versions. int discardFirst = 0; long? endOffset = null; bool bufferIV = false; long? userSpecifiedLength = length; options.AssertPolicyIfRequired(); if (isRangeGet && options.EncryptionPolicy != null) { #if WINDOWS_PHONE // Windows phone does not allow setting padding mode to none. Uses PKCS7 by default. So we cannot download closed ranges that do not // cover the last block of data. However, since we cannot know the length unless we do a fetch attributes, we will just throw // for now if length is specified. Open ranges like x - are still allowed. if (length.HasValue) { throw new InvalidOperationException(SR.RangeDownloadNotPermittedOnPhone); } #endif // Let's say the user requests a download with offset = 39 and length = 54 // First calculate the endOffset if length has value. // endOffset starts at 92 (39 + 54 - 1), but then gets increased to 95 (one less than the next higher multiple of 16) if (length.HasValue) { endOffset = offset.Value + length.Value - 1; // AES-CBC works in 16 byte blocks. So if a user specifies a range whose start and end offsets are not multiples of 16, // update them so we can download entire AES blocks to decrypt. // Adjust the end offset to be a multiple of 16. if ((endOffset.Value + 1) % 16 != 0) { endOffset += (int)(16 - ((endOffset.Value + 1) % 16)); } } // Adjust the end offset to be a multiple of 16. // offset gets reduced down to the highest multiple of 16 lower then the current value (32) discardFirst = (int)(offset.Value % 16); offset -= discardFirst; // We need another 16 bytes for IV if offset is not 0. If the offset is 0, it is the first AES block // and the IV is obtained from blob metadata. // offset is reduced by another 16 (to a final value of 16) if (offset > 15) { offset -= 16; bufferIV = true; } // Adjust the length according to the new start and end offsets. // length = 80 (a multiple of 16) if (endOffset.HasValue) { length = endOffset.Value - offset.Value + 1; } } bool arePropertiesPopulated = false; bool decryptStreamCreated = false; ICryptoTransform transform = null; string storedMD5 = null; long startingOffset = offset.HasValue ? offset.Value : 0; long? startingLength = length; long? validateLength = null; RESTCommand<NullType> getCmd = new RESTCommand<NullType>(this.ServiceClient.Credentials, blobAttributes.StorageUri); options.ApplyToStorageCommand(getCmd); getCmd.CommandLocationMode = CommandLocationMode.PrimaryOrSecondary; getCmd.RetrieveResponseStream = true; getCmd.DestinationStream = destStream; getCmd.CalculateMd5ForResponseStream = !options.DisableContentMD5Validation.Value; getCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, ctx) => BlobHttpWebRequestFactory.Get(uri, serverTimeout, blobAttributes.SnapshotTime, offset, length, options.UseTransactionalMD5.Value, accessCondition, useVersionHeader, ctx); getCmd.SignRequest = this.ServiceClient.AuthenticationHandler.SignRequest; getCmd.RecoveryAction = (cmd, ex, ctx) => { if ((lockedAccessCondition == null) && !string.IsNullOrEmpty(lockedETag)) { lockedAccessCondition = AccessCondition.GenerateIfMatchCondition(lockedETag); if (accessCondition != null) { lockedAccessCondition.LeaseId = accessCondition.LeaseId; } } if (cmd.StreamCopyState != null) { offset = startingOffset + cmd.StreamCopyState.Length; if (startingLength.HasValue) { length = startingLength.Value - cmd.StreamCopyState.Length; } } getCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, context) => BlobHttpWebRequestFactory.Get(uri, serverTimeout, blobAttributes.SnapshotTime, offset, length, options.UseTransactionalMD5.Value && !arePropertiesPopulated, lockedAccessCondition ?? accessCondition, useVersionHeader, context); }; getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(offset.HasValue ? HttpStatusCode.PartialContent : HttpStatusCode.OK, resp, NullType.Value, cmd, ex); if (!arePropertiesPopulated) { CloudBlob.UpdateAfterFetchAttributes(blobAttributes, resp, isRangeGet); storedMD5 = resp.Headers[HttpResponseHeader.ContentMd5]; if (options.EncryptionPolicy != null) { cmd.DestinationStream = BlobEncryptionPolicy.WrapUserStreamWithDecryptStream(this, cmd.DestinationStream, options, blobAttributes, isRangeGet, out transform, endOffset, userSpecifiedLength, discardFirst, bufferIV); decryptStreamCreated = true; } if (!options.DisableContentMD5Validation.Value && options.UseTransactionalMD5.Value && string.IsNullOrEmpty(storedMD5)) { throw new StorageException( cmd.CurrentResult, SR.MD5NotPresentError, null) { IsRetryable = false }; } // If the download fails and Get Blob needs to resume the download, going to the // same storage location is important to prevent a possible ETag mismatch. getCmd.CommandLocationMode = cmd.CurrentResult.TargetLocation == StorageLocation.Primary ? CommandLocationMode.PrimaryOnly : CommandLocationMode.SecondaryOnly; lockedETag = blobAttributes.Properties.ETag; if (resp.ContentLength >= 0) { validateLength = resp.ContentLength; } arePropertiesPopulated = true; } return NullType.Value; }; getCmd.PostProcessResponse = (cmd, resp, ctx) => { HttpResponseParsers.ValidateResponseStreamMd5AndLength(validateLength, storedMD5, cmd); return NullType.Value; }; getCmd.DisposeAction = (cmd) => { // Crypto stream should be closed in order for it to flush the final block of data to the underlying stream and to clear internal buffers. // It is ok to do this here because we have ensured that we don't end up closing the user provided stream in the process of closing the // cryptostream by wrapping the user provided stream within the NonCloseableStream. if (decryptStreamCreated) { try { // This only throws a NotSupportedException if the current stream is not writable (which should never be true in our case). // But the try/catch is for safe exit if something unexpected happens and we get an exception // when Close is invoked. cmd.DestinationStream.Close(); // Dispose the ICryptoTransform object created for decryption if required. For the range download case, BlobDecryptStream will // dispose the transform function. For full blob downloads, we will dispose it here. if (transform != null) { transform.Dispose(); } } catch (Exception ex) { throw new StorageException( cmd.CurrentResult, SR.CryptoError, ex) { IsRetryable = false }; } } }; return getCmd; }
private RESTCommand <TimeSpan> BreakLeaseImpl(BlobAttributes attributes, TimeSpan?breakPeriod, AccessCondition accessCondition, BlobRequestOptions options) { throw new System.NotImplementedException(); }
/// <summary> /// Implementation for the Exists method. /// </summary> /// <param name="blobAttributes">The attributes.</param> /// <param name="options">A <see cref="BlobRequestOptions"/> object that specifies additional options for the request.</param> /// <param name="primaryOnly">If <c>true</c>, the command will be executed against the primary location.</param> /// <returns> /// A <see cref="RESTCommand{T}"/> that checks existence. /// </returns> private RESTCommand<bool> ExistsImpl(BlobAttributes blobAttributes, BlobRequestOptions options, bool primaryOnly) { RESTCommand<bool> getCmd = new RESTCommand<bool>(this.ServiceClient.Credentials, blobAttributes.StorageUri); options.ApplyToStorageCommand(getCmd); getCmd.CommandLocationMode = primaryOnly ? CommandLocationMode.PrimaryOnly : CommandLocationMode.PrimaryOrSecondary; getCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, ctx) => BlobHttpWebRequestFactory.GetProperties(uri, serverTimeout, blobAttributes.SnapshotTime, null /* accessCondition */, useVersionHeader, ctx); getCmd.SignRequest = this.ServiceClient.AuthenticationHandler.SignRequest; getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { if (resp.StatusCode == HttpStatusCode.NotFound) { return false; } HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, true, cmd, ex); CloudBlob.UpdateAfterFetchAttributes(blobAttributes, resp, false); return true; }; return getCmd; }
internal RESTCommand <string> StartCopyImpl(BlobAttributes attributes, Uri source, bool incrementalCopy, PremiumPageBlobTier?premiumPageBlobTier, AccessCondition sourceAccessCondition, AccessCondition destAccessCondition, BlobRequestOptions options) { throw new System.NotImplementedException(); }
/// <summary> /// Implements the DeleteBlob method. /// </summary> /// <param name="blobAttributes">The attributes.</param> /// <param name="deleteSnapshotsOption">Whether to only delete the blob, to delete the blob and all snapshots, or to only delete the snapshots.</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> /// <returns> /// A <see cref="RESTCommand{T}"/> that deletes the blob. /// </returns> private RESTCommand<NullType> DeleteBlobImpl(BlobAttributes blobAttributes, DeleteSnapshotsOption deleteSnapshotsOption, AccessCondition accessCondition, BlobRequestOptions options) { RESTCommand<NullType> deleteCmd = new RESTCommand<NullType>(this.ServiceClient.Credentials, blobAttributes.StorageUri); options.ApplyToStorageCommand(deleteCmd); deleteCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, ctx) => BlobHttpWebRequestFactory.Delete(uri, serverTimeout, blobAttributes.SnapshotTime, deleteSnapshotsOption, accessCondition, useVersionHeader, ctx); deleteCmd.SignRequest = this.ServiceClient.AuthenticationHandler.SignRequest; deleteCmd.PreProcessResponse = (cmd, resp, ex, ctx) => HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.Accepted, resp, NullType.Value, cmd, ex); return deleteCmd; }
private RESTCommand <NullType> AbortCopyImpl(BlobAttributes attributes, string copyId, AccessCondition accessCondition, BlobRequestOptions options) { throw new System.NotImplementedException(); }
/// <summary> /// Generates a <see cref="RESTCommand{T}"/> for releasing a lease. /// </summary> /// <param name="blobAttributes">The attributes.</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> /// <returns> /// A <see cref="RESTCommand{T}"/> implementing the release lease operation. /// </returns> /// <exception cref="System.ArgumentException">accessCondition</exception> private RESTCommand<NullType> ReleaseLeaseImpl(BlobAttributes blobAttributes, AccessCondition accessCondition, BlobRequestOptions options) { CommonUtility.AssertNotNull("accessCondition", accessCondition); if (accessCondition.LeaseId == null) { throw new ArgumentException(SR.MissingLeaseIDReleasing, "accessCondition"); } RESTCommand<NullType> putCmd = new RESTCommand<NullType>(this.ServiceClient.Credentials, blobAttributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, ctx) => BlobHttpWebRequestFactory.Lease(uri, serverTimeout, LeaseAction.Release, null /* proposedLeaseId */, null /* leaseDuration */, null /* leaseBreakPeriod */, accessCondition, useVersionHeader, ctx); putCmd.SignRequest = this.ServiceClient.AuthenticationHandler.SignRequest; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, NullType.Value, cmd, ex); CloudBlob.UpdateETagLMTLengthAndSequenceNumber(blobAttributes, resp, false); return NullType.Value; }; return putCmd; }
/// <summary> /// Implementation for the SetProperties method. /// </summary> /// <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 any additional options for the request.</param> /// <returns>A <see cref="RESTCommand{T}"/> that sets the metadata.</returns> internal static RESTCommand<NullType> SetPropertiesImpl(ICloudBlob blob, BlobAttributes attributes, AccessCondition accessCondition, BlobRequestOptions options) { RESTCommand<NullType> putCmd = new RESTCommand<NullType>(blob.ServiceClient.Credentials, attributes.Uri); putCmd.ApplyRequestOptions(options); putCmd.BuildRequestDelegate = (uri, builder, serverTimeout, ctx) => BlobHttpWebRequestFactory.SetProperties(uri, serverTimeout, attributes.Properties, accessCondition, ctx); putCmd.SetHeaders = (r, ctx) => BlobHttpWebRequestFactory.AddMetadata(r, attributes.Metadata); putCmd.SignRequest = blob.ServiceClient.AuthenticationHandler.SignRequest; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, NullType.Value, cmd, ex, ctx); CloudBlobSharedImpl.ParseSizeAndLastModified(attributes, resp); return NullType.Value; }; return putCmd; }
/// <summary> /// Implementation of the StartCopy method. Result is a BlobAttributes object derived from the response headers. /// </summary> /// <param name="blobAttributes">The attributes.</param> /// <param name="source">The URI of the source blob.</param> /// <param name="sourceAccessCondition">An <see cref="AccessCondition"/> object that represents the access conditions for the source object. If <c>null</c>, no condition is used.</param> /// <param name="destAccessCondition">An <see cref="AccessCondition"/> object that represents the access conditions for the destination blob. 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> /// <returns> /// A <see cref="RESTCommand{T}"/> that starts to copy. /// </returns> /// <exception cref="System.ArgumentException">sourceAccessCondition</exception> private RESTCommand<string> StartCopyImpl(BlobAttributes blobAttributes, Uri source, AccessCondition sourceAccessCondition, AccessCondition destAccessCondition, BlobRequestOptions options) { if (sourceAccessCondition != null && !string.IsNullOrEmpty(sourceAccessCondition.LeaseId)) { throw new ArgumentException(SR.LeaseConditionOnSource, "sourceAccessCondition"); } RESTCommand<string> putCmd = new RESTCommand<string>(this.ServiceClient.Credentials, blobAttributes.StorageUri); options.ApplyToStorageCommand(putCmd); putCmd.BuildRequestDelegate = (uri, builder, serverTimeout, useVersionHeader, ctx) => BlobHttpWebRequestFactory.CopyFrom(uri, serverTimeout, source, sourceAccessCondition, destAccessCondition, useVersionHeader, ctx); putCmd.SetHeaders = (r, ctx) => BlobHttpWebRequestFactory.AddMetadata(r, blobAttributes.Metadata); putCmd.SignRequest = this.ServiceClient.AuthenticationHandler.SignRequest; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.Accepted, resp, null /* retVal */, cmd, ex); CloudBlob.UpdateETagLMTLengthAndSequenceNumber(blobAttributes, resp, false); CopyState state = BlobHttpResponseParsers.GetCopyAttributes(resp); blobAttributes.CopyState = state; return state.CopyId; }; return putCmd; }
/// <summary> /// Generates a <see cref="RESTCommand{T}"/> for acquiring a lease. /// </summary> /// <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 any 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) { CommonUtils.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, ctx); return BlobHttpResponseParsers.GetLeaseId(resp); }; return putCmd; }
/// <summary> /// Updates this blob with the given attributes at the end of a fetch attributes operation. /// </summary> /// <param name="blobAttributes">The new attributes.</param> /// <param name="response">The response.</param> /// <param name="ignoreMD5">if set to <c>true</c>, blob's MD5 will not be updated.</param> /// <exception cref="System.InvalidOperationException"></exception> internal static void UpdateAfterFetchAttributes(BlobAttributes blobAttributes, HttpWebResponse response, bool ignoreMD5) { BlobProperties properties = BlobHttpResponseParsers.GetProperties(response); // If BlobType is specified and the value returned from cloud is different, // then it's a client error and we need to throw. if (blobAttributes.Properties.BlobType != BlobType.Unspecified && blobAttributes.Properties.BlobType != properties.BlobType) { throw new InvalidOperationException(SR.BlobTypeMismatch); } if (ignoreMD5) { properties.ContentMD5 = blobAttributes.Properties.ContentMD5; } blobAttributes.Properties = properties; blobAttributes.Metadata = BlobHttpResponseParsers.GetMetadata(response); blobAttributes.CopyState = BlobHttpResponseParsers.GetCopyAttributes(response); }
/// <summary> /// Generates a <see cref="RESTCommand{T}"/> for releasing a lease. /// </summary> /// <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 any additional options for the request.</param> /// <returns>A <see cref="RESTCommand{T}"/> implementing the release lease operation.</returns> internal static RESTCommand<NullType> ReleaseLeaseImpl(ICloudBlob blob, BlobAttributes attributes, AccessCondition accessCondition, BlobRequestOptions options) { CommonUtils.AssertNotNull("accessCondition", accessCondition); if (accessCondition.LeaseId == null) { throw new ArgumentException(SR.MissingLeaseIDReleasing, "accessCondition"); } RESTCommand<NullType> putCmd = new RESTCommand<NullType>(blob.ServiceClient.Credentials, attributes.Uri); putCmd.ApplyRequestOptions(options); putCmd.BuildRequestDelegate = (uri, builder, serverTimeout, ctx) => BlobHttpWebRequestFactory.Lease(uri, serverTimeout, LeaseAction.Release, null /* proposedLeaseId */, null /* leaseDuration */, null /* leaseBreakPeriod */, accessCondition, ctx); putCmd.SignRequest = blob.ServiceClient.AuthenticationHandler.SignRequest; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, NullType.Value, cmd, ex, ctx); return putCmd; }
/// <summary> /// Internal helper method to wrap a user provided stream with the appropriate crypto stream. /// </summary> internal static Stream WrapUserStreamWithDecryptStream(CloudBlob blob, Stream userProvidedStream, BlobRequestOptions options, BlobAttributes attributes, bool rangeRead, out ICryptoTransform transform, long? endOffset = null, long? userSpecifiedLength = null, int discardFirst = 0, bool bufferIV = false) { if (!rangeRead) { // The user provided stream should be wrapped in a NonCloseableStream in order to // avoid closing the user stream when the crypto stream is closed to flush the final decrypted // block of data. Stream decryptStream = options.EncryptionPolicy.DecryptBlob(new NonCloseableStream(userProvidedStream), attributes.Metadata, out transform, options.RequireEncryption, null, blob.BlobType == BlobType.PageBlob); return decryptStream; } else { // Check if end offset lies in the last AES block and send this information over to set the correct padding mode. bool noPadding = blob.BlobType == BlobType.PageBlob || (endOffset.HasValue && endOffset.Value < attributes.Properties.Length - 16); transform = null; return new BlobDecryptStream(userProvidedStream, attributes.Metadata, userSpecifiedLength, discardFirst, bufferIV, noPadding, options.EncryptionPolicy, options.RequireEncryption); } }
/// <summary> /// Implements getting the stream without specifying a range. /// </summary> /// <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 any additional options for the request.</param> /// <returns>A <see cref="RESTCommand{T}"/> that gets the stream.</returns> internal static RESTCommand<NullType> GetBlobImpl(ICloudBlob blob, BlobAttributes attributes, Stream destStream, long? offset, long? length, AccessCondition accessCondition, BlobRequestOptions options) { string lockedETag = null; AccessCondition lockedAccessCondition = null; bool isRangeGet = offset.HasValue; bool arePropertiesPopulated = false; string storedMD5 = null; long startingOffset = offset.HasValue ? offset.Value : 0; long? startingLength = length; RESTCommand<NullType> getCmd = new RESTCommand<NullType>(blob.ServiceClient.Credentials, attributes.Uri); getCmd.ApplyRequestOptions(options); getCmd.RetrieveResponseStream = true; getCmd.DestinationStream = destStream; getCmd.CalculateMd5ForResponseStream = !options.DisableContentMD5Validation.Value; getCmd.BuildRequestDelegate = (uri, builder, serverTimeout, ctx) => BlobHttpWebRequestFactory.Get(uri, serverTimeout, attributes.SnapshotTime, offset, length, options.UseTransactionalMD5.Value, accessCondition, ctx); getCmd.SignRequest = blob.ServiceClient.AuthenticationHandler.SignRequest; getCmd.RecoveryAction = (cmd, ex, ctx) => { if ((lockedAccessCondition == null) && !string.IsNullOrEmpty(lockedETag)) { lockedAccessCondition = AccessCondition.GenerateIfMatchCondition(lockedETag); if (accessCondition != null) { lockedAccessCondition.LeaseId = accessCondition.LeaseId; } } if (cmd.StreamCopyState != null) { offset = startingOffset + cmd.StreamCopyState.Length; if (startingLength.HasValue) { length = startingLength.Value - cmd.StreamCopyState.Length; } } getCmd.BuildRequestDelegate = (uri, builder, serverTimeout, context) => BlobHttpWebRequestFactory.Get(uri, serverTimeout, attributes.SnapshotTime, offset, length, options.UseTransactionalMD5.Value && !arePropertiesPopulated, lockedAccessCondition ?? accessCondition, context); }; getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(offset.HasValue ? HttpStatusCode.PartialContent : HttpStatusCode.OK, resp, NullType.Value, cmd, ex, ctx); if (!arePropertiesPopulated) { CloudBlobSharedImpl.UpdateAfterFetchAttributes(attributes, resp, isRangeGet); storedMD5 = resp.Headers[HttpResponseHeader.ContentMd5]; if (!options.DisableContentMD5Validation.Value && options.UseTransactionalMD5.Value && string.IsNullOrEmpty(storedMD5)) { throw new StorageException( cmd.CurrentResult, SR.MD5NotPresentError, null) { IsRetryable = false }; } lockedETag = attributes.Properties.ETag; arePropertiesPopulated = true; } return NullType.Value; }; getCmd.PostProcessResponse = (cmd, resp, ex, ctx) => { long validateLength = startingLength.HasValue ? startingLength.Value : (attributes.Properties.Length - startingOffset); HttpResponseParsers.ValidateResponseStreamMd5AndLength(validateLength, storedMD5, cmd, cmd.StreamCopyState); return NullType.Value; }; return getCmd; }
/// <summary> /// Implements the FetchAttributes method. The attributes are updated immediately. /// </summary> /// <param name="blobUri">The URI of the blob.</param> /// <param name="accessCondition">An object that represents the access conditions for the blob. If <c>null</c>, no condition is used.</param> /// <param name="options">A <see cref="BlobRequestOptions"/> object that specifies any additional options for the request.</param> /// <returns>A <see cref="RESTCommand{T}"/> that fetches the attributes.</returns> private RESTCommand<ICloudBlob> GetBlobReferenceImpl(Uri blobUri, AccessCondition accessCondition, BlobRequestOptions options) { // If the blob Uri contains SAS credentials, we need to use those // credentials instead of this service client's stored credentials. StorageCredentials parsedCredentials; DateTimeOffset? parsedSnapshot; blobUri = NavigationHelper.ParseBlobQueryAndVerify(blobUri, out parsedCredentials, out parsedSnapshot); CloudBlobClient client = parsedCredentials != null ? new CloudBlobClient(this.BaseUri, parsedCredentials) : this; RESTCommand<ICloudBlob> getCmd = new RESTCommand<ICloudBlob>(client.Credentials, blobUri); getCmd.ApplyRequestOptions(options); getCmd.BuildRequestDelegate = (uri, builder, serverTimeout, ctx) => BlobHttpWebRequestFactory.GetProperties(uri, serverTimeout, null /* snapshot */, accessCondition, ctx); getCmd.SignRequest = client.AuthenticationHandler.SignRequest; getCmd.PreProcessResponse = (cmd, resp, ex, ctx) => { HttpResponseParsers.ProcessExpectedStatusCodeNoException(HttpStatusCode.OK, resp, null /* retVal */, cmd, ex, ctx); BlobAttributes attributes = new BlobAttributes() { Uri = blobUri, SnapshotTime = parsedSnapshot, }; CloudBlobSharedImpl.UpdateAfterFetchAttributes(attributes, resp, false); switch (attributes.Properties.BlobType) { case BlobType.BlockBlob: return new CloudBlockBlob(attributes, client); case BlobType.PageBlob: return new CloudPageBlob(attributes, client); default: throw new InvalidOperationException(); } }; return getCmd; }
/// <summary> /// Implementation of the AbortCopy method. No result is produced. /// </summary> /// <param name="copyId">The copy ID of the copy operation to abort.</param> /// <param name="accessCondition">An object that represents the access conditions for the operation. If null, no condition is used.</param> /// <param name="options">An object that specifies any additional options for the request.</param> /// <returns>A <see cref="RESTCommand{T}"/> that aborts the copy.</returns> internal static RESTCommand<NullType> AbortCopyImpl(ICloudBlob blob, BlobAttributes attributes, string copyId, AccessCondition accessCondition, BlobRequestOptions options) { CommonUtils.AssertNotNull("copyId", copyId); RESTCommand<NullType> putCmd = new RESTCommand<NullType>(blob.ServiceClient.Credentials, attributes.Uri); putCmd.ApplyRequestOptions(options); putCmd.BuildRequestDelegate = (uri, builder, serverTimeout, ctx) => BlobHttpWebRequestFactory.AbortCopy(uri, serverTimeout, copyId, accessCondition, ctx); putCmd.SignRequest = blob.ServiceClient.AuthenticationHandler.SignRequest; putCmd.PreProcessResponse = (cmd, resp, ex, ctx) => HttpResponseParsers.ProcessExpectedStatusCodeNoException(new HttpStatusCode[] { HttpStatusCode.OK, HttpStatusCode.NoContent }, resp, NullType.Value, cmd, ex, ctx); return putCmd; }
/// <summary> /// Initializes a new instance of the <see cref="CloudBlockBlob"/> class. /// </summary> /// <param name="attributes">The attributes.</param> /// <param name="serviceClient">The service client.</param> internal CloudBlockBlob(BlobAttributes attributes, CloudBlobClient serviceClient) : base(attributes, serviceClient) { this.Properties.BlobType = BlobType.BlockBlob; }
/// <summary> /// Retreive ETag and LastModified date time from response. /// </summary> /// <param name="response">The response to parse.</param> internal static void ParseSizeAndLastModified(BlobAttributes attributes, HttpWebResponse response) { BlobProperties parsedProperties = BlobHttpResponseParsers.GetProperties(response); attributes.Properties.ETag = parsedProperties.ETag ?? attributes.Properties.ETag; attributes.Properties.LastModified = parsedProperties.LastModified ?? attributes.Properties.LastModified; if (parsedProperties.Length > 0) { attributes.Properties.Length = parsedProperties.Length; } }