private async Task ParseMultipart(DocumentsOperationContext context, MergedBatchCommand command) { var boundary = MultipartRequestHelper.GetBoundary( MediaTypeHeaderValue.Parse(HttpContext.Request.ContentType), MultipartRequestHelper.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, RequestBodyStream()); for (var i = 0; i < int.MaxValue; i++) { var section = await reader.ReadNextSectionAsync().ConfigureAwait(false); if (section == null) { break; } var bodyStream = GetBodyStream(section); if (i == 0) { command.ParsedCommands = await BatchRequestParser.BuildCommandsAsync(context, bodyStream, Database, ServerStore); continue; } if (command.AttachmentStreams == null) { command.AttachmentStreams = new Queue <MergedBatchCommand.AttachmentStream>(); command.AttachmentStreamsTempFile = Database.DocumentsStorage.AttachmentsStorage.GetTempFile("batch"); } var attachmentStream = new MergedBatchCommand.AttachmentStream { Stream = command.AttachmentStreamsTempFile.StartNewStream() }; attachmentStream.Hash = await AttachmentsStorageHelper.CopyStreamToFileAndCalculateHash(context, bodyStream, attachmentStream.Stream, Database.DatabaseShutdown); attachmentStream.Stream.Flush(); command.AttachmentStreams.Enqueue(attachmentStream); } }
public async Task BulkDocs() { using (ContextPool.AllocateOperationContext(out DocumentsOperationContext context)) using (var command = new MergedBatchCommand { Database = Database }) { var contentType = HttpContext.Request.ContentType; if (contentType == null || contentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) { command.ParsedCommands = await BatchRequestParser.BuildCommandsAsync(context, RequestBodyStream(), Database, ServerStore); } else if (contentType.StartsWith("multipart/mixed", StringComparison.OrdinalIgnoreCase)) { await ParseMultipart(context, command); } else { ThrowNotSupportedType(contentType); } var waitForIndexesTimeout = GetTimeSpanQueryString("waitForIndexesTimeout", required: false); if (waitForIndexesTimeout != null) { command.ModifiedCollections = new HashSet <string>(); } try { await Database.TxMerger.Enqueue(command); command?.ExceptionDispatchInfo?.Throw(); } catch (ConcurrencyException) { HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; throw; } var waitForReplicasTimeout = GetTimeSpanQueryString("waitForReplicasTimeout", required: false); if (waitForReplicasTimeout != null) { await WaitForReplicationAsync(waitForReplicasTimeout.Value, command); } if (waitForIndexesTimeout != null) { await WaitForIndexesAsync(waitForIndexesTimeout.Value, command.LastChangeVector, command.LastTombstoneEtag, command.ModifiedCollections); } HttpContext.Response.StatusCode = (int)HttpStatusCode.Created; using (var writer = new BlittableJsonTextWriter(context, ResponseBodyStream())) { context.Write(writer, new DynamicJsonValue { ["Results"] = command.Reply }); } } }
public async Task BulkDocs() { using (ContextPool.AllocateOperationContext(out DocumentsOperationContext context)) using (var command = new MergedBatchCommand(Database)) { var contentType = HttpContext.Request.ContentType; if (contentType == null || contentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)) { await BatchRequestParser.BuildCommandsAsync(context, command, RequestBodyStream(), Database, ServerStore); } else if (contentType.StartsWith("multipart/mixed", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("multipart/form-data", StringComparison.OrdinalIgnoreCase)) { await ParseMultipart(context, command); } else { ThrowNotSupportedType(contentType); } var waitForIndexesTimeout = GetTimeSpanQueryString("waitForIndexesTimeout", required: false); var waitForIndexThrow = GetBoolValueQueryString("waitForIndexThrow", required: false) ?? true; var specifiedIndexesQueryString = HttpContext.Request.Query["waitForSpecificIndex"]; if (command.IsClusterTransaction) { if (Server.Configuration.Core.FeaturesAvailability == FeaturesAvailability.Stable) { FeaturesAvailabilityException.Throw("Cluster Transactions"); } using (Database.ClusterTransactionWaiter.CreateTask(out var taskId)) { // Since this is a cluster transaction we are not going to wait for the write assurance of the replication. // Because in any case the user will get a raft index to wait upon on his next request. var options = new ClusterTransactionCommand.ClusterTransactionOptions(taskId) { WaitForIndexesTimeout = waitForIndexesTimeout, WaitForIndexThrow = waitForIndexThrow, SpecifiedIndexesQueryString = specifiedIndexesQueryString.Count > 0 ? specifiedIndexesQueryString.ToList() : null }; await HandleClusterTransaction(context, command, options); } return; } if (waitForIndexesTimeout != null) { command.ModifiedCollections = new HashSet <string>(); } try { await Database.TxMerger.Enqueue(command); command.ExceptionDispatchInfo?.Throw(); } catch (ConcurrencyException) { HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; throw; } var waitForReplicasTimeout = GetTimeSpanQueryString("waitForReplicasTimeout", required: false); if (waitForReplicasTimeout != null) { var numberOfReplicasStr = GetStringQueryString("numberOfReplicasToWaitFor", required: false) ?? "1"; var throwOnTimeoutInWaitForReplicas = GetBoolValueQueryString("throwOnTimeoutInWaitForReplicas", required: false) ?? true; await WaitForReplicationAsync(Database, waitForReplicasTimeout.Value, numberOfReplicasStr, throwOnTimeoutInWaitForReplicas, command.LastChangeVector); } if (waitForIndexesTimeout != null) { await WaitForIndexesAsync(ContextPool, Database, waitForIndexesTimeout.Value, specifiedIndexesQueryString.ToList(), waitForIndexThrow, command.LastChangeVector, command.LastTombstoneEtag, command.ModifiedCollections); } HttpContext.Response.StatusCode = (int)HttpStatusCode.Created; using (var writer = new BlittableJsonTextWriter(context, ResponseBodyStream())) { context.Write(writer, new DynamicJsonValue { [nameof(BatchCommandResult.Results)] = command.Reply }); } } }