ramondeklein / nwebdav

.NET implementation of the WebDAV protocol
MIT License
154 stars 42 forks source link

TCP connections maxed out #79

Open fiddyschmitt opened 11 hours ago

fiddyschmitt commented 11 hours ago

Hi Ramon,

It appears that nwebdav can exhaust the ephemeral port range when handling thousands of requests.

I'm using nwebdav to host 300,000 files in 100,000 folders.

In Windows 10, I can use File Explorer to inspect the files: \\localhost@5000\DavWWWRoot\

I use a separate program (FileLocator Pro) to search through that folder. It uses 10 threads to search in parallel, and most of the time it's just retrieving file attributes. nwebdav handles the requests well for about 10-15 minutes, but then FileLocator Pro starts reporting issues with retrieving attributes.

It appears that all the ephemeral ports are in use. Running this Powershell command shows that nwebdav has 10's of thousands of established TCP connections.

Get-NetTCPConnection | Group-Object -Property State, OwningProcess | Select -Property Count, Name, @{Name="ProcessName";Expression={(Get-Process -PID ($_.Name.Split(',')[-1].Trim(' '))).Name}}, Group | Sort Count -Descending | Select-Object -First 10

I fixed the issue by closing the connection after handling the request.

var handled = await handler.HandleRequestAsync(context).ConfigureAwait(false);

if (handled)
{
    //Address port exhaustion of the ephemeral ports.
    context.Connection.RequestClose();
    return;
}

Is this the correct thing to do, or is there a better way?

Thank you, Fidel

ramondeklein commented 10 hours ago

Most HTTP implementations leave the underlying TCP connection open for a while. This is called HTTP keep-alive and prevents that each request has to go through the TCP and TLS handshake. So generally closing the connection is a bad idea and that's why the default in HTTP 1.1 changed to make keep-alive the default instead of explicitly asking for it. If you close down the connection like this, you're essentially going back to HTTP 1.0 behaviour, so that's not something that I would take a PR for.

Does FileLocator Pro have support for WebDAV or will it just use the underlying Windows WebDAV client driver? It looks like either one of them is not closing the connections properly. You should be able to see it (it's easier to use two machines) if it maintains the connection too. It's the client's responsibility to close down the connection.

I would rather check why clients don't close the connection. Connection re-use is a good thing, but if the client doesn't re-use it, but keeps hanging on to the connection then something is wrong. You may want to check if https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/options?view=aspnetcore-9.0 may provide some options to reduce the amount of connections (server-side), so you'll hit the limits earlier. Unfortunately, Kestrel doesn't have options to limit the number of client connections per client IP.

I don't think this a server-side issue, but a client-side issue.

fiddyschmitt commented 9 hours ago

Thanks Ramon,

FileLocator Pro treats it as a normal drive, meaning it's the Windows WebDAV Client making the connection. Indeed the Get-NetTCPConnection command shows an equal number of TCP connections between svhost.exe & my nwebdav app.

Even with closing the TCP connection, my nwebdav app takes 1 ms to respond so I'm happy with the workaround.

Thanks, Fidel