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));
        }
Exemple #3
0
        /// <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));
        }
Exemple #6
0
 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));
 }
Exemple #12
0
        /// <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);
        }
Exemple #13
0
 /// <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);
 }