ruby-on-railssessioncookiesfile-uploadsession-cookies

Strange Cookie Overflow error in Ruby on Rails application


I have a Ruby on Rails application with which wish to import a user defined CSV file, manipulate some aspects of that file, and then use it to create objects in my database. My controller action looks something like this:

def validate_annual_sales
    uploaded_file = params[:client][:annual_sales_file]
    filename = filename_with_timestamp(uploaded_file.original_filename)
    file_path = Rails.root.join('files', 'uploads', filename)
    File.open(file_path, 'wb') { |file| file.write(uploaded_file.read) }

    @fyear = 2024
    @season_desc = "Season 2024"

    handler = SpektrixReportHandler.new(@client.id, file_path.to_s, @fyear, @season_desc)
    @validation_errors = handler.validate_csv

    unless @validation_errors.any?
      # perform import
      handler.translate_csv_to_data

      File.delete(file_path) if File.exist?(file_path)
      redirect_to historicals_client_url(@client), notice: 'Annual Sales Report information successfully imported.'
    else
    end
  rescue => ex
    AppSignaler.set_client_error(ex, @client.try(:id))
    File.delete(file_path) if File.exist?(file_path)
    redirect_to upload_annual_sales_client_url(@client), alert: ex.message
  end

The annual_sales_file parameter is a file_field in the view that we use in other portions of the application for this same purpose, which work fine. Similarly with the opening and reading the file.

The relevant portions of the service object I have processing this situation are:

def validate_csv
    json_schema_validator = mapping.dig(:schema_validator)

    errors = []

    get_parsed_csv.each.with_index(1) do |row, index|
      data = row.to_hash

      json_errors = JSON::Validator.fully_validate(json_schema_validator, data.to_json, errors_as_objects: true)

      if json_errors.empty?
        # don't try to validate the values if the basic json validations failed
        csv_validator = CsvValidator.validate(data, mapping)

        unless csv_validator[:errors].try(:empty?)
          errors << ["Error processing row #{index}:"] + csv_validator[:errors]
        end
      else
        cleaned_errors = clean_json_errors(json_errors)

        errors << ["Error processing row #{index}:"] + cleaned_errors
      end
    end

    errors
  end
def translate_csv_to_data
    blank_values = [:fiscal_year, :season_description].select{ |attr_name| self.send(attr_name).blank? }.map{|value| value.to_s.titleize}
    if blank_values.any?
      raise "Missing #{blank_values.to_sentence}"
    end

    get_parsed_csv.each.with_index(1) do |row, index|
      ap "row #{index}"
      hashed_row = row.to_hash
      row = parser_factory.parse(hashed_row, mapping)

      row[:client_id] = client.id
      row[:fyear] = fiscal_year
      row[:season_desc] = season_description
      row[:sin_amount] = row.delete(:sin_amt)
      row[:pkg_amount] = row.delete(:pkg_amt)

      HistoricalDaily.create!(row)
    end
  ensure
    File.delete(file_path) if File.exist?(file_path)
  end
private

  # from EmailScrapperLogger#get_parsed_csv
  def get_parsed_csv
    CSV.parse(File.read(csv, encoding: 'bom|utf-8'), headers: true, force_quotes: true, header_converters: :symbol)
  end

However, when I actually choose a file and run this through, I get a ActionDispatch::Cookies::CookieOverflow exception raised that crashes the page.

As far as I can tell, I'm not manipulating the given file at all, so I am very confused as to where the cookies are even being changed. I have tried loading everything into memory as a hash and manually creating the objects from that data. I have tried loading it in as a zip file, extracting the csv file into another directory entirely and going from there. That's how we do it elsewhere, and it seems to work fine there. Not sure what I'm doing differently here.

Please help!


Solution

  • I did finally figure this out. As mentioned in the comment, it was indeed something completely different that didn't have to do with manipulating the cookies or even session directly.

    What happened was that an error occurred elsewhere. One of those silly NoMethodErrors that are pretty obvious once you see the message and the backtrace. The difficulty in this case was that there was a line of code in the controller which printed out an error message to the browser. It put that message in the flash object, which is part of the session object, which counts as a cookie. So, when that error was thrown, and the naive error.message was put in the flash object, a serialized version of a model object was put in there too, quickly overflowing the cookie limit. Very indirect and very subtle.

    In retrospect, I included a hint to the answer in my question with this line of controller code:

    redirect_to upload_annual_sales_client_url(@client), alert: ex.message
    

    Pretty innocent at first glance, and the danger and implications were not noticed by me or the person who kindly took the time to comment here (I appreciate you!). Hopefully someone else will have this problem and find this helpful!