tgstation-server 6.12.0
The /tg/station 13 server suite
Loading...
Searching...
No Matches
FileUploadProvider.cs
Go to the documentation of this file.
1using System;
2using System.IO;
3using System.Threading;
4using System.Threading.Tasks;
5
6using Microsoft.AspNetCore.WebUtilities;
7
11
13{
16 {
18 public FileTicketResponse Ticket { get; }
19
23 readonly CancellationTokenSource ticketExpiryCts;
24
28 readonly TaskCompletionSource<Stream?> streamTcs;
29
33 readonly TaskCompletionSource completionTcs;
34
39
44
51 {
52 Ticket = ticket ?? throw new ArgumentNullException(nameof(ticket));
53
54 ticketExpiryCts = new CancellationTokenSource();
55 streamTcs = new TaskCompletionSource<Stream?>();
56 completionTcs = new TaskCompletionSource();
57 this.streamKind = streamKind;
58 }
59
61 public ValueTask DisposeAsync()
62 {
63 ticketExpiryCts.Dispose();
64 completionTcs.TrySetResult();
65 return ValueTask.CompletedTask;
66 }
67
71 public void Expire()
72 {
73 if (!completionTcs.Task.IsCompleted)
74 ticketExpiryCts.Cancel();
75 }
76
83 public async ValueTask<ErrorMessageResponse?> Completion(Stream stream, CancellationToken cancellationToken)
84 {
85 ArgumentNullException.ThrowIfNull(stream);
86
87 if (ticketExpiryCts.IsCancellationRequested)
88 return new ErrorMessageResponse(ErrorCode.ResourceNotPresent);
89
90 Stream? bufferedStream = null;
91 try
92 {
93 switch (streamKind)
94 {
95 case FileUploadStreamKind.ForSynchronousIO:
96 // big reads, we should buffer to disk
97 // NOTE: We do this here ONLY because we can't use async methods in a synchronous context
98 // Ideally we should be doing 0 buffering here, but this is a compromise
99 bufferedStream = new FileBufferingReadStream(stream, DefaultIOManager.DefaultBufferSize);
100 await bufferedStream.DrainAsync(cancellationToken);
101 break;
102 case FileUploadStreamKind.None:
103 break;
104 default:
105 throw new InvalidOperationException($"Invalid FileUploadStreamKind: {streamKind}");
106 }
107 }
108 catch
109 {
110 if (bufferedStream != null)
111 await bufferedStream.DisposeAsync();
112
113 throw;
114 }
115
116 await using (bufferedStream)
117 {
118 streamTcs.TrySetResult(bufferedStream ?? stream);
119
120 await completionTcs.Task.WaitAsync(cancellationToken);
121 return errorMessage;
122 }
123 }
124
126 public void SetError(ErrorCode errorCode, string? additionalData)
127 {
128 if (errorMessage != null)
129 throw new InvalidOperationException("Error already set!");
130
131 errorMessage = new ErrorMessageResponse(errorCode)
132 {
133 AdditionalData = additionalData,
134 };
135 completionTcs.TrySetResult();
136 }
137
139 public async ValueTask<Stream> GetResult(CancellationToken cancellationToken)
140 => await ((IFileUploadTicket)this).GetResult(cancellationToken)
141 ?? throw new InvalidOperationException("Upload ticket expired!");
142
144 async ValueTask<Stream?> IFileUploadTicket.GetResult(CancellationToken cancellationToken)
145 {
146 using (cancellationToken.Register(() => streamTcs.TrySetCanceled(cancellationToken)))
147 using (ticketExpiryCts.Token.Register(() => streamTcs.TrySetResult(null)))
148 return await streamTcs.Task;
149 }
150 }
151}
Represents an error message returned by the server.
Response for when file transfers are necessary.
IIOManager that resolves paths to Environment.CurrentDirectory.
const int DefaultBufferSize
Default FileStream buffer size used by .NET.
FileTicketResponse Ticket
The FileTicketResponse.
FileUploadProvider(FileTicketResponse ticket, FileUploadStreamKind streamKind)
Initializes a new instance of the FileUploadProvider class.
readonly TaskCompletionSource< Stream?> streamTcs
The TaskCompletionSource<TResult> for the Stream.
async ValueTask< ErrorMessageResponse?> Completion(Stream stream, CancellationToken cancellationToken)
Resolve the stream for the FileUploadProvider and awaits the upload.
void SetError(ErrorCode errorCode, string? additionalData)
Sets an errorCode that indicates why the consuming operation could not complete. May only be called ...
readonly FileUploadStreamKind streamKind
Determines the backing for the Stream returned from GetResult(CancellationToken).
ErrorMessageResponse? errorMessage
The ErrorMessageResponse that occurred while processing the upload if any.
readonly CancellationTokenSource ticketExpiryCts
The CancellationTokenSource for the ticket duration.
async ValueTask< Stream > GetResult(CancellationToken cancellationToken)
Gets the provided Stream. May be called multiple times, though cancelling any may cause all calls to ...
readonly TaskCompletionSource completionTcs
The TaskCompletionSource that completes in IDisposable.Dispose or when SetError(ErrorCode,...
void Expire()
Expire the FileUploadProvider.
A FileTicketResponse that waits for a pending upload.
new ValueTask< Stream?> GetResult(CancellationToken cancellationToken)
Gets the provided Stream. May be called multiple times, though cancelling any may cause all calls to ...
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
Definition ErrorCode.cs:12
FileUploadStreamKind
Determines the type of global::System.IO.Stream returned from IFileUploadTicket's created from IFileT...