Project

General

Profile

Limit number of concurrent downloads

Added by Richard Rauch over 9 years ago

Because of 2 reasons I want to limit the number of concurrent downloads

A: I have frequently DoS attacks on my server, which are initiating a storm of download requests on huge files
B: when a user wants to download a huge file, it needs time and it seems, that the server is not responding. Then sometimes a user is initating the download request again several times

In both cases there are concurrent download requests, which are causing huge memory consumption on the server. When there is no further memory, the Redmine processes are crashing and the server stops.

Therefore I am trying to find anyhow a solutions.
My idea is now to change the attachments controller behaviour.

!!!Please excuse, I do not have server programming experience, no Ruby, no Rails experience!!!

I tried following changes in attachments_controller.rb:

  def download
    if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
      @attachment.increment_download
    end

    if stale?(:etag => @attachment.digest)

    if User.current.dnld_curr > 0 
        render_403 :message => :'Please finish previous download first'
    else
        temp = User.current.dnld_curr;
        temp = temp +1;
        User.current.update_column(:dnld_curr,temp)

        temp = User.current.dnld_cnt;
        temp = temp +1;
        User.current.update_column(:dnld_cnt,temp)

          # images are sent inline
          send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
                                          :type => detect_content_type(@attachment),
                                          :disposition => (@attachment.image? ? 'inline' : 'attachment')

        temp = User.current.dnld_curr;
        if temp > 0
            temp = temp  - 1;
            User.current.update_column(:dnld_curr,temp)
        end

    end

    end
  end

My idea is to count the concurrent downloads and restrict it e.g. to one.
In principle my code works (I added the rows to the database). The dnld_curr counter is incrementing and decrementing, BUT the value is decremented immediately, before the download is finished. I cannot detect the end of the download.
Has anybody an idea, how to improve my code?

I know, it is very dirty to patch the code directly. But I need a fast solution. My server is down nearby every morning because of some chinese hackers.

Many Thanks

Richard


Replies (1)

RE: Limit number of concurrent downloads - Added by Martin Denizet (redmine.org team member) over 9 years ago

Hello Richard,
I don't see a good way to implement this in Rails because from what I know there is no callback to send_file (to know when the download is completed, it's an async method).
Maybe you could be using time to limit downloads:

def download
    if @attachment.container.is_a?(Version) || @attachment.container.is_a?(Project)
      @attachment.increment_download
    end

    if stale?(:etag => @attachment.digest)

    downloads_blocked_until = User.current.last_download + 10.minutes
    if downloads_blocked_until < DateTime.now 
        render_403 :message => :'One download allowed every 10 minutes'
    else
        User.current.update_column(:last_download ,DateTime.now)

          # images are sent inline
          send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename),
                                          :type => detect_content_type(@attachment),
                                          :disposition => (@attachment.image? ? 'inline' : 'attachment')

    end

    end
  end

Maybe the solution for you would be to make a stream to have more control on the download.
I can't think of a way to implement this on the web server itself.
Cheers,

    (1-1/1)