Coder Social home page Coder Social logo

Comments (12)

plachor avatar plachor commented on June 2, 2024 1

For the lowest overhead, make sure to override at least the ValueTask-based methods. As you're only interested in write byte count, you don't have to await anything either.

public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{
    BytesWritten += buffer.Length;
    return innerStream.WriteAsync(buffer, cancellationToken);
}

public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
    return innerStream.ReadAsync(buffer, cancellationToken);
}

public override Task FlushAsync(CancellationToken cancellationToken)
{
    return innerStream.FlushAsync(cancellationToken);
}

Do I have a guarantee that entire response is present at this moment in a stream especially when Transfer-Encoding: chunked is used?

Yes. Note that the amount of data here is the amount of actual application-level data, not counting the chunked encoding overhead.

Hi @MihaZupan / @Tratcher in your sample you suggest to pass through reads is that on purpose ? I thought I should throw there not supported. Or is it required for websockets case (like SingalR)?

from reverse-proxy.

MihaZupan avatar MihaZupan commented on June 2, 2024 1

You can either throw your own exception, or pass the read through and have the original stream do it for you.
In either case, nothing should ever be reading from this stream, so it shouldn't matter in practice.
You can skip overriding the ReadAsync(Memory) overload.

WebSocket connections won't go through the Response.Body stream, they use a different mechanism (IHttpUpgradeFeature / IHttpExtendedConnectFeature).

from reverse-proxy.

MihaZupan avatar MihaZupan commented on June 2, 2024

Are you aware of https://learn.microsoft.com/aspnet/core/fundamentals/http-logging? You may not have to implement many of these things yourself.

Re: response content length, you can either set a custom response body stream that counts the amount of data written, or you can listen to the IForwarderTelemetryConsumer.OnContentTransferred event that includes the content length. See https://microsoft.github.io/reverse-proxy/articles/diagnosing-yarp-issues.html#using-telemetry-events

from reverse-proxy.

marekott avatar marekott commented on June 2, 2024

@MihaZupan thanks for a quick answer. Yes I am aware of http-logging provided by framework but it won't help me a lot. My reverse proxy is using common infrastructure components (nuget packages) which abstracts regular logging from me. Http logging send by you would be handled by it and produced to X sink with common enrichers. With those requests logs I am manually creating custom DTO and sending them to message bus in order to process them in a different way. I don't have possibility to change infrastructure logging and I still should use it to produce coherent logs (errors, warning etc. not request logs) with another microservices.

Regarding second suggestion with custom response body stream, you mean something like this?

public class CountingStreamDecorator : Stream
{
    private readonly Stream innerStream;
    public long BytesRead { get; private set; }

    public CountingStreamDecorator(Stream innerStream) => this.innerStream = innerStream;

    public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        await innerStream.WriteAsync(buffer, offset, count, cancellationToken);
        BytesRead += count;
    }
    
    //Other required staff
}

Usage in custom middelware:

public async Task Invoke(HttpContext context)
{
    var originalBodyStream = context.Response.Body;

    using (var customStream = new CountingStreamDecorator(originalBodyStream))
    {
        context.Response.Body = customStream;
            
        await next(context);
        
        await TryLogIncomingRequest(customStream.BytesRead);
        
        context.Response.Body = originalBodyStream;
    }
}

Do I have a guarantee that entire response is present at this moment in a stream especially when Transfer-Encoding: chunked is used?

from reverse-proxy.

MihaZupan avatar MihaZupan commented on June 2, 2024

For the lowest overhead, make sure to override at least the ValueTask-based methods. As you're only interested in write byte count, you don't have to await anything either.

public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{
    BytesWritten += buffer.Length;
    return innerStream.WriteAsync(buffer, cancellationToken);
}

public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
    // Not really needed - no one should be reading from this stream
    return innerStream.ReadAsync(buffer, cancellationToken);
}

public override Task FlushAsync(CancellationToken cancellationToken)
{
    return innerStream.FlushAsync(cancellationToken);
}

Do I have a guarantee that entire response is present at this moment in a stream especially when Transfer-Encoding: chunked is used?

Yes. Note that the amount of data here is the amount of actual application-level data, not counting the chunked encoding overhead.

from reverse-proxy.

marekott avatar marekott commented on June 2, 2024

I was assuming that YARP would close connection to cluster when all data were transferred to client using small buffer to transfer it in chunks - streaming. This way YARP could reduce allocations.

So when you confirmed that in my custom middleware I have entire response already buffered, this is totally opposite to what I was assuming.

Could you clarify more?

from reverse-proxy.

MihaZupan avatar MihaZupan commented on June 2, 2024

YARP won't buffer the entire response. When you call await next(context), YARP will make the request to the backend server and stream all of the response content back to the client. Writes to the response body stream all happen during that call.

By the time next returns, all of the content will have been transferred, and the amount of data reflected in your stream's BytesWritten.

from reverse-proxy.

marekott avatar marekott commented on June 2, 2024

Thank you for clarification @MihaZupan, it helps me a lot.

from reverse-proxy.

marekott avatar marekott commented on June 2, 2024

@MihaZupan one more question if I may. I am wondering what would be the best option to dispose my stream decorator. I am considering two options:

  1. Dispose my decorator but not propagate that operation to inner stream and reassign original stream to context.Response.Body when I am finished in order to let framework dispose it.
public async Task Invoke(HttpContext context)
{
    var originalBodyStream = context.Response.Body;

    using (var customStream = new CountingStreamDecorator(originalBodyStream))
    {
        context.Response.Body = customStream;
            
        await next(context);
        
        await TryLogIncomingRequest(customStream.BytesRead);
        
        //Reassign original stream hoping that framework will dispose it
        context.Response.Body = originalBodyStream;
    }
}
  1. When disposing my decorator, propagate it to inner stream and skip reassigning value. My custom middlewares that are executed after logging one are not using context.Response.Body.
public async Task Invoke(HttpContext context)
{
    var originalBodyStream = context.Response.Body;

    //Disposing the CountingStreamDecorator will also dispose originalBodyStream 
    using (var customStream = new CountingStreamDecorator(originalBodyStream))
    {
        context.Response.Body = customStream;
            
        await next(context);
        
        await TryLogIncomingRequest(customStream.BytesRead);
        
        //No reassign  of context.Response.Body
    }
}

Could you advice which option is correct or if both are not, how should it be handled? I am asking because I am not sure how framework is disposing context.Response.Body stream, I am guessing that it can either dispose anything that is assigned to it or it can hold reference to originalBodyStream and dispose only it no matter how many decorators are above it.

from reverse-proxy.

Tratcher avatar Tratcher commented on June 2, 2024

It's good practice to re-assign the original stream back to context.Response.Body at the end of your middleware so that upstream middleware see the context in the expected state.

Only dispose things you own, like your decorator. You don't own the original stream, the server does, it will take care of it.

from reverse-proxy.

marekott avatar marekott commented on June 2, 2024

Thank you @Tratcher.

from reverse-proxy.

plachor avatar plachor commented on June 2, 2024

I know this is closed but @MihaZupan could you comment ?

from reverse-proxy.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.