dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.38k stars 9.99k forks source link

Is it possible to limit file upload speed? #45298

Open pedoc opened 1 year ago

pedoc commented 1 year ago

Is there an existing issue for this?

Is your feature request related to a problem? Please describe the problem.

In some scenarios, we need to limit the file upload speed so as not to affect other services, and reduce the overall load of the machine (cpu, network disk, etc.), is there currently an out-of-the-box function or support for custom implementation?

Any help would be greatly appreciated.

Describe the solution you'd like

It would be nice if the framework provided easy configuration options to achieve this goal.

Additional context

No response

davidfowl commented 1 year ago

@BrennanConroy seems like a job for a rate limiting stream implementation?

Tratcher commented 1 year ago

The way networking works there are limits to how much you can enforce this on the server side. Even if you're not consuming the data the client can keep sending until it fills up ever network buffer between you and them. If you control the client application, then you can implement far more consistent handling from that end.

adityamandaleeka commented 1 year ago

Triage: this would make an interesting sample that we could add to our docs (showing both upload speed limiting and download speed limiting)

If we got enough demand for this we may consider adding an in-box feature to support this.

ghost commented 1 year ago

Thanks for contacting us.

We're moving this issue to the .NET 8 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

pedoc commented 1 year ago

If you control the client application

This is unlikely, we can't control every browser client and make them upload at 10KB/s, as far as I know it can't be done. Furthermore, even if all clients can be controlled, their cumulative bandwidth may still exceed the limit, and it is difficult to calculate the limit for each client when the number of clients is uncertain. So throttling from the server side is the only option.

pedoc commented 1 year ago

If we got enough demand for this we may consider adding an in-box feature to support this.

As far as I know, uploading or downloading can be speed-limited (Qos) by adjusting the "TCP windowing". Of course, this requires the framework to expose the Socket used in the underlying communication in order to control it. Is this currently possible?

The following scenarios will require speed limit support

  1. To deploy multiple API services on a single machine, we need to limit the upload and download speed of each service in order to balance the overall load of the machine
  2. Reduce the pressure on network bandwidth, especially cloud services that are billed according to usage. At the same time, it will also enhance service reliability and avoid network jitter.
  3. Expand the business so that different users have different upload and download speeds, for example, VIP users can have faster speeds.

In addition, including the Spring framework of java, it seems that there is no out-of-the-box support for this function, and it will be more advantageous if it is supported. 😄

gitlsl commented 1 year ago

+1, it is a nice feature support upload speed limiting and download speed limiting

Tratcher commented 1 year ago

Note capping download speeds would be much easier, the server controls when and how much data is sent. It should be possible to intercept response body writes with a middleware and Stream shim, and to add delays if the data rate is too fast.

david-acker commented 1 year ago

I started putting together a rough sample for capping download speeds. It includes a custom middleware that wraps the response body stream in a ThrottledStream, which intercepts writes to the stream and adds delays to ensure the download rate stays below the specified limit.

Right now I just have this in a personal repo here david-acker/ThrottlingSample, but I can put up a PR once I polish this a bit. Would this sample live in this repo or in dotnet/AspNetCore.Docs or dotnet/AspNetCore.Docs.Samples?

Edit: Still requires some fixes to address the issue mentioned below.

mitchdenny commented 1 year ago

For the upload scenario, it seems to me that the cure is worse than the disease when it comes to upload throttling. Clients will upload as fast as they can and at the layer that ASP.NET works at you won't really be able to exert any control over the rate at which data is arriving on the box. Once you've got the data on the box, its generally best to store it where you need to store it and free up the resources for another request.

Coming back to the original reason for the requestion:

In some scenarios, we need to limit the file upload speed so as not to affect other services, and reduce the overall load of the machine (cpu, network disk, etc.), is there currently an out-of-the-box function or support for custom implementation?

You've got no real control over network. It will likely cost you more in CPU you manage the throttling, and often disk is actually some other machine on the network (unless the files are quite large and need to be staged on disk before uploading).

Tratcher commented 1 year ago

@mitchdenny, there is some ability to push back on client uploads by simply limiting how quickly you consume the request body. Yes there are several intermediate buffers, but once they're full TCP will start telling the client to back off. Limiting consumption speed will also limit/spread the CPU used to process the request.

Tratcher commented 1 year ago

@david-acker, thanks for the sample. I think the throttling methods are incomplete, there's no division to normalize between bytes written and time elapsed. E.g. if the producer takes more than a second to write _maxBytesPerSecond, or the consumer takes more than a second to read it, then _lastWriteTime and _bytesWritten will never be reset and elapsed will be stuck at greater than one second. https://github.com/david-acker/ThrottlingSample/blob/fd19556720c576e2fafe8756ae940a7020ebdc77/src/Middleware/ThrottledStream.cs#L109-L139

davidfowl commented 1 year ago

@mitchdenny, there is some ability to push back on client uploads by simply limiting how quickly you consume the request body. Yes there are several intermediate buffers, but once they're full TCP will start telling the client to back off. Limiting consumption speed will also limit/spread the CPU used to process the request.

In theory but I'd be surprised if you could do much in practice that would make a different in the application layer.

david-acker commented 1 year ago

@Tratcher Finally got around to fixing the normalization issue with bytes written and time elapsed in the sample. I also added some documentation and usage examples to the README.

Sample: david-acker/ThrottlingSample

smatsson commented 1 year ago

FWIW I put together a POC for limiting upload speed for tusdotnet back in 2020. See the issue linked by egbakou. The POC worked pretty well when tested on an Azure app service and uploading using Chrome. The idea was the same as @david-acker has here with a throttled stream but applied to the request body stream instead of the response stream.

https://github.com/tusdotnet/tusdotnet/issues/78#issuecomment-577726013