internal static Tuple <HttpWebRequest, Stream> BuildRequestForTableOperation(Uri uri, UriQueryBuilder builder, IBufferManager bufferManager, int?timeout, TableOperation operation, bool useVersionHeader, OperationContext ctx, TableRequestOptions options, string accountName) { HttpWebRequest msg = BuildRequestCore(uri, builder, operation.HttpMethod, timeout, useVersionHeader, ctx); TablePayloadFormat payloadFormat = options.PayloadFormat.Value; // Set Accept and Content-Type based on the payload format. SetAcceptHeaderForHttpWebRequest(msg, payloadFormat); Logger.LogInformational(ctx, SR.PayloadFormat, payloadFormat); if (operation.HttpMethod != "HEAD" && operation.HttpMethod != "GET") { SetContentTypeForHttpWebRequest(msg, payloadFormat); } if (operation.OperationType == TableOperationType.InsertOrMerge || operation.OperationType == TableOperationType.Merge) { options.AssertNoEncryptionPolicyOrStrictMode(); // post tunnelling msg.Headers.Add("X-HTTP-Method", "MERGE"); } // etag if (operation.OperationType == TableOperationType.Delete || operation.OperationType == TableOperationType.Replace || operation.OperationType == TableOperationType.Merge) { if (operation.Entity.ETag != null) { msg.Headers.Add("If-Match", operation.Entity.ETag); } } // Prefer header if (operation.OperationType == TableOperationType.Insert) { msg.Headers.Add("Prefer", operation.EchoContent ? "return-content" : "return-no-content"); } if (operation.OperationType == TableOperationType.Insert || operation.OperationType == TableOperationType.Merge || operation.OperationType == TableOperationType.InsertOrMerge || operation.OperationType == TableOperationType.InsertOrReplace || operation.OperationType == TableOperationType.Replace) { // create the writer, indent for readability of the examples. ODataMessageWriterSettings writerSettings = new ODataMessageWriterSettings() { CheckCharacters = false, // sets this flag on the XmlWriter for ATOM Version = TableConstants.ODataProtocolVersion // set the Odata version to use when writing the entry }; HttpWebRequestAdapterMessage adapterMsg = new HttpWebRequestAdapterMessage(msg, bufferManager); if (operation.HttpMethod != "HEAD" && operation.HttpMethod != "GET") { SetContentTypeForAdapterMessage(adapterMsg, payloadFormat); } ODataMessageWriter odataWriter = new ODataMessageWriter(adapterMsg, writerSettings, new TableStorageModel(accountName)); ODataWriter writer = odataWriter.CreateODataEntryWriter(); WriteOdataEntity(operation.Entity, operation.OperationType, ctx, writer, options); return(new Tuple <HttpWebRequest, Stream>(adapterMsg.GetPopulatedMessage(), adapterMsg.GetStream())); } return(new Tuple <HttpWebRequest, Stream>(msg, null)); }
internal static Tuple <HttpWebRequest, Stream> BuildRequestForTableBatchOperation(Uri uri, UriQueryBuilder builder, IBufferManager bufferManager, int?timeout, string tableName, TableBatchOperation batch, bool useVersionHeader, OperationContext ctx, TableRequestOptions options, string accountName) { HttpWebRequest msg = BuildRequestCore(NavigationHelper.AppendPathToSingleUri(uri, "$batch"), builder, "POST", timeout, useVersionHeader, ctx); TablePayloadFormat payloadFormat = options.PayloadFormat.Value; Logger.LogInformational(ctx, SR.PayloadFormat, payloadFormat); // create the writer, indent for readability of the examples. ODataMessageWriterSettings writerSettings = new ODataMessageWriterSettings() { CheckCharacters = false, // sets this flag on the XmlWriter for ATOM Version = TableConstants.ODataProtocolVersion // set the Odata version to use when writing the entry }; HttpWebRequestAdapterMessage adapterMsg = new HttpWebRequestAdapterMessage(msg, bufferManager); // Start Batch ODataMessageWriter odataWriter = new ODataMessageWriter(adapterMsg, writerSettings); ODataBatchWriter batchWriter = odataWriter.CreateODataBatchWriter(); batchWriter.WriteStartBatch(); bool isQuery = batch.Count == 1 && batch[0].OperationType == TableOperationType.Retrieve; // Query operations should not be inside changeset in payload if (!isQuery) { // Start Operation batchWriter.WriteStartChangeset(); batchWriter.Flush(); } foreach (TableOperation operation in batch) { string httpMethod = operation.HttpMethod; if (operation.OperationType == TableOperationType.Merge || operation.OperationType == TableOperationType.InsertOrMerge) { options.AssertNoEncryptionPolicyOrStrictMode(); httpMethod = "MERGE"; } ODataBatchOperationRequestMessage mimePartMsg = batchWriter.CreateOperationRequestMessage(httpMethod, operation.GenerateRequestURI(uri, tableName)); SetAcceptAndContentTypeForODataBatchMessage(mimePartMsg, payloadFormat); // etag if (operation.OperationType == TableOperationType.Delete || operation.OperationType == TableOperationType.Replace || operation.OperationType == TableOperationType.Merge) { mimePartMsg.SetHeader("If-Match", operation.Entity.ETag); } // Prefer header if (operation.OperationType == TableOperationType.Insert) { mimePartMsg.SetHeader("Prefer", operation.EchoContent ? "return-content" : "return-no-content"); } if (operation.OperationType != TableOperationType.Delete && operation.OperationType != TableOperationType.Retrieve) { using (ODataMessageWriter batchEntryWriter = new ODataMessageWriter(mimePartMsg, writerSettings, new TableStorageModel(accountName))) { // Write entity ODataWriter entryWriter = batchEntryWriter.CreateODataEntryWriter(); WriteOdataEntity(operation.Entity, operation.OperationType, ctx, entryWriter, options); } } } if (!isQuery) { // End Operation batchWriter.WriteEndChangeset(); } // End Batch batchWriter.WriteEndBatch(); batchWriter.Flush(); return(new Tuple <HttpWebRequest, Stream>(adapterMsg.GetPopulatedMessage(), adapterMsg.GetStream())); }
internal static Tuple<HttpWebRequest, Stream> BuildRequestForTableOperation(Uri uri, UriQueryBuilder builder, IBufferManager bufferManager, int? timeout, TableOperation operation, bool useVersionHeader, OperationContext ctx, TableRequestOptions options, string accountName) { HttpWebRequest msg = BuildRequestCore(uri, builder, operation.HttpMethod, timeout, useVersionHeader, ctx); TablePayloadFormat payloadFormat = options.PayloadFormat.Value; // Set Accept and Content-Type based on the payload format. SetAcceptHeaderForHttpWebRequest(msg, payloadFormat); Logger.LogInformational(ctx, SR.PayloadFormat, payloadFormat); if (operation.HttpMethod != "HEAD" && operation.HttpMethod != "GET") { SetContentTypeForHttpWebRequest(msg, payloadFormat); } if (operation.OperationType == TableOperationType.InsertOrMerge || operation.OperationType == TableOperationType.Merge) { options.AssertNoEncryptionPolicyOrStrictMode(); // post tunnelling msg.Headers.Add("X-HTTP-Method", "MERGE"); } // etag if (operation.OperationType == TableOperationType.Delete || operation.OperationType == TableOperationType.Replace || operation.OperationType == TableOperationType.Merge) { if (operation.Entity.ETag != null) { msg.Headers.Add("If-Match", operation.Entity.ETag); } } // prefer header if (operation.OperationType == TableOperationType.Insert) { msg.Headers.Add("Prefer", operation.EchoContent ? "return-content" : "return-no-content"); } if (operation.OperationType == TableOperationType.Insert || operation.OperationType == TableOperationType.Merge || operation.OperationType == TableOperationType.InsertOrMerge || operation.OperationType == TableOperationType.InsertOrReplace || operation.OperationType == TableOperationType.Replace) { // create the writer, indent for readability of the examples. ODataMessageWriterSettings writerSettings = new ODataMessageWriterSettings() { CheckCharacters = false, // sets this flag on the XmlWriter for ATOM Version = TableConstants.ODataProtocolVersion // set the Odata version to use when writing the entry }; HttpWebRequestAdapterMessage adapterMsg = new HttpWebRequestAdapterMessage(msg, bufferManager); if (operation.HttpMethod != "HEAD" && operation.HttpMethod != "GET") { SetContentTypeForAdapterMessage(adapterMsg, payloadFormat); } ODataMessageWriter odataWriter = new ODataMessageWriter(adapterMsg, writerSettings, new TableStorageModel(accountName)); ODataWriter writer = odataWriter.CreateODataEntryWriter(); WriteOdataEntity(operation.Entity, operation.OperationType, ctx, writer, options); return new Tuple<HttpWebRequest, Stream>(adapterMsg.GetPopulatedMessage(), adapterMsg.GetStream()); } return new Tuple<HttpWebRequest, Stream>(msg, null); }
internal static Tuple<HttpWebRequest, Stream> BuildRequestForTableBatchOperation(Uri uri, UriQueryBuilder builder, IBufferManager bufferManager, int? timeout, string tableName, TableBatchOperation batch, bool useVersionHeader, OperationContext ctx, TableRequestOptions options, string accountName) { HttpWebRequest msg = BuildRequestCore(NavigationHelper.AppendPathToSingleUri(uri, "$batch"), builder, "POST", timeout, useVersionHeader, ctx); TablePayloadFormat payloadFormat = options.PayloadFormat.Value; Logger.LogInformational(ctx, SR.PayloadFormat, payloadFormat); // create the writer, indent for readability of the examples. ODataMessageWriterSettings writerSettings = new ODataMessageWriterSettings() { CheckCharacters = false, // sets this flag on the XmlWriter for ATOM Version = TableConstants.ODataProtocolVersion // set the Odata version to use when writing the entry }; HttpWebRequestAdapterMessage adapterMsg = new HttpWebRequestAdapterMessage(msg, bufferManager); ODataMessageWriter odataWriter = new ODataMessageWriter(adapterMsg, writerSettings); ODataBatchWriter batchWriter = odataWriter.CreateODataBatchWriter(); batchWriter.WriteStartBatch(); bool isQuery = batch.Count == 1 && batch[0].OperationType == TableOperationType.Retrieve; // Query operations should not be inside changeset in payload if (!isQuery) { // Start Operation batchWriter.WriteStartChangeset(); batchWriter.Flush(); } foreach (TableOperation operation in batch) { string httpMethod = operation.HttpMethod; if (operation.OperationType == TableOperationType.Merge || operation.OperationType == TableOperationType.InsertOrMerge) { options.AssertNoEncryptionPolicyOrStrictMode(); httpMethod = "MERGE"; } ODataBatchOperationRequestMessage mimePartMsg = batchWriter.CreateOperationRequestMessage(httpMethod, operation.GenerateRequestURI(uri, tableName)); SetAcceptAndContentTypeForODataBatchMessage(mimePartMsg, payloadFormat); // etag if (operation.OperationType == TableOperationType.Delete || operation.OperationType == TableOperationType.Replace || operation.OperationType == TableOperationType.Merge) { mimePartMsg.SetHeader("If-Match", operation.Entity.ETag); } // Prefer header if (operation.OperationType == TableOperationType.Insert) { mimePartMsg.SetHeader("Prefer", operation.EchoContent ? "return-content" : "return-no-content"); } if (operation.OperationType != TableOperationType.Delete && operation.OperationType != TableOperationType.Retrieve) { using (ODataMessageWriter batchEntryWriter = new ODataMessageWriter(mimePartMsg, writerSettings, new TableStorageModel(accountName))) { // Write entity ODataWriter entryWriter = batchEntryWriter.CreateODataEntryWriter(); WriteOdataEntity(operation.Entity, operation.OperationType, ctx, entryWriter, options); } } } if (!isQuery) { // End Operation batchWriter.WriteEndChangeset(); } // End Batch batchWriter.WriteEndBatch(); batchWriter.Flush(); return new Tuple<HttpWebRequest, Stream>(adapterMsg.GetPopulatedMessage(), adapterMsg.GetStream()); }
internal static Tuple <HttpWebRequest, Stream> BuildRequestForTableOperation(Uri uri, UriQueryBuilder builder, IBufferManager bufferManager, int?timeout, TableOperation operation, bool useVersionHeader, OperationContext ctx, TableRequestOptions options) { HttpWebRequest msg = BuildRequestCore(uri, builder, operation.HttpMethod, timeout, useVersionHeader, ctx); TablePayloadFormat payloadFormat = options.PayloadFormat.Value; // Set Accept and Content-Type based on the payload format. SetAcceptHeaderForHttpWebRequest(msg, payloadFormat); Logger.LogInformational(ctx, SR.PayloadFormat, payloadFormat); msg.Headers.Add(Constants.HeaderConstants.DataServiceVersion, Constants.HeaderConstants.DataServiceVersionValue); if (operation.HttpMethod != "HEAD" && operation.HttpMethod != "GET") { msg.ContentType = Constants.JsonContentTypeHeaderValue; } if (operation.OperationType == TableOperationType.InsertOrMerge || operation.OperationType == TableOperationType.Merge) { // Client-side encryption is not supported on merge requests. // This is because we maintain the list of encrypted properties as a property on the entity, and we can't update this // properly for merge operations. options.AssertNoEncryptionPolicyOrStrictMode(); // post tunnelling msg.Headers.Add(Constants.HeaderConstants.PostTunnelling, "MERGE"); } if (operation.OperationType == TableOperationType.RotateEncryptionKey) { // post tunnelling msg.Headers.Add(Constants.HeaderConstants.PostTunnelling, "MERGE"); } // etag if (operation.OperationType == TableOperationType.Delete || operation.OperationType == TableOperationType.Replace || operation.OperationType == TableOperationType.Merge || operation.OperationType == TableOperationType.RotateEncryptionKey) { if (operation.ETag != null) { msg.Headers.Add(Constants.HeaderConstants.IfMatch, operation.ETag); } } // Prefer header if (operation.OperationType == TableOperationType.Insert) { msg.Headers.Add(Constants.HeaderConstants.Prefer, operation.EchoContent ? Constants.HeaderConstants.PreferReturnContent : Constants.HeaderConstants.PreferReturnNoContent); } if (operation.OperationType == TableOperationType.Insert || operation.OperationType == TableOperationType.Merge || operation.OperationType == TableOperationType.InsertOrMerge || operation.OperationType == TableOperationType.InsertOrReplace || operation.OperationType == TableOperationType.Replace || operation.OperationType == TableOperationType.RotateEncryptionKey) { MultiBufferMemoryStream ms = new MultiBufferMemoryStream(bufferManager); using (JsonTextWriter jsonWriter = new JsonTextWriter(new StreamWriter(new NonCloseableStream(ms)))) { WriteEntityContent(operation, ctx, options, jsonWriter); } ms.Seek(0, SeekOrigin.Begin); msg.ContentLength = ms.Length; return(new Tuple <HttpWebRequest, Stream>(msg, ms)); } return(new Tuple <HttpWebRequest, Stream>(msg, null)); }
internal static Tuple <HttpWebRequest, Stream> BuildRequestForTableBatchOperation(Uri uri, UriQueryBuilder builder, IBufferManager bufferManager, int?timeout, string tableName, TableBatchOperation batch, bool useVersionHeader, OperationContext ctx, TableRequestOptions options) { HttpWebRequest msg = BuildRequestCore(NavigationHelper.AppendPathToSingleUri(uri, "$batch"), builder, "POST", timeout, useVersionHeader, ctx); TablePayloadFormat payloadFormat = options.PayloadFormat.Value; Logger.LogInformational(ctx, SR.PayloadFormat, payloadFormat); MultiBufferMemoryStream batchContentStream = new MultiBufferMemoryStream(bufferManager); using (StreamWriter contentWriter = new StreamWriter(new NonCloseableStream(batchContentStream))) { string batchID = Guid.NewGuid().ToString(); string changesetID = Guid.NewGuid().ToString(); msg.Headers.Add(Constants.HeaderConstants.DataServiceVersion, Constants.HeaderConstants.DataServiceVersionValue); msg.ContentType = Constants.BatchBoundaryMarker + batchID; string batchSeparator = Constants.BatchSeparator + batchID; string changesetSeparator = Constants.ChangesetSeparator + changesetID; string acceptHeader = "Accept: "; switch (payloadFormat) { case TablePayloadFormat.Json: acceptHeader = acceptHeader + Constants.JsonLightAcceptHeaderValue; break; case TablePayloadFormat.JsonFullMetadata: acceptHeader = acceptHeader + Constants.JsonFullMetadataAcceptHeaderValue; break; case TablePayloadFormat.JsonNoMetadata: acceptHeader = acceptHeader + Constants.JsonNoMetadataAcceptHeaderValue; break; } contentWriter.WriteLine(batchSeparator); bool isQuery = batch.Count == 1 && batch[0].OperationType == TableOperationType.Retrieve; // Query operations should not be inside changeset in payload if (!isQuery) { // Start Operation contentWriter.WriteLine(Constants.ChangesetBoundaryMarker + changesetID); contentWriter.WriteLine(); } foreach (TableOperation operation in batch) { string httpMethod = operation.HttpMethod; if (operation.OperationType == TableOperationType.Merge || operation.OperationType == TableOperationType.InsertOrMerge) { options.AssertNoEncryptionPolicyOrStrictMode(); httpMethod = "MERGE"; } if (operation.OperationType == TableOperationType.RotateEncryptionKey) { httpMethod = "MERGE"; } if (!isQuery) { contentWriter.WriteLine(changesetSeparator); } contentWriter.WriteLine(Constants.ContentTypeApplicationHttp); contentWriter.WriteLine(Constants.ContentTransferEncodingBinary); contentWriter.WriteLine(); string tableURI = Uri.EscapeUriString(operation.GenerateRequestURI(uri, tableName).ToString()); // "EscapeUriString" is almost exactly what we need, except that it contains special logic for // the percent sign, which results in an off-by-one error in the number of times "%" is encoded. // This corrects for that. tableURI = tableURI.Replace(@"%25", @"%"); contentWriter.WriteLine(httpMethod + " " + tableURI + " " + Constants.HTTP1_1); contentWriter.WriteLine(acceptHeader); contentWriter.WriteLine(Constants.ContentTypeApplicationJson); if (operation.OperationType == TableOperationType.Insert) { contentWriter.WriteLine(Constants.HeaderConstants.Prefer + @": " + (operation.EchoContent ? Constants.HeaderConstants.PreferReturnContent : Constants.HeaderConstants.PreferReturnNoContent)); } contentWriter.WriteLine(Constants.HeaderConstants.DataServiceVersion + ": " + Constants.HeaderConstants.DataServiceVersionValue); // etag if (operation.OperationType == TableOperationType.Delete || operation.OperationType == TableOperationType.Replace || operation.OperationType == TableOperationType.Merge || operation.OperationType == TableOperationType.RotateEncryptionKey) { contentWriter.WriteLine(Constants.HeaderConstants.IfMatch + @": " + operation.ETag); } contentWriter.WriteLine(); if (operation.OperationType != TableOperationType.Delete && operation.OperationType != TableOperationType.Retrieve) { using (JsonTextWriter jsonWriter = new JsonTextWriter(contentWriter)) { jsonWriter.CloseOutput = false; WriteEntityContent(operation, ctx, options, jsonWriter); } contentWriter.WriteLine(); } } if (!isQuery) { contentWriter.WriteLine(changesetSeparator + "--"); } contentWriter.WriteLine(batchSeparator + "--"); } batchContentStream.Seek(0, SeekOrigin.Begin); msg.ContentLength = batchContentStream.Length; return(new Tuple <HttpWebRequest, Stream>(msg, batchContentStream)); }