Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Socket.SendFile should fail if when Socket.Blocking = false #47287

Open
geoffkizer opened this issue Jan 21, 2021 · 14 comments
Open

Socket.SendFile should fail if when Socket.Blocking = false #47287

geoffkizer opened this issue Jan 21, 2021 · 14 comments
Labels
area-System.Net.Sockets enhancement Product code improvement that does NOT require public API changes/additions good first issue Issue should be easy to implement, good for first-time contributors help wanted [up-for-grabs] Good issue for external contributors
Milestone

Comments

@geoffkizer
Copy link
Contributor

geoffkizer commented Jan 21, 2021

If you put a socket into nonblocking mode (i.e. set Socket.Blocking=false) and then call synchronous SendFile, you get unexpected behavior.

On Windows, the call will block but will ultimately succeed.
(EDIT: See my comment below for clarification.) On Linux, you will likely end up with an EWOULDBLOCK result, but this isn't not reliable; sometimes the call will succeed, sometimes it will send partial data, etc.

SendFile is not intended to be used in nonblocking mode. If a user attempts this we should throw InvalidOperationException.

More details here: #47230 (review)

@ghost
Copy link

ghost commented Jan 21, 2021

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Issue Details

If you put a socket into nonblocking mode (i.e. set Socket.Blocking=false) and then call synchronous SendFile, you get unexpected behavior.

On Windows, the call will block but will ultimately succeed.
On Linux, you will likely end up with an EWOULDBLOCK result, but this isn't not reliable; sometimes the call will succeed, sometimes it will send partial data, etc.

SendFile is not intended to be used in nonblocking mode. If a user attempts this we should throw InvalidOperationException.

More details here: #47230 (review)

Author: geoffkizer
Assignees: -
Labels:

area-System.Net.Sockets

Milestone: -

@karelz
Copy link
Member

karelz commented Feb 4, 2021

Triage: We should throw as suggested above. Should be fairly straightforward to do.

@karelz karelz added good first issue Issue should be easy to implement, good for first-time contributors enhancement Product code improvement that does NOT require public API changes/additions help wanted [up-for-grabs] Good issue for external contributors and removed untriaged New issue has not been triaged by the area owner labels Feb 4, 2021
@karelz karelz added this to the Future milestone Feb 4, 2021
@jaymin93
Copy link
Contributor

jaymin93 commented Feb 5, 2021

Just to understand it better this bug fix would be checking for blocking =false in every synchronous method and throw in case of it is false need to throw invalid operation exception , is my understanding towards this bug is correct?

@tmds
Copy link
Member

tmds commented Feb 5, 2021

On Linux, you will likely end up with an EWOULDBLOCK result, but this isn't not reliable; sometimes the call will succeed, sometimes it will send partial data, etc.

On Linux this should work.

sendfile works the same as sendmsg: you need to call it again until everything was sent, and in case of EAGAIN/EWOULDBLOCK wait for poll.

TryCompleteSendFile is missing some logic that calls it again on partial sends, like what we have in TryCompleteSendTo.

@geoffkizer
Copy link
Contributor Author

geoffkizer commented Feb 6, 2021

Yeah, my comment above is wrong/confusing.

I should have said, on Linux, the kernel will buffer as much of the file as it can/wants to, and then tell you how much it sent, just like any other send operation. This will likely result in EWOULDBLOCK if you need to call sendfile multiple times, but that's not the real problem.

The problem is, Socket.SendFile doesn't return a value telling you how much it actually sent, like Socket.Send does. So there's no way for the user to know how much of the file was actually sent, which makes the API pretty useless.

We could in theory add a new method that returns the bytesSent. But there's no obvious way to make this work on Windows, since Windows doesn't seem to actually respect the non-blocking state of the socket for TransmitFile.

I think the conclusion still stands: we should just fail Socket.SendFile when Socket.Blocking = false.

@geoffkizer
Copy link
Contributor Author

TryCompleteSendFile is missing some logic that calls it again on partial sends, like what we have in TryCompleteSendTo.

In what case in particular? It looks like we are doing the retry logic in SocketAsyncContext.

@geoffkizer
Copy link
Contributor Author

Just to understand it better this bug fix would be checking for blocking =false in every synchronous method and throw in case of it is false need to throw invalid operation exception , is my understanding towards this bug is correct?

No, this is specifically about SendFile.

@jaymin93
Copy link
Contributor

jaymin93 commented Feb 6, 2021

Just to understand it better this bug fix would be checking for blocking =false in every synchronous method and throw in case of it is false need to throw invalid operation exception , is my understanding towards this bug is correct?

No, this is specifically about SendFile.

ok so i need to apply blocking =false check for specific method of send file and this will fix this bug , asking because i want to make pr , so before that i need to be clear about what actually case of bug fix, Thanks

@geoffkizer
Copy link
Contributor Author

SendFile in when Socket.Blocking = false.

@tmds
Copy link
Member

tmds commented Feb 9, 2021

In what case in particular? It looks like we are doing the retry logic in SocketAsyncContext.

We're not looping on partial sends like we do for TryCompleteSendTo until there is nothing more to be sent:

bool isComplete = sent == 0 ||
(buffers == null && count == 0) ||
(buffers != null && bufferIndex == buffers.Count);

@geoffkizer
Copy link
Contributor Author

We are looping in TryCompleteSendFile:

@tmds
Copy link
Member

tmds commented Feb 9, 2021

Ah yes.

Is there a test that occasionally fails on Linux that I can run?

@geoffkizer
Copy link
Contributor Author

I don't think there's an existing test, but there's discussion of what tests we should have here: #47230 (review)

@antonfirsov Did we add a (disabled) test for this?

@vijaya-lakshmi-venkatraman
Copy link

vijaya-lakshmi-venkatraman commented Apr 28, 2021

Hi,
Please check if my below summary of the required changes is right:

  • File : runtime/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs
  • Function : TryCompleteSendFile
  • Changes:
try 
{
     if(Socket.Binding == false)
     {
            sent = SendFile(socket, handle, ref offset, ref count, out errno);
            bytesSent += sent;
      }
}
  1. Is this right?
  2. Is it safe to assume that Socket.Binding is always available? I see that Socket.Binding is mentioned in one of the places
case FIONBIO:
                    // The Windows implementation explicitly throws this exception, so that all
                    // changes to blocking/non-blocking are done via Socket.Blocking.
                    throw new InvalidOperationException(SR.net_sockets_useblocking);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Net.Sockets enhancement Product code improvement that does NOT require public API changes/additions good first issue Issue should be easy to implement, good for first-time contributors help wanted [up-for-grabs] Good issue for external contributors
Projects
None yet
Development

No branches or pull requests

5 participants