Помогите с анализом http запроса

Тема в разделе "Arduino & Shields", создана пользователем Иван С, 27 янв 2013.

  1. Иван С

    Иван С Гик

    Добрый день. Идея следующая) контролер смотрит в инет и позволяет удалённо (через web интерфейс) включать компы.

    Как работает http протокол (его хедеры) я знаю.

    Вот код разбора запроса:
    Код (Text):
    void query_analysis(EthernetClient client){
      String first_line = "";
      String query = "";
      String location = "";
      int cookie_index = 0;
      while(true){
        char r = client.read();
        if(r != '\r' && client.available() > 0){
          first_line += r;
        }
        else
          break;
      }
      if(first_line.indexOf("GET") == 0)
        http_query_type = "GET";
      else if(first_line.indexOf("POST") == 0)
        http_query_type = "POST";
     
      if(http_query_type == "GET" || http_query_type == "POST"){
        location = first_line.substring(http_query_type.length() + 1, first_line.lastIndexOf(" "));
     
      [SIZE=5] while(client.available()>0)  query += String(char(client.read()));[/SIZE]
     
        Serial.print(query);
        cookie_index = query.indexOf("Cookie:");
     
      }
      else{
        //не поддерживаемый тип запроса
      }
     
      client.println("HTTP/1.1 200 OK");
      client.println();
      client.print(location);
      client.print("index of cookie: " + String(cookie_index));
      client.print(query);
      first_line = "";
      query = "";
      location = "";
      cookie_index = 0;
      client.stop();
    }
    Проблема в строчке, к которой я пытался применить bb-код))
    Если её заменить на:
    Код (Text):

    while(client.available()>0)  Serial.print(String(char(client.read())));
     
    То в сериал выводится весь запрос браузера (т.е. выходит что запросы полностью доходят до дуни), однако если оставить как есть и в сериал кинуть "query", то в ней не окажется доброй половины запроса! Причём, вся эта катавасия начинается со второго запроса (после включения), первый же и так и так приходит полностью.
     
  2. nailxx

    nailxx Официальный Нерд Администратор

    Полагаю, что ваш оригинальный код значительно быстрее, чем побуквенный вывод в Serial (не нужно на каждой итерации взаимодействовать с медленным IO), поэтому в оригинальном коде вы успеваете вычитывать всё полностью из первой порции данных, serial.available() начинает говорить о пустом буфере, вы выходите из цикла и понеслось…

    Порций может быть много и их будет много. Вам нужно ориентироваться не на serial.available(), а на логический конец запроса: считывать Content-Length из заголовка и затем считывать именно такой объём payload'а в запросе.
     
  3. Иван С

    Иван С Гик

    Точно, всегда на этом available = 0 попадаюсь)))
    А про content-length я совсем забыл, спасибо. Только вот незадача, ни один браузер не передаёт этот параметр(
    Придётся читать/ждать байты пока не придёт конец запроса (пустая строка).
     
  4. Unixon

    Unixon Оракул Модератор

    Или пока таймаут не выйдет...
     
  5. Иван С

    Иван С Гик

    Как ни странно, но новый вариант выдаёт тот же результат :confused:

    Код (Text):
    while(true){
         
          if(client.available()>0){
            now_byte = char(client.read());
            if(now_byte == '\r' && last_byte == '\n') break;
            query += now_byte;
            last_byte = now_byte;
          }
          else {
            Serial.println("delaing");
            delay(30);
          }
         
        }
    Причём "delaing" ни разу не вывелся.

    Вот что осталось в сериал:

    Первый запрос:
    Host: ---------------------
    Connection: keep-alive
    Cache-Control: max-age=0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17
    Accept-Encoding: gzip,deflate,sdch
    Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4
    Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.3

    Второй запрос (кстати, этим запросом хром пытается загрузить favicon):
    Host: --------------------
    Connection: keep-alive
    Accept: */*
    User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.17 (KHTML, like Gecko) Ch
     
  6. nailxx

    nailxx Официальный Нерд Администратор

    Теория номер два. Вы имеете дело с фрагментацией памяти от использования класса String и аккурат на момент, когда «Ethernet больше ничего не получает» на самом деле МК падает в segfault в результате out of memory.

    Рекомендация: выделить заранее буфер побольше под получение запросов и использовать низкоуровневые функции типа `strcat` для его наполнения. То есть не использовать `String` вовсе, использовать `char*`.
     
  7. Unixon

    Unixon Оракул Модератор

    А нужно ли целиком хранить в памяти весь запрос? Может из него на ходу выцарапывать нужные поля...
     
    nailxx нравится это.
  8. Иван С

    Иван С Гик

    Ух ты, не знал у стрингов всё так сложно))

    Согласен с Unixon, так и сделал...

    Вот код, если кому интересно:
    Код (Text):
    while(q_param_host == "" && q_param_cookie == ""){
     
          if(client.available() > 0){
            tmp_char = client.read();
            if(!key_is_set){
              if(tmp_char != ':') key += tmp_char;
              else key_is_set = true;
            }
            else{
              if(tmp_char != '\r') value += tmp_char;
              else{
                client.read(); // Избавляемся от ненужного нам символа "\n"
                key_is_set = false;
             
                if(key == "Host") q_param_host = value;
                if(key == "Cookie") q_param_cookie = value;
             
                key = "";
                value = "";
              }
            }
          }
          else delay(30);
     
        }
    Time out потом приделаю, как и все обработчики ошибок)
     
    nailxx нравится это.