Skip to content

HttpWebRequest.GetRequestStreamAsync doesn't open the connection to the server - we always buffer the content #18632

Closed

Description

@stephentoub

The corefx implementation of HttpWebRequest.GetRequestStreamAsync effectively just returns an in-memory stream. The connection to the server isn't made and the data isn't uploaded until Stream.Dispose is called on the that Stream. This is problematic for several reasons, including that the actual asynchronous operation ends up happening in the synchronous Dispose call on the stream, and deadlocks can easily happen if code assumes that the server will respond once the supplied data has been written (but before Dispose is called).

Activity

  1. davidsh commented on Sep 19, 2016

    @davidsh
    Contributor

    This is actually the same design as .NET Framework (Desktop). The only difference is that in Desktop, there is a property on HttpWebRequest (.AllowWriteStreamBuffering) which can toggle this behavior.

    Eventually, when we add all the API surface back, we will have to solve it then. The difficulty at this point, though, is that the HttpWebRequest API surface is built on top of HttpClient and it has a different data model for handling when data actually starts going to a server.

  2. davidsh commented on Sep 19, 2016

    @davidsh
    Contributor

    I am removing the 'bug' label because it isn't a bug, per se. It is the proper behavior if the .AllowWriteStreamBuffering option is TRUE, which it always is right now in the current implementation.

  3. stephentoub commented on Sep 19, 2016

    @stephentoub
    MemberAuthor

    Ok. Thanks, David.

  4. Im5tu commented on Sep 19, 2016

    @Im5tu

    Will you actually come around to fixing these sorts of things though considering how it could break applications? I'm guessing I'm saying why not fix it now?

  5. karelz commented on Mar 2, 2017

    @karelz
    Member

    We should evaluate it for 2.0 based on usage data of AllowWriteStreamBuffering.

  6. self-assigned this
    on Mar 2, 2017
  7. karelz commented on Mar 3, 2017

    @karelz
    Member

    @weshaggard can you please help us evaluate usage of AllowWriteStreamBuffering?

  8. karelz commented on Mar 3, 2017

    @karelz
    Member

    Usage data is on apisof.net (thanks @stephentoub for pointing it out): https://apisof.net/catalog/System.Net.HttpWebRequest.AllowWriteStreamBuffering

    • nuget org - 1.1%
    • API Port Telemetry - 3.1%

    That seems above the threshold we went after in .NET Standard 2.0 - @danmosemsft, @weshaggard what do you think?

  9. karelz commented on Mar 3, 2017

    @karelz
    Member

    Our threshold was around 0.1%, so this is clearly in from that point of view.

    @davidsh @CIPop - can you guys please cost it roughly? (you said it is complex/costly), we will have to roll it up to our management chains for 2.0

  10. removed their assignment
    on Mar 3, 2017
  11. weshaggard commented on Mar 3, 2017

    @weshaggard
    Member

    @karelz I'm not sure I understand the question. AllowWriteStreamBuffering is part of .NET Standard 2.0 so the API has been added back, is the question whether or not we should fix the implementation to do the buffering correctly again? If so I would yes we probably should in order to be compatible as that flag is set in the default set of flags we use (https://github.com/dotnet/corefx/blob/ec2a6190efa743ab600317f44d757433e44e859b/src/System.Net.Requests/src/System/Net/HttpWebRequest.cs#L84).

  12. karelz commented on Mar 3, 2017

    @karelz
    Member

    @weshaggard the buffering is hidden under AllowWriteStreamBuffering = true. It is not (EDITED) the default.
    We decided to leave it in 2.0 for now and figure out the cost.

  13. karelz commented on Mar 3, 2017

    @karelz
    Member

    I appologize (@weshaggard corrected me) - the flag is set in Default on .NET Core.
    @davidsh @CIPop does Desktop have the same Default value with the flag set?
    When the flag set, what does the buffering mean? Is the in-memory stream considered the buffer? (on Desktop and on .NET Core)

  14. davidsh commented on Mar 4, 2017

    @davidsh
    Contributor

    When the flag set, what does the buffering mean? Is the in-memory stream considered the buffer? (on Desktop and on .NET Core)

    The default value is TRUE (which works). And yes, "buffering" means that the data is held in a MemoryStream. The dev gets the request stream and writes to it. But those writes are not sent on the wire but fill up a MemoryStream. Later, when the request is sent to the HTTP server, the MemoryStream is replayed (usually by grabbing the stored buffer) and sent on the wire.

    The Desktop and .NET Core both use a MemoryStream for the buffering.

  15. karelz commented on Mar 4, 2017

    @karelz
    Member

    Thanks!
    @weshaggard I noticed that you looked at (private) enum value instead of on the property with the same name AllowWriteStreamBuffering

    I think our usage analysis above is valid. Assuming people will (mostly) use the property only to set it to non-default value, which is false

  16. 1 remaining item

  17. vitek-karas commented on Apr 20, 2017

    @vitek-karas
    Member

    Per the code at https://github.com/dotnet/corefx/blob/master/src/System.Net.Requests/src/System/Net/HttpWebRequest.cs#L137
    the value of the AllowReadStream property is now always false... not sure if that is intentional, but the above discussion says otherwise (that we always buffer and thus the property should always be true).

  18. davidsh commented on Apr 20, 2017

    @davidsh
    Contributor

    Per the code at https://github.com/dotnet/corefx/blob/master/src/System.Net.Requests/src/System/Net/HttpWebRequest.cs#L137
    the value of the AllowReadStream property is now always false... not sure if that is intentional, but the above discussion says otherwise (that we always buffer and thus the property should always be true).

    Actually, I should not have linked these issues. The new issue you entered dotnet/corefx#18668 has nothing to do with this issue. Let's continue discussion on that issue instead.

  19. karelz commented on Apr 26, 2017

    @karelz
    Member

    Triage discussion: While important, this is P2 at this moment and won't make it into 2.0, unless we get additional feedback from customers.

  20. jokker87 commented on Mar 7, 2019

    @jokker87

    Do i get this right, even when I set AllowWriteStreamBuffering to false, in .net core, when i call GetRequestStream, the stream always gets buffered in a MemoryStream till the stream is closed? So we have a memory issue when uploading big files to an endpoint?

  21. davidsh commented on Mar 7, 2019

    @davidsh
    Contributor

    Do i get this right, even when I set AllowWriteStreamBuffering to false, in .net core, when i call GetRequestStream, the stream always gets buffered in a MemoryStream till the stream is closed? So we have a memory issue when uploading big files to an endpoint?

    Yes. We recommend that you use HttpClient APIs and not the legacy HttpWebRequest APIs. HttpClient has APIs that can support the similar scenario where uploading the request body from a large source (i.e. filestream) will not be completely buffered into memory first. Instead, it will read directly from the filestream and write directly to the network.

  22. karelz commented on Oct 2, 2019

    @karelz
    Member

    Triage: Related to @alnikola's effort in HttpWebRequest. This may be non-trivial.

  23. transferred this issue fromdotnet/corefxon Jan 31, 2020
  24. added this to the Future milestone on Jan 31, 2020
  25. liveans commented on Apr 8, 2024

    @liveans
    Contributor

    Fixed by #95001

  26. modified the milestones: Future, 9.0.0 on Apr 8, 2024
  27. locked and limited conversation to collaborators on May 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area-System.NetenhancementProduct code improvement that does NOT require public API changes/additions

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions