Skip to content

Commit

Permalink
aspnetcore response output using Streams again
Browse files Browse the repository at this point in the history
pipelines needs to be done properly so it works from outside VS
fixes #1070
  • Loading branch information
jakubmisek committed Oct 7, 2022
1 parent bcdc643 commit e599f28
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 10 deletions.
3 changes: 2 additions & 1 deletion src/Peachpie.AspNetCore.Web/RequestContextCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using System.Threading;
using Peachpie.AspNetCore.Web.Session;
using System.Xml.Schema;
using Peachpie.AspNetCore.Web.ResponseOutput;

namespace Peachpie.AspNetCore.Web
{
Expand Down Expand Up @@ -440,7 +441,7 @@ public RequestContextCore(HttpContext httpcontext, string rootPath, Encoding enc
//
this.RootPath = rootPath;

this.InitOutput(httpcontext.Response.Body, new SynchronizedTextWriter(httpcontext.Response, encoding));
this.InitOutput(httpcontext.Response.Body, new DefaultTextWriter(httpcontext.Response, encoding));
this.InitSuperglobals();

this.SetupHeaders();
Expand Down
102 changes: 102 additions & 0 deletions src/Peachpie.AspNetCore.Web/ResponseOutput/DefaultTextWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipelines;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace Peachpie.AspNetCore.Web.ResponseOutput
{
/// <summary>
/// <see cref="TextWriter"/> implementation passing text to underlying response stream in given encoding.
/// </summary>
sealed class DefaultTextWriter : TextWriter
{
HttpResponse HttpResponse { get; }

public override Encoding Encoding { get; }

/// <summary>
/// Invariant number format provider.
/// </summary>
public override IFormatProvider FormatProvider => Pchp.Core.Context.InvariantNumberFormatInfo;

public DefaultTextWriter(HttpResponse response, Encoding encoding)
{
HttpResponse = response ?? throw new ArgumentNullException(nameof(response));
Encoding = encoding ?? throw new ArgumentNullException(nameof(encoding));
}

const int UTF8MaxByteLength = 6;

static int GetEncodingMaxByteSize(Encoding encoding)
{
if (encoding == Encoding.UTF8)
{
return UTF8MaxByteLength;
}

return encoding.GetMaxByteCount(1);
}

/// <summary>
/// Writes a sequence of bytes into the underlying stream.
/// </summary>
public void Write(byte[] buffer, int count)
{
Debug.Assert(buffer != null);
Debug.Assert(count <= buffer.Length);

HttpResponse.Body.WriteAsync(new ReadOnlyMemory<byte>(buffer, 0, count)).GetAwaiter().GetResult();
}

public void Write(ReadOnlySpan<byte> buffer)
{
var array = ArrayPool<byte>.Shared.Rent(buffer.Length);

buffer.CopyTo(array);
Write(array, buffer.Length);

ArrayPool<byte>.Shared.Return(array);
}

public override void Write(string value)
{
HttpResponse.WriteAsync(value, Encoding).GetAwaiter().GetResult();
}

public override void Write(char[] chars, int index, int count)
{
Debug.Assert(chars != null);
Debug.Assert(index <= chars.Length && index >= 0);
Debug.Assert(count >= 0 && count <= chars.Length - index);

var encodedLength = Encoding.GetByteCount(chars, index, count);
var bytes = ArrayPool<byte>.Shared.Rent(encodedLength);
var nbytes = Encoding.GetBytes(chars, index, count, bytes, 0); // == encodedLength

Write(bytes, nbytes);

ArrayPool<byte>.Shared.Return(bytes);
}


public override void Write(char value)
{
// encode the char on stack
Span<byte> encodedCharBuffer = stackalloc byte[GetEncodingMaxByteSize(Encoding)];
Span<char> chars = stackalloc char[1] { value };
var nbytes = Encoding.GetBytes(chars, encodedCharBuffer);

Write(encodedCharBuffer.Slice(0, nbytes)); // NOTE: _tmp is copied by the underlying pipe
}

public override void Flush() => FlushAsync().GetAwaiter().GetResult();

public override Task FlushAsync() => HttpResponse.Body.FlushAsync(CancellationToken.None);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipelines;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace Peachpie.AspNetCore.Web
namespace Peachpie.AspNetCore.Web.ResponseOutput
{
/// <summary>
/// <see cref="TextWriter"/> implementation passing text to underlying response stream in given encoding.
/// </summary>
sealed class SynchronizedTextWriter : TextWriter
internal class PipeTextWriter : TextWriter
{
HttpResponse HttpResponse { get; }

Expand All @@ -24,7 +19,7 @@ sealed class SynchronizedTextWriter : TextWriter
/// </summary>
public override IFormatProvider FormatProvider => Pchp.Core.Context.InvariantNumberFormatInfo;

public SynchronizedTextWriter(HttpResponse response, Encoding encoding)
public PipeTextWriter(HttpResponse response, Encoding encoding)
{
HttpResponse = response ?? throw new ArgumentNullException(nameof(response));
Encoding = encoding ?? throw new ArgumentNullException(nameof(encoding));
Expand All @@ -46,7 +41,7 @@ static int GetEncodingMaxByteSize(Encoding encoding)

public override void Write(string value)
{
if (!string.IsNullOrEmpty(value))
if (value != null)
{
Write(value.AsSpan());
}
Expand All @@ -61,6 +56,14 @@ public override void Write(char[] chars, int index, int count)
Write(chars.AsSpan(index, count));
}

public override void Write(char[] buffer)
{
if (buffer != null)
{
Write(buffer.AsSpan());
}
}

public void Write(ReadOnlySpan<byte> bytes)
{
HttpResponse.BodyWriter.Write(bytes);
Expand Down

0 comments on commit e599f28

Please sign in to comment.