Чтение данных CGI POST наиболее эффективным способом - PullRequest
1 голос
/ 14 июля 2010

Мне очень нужен способ копаться в потенциально огромных объемах данных POST, предоставленных CGI.

С чтением данных GET это не составляет большого труда, так как я могу просто повторно запрашивать переменную окружения QUERY_STRING так часто, как я хочу, но с данными POST, которые передаются через stdin. Я могу прочитать его только один раз и хранить где-то.

Мой текущий метод состоит в том, чтобы прочитать всю кучу данных POST во временном файле, который будет удален при выходе из программы, и просканировать его, чтобы найти ключи, которые я хочу найти. В подходе разбора GET я мог бы просто использовать strtok () над QUERY_STRING, потому что данные GET имеют довольно низкие пределы, поэтому их можно извлекать из оперативной памяти, но данные POST могут быть любыми, от пустых до «name = Bob» и до фильма 4 Гигабайта файл.

Итак, вот мой текущий подход:

int get_post_data(const char *s_key, char *target, size_t target_size)
{
   FILE *tmp;
   int ret_val = -1;

   /* postdata_temp = global variable containing the temporary file name */
   if ((tmp = fopen(postdata_tempfile, "r")) == NULL)
      return -1;
   else
   {
      char *buffer = NULL;
      char *temp_buffer = NULL;
      int buffer_size;
      int i;

      if ((buffer = malloc(BUFFER_SIZE)) == NULL)
         return -1;

      memset(buffer, 0, sizeof(BUFFER_SIZE));
      buffer_size = BUFFER_SIZE;

      for (i = 0;; i++)
      {
         int c = fgetc(tmp);

         if ((c == '&') || feof(tmp))
         {
            char *key = strtok(buffer, "=");
            char *val = strtok(NULL, "");            

            if (key)
            {
               if (strcmp(s_key, key) == 0)
               {
                  if (val)
                  {
                     strncpy(target, val, target_size);
                     ret_val = strlen(val);
                  }
                  else
                  {
                     target = NULL;
                     ret_val = 0;
                  }

                  break;
               }
            }

            if (feof(tmp))
               break;

            memset(buffer, 0, buffer_size);
            i = -1; /* because it will be 0 when the fgetc() is called the 
                     * next time */
         }
         else
         {
            if (!(i < buffer_size))
            {
               buffer_size += BUFFER_SIZE;

               if ((temp_buffer = realloc(buffer, buffer_size)) == NULL)
               {
                  free(temp_buffer);
                  free(buffer);
                  target = NULL;

                  return -1;
               }
               else
                  buffer = temp_buffer;
            }

            buffer[i] = c;
         }

      }

      free(buffer);

      // printf("Final buffer size: %d<br />\n", buffer_size);
   }

   fclose(tmp);

   return ret_val;
}

Это работает, я могу позвонить get_post_data("user_password", pass, sizeof(pass));, проверить возвращаемое значение (<0 = ошибка, = 0 = ключ существует, но значение равно NULL,> 0 = длина данных), но кажется, что он слишком толстый. Я имею в виду ... огромные накладные расходы на ввод-вывод для каждого параметра POST, который я хочу найти, просто чтобы в моей оперативной памяти не было всей строки для загружаемых потенциально больших файлов?

Что думает Stackoverflow?

Ответы [ 2 ]

1 голос
/ 14 июля 2010

Если вы хотите избежать загрузки большого файла в ОЗУ, вы можете использовать файл с отображением памяти - не переносимый, но это правильный способ сделать это.Если ваша платформа POSIX, вы могли бы использовать mmap() для этого.

Кстати, я не полностью прочитал или протестировал ваш код, но мне было бы интересно, правильно ли использовать strtok(), потому что это правильноуничтожает данные по мере их поступления.Я также хотел бы спросить об использовании str...() функций, если ваши данные могут быть двоичным файлом, но я не знаю, как работает CGI-часть, так что вы можете быть прямо там.

0 голосов
/ 14 июля 2010

Я думаю, что было бы проще просто отклонить запросы POST, превышающие установленный предел, скажем, 2 МБ.

Таким образом:

  • У вас есть блок данных контролируемого размера для работы.
  • Вы предотвращаете вредоносные POST-запросы объемом 4 ГБ.
...