Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal static partial class Mswsock
[DllImport(Interop.Libraries.Mswsock, SetLastError = true)]
internal static extern unsafe bool TransmitFile(
SafeHandle socket,
SafeHandle? fileHandle,
IntPtr fileHandle,
int numberOfBytesToWrite,
int numberOfBytesPerSend,
NativeOverlapped* overlapped,
Expand Down
2 changes: 1 addition & 1 deletion src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ public static void Select(System.Collections.IList? checkRead, System.Collection
public int Send(System.ReadOnlySpan<byte> buffer, System.Net.Sockets.SocketFlags socketFlags) { throw null; }
public int Send(System.ReadOnlySpan<byte> buffer, System.Net.Sockets.SocketFlags socketFlags, out System.Net.Sockets.SocketError errorCode) { throw null; }
public bool SendAsync(System.Net.Sockets.SocketAsyncEventArgs e) { throw null; }
public void SendFile(string fileName) { }
public void SendFile(string? fileName) { }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we decided in #32675 (comment) that this should be non-null. Calling SendFile(null) won't do anything, so the assumption is you should pass in a fileName.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are moving this to be nullable, to be consistent, we should annotate the BeginSendFile overload as nullable as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We did, but issuing a warning doesn't actually solve any problems here, and it leads to an inconsistency with the other overload. It's not harmful to pass null, it's always been allowed on .NET Framework, etc., so we may as well gain the consistency, avoid potential noisy warnings, and allow null.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are moving this to be nullable, to be consistent, we should annotate the BeginSendFile overload as nullable as well.

I thought I did. I'll fix it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The docs are also explicit about null being allowed.)

public void SendFile(string? fileName, byte[]? preBuffer, byte[]? postBuffer, System.Net.Sockets.TransmitFileOptions flags) { }
public bool SendPacketsAsync(System.Net.Sockets.SocketAsyncEventArgs e) { throw null; }
public int SendTo(byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,7 @@ public int Send(ReadOnlySpan<byte> buffer, SocketFlags socketFlags, out SocketEr
return bytesTransferred;
}

public void SendFile(string fileName)
public void SendFile(string? fileName)
{
SendFile(fileName, null, null, TransmitFileOptions.UseDefaultWorkerThread);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1809,9 +1809,9 @@ public static SocketError SendAsync(SafeSocketHandle handle, IList<ArraySegment<
}

public static SocketError SendFileAsync(SafeSocketHandle handle, FileStream fileStream, Action<long, SocketError> callback) =>
SendFileAsync(handle, fileStream, 0, (int)fileStream.Length, callback);
SendFileAsync(handle, fileStream, 0, fileStream.Length, callback);

private static SocketError SendFileAsync(SafeSocketHandle handle, FileStream fileStream, long offset, int count, Action<long, SocketError> callback)
private static SocketError SendFileAsync(SafeSocketHandle handle, FileStream fileStream, long offset, long count, Action<long, SocketError> callback)
{
long bytesSent;
SocketError socketError = handle.AsyncContext.SendFileAsync(fileStream.SafeFileHandle, offset, count, out bytesSent, callback);
Expand Down Expand Up @@ -1849,7 +1849,7 @@ public static async void SendPacketsAsync(

var tcs = new TaskCompletionSource<SocketError>();
error = SendFileAsync(socket.InternalSafeHandle, fs, e.OffsetLong,
e.Count > 0 ? e.Count : checked((int)(fs.Length - e.OffsetLong)),
e.Count > 0 ? e.Count : fs.Length - e.OffsetLong,
(transferred, se) =>
{
bytesTransferred += transferred;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1137,10 +1137,27 @@ private static unsafe bool TransmitFileHelper(
transmitFileBuffers.TailLength = postBuffer.Length;
}

bool success = Interop.Mswsock.TransmitFile(socket, fileHandle, 0, 0, overlapped,
needTransmitFileBuffers ? &transmitFileBuffers : null, flags);
bool releaseRef = false;
IntPtr fileHandlePtr = IntPtr.Zero;
try
{
if (fileHandle != null)
{
fileHandle.DangerousAddRef(ref releaseRef);
fileHandlePtr = fileHandle.DangerousGetHandle();
}

return success;
return Interop.Mswsock.TransmitFile(
socket, fileHandlePtr, 0, 0, overlapped,
needTransmitFileBuffers ? &transmitFileBuffers : null, flags);
}
finally
{
if (releaseRef)
{
fileHandle!.DangerousRelease();
}
}
}

public static unsafe SocketError SendFileAsync(SafeSocketHandle handle, FileStream? fileStream, byte[]? preBuffer, byte[]? postBuffer, TransmitFileOptions flags, TransmitFileAsyncResult asyncResult)
Expand Down
102 changes: 102 additions & 0 deletions src/libraries/System.Net.Sockets/tests/FunctionalTests/SendFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,108 @@ public void NotConnected_ThrowsException()
}
}

[Theory]
[InlineData(false, false, false)]
[InlineData(false, false, true)]
[InlineData(false, true, false)]
[InlineData(false, true, true)]
[InlineData(true, false, false)]
[InlineData(true, false, true)]
[InlineData(true, true, false)]
[InlineData(true, true, true)]
public async Task SendFile_NoFile_Succeeds(bool useAsync, bool usePreBuffer, bool usePostBuffer)
{
using var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
using var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.BindToAnonymousPort(IPAddress.Loopback);
listener.Listen(1);

client.Connect(listener.LocalEndPoint);
using Socket server = listener.Accept();

if (useAsync)
{
await Task.Factory.FromAsync<string>(server.BeginSendFile, server.EndSendFile, null, null);
}
else
{
server.SendFile(null);
}
Assert.Equal(0, client.Available);

byte[] preBuffer = usePreBuffer ? new byte[1] : null;
byte[] postBuffer = usePostBuffer ? new byte[1] : null;
int bytesExpected = (usePreBuffer ? 1 : 0) + (usePostBuffer ? 1 : 0);

if (useAsync)
{
await Task.Factory.FromAsync((c, s) => server.BeginSendFile(null, preBuffer, postBuffer, TransmitFileOptions.UseDefaultWorkerThread, c, s), server.EndSendFile, null);
}
else
{
server.SendFile(null, preBuffer, postBuffer, TransmitFileOptions.UseDefaultWorkerThread);
}

byte[] receiveBuffer = new byte[1];
for (int i = 0; i < bytesExpected; i++)
{
Assert.Equal(1, client.Receive(receiveBuffer));
}

Assert.Equal(0, client.Available);
}

[ActiveIssue("https://github.com/dotnet/runtime/issues/42534", TestPlatforms.Windows)]
[OuterLoop("Creates and sends a file several gigabytes long")]
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task SendFile_GreaterThan2GBFile_SendsAllBytes(bool useAsync)
{
const long FileLength = 100L + int.MaxValue;

string tmpFile = GetTestFilePath();
using (FileStream fs = File.Create(tmpFile))
{
fs.SetLength(FileLength);
}

using var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
using var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.BindToAnonymousPort(IPAddress.Loopback);
listener.Listen(1);

client.Connect(listener.LocalEndPoint);
using Socket server = listener.Accept();

await new Task[]
{
Task.Run(async () =>
{
if (useAsync)
{
await Task.Factory.FromAsync(server.BeginSendFile, server.EndSendFile, tmpFile, null);
}
else
{
server.SendFile(tmpFile);
}
}),
Task.Run(() =>
{
byte[] buffer = new byte[100_000];
long count = 0;
while (count < FileLength)
{
int received = client.Receive(buffer);
Assert.NotEqual(0, received);
count += received;
}
Assert.Equal(0, client.Available);
})
}.WhenAllOrAnyFailed();
}

[OuterLoop]
[Theory]
[MemberData(nameof(SendFileSync_MemberData))]
Expand Down