private static async Task StartAndWriteAsyncAwaited(this ProtoResponse response, string text, Encoding encoding, CancellationToken cancellationToken, Task startAsyncTask) { await startAsyncTask; Write(response, text, encoding); await response.BodyWriter.FlushAsync(cancellationToken); }
private static Task SendFileAsyncCore(ProtoResponse response, string fileName, long offset, long?count, CancellationToken cancellationToken = default) { var sendFile = response.ProtoContext.Features.Get <IProtoSendFileFeature>(); if (sendFile == null) { return(SendFileAsyncCore(response.Body, fileName, offset, count, cancellationToken)); } return(sendFile.SendFileAsync(fileName, offset, count, cancellationToken)); }
/// <summary> /// Adds the given trailer header to the trailers collection to be sent at the end of the response body. /// Check <see cref="SupportsTrailers" /> or an InvalidOperationException may be thrown. /// </summary> /// <param name="response"></param> /// <param name="trailerName"></param> /// <param name="trailerValues"></param> public static void AppendTrailer(this ProtoResponse response, string trailerName, StringValues trailerValues) { var feature = response.ProtoContext.Features.Get <IProtoResponseTrailersFeature>(); if (feature?.Trailers == null || feature.Trailers.IsReadOnly) { throw new InvalidOperationException("Trailers are not supported for this response."); } feature.Trailers.Append(trailerName, trailerValues); }
/// <summary> /// Sends the given file using the SendFile extension. /// </summary> /// <param name="response"></param> /// <param name="file">The file.</param> /// <param name="offset">The offset in the file.</param> /// <param name="count">The number of bytes to send, or null to send the remainder of the file.</param> /// <param name="cancellationToken"></param> /// <returns></returns> public static Task SendFileAsync(this ProtoResponse response, IFileInfo file, long offset, long?count, CancellationToken cancellationToken = default) { if (response == null) { throw new ArgumentNullException(nameof(response)); } if (file == null) { throw new ArgumentNullException(nameof(file)); } return(SendFileAsyncCore(response, file, offset, count, cancellationToken)); }
/// <summary> /// Writes the given text to the response body. UTF-8 encoding will be used. /// </summary> /// <param name="response">The <see cref="ProtoResponse"/>.</param> /// <param name="text">The text to write to the response.</param> /// <param name="cancellationToken">Notifies when request operations should be cancelled.</param> /// <returns>A task that represents the completion of the write operation.</returns> public static Task WriteAsync(this ProtoResponse response, string text, CancellationToken cancellationToken = default(CancellationToken)) { if (response == null) { throw new ArgumentNullException(nameof(response)); } if (text == null) { throw new ArgumentNullException(nameof(text)); } return(response.WriteAsync(text, Encoding.UTF8, cancellationToken)); }
public static void Clear(this ProtoResponse response) { if (response.HasStarted) { throw new InvalidOperationException("The response cannot be cleared, it has already started sending."); } response.StatusCode = 200; response.ProtoContext.Features.Get <IProtoResponseFeature>().ReasonPhrase = null; response.Headers.Clear(); if (response.Body.CanSeek) { response.Body.SetLength(0); } }
/// <summary> /// Sends the given file using the SendFile extension. /// </summary> /// <param name="response"></param> /// <param name="fileName">The full path to the file.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param> /// <returns></returns> public static Task SendFileAsync(this ProtoResponse response, string fileName, CancellationToken cancellationToken = default) { if (response == null) { throw new ArgumentNullException(nameof(response)); } if (fileName == null) { throw new ArgumentNullException(nameof(fileName)); } return(SendFileAsyncCore(response, fileName, 0, null, cancellationToken)); }
private static void Write(this ProtoResponse response, string text, Encoding encoding) { var minimumByteSize = GetEncodingMaxByteSize(encoding); var pipeWriter = response.BodyWriter; var encodedLength = encoding.GetByteCount(text); var destination = pipeWriter.GetSpan(minimumByteSize); if (encodedLength <= destination.Length) { // Just call Encoding.GetBytes if everything will fit into a single segment. var bytesWritten = encoding.GetBytes(text, destination); pipeWriter.Advance(bytesWritten); } else { WriteMultiSegmentEncoded(pipeWriter, text, encoding, destination, encodedLength, minimumByteSize); } }
private static async Task SendFileAsyncCore(ProtoResponse response, IFileInfo file, long offset, long?count, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(file.PhysicalPath)) { CheckRange(offset, count, file.Length); using (var fileContent = file.CreateReadStream()) { if (offset > 0) { fileContent.Seek(offset, SeekOrigin.Begin); } await StreamCopyOperation.CopyToAsync(fileContent, response.Body, count, cancellationToken); } } else { await response.SendFileAsync(file.PhysicalPath, offset, count, cancellationToken); } }
/// <summary> /// Writes the given text to the response body using the given encoding. /// </summary> /// <param name="response">The <see cref="ProtoResponse"/>.</param> /// <param name="text">The text to write to the response.</param> /// <param name="encoding">The encoding to use.</param> /// <param name="cancellationToken">Notifies when request operations should be cancelled.</param> /// <returns>A task that represents the completion of the write operation.</returns> public static Task WriteAsync(this ProtoResponse response, string text, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken)) { if (response == null) { throw new ArgumentNullException(nameof(response)); } if (text == null) { throw new ArgumentNullException(nameof(text)); } if (encoding == null) { throw new ArgumentNullException(nameof(encoding)); } // Need to call StartAsync before GetMemory/GetSpan if (!response.HasStarted) { var startAsyncTask = response.StartAsync(cancellationToken); if (!startAsyncTask.IsCompletedSuccessfully) { return(StartAndWriteAsyncAwaited(response, text, encoding, cancellationToken, startAsyncTask)); } } Write(response, text, encoding); var flushAsyncTask = response.BodyWriter.FlushAsync(cancellationToken); if (flushAsyncTask.IsCompletedSuccessfully) { // Most implementations of ValueTask reset state in GetResult, so call it before returning a completed task. flushAsyncTask.GetAwaiter().GetResult(); return(Task.CompletedTask); } return(flushAsyncTask.AsTask()); }
public static ResponseHeaders GetTypedHeaders(this ProtoResponse response) { return(new ResponseHeaders(response.Headers)); }
/// <summary> /// Indicates if the server supports sending trailer headers for this response. /// </summary> /// <param name="response"></param> /// <returns></returns> public static bool SupportsTrailers(this ProtoResponse response) { var feature = response.ProtoContext.Features.Get <IProtoResponseTrailersFeature>(); return(feature?.Trailers != null && !feature.Trailers.IsReadOnly); }
/// <summary> /// Adds the given trailer name to the 'Trailer' response header. This must happen before the response headers are sent. /// </summary> /// <param name="response"></param> /// <param name="trailerName"></param> public static void DeclareTrailer(this ProtoResponse response, string trailerName) { response.Headers.AppendCommaSeparatedValues(Trailer, trailerName); }